Manifest for Office Add-In not pulling for some reason, stiil debugging.

This commit is contained in:
2025-12-05 15:53:11 -06:00
parent 69ced6d180
commit 40bcd939ff
30 changed files with 22745 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+34
View File
@@ -0,0 +1,34 @@
# dependencies (bun install)
node_modules
# output
out
dist
*.tgz
# code coverage
coverage
*.lcov
# logs
logs
_.log
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# caches
.eslintcache
.cache
*.tsbuildinfo
# IntelliJ based IDEs
.idea
# Finder (MacOS) folder config
.DS_Store
@@ -0,0 +1,8 @@
{
"plugins": [
"office-addins"
],
"extends": [
"plugin:office-addins/recommended"
]
}
+14
View File
@@ -0,0 +1,14 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"ms-edgedevtools.vscode-edge-devtools",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"msoffice.microsoft-office-add-in-debugger"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": []
}
+26
View File
@@ -0,0 +1,26 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Excel Desktop (Edge Chromium)",
"type": "msedge",
"request": "attach",
"port": 9229,
"timeout": 600000,
"webRoot": "${workspaceRoot}",
"preLaunchTask": "Debug: Excel Desktop",
"postDebugTask": "Stop Debug"
},
{
"name": "Excel Desktop (Edge Legacy)",
"type": "office-addin",
"request": "attach",
"url": "https://localhost:3000/taskpane.html?_host_Info=Excel$Win32$16.01$en-US$$$$0",
"port": 9222,
"timeout": 600000,
"webRoot": "${workspaceRoot}",
"preLaunchTask": "Debug: Excel Desktop",
"postDebugTask": "Stop Debug"
}
]
}
+8
View File
@@ -0,0 +1,8 @@
{
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript"
]
}
+160
View File
@@ -0,0 +1,160 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Build (Development)",
"type": "npm",
"script": "build:dev",
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"clear": true,
"panel": "shared",
"showReuseMessage": false
}
},
{
"label": "Build (Production)",
"type": "npm",
"script": "build",
"group": "build",
"presentation": {
"clear": true,
"panel": "shared",
"showReuseMessage": false
}
},
{
"label": "Debug: Excel Desktop",
"type": "shell",
"command": "npm",
"args": [
"run",
"start",
"--",
"desktop",
"--app",
"excel"
],
"presentation": {
"clear": true,
"panel": "dedicated"
},
"problemMatcher": []
},
{
"label": "Debug: Outlook Desktop",
"type": "shell",
"command": "npm",
"args": [
"run",
"start",
"--",
"desktop",
"--app",
"outlook"
],
"presentation": {
"clear": true,
"panel": "dedicated"
},
"problemMatcher": []
},
{
"label": "Debug: PowerPoint Desktop",
"type": "shell",
"command": "npm",
"args": [
"run",
"start",
"--",
"desktop",
"--app",
"powerpoint"
],
"presentation": {
"clear": true,
"panel": "dedicated"
},
"problemMatcher": []
},
{
"label": "Debug: Word Desktop",
"type": "shell",
"command": "npm",
"args": [
"run",
"start",
"--",
"desktop",
"--app",
"word"
],
"presentation": {
"clear": true,
"panel": "dedicated"
},
"problemMatcher": []
},
{
"label": "Dev Server",
"type": "npm",
"script": "dev-server",
"presentation": {
"clear": true,
"panel": "dedicated"
},
"problemMatcher": []
},
{
"label": "Install",
"type": "npm",
"script": "install",
"presentation": {
"clear": true,
"panel": "shared",
"showReuseMessage": false
},
"problemMatcher": []
},
{
"label": "Lint: Check for problems",
"type": "npm",
"script": "lint",
"problemMatcher": [
"$eslint-stylish"
]
},
{
"label": "Lint: Fix all auto-fixable problems",
"type": "npm",
"script": "lint:fix",
"problemMatcher": [
"$eslint-stylish"
]
},
{
"label": "Stop Debug",
"type": "npm",
"script": "stop",
"presentation": {
"clear": true,
"panel": "shared",
"showReuseMessage": false
},
"problemMatcher": []
},
{
"label": "Watch",
"type": "npm",
"script": "watch",
"presentation": {
"clear": true,
"panel": "dedicated"
},
"problemMatcher": []
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

@@ -0,0 +1,12 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"esmodules": false
}
}
],
]
}
File diff suppressed because it is too large Load Diff
+103
View File
@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" xmlns:ov="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="TaskPaneApp">
<Id>0b7d2846-9305-4e4b-906d-d73a6a4d286a</Id>
<Version>1.0.0.0</Version>
<ProviderName>Contoso</ProviderName>
<DefaultLocale>en-US</DefaultLocale>
<DisplayName DefaultValue="Create Job Folder"/>
<Description DefaultValue="A template to get started."/>
<IconUrl DefaultValue="https://localhost:3000/assets/icon-32.png"/>
<HighResolutionIconUrl DefaultValue="https://localhost:3000/assets/icon-64.png"/>
<SupportUrl DefaultValue="https://www.contoso.com/help"/>
<AppDomains>
<AppDomain>https://www.contoso.com</AppDomain>
</AppDomains>
<Hosts>
<Host Name="Workbook"/>
</Hosts>
<DefaultSettings>
<SourceLocation DefaultValue="https://localhost:3000/taskpane.html"/>
</DefaultSettings>
<Permissions>ReadWriteDocument</Permissions>
<VersionOverrides xmlns="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="VersionOverridesV1_0">
<Hosts>
<Host xsi:type="Workbook">
<DesktopFormFactor>
<GetStarted>
<Title resid="GetStarted.Title"/>
<Description resid="GetStarted.Description"/>
<LearnMoreUrl resid="GetStarted.LearnMoreUrl"/>
</GetStarted>
<FunctionFile resid="Commands.Url"/>
<ExtensionPoint xsi:type="PrimaryCommandSurface">
<OfficeTab id="TabHome">
<Group id="CommandsGroup">
<Label resid="CommandsGroup.Label"/>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Control xsi:type="Button" id="TaskpaneButton">
<Label resid="TaskpaneButton.Label"/>
<Supertip>
<Title resid="TaskpaneButton.Label"/>
<Description resid="TaskpaneButton.Tooltip"/>
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Action xsi:type="ShowTaskpane">
<TaskpaneId>ButtonId1</TaskpaneId>
<SourceLocation resid="Taskpane.Url"/>
</Action>
</Control>
</Group>
</OfficeTab>
</ExtensionPoint>
</DesktopFormFactor>
</Host>
</Hosts>
<Resources>
<bt:Images>
<bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/assets/icon-16.png"/>
<bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/assets/icon-32.png"/>
<bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/assets/icon-80.png"/>
</bt:Images>
<bt:Urls>
<bt:Url id="GetStarted.LearnMoreUrl" DefaultValue="https://go.microsoft.com/fwlink/?LinkId=276812"/>
<bt:Url id="Commands.Url" DefaultValue="https://localhost:3000/commands.html"/>
<bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/taskpane.html"/>
</bt:Urls>
<bt:ShortStrings>
<bt:String id="GetStarted.Title" DefaultValue="Get started with your sample add-in!"/>
<bt:String id="CommandsGroup.Label" DefaultValue="Commands Group"/>
<bt:String id="TaskpaneButton.Label" DefaultValue="Show Task Pane"/>
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="GetStarted.Description" DefaultValue="Your sample add-in loaded successfully. Go to the HOME tab and click the 'Show Task Pane' button to get started."/>
<bt:String id="TaskpaneButton.Tooltip" DefaultValue="Click to Show a Taskpane"/>
</bt:LongStrings>
</Resources>
<Requirements>
<Sets DefaultMinVersion="1.1">
<Set Name="IdentityAPI" MinVersion="1.3"/>
</Sets>
</Requirements>
<WebApplicationInfo>
<!-- Application (client) ID from your Azure AD / Entra app registration -->
<Id>3c846e71-9609-40e1-b458-0eb805e21b9f</Id>
<!-- The resource/audience URI your app uses; common pattern is api://{clientId} -->
<Resource>api://3c846e71-9609-40e1-b458-0eb805e21b9f</Resource>
<!-- Scopes your backend will request via OBO to call Microsoft Graph -->
<Scopes>
<Scope>User.Read</Scope>
<Scope>Files.ReadWrite.All</Scope>
<Scope>Sites.ReadWrite.All</Scope>
<!-- Add any other Graph scopes you need -->
</Scopes>
</WebApplicationInfo>
</VersionOverrides>
</OfficeApp>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,62 @@
{
"name": "office-addin-taskpane-js",
"version": "0.0.1",
"repository": {
"type": "git",
"url": "https://github.com/OfficeDev/Office-Addin-TaskPane-JS.git"
},
"license": "MIT",
"config": {
"app_to_debug": "excel",
"app_type_to_debug": "desktop",
"dev_server_port": 3000
},
"scripts": {
"build": "webpack --mode production",
"build:dev": "webpack --mode development",
"dev-server": "webpack serve --mode development",
"lint": "office-addin-lint check",
"lint:fix": "office-addin-lint fix",
"prettier": "office-addin-lint prettier",
"signin": "office-addin-dev-settings m365-account login",
"signout": "office-addin-dev-settings m365-account logout",
"start": "office-addin-debugging start manifest.xml",
"stop": "office-addin-debugging stop manifest.xml",
"validate": "office-addin-manifest validate manifest.xml",
"watch": "webpack --mode development --watch"
},
"dependencies": {
"core-js": "^3.36.0",
"regenerator-runtime": "^0.14.1"
},
"devDependencies": {
"@babel/core": "^7.24.0",
"@babel/preset-env": "^7.25.4",
"@types/office-js": "^1.0.377",
"@types/office-runtime": "^1.0.35",
"acorn": "^8.11.3",
"babel-loader": "^9.1.3",
"copy-webpack-plugin": "^12.0.2",
"eslint-plugin-office-addins": "^4.0.3",
"file-loader": "^6.2.0",
"html-loader": "^5.0.0",
"html-webpack-plugin": "^5.6.0",
"office-addin-cli": "^2.0.3",
"office-addin-debugging": "^6.0.3",
"office-addin-dev-certs": "^2.0.3",
"office-addin-lint": "^3.0.3",
"office-addin-manifest": "^2.0.3",
"office-addin-prettier-config": "^2.0.1",
"os-browserify": "^0.3.0",
"process": "^0.11.10",
"source-map-loader": "^5.0.0",
"webpack": "^5.95.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "5.1.0"
},
"prettier": "office-addin-prettier-config",
"browserslist": [
"last 2 versions",
"ie 11"
]
}
@@ -0,0 +1,18 @@
<!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<!-- Office JavaScript API -->
<script type="text/javascript" src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"></script>
</head>
<body>
</body>
</html>
@@ -0,0 +1,35 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
* See LICENSE in the project root for license information.
*/
/* global Office */
Office.onReady(() => {
// If needed, Office.js is ready to be called.
});
/**
* Shows a notification when the add-in command is executed.
* @param event {Office.AddinCommands.Event}
*/
function action(event) {
const message = {
type: Office.MailboxEnums.ItemNotificationMessageType.InformationalMessage,
message: "Performed action.",
icon: "Icon.80x80",
persistent: true,
};
// Show a notification message.
Office.context.mailbox.item.notificationMessages.replaceAsync(
"ActionPerformanceNotification",
message
);
// Be sure to indicate when the add-in command function is complete.
event.completed();
}
// Register the function with Office.
Office.actions.associate("action", action);
@@ -0,0 +1,115 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
* See LICENSE in the project root for license information.
*/
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
ul {
margin: 0;
padding: 0;
}
.ms-welcome__header {
padding: 20px;
padding-bottom: 30px;
padding-top: 100px;
display: -webkit-flex;
display: flex;
-webkit-flex-direction: column;
flex-direction: column;
align-items: center;
}
.ms-welcome__main {
display: -webkit-flex;
display: flex;
-webkit-flex-direction: column;
flex-direction: column;
-webkit-flex-wrap: nowrap;
flex-wrap: nowrap;
-webkit-align-items: center;
align-items: center;
-webkit-flex: 1 0 0;
flex: 1 0 0;
padding: 10px 20px;
}
.ms-welcome__main > h2 {
width: 100%;
text-align: center;
}
.ms-welcome__features {
list-style-type: none;
margin-top: 20px;
}
.ms-welcome__features.ms-List .ms-ListItem {
padding-bottom: 20px;
display: -webkit-flex;
display: flex;
}
.ms-welcome__features.ms-List .ms-ListItem > .ms-Icon {
margin-right: 10px;
}
.ms-welcome__action.ms-Button--hero {
margin-top: 30px;
}
.ms-Button.ms-Button--hero .ms-Button-label {
color: #0078d7;
}
.ms-Button.ms-Button--hero:hover .ms-Button-label,
.ms-Button.ms-Button--hero:focus .ms-Button-label{
color: #005a9e;
cursor: pointer;
}
b {
font-weight: bold;
}
/* taskpane.css */
.container {
padding: 10px;
font-family: Arial, sans-serif;
}
input[type="text"] {
width: 80%;
padding: 8px;
margin-bottom: 10px;
border: 1px solid #ccc;
border-radius: 4px;
}
.action-btn {
background-color: #0078d4;
color: white;
border: none;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.action-btn:hover {
background-color: #005a9e;
}
#statusMessage {
margin-top: 10px;
font-size: 12px;
color: #333;
}
@@ -0,0 +1,63 @@
<!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. -->
<!-- This file shows how to design a first-run page that provides a welcome screen to the user about the features of the add-in. -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Contoso Task Pane Add-in</title>
<!-- Office JavaScript API -->
<script type="text/javascript" src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"></script>
<!-- For more information on Fluent UI, visit https://developer.microsoft.com/fluentui#/. -->
<link rel="stylesheet" href="https://res-1.cdn.office.net/files/fabric-cdn-prod_20230815.002/office-ui-fabric-core/11.1.0/css/fabric.min.css"/>
<!-- Template styles -->
<link href="taskpane.css" rel="stylesheet" type="text/css" />
</head>
<body class="ms-font-m ms-welcome ms-Fabric">
<div class="container">
<h3>Create Project Folder</h3>
<input type="text" id="projectName" placeholder="Enter Job Number" />
<button id="createFolderBtn" class="action-btn">Create Folder & Document</button>
<p id="statusMessage"></p>
</div>
<header class="ms-welcome__header ms-bgColor-neutralLighter">
<img width="90" height="90" src="../../assets/logo-filled.png" alt="Contoso" title="Contoso" />
<h1 class="ms-font-su">Welcome</h1>
</header>
<section id="sideload-msg" class="ms-welcome__main">
<h2 class="ms-font-xl">Please <a target="_blank" href="https://learn.microsoft.com/office/dev/add-ins/testing/test-debug-office-add-ins#sideload-an-office-add-in-for-testing">sideload</a> your add-in to see app body.</h2>
</section>
<main id="app-body" class="ms-welcome__main" style="display: none;">
<h2 class="ms-font-xl"> Discover what Office Add-ins can do for you today! </h2>
<ul class="ms-List ms-welcome__features">
<li class="ms-ListItem">
<i class="ms-Icon ms-Icon--Ribbon ms-font-xl"></i>
<span class="ms-font-m">Achieve more with Office integration</span>
</li>
<li class="ms-ListItem">
<i class="ms-Icon ms-Icon--Unlock ms-font-xl"></i>
<span class="ms-font-m">Unlock features and functionality</span>
</li>
<li class="ms-ListItem">
<i class="ms-Icon ms-Icon--Design ms-font-xl"></i>
<span class="ms-font-m">Create and visualize like a pro</span>
</li>
</ul>
<p class="ms-font-l">Modify the source files, then click <b>Run</b>.</p>
<div role="button" id="run" class="ms-welcome__action ms-Button ms-Button--hero ms-font-xl">
<span class="ms-Button-label">Run</span>
</div>
<p><label id="item-subject"></label></p>
</main>
</body>
</html>
@@ -0,0 +1,252 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
* See LICENSE in the project root for license information.
*/
/* global console, document, Excel, Office */
window.addEventListener('error', (evt) => {
console.error('WindowError:', {
message: evt.message,
filename: evt.filename,
lineno: evt.lineno,
colno: evt.colno,
error: evt.error, // often the real Error object
});
});
window.addEventListener('unhandledrejection', (evt) => {
console.error('UnhandledRejection:', {
reason: evt.reason, // might be the original Error
});
});
Office.onReady((info) => {
if (info.host === Office.HostType.Excel) {
document.getElementById("sideload-msg").style.display = "none";
document.getElementById("app-body").style.display = "flex";
document.getElementById("run").onclick = run;
OfficeRuntime.auth.getAccessToken().then(token => console.log(token));
document.getElementById("createFolderBtn").onclick = handleCreatePDF;
}
});
async function handleCreatePDF() {
const projectInput = document.getElementById("projectName").value.trim();
const statusEl = document.getElementById("statusMessage");
if (!projectInput) {
statusEl.textContent = "Please enter a project number.";
return;
}
try {
statusEl.textContent = "Processing...";
// 1. Get Excel data
const jobData = await getJobData(projectInput);
if (!jobData) {
statusEl.textContent = "Job not found.";
return;
}
const formData = await getFormData();
// 2. Get access token for Graph
const accessToken = await getAccessToken();
// 3. Hardcoded SharePoint IDs
const driveId = "b!9YOqqr2xM0G2DFBwMEJYY4UzQgocFddEtpLYWuS9_AtkCKl2q8yjQJteQ7Ti4QSx";
const parentItemId = "01SPNXLDW3Y56HAWK5CNAJ4YJLLSQCUCJ7";
// 4. Create folder structure
const mainFolderName = `[ ${jobData.Job_Number} ] - ${jobData.Job_Name}`;
const mainFolderId = await createSharePointFolder(accessToken, driveId, parentItemId, mainFolderName);
const subFolderId = await createSharePointFolder(accessToken, driveId, mainFolderId, "Manager Info");
// 5. Generate Word document and export as PDF
const pdfBlob = await generatePDF(jobData, formData);
// 6. Upload PDF to SharePoint
await uploadFileToSharePoint(accessToken, driveId, subFolderId, `${mainFolderName}.pdf`, pdfBlob);
statusEl.textContent = "✅ PDF created and uploaded successfully!";
} catch (error) {
console.error(error);
statusEl.textContent = "❌ Error: " + error.message;
}
}
// Get Job Data from "Job Sheet"
async function getJobData(jobNumber) {
return Excel.run(async (context) => {
const sheet = context.workbook.worksheets.getItem("Job Sheet");
const usedRange = sheet.getUsedRange();
usedRange.load("values");
await context.sync();
const headers = usedRange.values[0];
const rows = usedRange.values.slice(1);
const idxJobNumber = headers.indexOf("Job_Number");
const idxJobName = headers.indexOf("Job_Name");
const idxJobAddress = headers.indexOf("Job_Address");
const idxJobDivision = headers.indexOf("Job_Division");
const idxClientCustomer = headers.indexOf("Client_Customer");
const idxContactPerson = headers.indexOf("Contact_Person");
const idxEmail = headers.indexOf("Email");
const match = rows.find(r => r[idxJobNumber] == jobNumber);
if (!match) return null;
return {
Job_Number: match[idxJobNumber],
Job_Name: match[idxJobName],
Job_Address: match[idxJobAddress],
Job_Division: match[idxJobDivision],
Client_Customer: match[idxClientCustomer],
Contact_Person: match[idxContactPerson],
Email: match[idxEmail]
};
});
}
// Get FormDat range
async function getFormData() {
return Excel.run(async (context) => {
const sheet = context.workbook.worksheets.getItem("FormDat");
const range = sheet.getRange("A1:C12");
range.load("values");
await context.sync();
return range.values;
});
}
// Generate PDF using Word API
async function generatePDF(jobData, formData) {
return Word.run(async (context) => {
const body = context.document.body;
body.clear();
body.insertParagraph("Project Information", Word.InsertLocation.start);
body.insertParagraph(`Job Number: ${jobData.Job_Number}`, Word.InsertLocation.end);
body.insertParagraph(`Job Name: ${jobData.Job_Name}`, Word.InsertLocation.end);
body.insertParagraph(`Address: ${jobData.Job_Address}`, Word.InsertLocation.end);
body.insertParagraph(`Division: ${jobData.Job_Division}`, Word.InsertLocation.end);
body.insertParagraph(`Client: ${jobData.Client_Customer}`, Word.InsertLocation.end);
body.insertParagraph(`Contact: ${jobData.Contact_Person}`, Word.InsertLocation.end);
body.insertParagraph(`Email: ${jobData.Email}`, Word.InsertLocation.end);
body.insertParagraph("\nForm Data:", Word.InsertLocation.end);
formData.forEach(row => {
body.insertParagraph(row.join(" | "), Word.InsertLocation.end);
});
await context.sync();
// Export as PDF
return new Promise((resolve, reject) => {
Office.context.document.getFileAsync(Office.FileType.Pdf, (result) => {
if (result.status === Office.AsyncResultStatus.Succeeded) {
const file = result.value;
const sliceCount = file.sliceCount;
const slices = [];
let sliceIndex = 0;
const getSlice = () => {
file.getSliceAsync(sliceIndex, (sliceResult) => {
if (sliceResult.status === Office.AsyncResultStatus.Succeeded) {
slices.push(sliceResult.value.data);
sliceIndex++;
if (sliceIndex < sliceCount) {
getSlice();
} else {
file.closeAsync();
const blob = new Blob(slices, { type: "application/pdf" });
resolve(blob);
}
} else {
reject(sliceResult.error);
}
});
};
getSlice();
} else {
reject(result.error);
}
});
});
});
}
// Get Access Token for Graph
async function getAccessToken() {
return new Promise((resolve, reject) => {
Office.context.auth.getAccessTokenAsync({ allowSignInPrompt: true }, (result) => {
if (result.status === "succeeded") {
resolve(result.value);
} else {
reject(new Error(result.error.message));
}
});
});
}
// Create folder in SharePoint
async function createSharePointFolder(token, driveId, parentId, folderName) {
const endpoint = `https://graph.microsoft.com/v1.0/drives/${driveId}/items/${parentId}/children`;
const response = await fetch(endpoint, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ name: folderName, folder: {}, "@microsoft.graph.conflictBehavior": "rename" })
});
const data = await response.json();
if (!data.id) throw new Error(`Failed to create folder: ${JSON.stringify(data)}`);
return data.id;
}
// Upload PDF
async function uploadFileToSharePoint(token, driveId, folderId, fileName, pdfBlob) {
const endpoint = `https://graph.microsoft.com/v1.0/drives/${driveId}/items/${folderId}:/${fileName}:/content`;
const response = await fetch(endpoint, {
method: "PUT",
headers: {
Authorization: `Bearer ${token}`
},
body: pdfBlob
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Upload failed: ${errorText}`);
}
}
export async function run() {
try {
await Excel.run(async (context) => {
/**
* Insert your Excel code here
*/
const range = context.workbook.getSelectedRange();
// Read the range address
range.load("address");
// Update the fill color
range.format.fill.color = "yellow";
await context.sync();
console.log(`The range address was ${range.address}.`);
});
} catch (error) {
console.error(error);
}
}
@@ -0,0 +1,97 @@
/* eslint-disable no-undef */
const devCerts = require("office-addin-dev-certs");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const urlDev = "https://localhost:3000/";
const urlProd = "https://www.contoso.com/"; // CHANGE THIS TO YOUR PRODUCTION DEPLOYMENT LOCATION
async function getHttpsOptions() {
const httpsOptions = await devCerts.getHttpsServerOptions();
return { ca: httpsOptions.ca, key: httpsOptions.key, cert: httpsOptions.cert };
}
module.exports = async (env, options) => {
const dev = options.mode === "development";
const config = {
devtool: "source-map",
entry: {
polyfill: ["core-js/stable", "regenerator-runtime/runtime"],
taskpane: ["./src/taskpane/taskpane.js", "./src/taskpane/taskpane.html"],
commands: "./src/commands/commands.js",
},
output: {
clean: true,
},
resolve: {
extensions: [".html", ".js"],
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
{
test: /\.html$/,
exclude: /node_modules/,
use: "html-loader",
},
{
test: /\.(png|jpg|jpeg|gif|ico)$/,
type: "asset/resource",
generator: {
filename: "assets/[name][ext][query]",
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
filename: "taskpane.html",
template: "./src/taskpane/taskpane.html",
chunks: ["polyfill", "taskpane"],
}),
new CopyWebpackPlugin({
patterns: [
{
from: "assets/*",
to: "assets/[name][ext][query]",
},
{
from: "manifest*.xml",
to: "[name]" + "[ext]",
transform(content) {
if (dev) {
return content;
} else {
return content.toString().replace(new RegExp(urlDev, "g"), urlProd);
}
},
},
],
}),
new HtmlWebpackPlugin({
filename: "commands.html",
template: "./src/commands/commands.html",
chunks: ["polyfill", "commands"],
}),
],
devServer: {
headers: {
"Access-Control-Allow-Origin": "*",
},
server: {
type: "https",
options: env.WEBPACK_BUILD || options.https !== undefined ? options.https : await getHttpsOptions(),
},
port: process.env.npm_package_config_dev_server_port || 3000,
},
};
return config;
};
+15
View File
@@ -0,0 +1,15 @@
# officeaddin
To install dependencies:
```bash
bun install
```
To run:
```bash
bun run index.ts
```
This project was created using `bun init` in bun v1.3.3. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
+26
View File
@@ -0,0 +1,26 @@
{
"lockfileVersion": 1,
"configVersion": 1,
"workspaces": {
"": {
"name": "officeaddin",
"devDependencies": {
"@types/bun": "latest",
},
"peerDependencies": {
"typescript": "^5",
},
},
},
"packages": {
"@types/bun": ["@types/bun@1.3.3", "", { "dependencies": { "bun-types": "1.3.3" } }, "sha512-ogrKbJ2X5N0kWLLFKeytG0eHDleBYtngtlbu9cyBKFtNL3cnpDZkNdQj8flVf6WTZUX5ulI9AY1oa7ljhSrp+g=="],
"@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="],
"bun-types": ["bun-types@1.3.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-z3Xwlg7j2l9JY27x5Qn3Wlyos8YAp0kKRlrePAOjgjMGS5IG6E7Jnlx736vH9UVI4wUICwwhC9anYL++XeOgTQ=="],
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
}
}
+17
View File
@@ -0,0 +1,17 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<!-- Office.js must be present so the commands surface initializes -->
https://appsforoffice.microsoft.com/lib/1/hosted/office.js</script>
</head>
<body>
<script>
// Minimal init; no custom handlers needed for ShowTaskpane
Office.onReady(() => {
// Commands surface is ready. Nothing else required for ShowTaskpane.
});
</script>
</body>
</html>
+1
View File
@@ -0,0 +1 @@
console.log("Hello via Bun!");
+12
View File
@@ -0,0 +1,12 @@
{
"name": "officeaddin",
"module": "index.ts",
"type": "module",
"private": true,
"devDependencies": {
"@types/bun": "latest"
},
"peerDependencies": {
"typescript": "^5"
}
}
+29
View File
@@ -0,0 +1,29 @@
{
"compilerOptions": {
// Environment setup & latest features
"lib": ["ESNext"],
"target": "ESNext",
"module": "Preserve",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,
// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
// Best practices
"strict": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false
}
}