From 9a91118c1244f7d359ed774921baa7e019ea2253 Mon Sep 17 00:00:00 2001 From: quanfuxiao Date: Thu, 28 Aug 2025 15:43:44 +0800 Subject: [PATCH 1/3] feat: add UI changes for CF and shortcut --- packages/cli/src/commands/models/listTemplates.ts | 1 + packages/fx-core/resource/package.nls.json | 2 ++ .../src/component/generator/officeAddin/generator.ts | 1 + .../src/component/generator/templates/metadata/wxp.ts | 6 ++++++ .../src/component/generator/templates/templateNames.ts | 1 + .../src/question/scaffold/vsc/CapabilityOptions.ts | 8 ++++++++ .../fx-core/src/question/scaffold/vsc/createRootNode.ts | 1 + .../question/scaffold/vsc/officeAddinProjectTypeNode.ts | 1 + 8 files changed, 21 insertions(+) diff --git a/packages/cli/src/commands/models/listTemplates.ts b/packages/cli/src/commands/models/listTemplates.ts index 193aa54bd3..c10f5440eb 100644 --- a/packages/cli/src/commands/models/listTemplates.ts +++ b/packages/cli/src/commands/models/listTemplates.ts @@ -63,6 +63,7 @@ export function listAllCapabilities(): OptionItem[] { MeCapabilityOptions.collectFormMe(), MeCapabilityOptions.linkUnfurling(), OfficeAddinCapabilityOptions.wxpTaskPane(), + OfficeAddinCapabilityOptions.excelCFShortcut(), OfficeAddinCapabilityOptions.outlookTaskPane(), ...(featureFlagManager.getBooleanValue(FeatureFlags.TdpTemplateCliTest) ? [ diff --git a/packages/fx-core/resource/package.nls.json b/packages/fx-core/resource/package.nls.json index 5aa1fc5450..9fd4299de8 100644 --- a/packages/fx-core/resource/package.nls.json +++ b/packages/fx-core/resource/package.nls.json @@ -664,6 +664,8 @@ "core.officeContentAddin.detail": "Create new objects for Excel or PowerPoint", "core.newTaskpaneAddin.label": "Task pane", "core.newTaskpaneAddin.detail": "Customize the ribbon with a button and embed content in the task pane", + "core.newCFShortcut.label": "Custom Function and Shortcut", + "core.newCFShortcut.detail": "Task pane add-in with custom function and shortcut", "core.summary.actionDescription": "Action %s%s", "core.summary.lifecycleDescription": "Lifecycle stage: %s(%s step(s) in total). The following actions will be executed: %s", "core.summary.lifecycleNotExecuted": "%s Lifecycle stage %s was not executed.", diff --git a/packages/fx-core/src/component/generator/officeAddin/generator.ts b/packages/fx-core/src/component/generator/officeAddin/generator.ts index 62cdf8fe65..d90a9af56a 100644 --- a/packages/fx-core/src/component/generator/officeAddin/generator.ts +++ b/packages/fx-core/src/component/generator/officeAddin/generator.ts @@ -123,6 +123,7 @@ export class OfficeAddinGeneratorNew extends DefaultTemplateGenerator { return [ TemplateNames.OutlookTaskpane, TemplateNames.WXPTaskpane, + TemplateNames.ExcelCFShortcut, TemplateNames.OfficeAddinCommon, TemplateNames.DeclarativeAgentMetaOSNewProject, TemplateNames.DeclarativeAgentMetaOSUpgradeProject, diff --git a/packages/fx-core/src/component/generator/templates/metadata/wxp.ts b/packages/fx-core/src/component/generator/templates/metadata/wxp.ts index 17d178e910..8053d79b7f 100644 --- a/packages/fx-core/src/component/generator/templates/metadata/wxp.ts +++ b/packages/fx-core/src/component/generator/templates/metadata/wxp.ts @@ -17,6 +17,12 @@ export const wxpTemplates: Template[] = [ language: "typescript", description: "", }, + { + id: "office-addin-excel-cfshortcut-ts", + name: TemplateNames.ExcelCFShortcut, + language: "typescript", + description: "", + }, { id: "office-addin-config-ts", name: TemplateNames.OfficeAddinCommon, diff --git a/packages/fx-core/src/component/generator/templates/templateNames.ts b/packages/fx-core/src/component/generator/templates/templateNames.ts index e214b5d750..b91c0ff28d 100644 --- a/packages/fx-core/src/component/generator/templates/templateNames.ts +++ b/packages/fx-core/src/component/generator/templates/templateNames.ts @@ -63,6 +63,7 @@ export enum TemplateNames { // WXP OutlookTaskpane = "office-addin-outlook-taskpane", // handled by OfficeAddinGeneratorNew WXPTaskpane = "office-addin-wxpo-taskpane", // handled by OfficeAddinGeneratorNew + ExcelCFShortcut = "office-addin-excel-cfshortcut", // handled by OfficeAddinGeneratorNew OfficeAddinCommon = "office-addin-config", // handled by OfficeAddinGeneratorNew // from TDP only diff --git a/packages/fx-core/src/question/scaffold/vsc/CapabilityOptions.ts b/packages/fx-core/src/question/scaffold/vsc/CapabilityOptions.ts index 6601862949..ee68cc7181 100644 --- a/packages/fx-core/src/question/scaffold/vsc/CapabilityOptions.ts +++ b/packages/fx-core/src/question/scaffold/vsc/CapabilityOptions.ts @@ -340,6 +340,14 @@ export class OfficeAddinCapabilityOptions { data: TemplateNames.WXPTaskpane, }; } + static excelCFShortcut(): OptionItem { + return { + id: "wxp-json-cf-shortcut", + label: getLocalizedString("core.newCFShortcut.label"), + detail: getLocalizedString("core.newCFShortcut.detail"), + data: TemplateNames.ExcelCFShortcut, + }; + } static DAMetaOS(): OptionItem { return { id: "office-da-meta-os", diff --git a/packages/fx-core/src/question/scaffold/vsc/createRootNode.ts b/packages/fx-core/src/question/scaffold/vsc/createRootNode.ts index 5b5b90fe02..c0e29ba119 100644 --- a/packages/fx-core/src/question/scaffold/vsc/createRootNode.ts +++ b/packages/fx-core/src/question/scaffold/vsc/createRootNode.ts @@ -217,6 +217,7 @@ export function getProjectTypeByCapability(capability: string): string { if ( [ OfficeAddinCapabilityOptions.wxpTaskPane().id, + OfficeAddinCapabilityOptions.excelCFShortcut().id, OfficeAddinCapabilityOptions.officeAddinImport().id, ].includes(capability) ) { diff --git a/packages/fx-core/src/question/scaffold/vsc/officeAddinProjectTypeNode.ts b/packages/fx-core/src/question/scaffold/vsc/officeAddinProjectTypeNode.ts index 351bc81b42..4a1893dcf4 100644 --- a/packages/fx-core/src/question/scaffold/vsc/officeAddinProjectTypeNode.ts +++ b/packages/fx-core/src/question/scaffold/vsc/officeAddinProjectTypeNode.ts @@ -67,6 +67,7 @@ export function wxpAddinProjectTypeNode(): IQTreeNode { type: "singleSelect", staticOptions: [ OfficeAddinCapabilityOptions.wxpTaskPane(), + OfficeAddinCapabilityOptions.excelCFShortcut(), ...(featureFlagManager.getBooleanValue(FeatureFlags.DAMetaOS) ? [OfficeAddinCapabilityOptions.DAMetaOS()] : []), From 9e7ba3cef08d3ff057a61a4e30484d489a28b647 Mon Sep 17 00:00:00 2001 From: quanfuxiao Date: Tue, 2 Sep 2025 16:20:54 +0800 Subject: [PATCH 2/3] feat: template update for CF and Shortcut --- templates/.eslintignore | 1 + .../.eslintrc.json | 8 + .../.gitignore.tpl | 33 +++ .../.vscode/extensions.json | 5 + .../.vscode/launch.json | 15 ++ .../.vscode/settings.json | 8 + .../.vscode/tasks.json | 107 ++++++++ .../office-addin-excel-cfshortcut/README.md | 50 ++++ .../appPackage/assets/color.png | Bin 0 -> 1066 bytes .../appPackage/assets/icon-128.png | Bin 0 -> 4693 bytes .../appPackage/assets/icon-16.png | Bin 0 -> 1596 bytes .../appPackage/assets/icon-32.png | Bin 0 -> 2386 bytes .../appPackage/assets/icon-64.png | Bin 0 -> 2112 bytes .../appPackage/assets/icon-80.png | Bin 0 -> 4836 bytes .../appPackage/assets/logo-filled.png | Bin 0 -> 11915 bytes .../appPackage/assets/outline.png | Bin 0 -> 249 bytes .../appPackage/manifest.json.tpl | 247 ++++++++++++++++++ .../babel.config.json | 3 + .../env/.env.dev | 15 ++ .../infra/azure.bicep | 25 ++ .../infra/azure.parameters.json | 12 + .../m365agents.yml | 60 +++++ .../package.json.tpl | 69 +++++ .../src/taskpane/excel.ts | 56 ++++ .../src/taskpane/functions.ts | 71 +++++ .../src/taskpane/taskpane.css | 80 ++++++ .../src/taskpane/taskpane.html | 55 ++++ .../src/taskpane/taskpane.ts | 2 + .../tsconfig.json | 26 ++ .../webpack.config.js | 103 ++++++++ 30 files changed, 1051 insertions(+) create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/.eslintrc.json create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/.gitignore.tpl create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/.vscode/extensions.json create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/.vscode/launch.json create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/.vscode/settings.json create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/.vscode/tasks.json create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/README.md create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/appPackage/assets/color.png create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/appPackage/assets/icon-128.png create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/appPackage/assets/icon-16.png create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/appPackage/assets/icon-32.png create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/appPackage/assets/icon-64.png create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/appPackage/assets/icon-80.png create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/appPackage/assets/logo-filled.png create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/appPackage/assets/outline.png create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/appPackage/manifest.json.tpl create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/babel.config.json create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/env/.env.dev create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/infra/azure.bicep create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/infra/azure.parameters.json create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/m365agents.yml create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/package.json.tpl create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/src/taskpane/excel.ts create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/src/taskpane/functions.ts create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/src/taskpane/taskpane.css create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/src/taskpane/taskpane.html create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/src/taskpane/taskpane.ts create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/tsconfig.json create mode 100644 templates/vsc/ts/office-addin-excel-cfshortcut/webpack.config.js diff --git a/templates/.eslintignore b/templates/.eslintignore index e184e17bcb..72feec13ad 100644 --- a/templates/.eslintignore +++ b/templates/.eslintignore @@ -1,5 +1,6 @@ **/*.css vsc/ts/office-addin-wxpo-taskpane/* vsc/ts/office-addin-outlook-taskpane/* +vsc/ts/office-addin-excel-cfshortcut/* vsc/common/declarative-agent-meta-os-new-project/* vsc/common/declarative-agent-meta-os-upgrade-project/* \ No newline at end of file diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/.eslintrc.json b/templates/vsc/ts/office-addin-excel-cfshortcut/.eslintrc.json new file mode 100644 index 0000000000..e406c09e8b --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/.eslintrc.json @@ -0,0 +1,8 @@ +{ + "plugins": [ + "office-addins" + ], + "extends": [ + "plugin:office-addins/recommended" + ] +} diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/.gitignore.tpl b/templates/vsc/ts/office-addin-excel-cfshortcut/.gitignore.tpl new file mode 100644 index 0000000000..878f59ae21 --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/.gitignore.tpl @@ -0,0 +1,33 @@ +# TeamsFx files +env/.env.*.user +env/.env.local +appPackage/build + +# Dependency directories +node_modules + +# Azure Functions artifacts +bin +obj +appsettings.json + +# ignore local.settings.json if it contains your own credentials +# local.settings.json + +# misc +.DS_Store +.deployment +.env + +# build +/dist + +# Azurite emulator +_storage_emulator + +# Local data +.localConfigs +.notification.localstore.json + +# production +/build \ No newline at end of file diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/.vscode/extensions.json b/templates/vsc/ts/office-addin-excel-cfshortcut/.vscode/extensions.json new file mode 100644 index 0000000000..aac0a6e347 --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "TeamsDevApp.ms-teams-vscode-extension" + ] +} diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/.vscode/launch.json b/templates/vsc/ts/office-addin-excel-cfshortcut/.vscode/launch.json new file mode 100644 index 0000000000..ff023e35db --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + "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" + } + ] +} \ No newline at end of file diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/.vscode/settings.json b/templates/vsc/ts/office-addin-excel-cfshortcut/.vscode/settings.json new file mode 100644 index 0000000000..5dec57b1d2 --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript" + ] + } + \ No newline at end of file diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/.vscode/tasks.json b/templates/vsc/ts/office-addin-excel-cfshortcut/.vscode/tasks.json new file mode 100644 index 0000000000..a1db01d9dd --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/.vscode/tasks.json @@ -0,0 +1,107 @@ +{ + "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 + }, + "dependsOn": [ + "Install" + ] + }, + { + "label": "Build (Production)", + "type": "npm", + "script": "build", + "group": "build", + "presentation": { + "clear": true, + "panel": "shared", + "showReuseMessage": false + }, + "dependsOn": [ + "Install" + ] + }, + { + "label": "Debug: Excel Desktop", + "type": "npm", + "script": "start:desktop:excel", + "presentation": { + "clear": true, + "panel": "dedicated" + }, + "problemMatcher": [], + "dependsOn": [ + "Install" + ] + }, + { + "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": [] + } + ] +} \ No newline at end of file diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/README.md b/templates/vsc/ts/office-addin-excel-cfshortcut/README.md new file mode 100644 index 0000000000..3f1c8cc447 --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/README.md @@ -0,0 +1,50 @@ +# Build Office add-ins using Microsoft 365 Agents Toolkit +Office add-ins are integrations built by third parties into Office by using our web-based platform. This add-in template supports: Word, Excel, PowerPoint, Outlook. +Now you have the ability to create a single unit of distribution for all your Microsoft 365 extensions by using the same manifest format and schema, based on the current JSON-formatted Teams manifest. + +> Note: +> The unified app manifest for Word, Excel, and PowerPoint is in preview. Visit [this link](https://aka.ms/officeversions) to check the required Office Versions. Also, publishing a unified add-in for Word, Excel, PowerPoint is not supported currently. + +## Prerequisites + +- [Node.js](https://nodejs.org/), supported versions: 18, 20, 22. +- Word/Excel/PowerPoint for Windows: Beta Channel, Build 18514 or higher. Outlook For Windows, Build 16425 or higher. Follow [this link](https://github.com/OfficeDev/TeamsFx/wiki/How-to-switch-Outlook-client-update-channel-and-verify-Outlook-client-build-version) for switching update channels and check your Office client build version. +- Edge installed for debugging Office add-in. +- A M365 account. If you do not have M365 account, apply one from [M365 developer program](https://developer.microsoft.com/en-us/microsoft-365/dev-program) +- [Microsoft 365 Agents Toolkit Visual Studio Code Extension](https://aka.ms/teams-toolkit) version 5.0.0 and higher. + +## Debug Office add-in +- Please note that the same M365 account should be used both in Microsoft 365 Agents Toolkit and Office. +- From Visual Studio Code: Start debugging the project by choosing launch profile (default value is Word) in `Run and Debug` pane and hitting the `F5` key in Visual Studio Code. Please run VSCode as administrator if localhost loopback for Microsoft Edge Webview hasn't been enabled. Once enbaled, administrator priviledge is no longer required. + +## Edit the manifest + +You can find the app manifest in `./appPackage` folder. The folder contains one manifest file: +* `manifest.json`: Manifest file for Office add-in running locally or running remotely (After deployed to Azure). +You may add any extra properties or permissions you require to this file. See the [schema reference](https://raw.githubusercontent.com/OfficeDev/microsoft-teams-app-schema/preview/op/extensions/MicrosoftTeams.schema.json) for more information. + +## Deploy to Azure + +Deploy your project to Azure by following these steps: + +| From Visual Studio Code | From Microsoft 365 Agents Toolkit CLI | +| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | | +> Note: Provisioning and deployment may incur charges to your Azure Subscription. + +To sideload the deployed add-in: + +- Copy the production URL from the `ADDIN_ENDPOINT` in env/.env.dev file. +- Edit webpack.config.js file and change `urlProd` to the value you just copied. Please note to add a '/' at the end of the URL. +- Run `npm run build`. +- Run `npx office-addin-dev-settings sideload ./dist/manifest.json`. + +## Validate manifest file + +To check that your manifest file is valid: + +- From Visual Studio Code: open the command palette and select: `Microsoft 365 Agents: Validate Application` and select `Validate using manifest schema`. +- From Microsoft 365 Agents Toolkit CLI: run command `atk validate` in your project directory. + +## Known Issues +- Publish is not supported for an Office add-in project now. \ No newline at end of file diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/appPackage/assets/color.png b/templates/vsc/ts/office-addin-excel-cfshortcut/appPackage/assets/color.png new file mode 100644 index 0000000000000000000000000000000000000000..f27ccf2036bf2264dc0d11edf2af2bda62e4efdf GIT binary patch literal 1066 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvE4M+yv$zcaloCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&di49xpIT^vIy7~kGC%#(I!aJU%yVD^#PnvNe5 zY1um`Oj+#WC3y9e;Us63+9EE_EXjNxu|}7}jyc6;|Ee3L%wi^d-gxKYxi>fe9)4Wc zxa_YvcME5O3F8DchD$6Cvlu*t88Vp^d>NL|Lr`DL?D4N}c{_jp%(HAg{rPUu*Szg# zj!7mc`&s@7H-CTs|F4$!|Ce$kF#Fm5;7{vuU}%<3{UCovtdW7u?A8PO8JbLtJXu!` z)*E=Uq<`n{|J}Oq&G+9=pRT^{A7^iE9sTdm-|J7arQcTAE#7O4&s)AbmEKG-&pNxS zC@bvpTt>gz>5tZEFHbWKWmwE(Czx~Ggt5o$h06xsU>1W{3Bm_|n8_b_(d@&LeEW~i zg^dTlUYFmmyZpo3^85CcxxEL@T$u4k9$$#sDCao5UUz!>`!fG+?(yZBb?@R92k)%- zop#eIy}>bd?)!h82_c(x{)!wp;MSY4tn@sS#GMSmGuvLoGe{eFu^7LrP;BV6C}r84 z_dQ80!}%J=HG4bxbez$5Ys+@0HQnxRMXMf1ieE3d1B&Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D5$Z`qK~#8N?VSmf z6=inEKNa_=piaaF6>&q{Py}^cID!f)hzs$kg9b;VsN}>WInfLt0VOU&LZT<41`UIZ zQ6?(lhH)nD2qG$qj=P9Uy8w#&f|~#Dt5@*4U)6h6?^V67dj0**xu+Joi|V@fyZ3&( zaX1_fhr`h>S*LJpD0=tq&EK_^WIM^Wl5Hei3ipuBB^ygNkihqnZzP{fRurUJ@}A^< z$!_g21P*C_jk^>9JUJ?|(g#<;fFR6x~B;QF^N#2pXAXy-pCwWCi@w1M( zCYJ^vg;RpNNq#9gR&t1>qokgEB3USTRPvx?i45XL9dku)44_cT_m=dR949$ILg~%| zgb%#K9La3SpY!4!GHn1-`no#c#*dPWkl^C$UDD(7nPk3Xy5tTS!fI}2mCPD|l)r}# zgCrv*J996Gye_#_GEIg+LYX--X8=-scO8B$87|p@dkp}!ivK`l2p`)!86gt}P>Aop zko=wG97!k1dXh%tE6HyW8N%20P8-wa1|a1V;|EK|Np|C2JAlwIS#pyMp-pQJZE65g zejgpiO9o0haIc;5N695Jf<@kC{b>^ekmAYY&z6jlWId}-8>)~KB>y5isC$i}?gk*` zlig2|9BFSjeEv^zri@^jcUd3mW&lz=b^6mK{~+nfJx2*EB^Ut>N_C+wnYNU_i4OlL z`6me-G>%|s+5D@frlud7o135TZlz1=9ze>cdN)b(H}-}jD*q+9Sl)m>htx>b0Hl0M z`}Go9^auftcswLIMMgkqNQKl4K+5l`1F?***}5m24a94bgJlHEy~|`trBq7!^xDmp z>??7~uZ>+Lj~2dZ;v}vdy(j}Q{vbKX-f$$v%aVce1c`PkCF%jB{2g_eC2`769;(jw z>KkwAT_!_f2A~%s+b45nl#U}E2m^QNTQjaP5fW(!&d6(6bLJm(hvs0i=A!@X@vFl%E~Y#Wm86Ki4rb z)r5c(BsFKBj>MrhcD#2Qome427Dm#+#7LL;Ws?sjJroL<%o>Tf9)RIoPWjoTv*c3m zG#ZgTfJ#3zcAl2F*q?1k2z$y4M4EvV#{*E+aLUg<=!hzMBqDhLm3})YL4u2MWFNf1 z{_+A#y~}W-dH{O)obvMnUf@*kv^*6(fJ#3THJR^@7jWbUY6LsW3os!ylqep6x}{To z{=j21>9#Co4M3$I4?)9^BcBW}=rEK@9)Rv%H@+{wu$~gt_fRSs0KI)%d#8>Z!j!yW z-f1wQ1|Zi#rw=oF9QkIbbh%OCyo7QdfWe0>y6DI^%*qpM7(IHlx%Jjt&CWaLb;Y+_6WUE2XaI^C3>Rl5&cxVq%Pq~WyY6a_j=IBI zOo4=XfZZj3>768BZ4 zRy&OE0}-UVH5|^UXJ`!5Ezm9Xgm!ojRGl_S(zrw%cxI zlTBzzHTGH0KKrbB|9y6kG4}ozTWn#LFJEqc_~8e8uN+%#wUyavr=9G3at^Cjtuima z{IYrR#TU&NUwmQjr3~G|eHSfS#6lBm2r>YcZM{cQ?KUfcBxwL$yLL6FopzcTG-#07 zeDl(_s-J)U*}VPs+h*FdY3AXFAC9CX4Cs_oPBAB+eDWG474zknUz+>wyU$#6%{8WP z-@az@&oakL8$AAF?Y$FgK(Jx8Wu-|_BnQ`OBnSTBH+2`DO=bdKy^y%i7 zTW&FT+;N9_<&{^g!uQ;BPjl2!M_DB-T)5Eu_+$8*%ox$ci4)C9C!J)z|NeV(+ika* zn{U3^KJ(st?={PoEi+xZbTNk>dZ;bQOP4OSf6ogqybx;4DEzX^E;B=h3^70b^pjOG z&&4Qt&u5-_#;jPe!g?^Cd%yt)n0fQ&nXkSoJAR`U2=|Z3Lq6wSdIWiZu{vDlT_r~n z9-vR3KIW>ct}+{Juz|Vp#v5(R;_B7G%j@FGDV;rf^f0q$&o-A|&JqTtQ1W>u3cy?N zbDw|C=3%7_A3oe%c;SU+$&w{z_uY37^#D8Uu!A)yjOC$+9x_v=ObLDF#v5;JF249; zGkEY|8@kRp=N$9hckEM@3S;E~ShH~rK}ldHNB*Mpi!QpzY_!ov=BAr&GBak(2vt6O z`st_k=PA8>+5?$si7S1+!+&GE=8cwXuA?^}^TIz)<$sV-IUk zS6_W~*=OSRF+dCjqdN7}NJIImr6jq}jRBxULdX+OJYh?I6h8j=V;igCj5E%#$6`q4 zMvNF?6SC{ByRKr%BRpVuufNW6|G{+c-re-?-`~9V-h0+qqQrH6X2OIC=IgJ&Hp7Mu zvqqj0tmD*zAOm0=K&GJdAAIn^Ho2n|#&MB^n^#|bmC#m_h#hv=VK$*8K9f5{f&2m^ z4CC;_54SZ0!p;XDd{DSsnQy=S*36wd*Vb$fJdnMyQlVrWfgl4&&HSo`L=Lx074Ny{ zTI4VlAxu!!BG&G>Bdgf0g`5I~KJv&Tg*(x}ph)b3Ae>O5AA2nR@;%Q#|GYg?4NZv> z-3A#z>?y`+v)N{wS>eN%%jUx%TPJ3>?uy=JmtCx~sL#g%qrn>mftMlleD)c;$Hjxt z!rw<5L`szCHpl=nxBDg4G~(hr@34Doe2DQt&A?~<_1Cvu)72P27~~YZD+#Fof5w5( zMjn92s~15AkXjw2TKuFaN`p=izSmfH-L-!%20+Un|5ojDTSKk_JhK{4MOfkY)>)^d zcAgY$lHGz_7yzwsP+WrJ;5QSKX!-a`Cp46+(#3)IBsAa&FtE5_K>S{mnnRV8JUGYz z>e4VB2jVFog@%=C*k+q;Z0VwE=7Ry?rcqW<({DLYQcx)+;as_LrP+Gxt!+&pF1UW) zhnB{aD2Yo!29Vn31l97%C!g5FYP;>WTjOTqKvqrZZ~b902)qLZ5(lb|#o?qFI*u>^ zBrYVS7*@GJ}x{fb!^#BWa`ME)LsozrE?ztC#I`dE<>Y3U@sgELdO_ zdBhP%6z)`J!woky`|r;pX~EF?hZ5;jip$YQA8ivs`Uz5^M7Kc(z^u~Dar^DJTep4g zx#yZ~x81gIw>%_|C!Tns{dpRY$gqof;DHCMaUFBaF_E81Clq!4Fdl#WacgWSlg6j0 z9DD4sX79cCHV;1dpl!ZRiIUZ&gA9PKy3Fl;PwBn?{`+k``_fAl^uvV3~;uD_mbl8hNMW{eG6m7qcD?6c3dA&O@1 zq-rIt;GrLRr)vr74rU_3nUX}u1Nhy+w2)En#+8;6+TB}2FC6ZiE@70xbI1*fmBH^4 z(?^aRX-+@=bellZ*%Ha8wpAUTn@f>#V}nTKKShFgi`BzJ2u3N46c`?{OpHBS|9;({f1nE($3JwE#-% zA%`4da{`ROZ*nG^$1q418CNiN>{vV2Aj*IcdIirr?>wt)j0)HO?z`{We`9=sKkA4E zE|gD?V2aQ&(Q8qwZRmmwfO~9a%+RpJU<3~(H!760E3UYraMcnNf6ze(*|^C^B+>K1 zAW;1L`Sb1Ilc)nMsM;~Yh_Jyk{k8+V49|P=$tP{65lI>)n~=en1iGfn(XCrI>pkeq zLg{`j27^HHc#t`B=Gga687#WD44_4L4{UOh5%5?2+Plg&D6Y8sl)5-|{y05`t)avg zS2qbq#m^f$bf_IWFn;`aYh+R2J@GE(o|`gkRnLaqEeNj68dH~TOOt?viDb?f8jNcF zsQiBMGh088UPX+IQXCcDv(j@@WILQO*ETt&?O0hJw=u zUkOGRUwP$~_Q=q!M;~S3)Z7sIvVpY#P)&ff0T)X$H$*H+{PqckWYY}$)?07cbD%ar z)s_ASjx^lRK5@wbmrM_FjhtLVL>7y#9XfAdZqIpTJe{)(-+6%9ZS zVGh*qB)O^hIt(*tW_qXPiQ)k;0z$xb-jyQ<%#_X|Z6h1W01Ds**o?@LZ5RO)IdP9{ z0J5mJbhz2Oa%7k((pBVs>Np+%u9mnOL3W|bcZzo!jkpFt2w?lz@U>YSZ3Q~{txA8a znO(6AKo6%;f%REfN6?Wi=1BhFokpi@UqBeWd-rAqgC`}KTu-PSLic1p70`v=Wpt{V z7v0?4yrQY8iU0Iy?~bF5VfnY%r61zv0L4s~#9s)YR(qfo%aH|8>R zu{Rv`hQ^*DD*aW@C$Gf=kQ6e{X@sP7BA+8|m{c&S7Ue@t1^{}2Kk6{vyK>YKMr!@u zJFT8X7ROOS$iM^ELo{1 zAT@(yB=jCSl7Xr=Za-1wL*gF5N1lL{MyMeqy3j;BgCvhRnkN?uR^lY?0etkr^h&X3 zZM&Fbm;z+`%*^^#TFQrH)<_MZxw(0js))2vvrfC)j4~b!z8RH<+bGj`SI7Bk4b7FAOJR^8YW47HKZK#wq&Gaf|Q@y_F=`;y)-I0 zWLI(ql?<>q96qxpzmekq>|NG})I&gP6b`;pIKTi%64|9Z(FG}U2yUKXb`#T%_d&F2 zJeD`;p#xKwSpm8O_u3JR;bR!r7%4v5j#Vj8cMnjEyuosLgWGhR?mGOn zDh_$za?zdzDmju-eocaWZnRLWC*+~%Z+0MG$_%@gmGj6hR8Dx z0)@|#ype_Y9);W(Kru1~YA@`s+FvqA(nnIi-4;ElzcT`g4jTq)p!E2=MYb`yG=SE~ zAZSBixlgo+y$^woj*KvAKlsLDiG^%y2e@ZB>{6i?Fo{EpsS-nUw7o&vrN*4u`|hAQeMLcHa&~Ho zLQ-maW}dCm``!DM6f#q6mBLMZ4SWlnQ!_F>s)|yBtNcQetFn_VQY3>#8yZ_Em|N-@ znp#>Indm4O85o-B8(8Wan&=uBS{Ybc85k-+ffCTRqLehNAQv~NT|l0#QbtKhft9{~ zd3m{Bxv^e;QM$gNrKP35fswwEkuFe$ZgFK^Nn(X=Ua>OF1ees}t-3#_`hBq$Z(46Le)Ln;eW z^@CE2^Gl18f$@>14ATq@JNy=b6armiBkFOx%nuOw0|9EzK=pdOh=sOA_;vQ(<;z0_}$CHO8yg%DE^tu_V7JBtJg~ zmI?wg@=NlIGx7@*oSi|jZmysao|%`DUtX*UiYAD!T~doO%TiO^it=+6z~O9_iNy`X z`5&S`h1~Gd2Rce0lvt1w4@?M{B0)@eRseF~nJG07n1hOdS;gz?%1QF! zNj{#Qi51`9#YzfKSn=oo|9W)?217PhR>KB{%eS}Z|L5lB{?E>#u;IkvcK-jy4vfOg zQVbjlGv+ZU*nNF}e}B5piBtdo{k5LkIdkUB#_7|iAG~>UCS!)L+Tx8HKgupxVieM9 z;=ph)hmrG+qw#;SfJUnm*Ax~QmN$qQbaZt!^)fStg@u_ae3f7FKq5f(L`_@z`FV|^ zjX&(`{xGckeTwDEPbQ5698Y|blai8l)c!W}c*HDln&D_A15<(PF*A-M1|p>s*gIT~ zn1*Doc||WTP4AD)uh@2z0hc!;5D1oQ7ln-cB=>@Ff`rX+&w41;T!*&zXBV*Cz?$Vk!Iq)rRwX%^5-j9%af=3%!||AYENwP@joEdEZfX{dHR}v2RPpx>}6+lRN#BuRKcQm0YhU2PgTe~DWM4fV}M0Q literal 0 HcmV?d00001 diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/appPackage/assets/icon-32.png b/templates/vsc/ts/office-addin-excel-cfshortcut/appPackage/assets/icon-32.png new file mode 100644 index 0000000000000000000000000000000000000000..dcf56db7089a10edc61a0914be2af6736c8c676c GIT binary patch literal 2386 zcmbVOX;2es8t#w~5{5vyBA3zx&_zkoImjg-fq(=FA_j;fD+x&>QF53B2?{%Mh=3@& z=&Inch^2TUIO72_Gs>}`Fs_c*Dkvg~sGuSWjvP*-;_eS?YHPcytH1B-=Xu}fy{f*# z=%|GROBYK300^RRVGQyGr(PTu08p5$h$Lj=Yz&nemui!Z>2f^`2$b3+7!;}HE8!Se zuFP0-3HAp7^jeifYLtp2`3kL?DxbnoO==y&1_1vBCY@Zd8a9GS@Jf{?fc*0O&ty=g z3?Rq*i6OBr1Wr+fuhYXz*F{Mb>sBjxO7en0(BH&I45(qF95ks@H3q&Zfc()eA6ZWw z)5zdQh;elQ`I9KAI2sJm>S552%BCpjOfJabQR#j>E`#L@G9Wse2JvWg76qd7As!#1 zgP$KV;!Uqi=En%bKKnv;0pt{;QOBpz($mwa=}f9tzmi7h@pv?dL1Qo|2!dkB&=}<= zipJnMZ9xbd6nd4;sM2b{DU0$XZJIHFj41sSf?6jQe=)2vd`=XSGMY)QqtU4lO|71a z>m%A=jDi0nqogF$*_8SZ>tnC0cFj zbQeUYXpLG!idF}PgiKYP1O6#qrBQ0r4L(z4`V_Sg)~nXS$}qiF4SvisU-cCY|6BZ@ z_{#q`e>6lCZK_%Rt9ho6kdB;Mei;R1^JSP|4KhJ`WKNU&xk(?p< zmLEpzf`USrMP&qsR8qPeME$1Q&*kAxx&K*hynCoXQDAaGNHAFRbmcq z17NnP>Hd%FdpsqU#*&XU==W=1`)9r1-ZT8e6?*289-@T6Z>%bzU^vJ8sK#m?#=QbCPbk~`Rkez-iyA63Aq zsM~-70XVj!)WacEJG6guH%;f5T9ooze%!%6?84n@mEPxY?TKqd3)ckiiym#4YCX0j zlskU=&Hf7Sb)#>q8%_p#+PD!jM+3XFp19r4?4Gm!gqdc7H)7}KKd-%XUP2?P_h!*s zo8tM!FIlebr+z8{?@d@7V4Uh9_3q~0E%WX4Mo*%vDqje#x2)flub?N~2kHpdhr$V4 zU!m+TK*a)o2Uo7F-kaw{U#Y^5N%Ar_ZQSMJv{mku^SoYGKHDJ_Renei8+&y$z;DT| zr@ojj?{-E(Wx*3ksaN!EzyA6whNd&>3|@M#eedkcX|>W<-8jB3@E;*W%!76#mg)~5aU+mhL#c4#Je4VMAooIGr zg*gR}Sz@xZDJP~u%ZRj`Y^<#yd*?X%>FGr?Y~mJxQ2mXTdL!Z4t;GK5y}28$BdV-} z&kAqV$HtLFFV{Ex4ZiC*H>I zOXcO{?~krHc`W(+BM}Gf=HT+pm$6+4nnhlAM-!;?TGvQU#4UYl{MOOZd|y*RMq^~S z@x#4=n=8WSZ;PsGI^VeUhn`?p;cPeSVf#TTu6M}#@0(ka22T11&T*JzKAKy+=S88A zoJHCc2|l>JW_!3<5%y`2`<_{)N&8YypR5fRtiM3}kD$?m@9?AT_4EiEs! z@z^D_dyLa2#iI>|Mz@|jC3rtVZ^u#3gm;lFQ~S1}(}kRx#k;@`yAfPTU~j`!FA07} zN3HlBy>&>P+j8Z}aIP<(H7=Fa7y0lglD_z>?=Pyy(Eg=@VXU>-GXhgJn6t E1JVSZtpET3 literal 0 HcmV?d00001 diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/appPackage/assets/icon-64.png b/templates/vsc/ts/office-addin-excel-cfshortcut/appPackage/assets/icon-64.png new file mode 100644 index 0000000000000000000000000000000000000000..41051fce805b62ae4b779516cb29f9e36e1a9652 GIT binary patch literal 2112 zcmV-G2*3APx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2h~YLK~#8N?VEp0 zl~owWA4L>J<(EGe8O0B^Ak;K)W{IPsvD7BZwxlAP4$B-`wkeiSTdY|~HUH4HS?Oxh zvdz?GsdKpLQX!Y(QpYq)Lb9RwBNajWe$RXEE5fK?Yrkahs(=-p7%LF z-g6FehufT9+WM6V6DFj?JOtefr9k&VaZrEg7IYc<2dan8LN(AYH8nMLuztla0f57? zFqzN`PzE##@`el0x6m%=I|QVKmA=HM0f6JFFms{V&~VoI5N65e&<6;_U#i9j_#^;u zJQ3zyC=ZHcouBy{T8cpYX%xMuTLI9iz6^RDieOz2aua$FDnlS{s2VTuIskC~DVUF; z`&GsN2Rq6a5rm(OV%O+$0O0rlnAaetJg)(Fupq1-51Crn#Q!1-{P523dqUIz+& zpq%*l_~?d)h98Y8Ygoulrdi;U0eJd+rQHt=R288E`+y{5fzunLSq8iUg_`q()8(bc zWjjt8pnLvbp?g$C=y4w6h`_}aMaK-thCeVuI=ulk>I-b#1;g|srLh;6>XQHB_q<3$#{xc|u#lyC*QagIVdH?OgByF7n z;O*MITLujpBqK+TWb=|yqdea}Ha1qqjTO4o`C!kRT}Brh*du3o*W>Iw@Bl|u^`E|lxn zxwa`Xc<^9ZyLPQyx^zjE37uWCWQmlNl*r-3hjpz{zjEbDDJm+G`g-oHS;FqCV-pyr zZMVtK&zJJ@awq4>(W6IY`}XZJeY!E)4jD2;WeT0OIZrNLyeR9|tl6T=?C3D1`2>#~IihQv*|~G4YU?B;BSQ`xIG{41BPULr zkefHT_hFElnkokm9@Hgg_U+pzW5#r;54U$Pjsa-Xwp&C;M@v&vlia$+O`yrSnZbJ=KYm=7Eb+ItP66=AmUBx>i)`4iL6$9ZU4Mv=kC&-ar^=Qs zTU7bbp+l+{=a}vYXPVQdP17ZV%F0SL0(8t6Hlvv{XUdKpJ9LdD?7rHU#qs4HhIz~= zd4|JSOiYZLwD8N@)YjIf#{AmaTA4Csiuz)7hF_9HBZs_=8#hXIb+x{es9v4DJ5!v^ zgZE*NzkBy?H8Pu_82Ws&@vni`%la0&otM@Hie0CID4)zHWJgda#p0BU(* z?*mZ5S&L6P!tTQ6Fk{9Hl`(#R5BWFavcuLG`T@QY8?%%iqPVzNO?WtbvTbv^%xUfA z%a>JW>IXLAVQ<#gE^C6%!BiTho`EyCoSYoxID2@$Jh5G~Q@e4)>ys~@u(yBGF#w!t zaM{>r?*#$IA5aA@U3nev;vNIX*0kKpFTANA{pjYs1Wk?Q9C|mzRt3pb78|L_$6%- z2;Mgznh3oF4Tijdt5ZKgT*KQ7$D3H`OM0Z;A|U*C*+(GTzs>S622WXWbAj&~-1R&M qamR+|d5=Ly;4sfD-r){@L*zfZno~maY%z8K0000i0G&1d3xUWkLUc(d*1up_qo5<_r5;Y^|`k5J?BoeG&eZNA;(ExzD7Bvu0@Fh{;?j%p|01dH~`X({BH&H{(Udaq) z7N|?|@-{plOtL<2ZbLZlOTZAtv^3%BRJDBr{v-+>PWAT-2vMVIi2Z3-ZGZi1TTTrA zrwYYaL+me74rZ2cU2-r9u7p&UCCDqN!c{Oxc_oZ0T2Tg$M#(G7p)hjtin1tqH55h- zB@h4m65IC{O!QE*!s-9*YhTh3^P*4!)#T*D!^4r`3P^IWr<^iQdGhH#u^2LQ1Af+(r@41 z`@a9iV*V>uO*fc?r;vkf$Yj6YU0~@&rjSFt$boQO-CtE#flJzZ2N22OAyU7}^jFk4 zQm}UziKrh;_J{wOXEpDC;qZTp|1Ui8|C>L#eNuA2n&p2r&+o1Mj{LR!XB75@e};(^ zus=b;`=jz+J=YWf;077tur}1O6w8)_&2R|F>ZHOllOupREbH9TG?Q<5J2iFWY%dKE zF7#m#!*+36QmS+GeDnu5qt^$iqMaHUD!Je7UZ1^)_I8*t8I9z&dU5t%mcBcL1@;)& zdDPMv+b<9gt8z~SJNU-Z?+({SfEO0tzUZPnl!eh_oy}9|NR*wHhYitZRy)Jf(^Qi+1OAG+m1+}u%X3NlSfF{?1 zVN*DM+?AtXu_s*U+Sv9zQ2UQjbgPiqL0z^BmQfSkV11S#G%Jb8`6Sll@u#KClNRX8 z%1RUj0zs{=uEr>gT|Hsaj3>FdHN%93Q|r^U!BfwpSbc%sdwaX5b%{iYwk#>@W_Dve zy-Odu(nO$_xrBkqySqF5O3!XRE(a2)I_E^vMN3Odp`3g&Zf+hPoe#kC5NdxDYkR^L z^dpn$=FmWuH*c%VX7CrqSs10HsDF&_1Dd28YJ)XS(SoH8Hyxyx1yS=^;4yS$8~uRH zN%KNC>h87@^1FeyOfupr4_%{DiVFg1zP14&X@yHf=r@GIV3L`6yL&SV=?AV|Fg)-x zNMNfVu68RefQgmHRVqG-4YNV5vXA%(X;fu$XoHhxq=O_*oDe{mT?3J$=0AU?`ARul zpPQSz{6;&%4;(OB8T;Y;`W&>bt_~{a`W#eag>-3vl*-aXF&W9poMo~0L+8M)Jv}|l zComiPq{PHn-Au=qy}5Ew zO-)TV8dmXEA#em*)|b(%85tQ_X@ci-Fa2}|`cj-XvPewCLNxA7v-w@SFM6gkY4;H# zk5I%r?Wpn$pzUf7gvRF<0^pP^yY)s24hNR_$51(~#y=g0IRU?%a()T`!8?6i4nvB! zI5`d-k~5AIZiOx8OdG*?%U4}tz%PfX9G|C9f55%wuEf>Q<1E#wFfFd>upY+hwA7cZ zLyQ$neQNTvM>n;oi^v_1S(WwKnazZLUM#HW?PpoRv~vhzsHt|8ddfLgHV*u!g?3~kSh&2c|ku1qDf(Br{l z<=m&s`{D C@m3MUi7~72}#JBl4)B9XvmT=9V^h79P0usk-gVKw27uZrGLCJ_mh> zovZHiV9o8-TX~g{Z1E$k{=FMIU;nW0gj$Nec%c2k;ni*)^;fSN4JC9uFWwuD^{F-% z?w(u6$>jD9g_l^}+Eo%vwGv3~03J_c+uqg8>LsnUBn$Jxt*<)eGi&(bRDpS4j0P7d z5@nY9q4nnmKX0sQErPBT4===@Z`2f{_*9Q|Lo6CXctlNuwefj9#E+e?P*#8E6+L1vh z;ZNW)OZ2E>zhyb!bZ@-vLF)%p`%DnW2_(vjN-#u#TBP>;ORXs z>``HxQCeDl;b`g8wi zhorXt&(_Se=j#JXI!wI<;zQri1m5>hj@2|ghcj=N?0r;4%jxraa&J$qmT9scBNe@% zJ_LS13?T|7R_<`K9f8C(jkV7#r<(JYgSZwZD&OVQ9H~W|^_HytPMnzdU z3stzOM19)W_%Lb96mffyt-&KS)OK-kk>1v(`^HJP^78vYj>RWO*ix_M<-tr$Or(`J zzkO@}`js)iyv(Peq2cb~p})Ai{9u*dHN!!Ud9SRZ($U>bm&`UUc942+}^Spw(B?qcmp(lqGlW*jyMJQu1QnRzO7gk7BahMucbd_HGy50%30UC|wP9o`Hjh=Gf>HPLs zu(!8YEVs9GPVt&lC-r${Wsy6VP*->>I6wY!)S1ndA3xLqXhhR|`%dmBS zq!_$ZH!uH4&;u0CHb#6;(t z?rjPr3pO|;Xfm=XEH0-Hq!(+~&& zA;uB;FP$n3|mVGM94){up#|l^77Posljo(H&LCw7LiNr_KjePu&+g0(H~F0 zy)`1?V&j~BcK!AZD5T`)2pwDHjKQa+ruLvqZylh=*u!FSm9}Z0KKG4w2^`4Pr>{HO zn>9mTOu5uARDX8avBD16{y>ye6>%#)x`z^HpL~5ikj$C|YJ&2A3b8M_xVCq*@!Pdi zG3gm6WBi6ULz_3O4A*l)Ty{)10bcX<5q*u5=Cx>~l$UEQWNeo`lZ`S!yLs7d+gSmi zUZF44rV)`OEPbe*yaC@-^m^P%$CDc8ZJ0nQB|)rdPWt?nDoQ+3?usa_7IiQtOY=KA{`WP;y5ejN!D z2?&W$Fy=We?l+kld5!-;B99Ruo-mZju;+U199ErpVHzkp0gu7)Rv?yABOle(VeF-| zLEVT|TS3`E#w49}b;Bm$!EN2v(|dk}62QT*VRI9~p@Wf~A_d83MZZD$XE72fXQFTO zJz48|v~2NYrPQFPBjw7KXWzd^;LFo9^}HY)ZOhA#oI80kKYE^;YHk1E+8iKo{P?3Y zzD%|=*{q$M7}rWpK=K|H{D8|(fG!bpkXp%GG@t&C(b#x>BP@>~!+YaBJO(yCz{euQ z0{}s`?zm#-@Myls_5>i`+erfh19Xsf{D;bh1{pW&_4sH>E_V@{4r@U{0Wc|_JGzO% z_y}fWE1BqNZH@a%qQF$8?rgSY#BF0a9Bp)>QlJ7cdnYEA{*aKwvAc>$v*To?r77Xz z$zXxD%j|0k4ZQb5vx4GIDQRddB_t%^+VUg>2?Oc}b=lfrUld_~gyDb(PpD<&Nc5J-?B==FI0bs zbTjT;1q+lBbg}l2eIc5_h&5LR5T&yt;cL6O`fUZ|c1d$)w>TN&8 zWeeG>?l_Kl$;Maq^cV%9XWjgqM{y1EE5)vpE%jUVH-qwDiAFDBD%fl3;S(=PEvpv& zbK{(S^)*UIn;l*DPVn7j_uyv{I!UW3Vc}{zxgj(nKXyzRx!s;K?QQnMaa!P$_)l&u zfV(lQk7OF!>oi!je|mb4o?(q!;`AW+`u3)#q|k-eeO+U9Qi^!libP*lGn4zkFiR#k zL5^jxkbCUX5@Xki)(tQ~-hsHiE^xKejaZiOD!y)aP4YtBsb@Mo9jspzEs@bdo1j(6 z=(VV8F%SIuY*?GT39Ea7Z^pY;#IAb*zsl9sPjE8sTQ0}>PPh`nMcGg9XtN&N@dpTX zG1&dXQ=>}i_&vC>=JL5_KN*r#$rl2-#iPc2K3(>L)h*UP-{>@l66I|-(vxeeX>%qW z%rz-cs?Kq1$Jkz0ZJwj%{GssCRHu_i6_wlUaNoZ$ON$)=V2!udYRxS6vZ%Gl%+0{T zW#oL`>I0Fv?{WP=wzQplpl)$BD&b1>*>sh;EIHfkBsrBa33WN7_`c? zb1!%$aJZW4-4({&OwF>3#Zq?nn_q)D%qVgCjRn$ztEAfZ*;DV6eequ)$$~;O_431Pg&6!5Lt12ol_r;2Io)210Pn z<2(1Bd*2`LyuH@mtEH;Cs=D@C^{ehUO?5>)Y)Wh-BqTf~up9&l37O-c7xTsQ7vH~o z`+0)GQy%K6?ef;s$HLtjNyf?rW(`zwvaqp+SX)^6xs6&&AR(bL+vz|(p=zpPmM%_Q z7XQ$2`8v5ib0Z;1Ncp;2SUOmH0%6uRcFvLvryXAyfOb}r4ElmeutiKl}k!+$9S zRnr8@y0}{d1-XPcEqVDxfWo3&yn>=4d;%OmJ|12nZXQu?UI9)XUNIg~F&Zs+Q0=i&_fN1_GH#miHY;aSuFGQr99f5bX_{I{E)9mefz;mXa+ z#q*Cz|0YyZ`~Quzn~ z>Ef>A;^O#kFKXJlc)EDly0`*mW&iP-Fpx#x&e_Vv$Ak4>JZfrUO3ogh7S5K|N^+76 z&r-PT?5xCiWOxLHWfeejd>|QKUU?xI5gvXK0X~qhi~vYPmXGh>ymBsjfI_~3j0JTKDZVz5!Y56*x9Og%KG4n|4>F^E{)=Y5N%e8hZ!%N90Y zmRwjOS|M98WeoksTZS^klR6#dfgNT4#eDp4BczyeaNcMvKGXV^+uDJOhrAA!WgYS6 zD^A6T1|*7#oCCN2Ni2E$4PilQ2*2}BsxL)12FjJ`lS(POekY{It%_o$f$OJx;XnWz z>p^`v(W0iG9Mkq*byav)pyaJv8e`n8KEx@;pIS{?j!6hz{C&8=Vx~U)^Q zraWy#m16&}o*>=P+Fuof2G};IZl=w6HNU~aXj$S<01(&azovi91d+uSdU2#0O-tua z87+tBtcfI~-N;iqxPQT}Of6rXMyvIL??tHH0WjPfH(KjpW9dY5Sp zdA+Y!?`sJG!U%zX@r}GDwLT&yVoJ+>B$?9C@?sI`k0v3LW0fej70VwULQzK${wKrS zK5N21H>|53>+vM)i1=+P@zUH+(!Hm5rv;>?uMzZn z$SeDah*bNcmd%U_c>UxUy%qirA7oW)Hh0g0p*2JZ!e*sloJ?6UX-`iK$wl3$Fa#xk z=GB~1U{T}6&xpWe@*E0t1@N{Ll%z#`#Aj#$-;7GHropQ4EM8IFr0FFWmHZ|)m!?4; z5`uq9DT}_qejh$%0RWVHhofQwhk8Gk`SGA+%;Qj?4rYW8N>j@ge{=_)6njMEIOFAb zz8KgL6icS>?^Kugd4}6OZ%UAhu`fD*LGjfYIoDbi8_D+DA`C#vf10+)CLQPlL_Lz~ zpG5XyaT04t43+NN3sx|EF|SF$7>|;)i1PG<2bbkS+Ok|>jPhGpByKR$8FygVkN~A7 zOK6e>;Cmp!RYPJ$59KIw)Q=t}a_GH6KNhZUEEb0%M3uUNQxP?dW(w8et8F+#_W?jv zWnB7Y79k-a`PL0{*`)SKtIIn1@$oUw&+fC*3r?dJ9!(zS>vUwX)Dch$xsry44OmSL zJpg~DyM==PZRp;FOjzi#U8=t$a=6n=60oH0A(ehl#EuOAOr2>L!`v;bq-BE@1rgeZ z&d0|`-qzN(@i3S`#y{~6Y2k$|^l-JsiAw&7JmV-pP*9KppYDys{tx-= z9{=;7cp}XkC?hz+=9T7C#quI^5e=M`NEvvnODu}bd^q~&$XS;X5)znIvfmkQNa-xz zMJ&mWa9Sa~G7gjf!%PODmZ^#uJp&51UV(aVWlY{~q84(*Rxe589AR3^8afsgQzFgf zd7`}^ApYI?>6h0QS!EzHY<(*9**6IESae0y_#5wejA)srCl?@Ue8w`sYUId*8vJK~ z5gS5TiooA>rIdeEQ6}d}d#@AYKmUMwiK-+?&WwF7jA=$2zLexuC^qd6M!t3cywJo+ zzo*6XRiwl!)X>D&xm9=v3y;H3Rv50UuziA1drzVjmT+K`brRtWP(-sJVfL&wM~Dzc zZi>>y>SMmnE2Tsr5CEz;88j-w2yd7OgOCtI2`1FgHjS-R{MxYVU~26RJ4 z^p=mx0B`@!E`B?M5I!(5diu`k2L>D$n~)(zE`W7#;RpCHYP`$T9Y0etgVd#h?@Nm} zBvYyWDXwP_^$0BSs?j^8KS^n94aQ1pehez}6kGTwqv@B?ZRnfUUrmV!oEx}A$CS2q zCahf)AFNir^T33_|+@F z&N+Kty4>bFo4SP+kDv*2j_mQYKj>_jIG;DY^5u#U9Pg2i6}^K2O;JgPQ^Z(%7IK90 z-?fSyi5QPyhF&sX)(Z?@kdN*taTKjO4=Ypc@7vHUxS?X|P51-$1rQl>#Q<+vTTtYx z2rf%fhBA}2RnUSAsUE|}OCa;)FK?xKsE)$Cq6vCt&?h!c7UB_e9!-TT(pC7|MkUH5 zg=5~$%Q>4c-QIJk$Y-jm5yKf7Qf%_s_ZG;U1|rqnkV@BeSzU+h&(_GPR`LGi$BGW* z!8iV0w0;uR1@cCNZM7ipT+ZGp*Ni_RSA5 zw!UFQ^i`LU=Fsw)`e+VYni4S^;wdcIbPYFX;*pJklan2}2!R1@2c<-0HrylmU2|$A zT+PM!!3vp-dRGoC<+Wkt7CatoOMO{pq_-DzZrc4&j$w;%m zNxJ%h#e$&}MMLTrkAB@^xoGj5inB@iV!7eyl;IW5h1hfoa9#Rwd^DD@9-mfB4Q@+1 zWOuc^0!cjU-K@?TOc(tQU?3@XJmIVR_K__j#IyH$FDum(8?^fxGa#ERq*<>Nzu$QbxcRQT1ve2q@Exa`R-H|B&Cws|Njps}G}n ze|=2Bi9oK=zTsgfy^!+rE(Iq501}*3v16K(+!mLhU z1&<5qzi?yZS9{@AG3(>r5QU5nUIKZvDs!8w405#?5kfDU@?RwTv^xTqzrs4MY$*e& zn`C9~qsu|UNH%n2WQStfvXBPrSRLlj#E}L1y>=!EwwL>S^`wHsd05Li{RQ#`uQ)weNCQX$9!CZa z2T9`H#3nK)+Dz-WH^p8aN!p=t_1n^75T~2y4Pxn}4U4^c2#Q!x$jdD7#kzqS>QIJj|3PWnN8}e;9naMQj@147gd0xtkZ8+X zmee@__V0ssskySc17?L_Nv_np=*h9are*a?sDPn?IQIio0s~PG3vWM0?Bw6&=mc+_ zN@!cYdR)6IArDW;(P{3Bkhw3$h2>isE#R|kEpeqoB_MNT(A(B{AnCh!W*~@lAi? zYepc#M$_2T#26hN{n`Jg7O0H8#&#e#FHaRNh%5iAy}kWp-3?9WUQ>T5^**XBAakG0 za&?cbawD>~N zT|->-4Ry66T$0pGjK0WrB|Ms-lJ|VN`+b{YU^$8Jo(e5U?T(D|>e;dK^&=A#sO*XWbUs25|aB6z1kCnO~Fxga7UVxI6woNSvt_eH!b zmzt%YLKq?qf!UXwoa`-iN%9GAz@1H0c|6d|?<4vSCY;DiRZY!Hs@{DfzO60B8BoI1 z0Fv}!X{Q%K0A_$#S zb#F$er5)I~B=KeS=EnDWh~>QwcVrO7CD=wSBO^?M3TLA>8nv(RZCRX%R&_atdSQ?> zn`+WynPLg|>^VKWuI}|o1W}pROyF|)i|Dh!#q1(OG06L^+`@cP-0g(Ks&dx;eZ7~{ znXJWY&Noq&=J*-rob1`UMBdl;ipIa9jUui{W*F#x?(Ka~f!NWai-x|li||1y5q+|- zzoqQuyf^?T?2y}d>z};GIt;6A0Lgzi0PuR=JRT{+>alFYavX@5_NvgsJ znbX1Ijv__TQ40PL`lVMB@4H1f9~CVIPlfW0``x!`bivVA)?Y_GQ(y@WQG;LPZAIQL zt_T!&ZSlrEVJ_n>Uvjm(|G^th3en`3Sbo`;YVI9(W1K8~=v8>LN>D3Swz--ARA&6^ zFtXoABjGT9tc#9s3({fYtM|=ZdKQCx&@?ki3%&jVg#h=3Miegl?*gRb{D4GVMVet7 zoEG9zZa3a1*#i|aC1K_SP$g@94P>lTEzU}5(8kR`t;DNdO#m1+WQ(Ia9jR!i8g)25 z_Ci~)0U%^hh&hj!M44SCB$x^qk3V?F*>W&{-evRl`ncix<8Z#mw~4&d*YAJytOb0_ zTR#nW`*Y&5kTZ-vgU9#RmarG)hk@xWT;Dl$mvS@aR2|Sy z^`@I^+@P+Lnq^pC#`0k}_amj5w74o^a5uMO{zA^iwf-2&R8aTW_1gJZV_!&GcZx|- zz>ob*^-V>TLZIC#H&Hwr{)j^u9zzGpo`NlocR;LV20U3k*m(A%iVC^ub=|qCLEP1iP;~dCHB+K!UxvI9bheOs)7@h zNni#DULI?WD(IUQYypRPX+oC}(v;L;Zrj+uH{4P>*T`7lo;gf-#7L^s zHz?ru>RSyJPkUxfgckY_^a=7qoABbmd=Ax*6bxV{g>Jn=c zW1G<9JqyGb!%s`uM^@_bE&4*~Ty+UO{Q7{qx<}+jZzd{g@!0Jm(`)1MJ5w1p&iK5| zwBo$`nw*8qFO4bTd5<3g7I>4d)qjg_axe0LpdZlA9oywnu=+q`i^^sqie_tl{t0hf z(_hq0jHp0;v3GK%d~O3w`?jQGy9VOnu*f!gZnd zw%QE073vly3IRk)FaY(GKOO-eH~wZ^w&ne6hGmY-~GZOQMxcXz*W+Ou_broHpg&`4SA3TPE^--(?+!u+^>ywteW6NU7WYcHMqo26N*#KV!cnjEzb z2_5A(j(Mgkw(FqvAjcnVywiT+Ug75V_tme96lipC;y&0O&DR)UJGfQq+;uh1;CcL< zpdgw%oU48nXW#TrTucnqA;rXo$)v1Ol3XW8qQ7rZ41o;`Mp%z$Sv%cDix`x=mdUQsgK)rA3$+{LrKJ(k_`uJr-~F!ij=9u3na8v1?O3XZV?CHM&j$h(JN<1pQEQSc_;h&_GyrdHu@ z2_{dy=jr2P6YMP5UispT85fViJ-#pAALr_59Vi5667&6Cm}M9KT*FBL*uUu9fg>^q zeU2B?ZpeL?sG>?Ek}>I-D>duwOoa-)p(b=^{FowB8+xA z=N^IPk}P1jik;{BnO#R+X&N1E{ZZFHpD1rG<-V5ZP-WBax%LysGYT|arH<_!NtGHT zoabGMEX^YtPOEazUR8w3h1q}o{a7L6g*hm5O**#}n}-xTAG<7x;liT&=MXn8x##Z| z=J?obsxrANBd&>)MC9)r^NhE|!=D7TwE(el`-g{B%bc4xzS9FP#bQ)7Me&i+IK??f!q?d$hHVJW;Hit|%UAb)L3 z05ksVX~#v$mZ_X(1SfN%b?J4}G6a&~(D@YN6m9s-F%WLp2EMivKpXPB={ltB|Mu=( zg=yRhEQ|nM$=YzBi9}EkF~&|XY*!!qc2?W8=1ed8%&JsM=jFT$3r0{y?cx5^CoYqD z2c^itE)R$=Owen$!MO_Sh%O_7HatO+dMiaKN?ssrZt{?ImYxPn-harL;qeuWpvD3y%`OFXQWJC! zdq&PQiJ61IhqKuqeyNubBt5OhKF9Vf?$(slLlOPPp zCo@d&KkxW{DKV~iVIOrgy*|5fnB^XN4-AU%=9CaFr-v&nH&5dF@8%0cdd0VJ3e@ZQ z3NePQh$q9^IxCM3zO}-L15^%m1`riP^ixh&#&+Gna&nry5TrRCOZkuw9><{c=mma z@}BtRJvG3*k?E3jh)DmO7=E$CE!iLR{7)M2a+!rN&sz2e@<*897g7?Uxu2XIJeg1R zL=#HEihIajI5G$n?E9cA4i=+er8hb|JB*g?9clV&U5}P_d(;fWF(a;C~%ye|GYMm z^`MQ=z2CfdxySPxTAhS7$Xa#mJlVXL9nEt89b$=nTP{kCHGzxccNzSp{$DxNTWRjg zv1`@poJw(Rc)FJ*WOXdi3oI z2UEM4w^VEnGt6ox(+D3@Rn3?kukTHj(?Tat&H9vghMRSEo|$H2N1cO1V=J9CKdMdJ zjVb?PDGo%IZ!P`F4YoaxYHKjFNbnr8s`E6`^N}d|!F`nC*J;TmGSHmFLzwRI!E-vZ z@5dwQ{VuRm{m%T8!(-dHIGmw%W})PKcH{azb%7%t;3>gk)>=8)s|W${HFm+r#6$nU z04KSdmnmkt%hx|4dDpfb6&b zqBQw%NgHNbQ-}Brf;^M3r`!Rm9Q6=8OfoPKnci9$E5>SzfdSh==3UNq4(=wC4p2~j zM3sy8LW|5{fN%F7&;NdvK*d=P z&c62h`1jhhpw;R*J#a(^VSd5FqK^!h$1E}@1jT;cX+kp5*6z<@h$Lsqmc)jOe=yP1 zIiq$m(>CobsD+ti=&O&vxUyUM)YY!c*!mzRF{-&f!@A`|6U^mGYUYLv~Iw3}+8}X?dq0ln{r)yD5b#suJzb4}w zgyEVIhcEdf4I%H3w-eREoNmN%1^mrl(;{!6xa+?cAaFR9zZrY?+2cj>&55cI0Xln4 zVax8a^yH^PGPa1ki88n3cY{751UHR(^}(#@;}$%GPat5S)^vl^2d3tWmvr|}(We-V z1}xB)@JE$#6yyYw3R=gk36%9Z{DFSHI6_dbGTQ8Jhw{o1QED5&h5SR5KtE3$;Q%(* z3B_w(ITq;9O;bw&`)REZM}$<>FC0hG^w*1qXGU@K$MauQjKqa-(AlwhdhZJVc&luG zZm}Dy4ps#dn;T7OWcug|qB(YNfpF2QLxCWTL)2YP3;p_hu<8u%g7O6Y)mxU6cO~Y{ zNZwLk*jGI-O4_RP7|~&;dU5gtJ5V7kbez1JU-G+Z(Ok27L#e>5upDC`Z@lcPjV7XD z;j-Jn?;KKR+Yu@%Lyf!uJjtZrR%44`Pck5aCoc=JxVWFIUvb54%MQIK8mD4xVTH0H zV;EjL1jza|7hx*B>|$(L&w!Sji(*&Nntz}5-_5k6XZ#wd4x@Q_`H2O3;@ahzSxs`S znsyvk14zgeR+7pLx8`n*eMhS)aV3q}rs%#!ik)R5$tJSvJ@lGh&Anr1gn$(~s>Ql} zi9wzcxie75Xv4+|JqzQbxm^ATk@zeP?ruu^QK^rg)#;{$(Bwoewbv@!CLW@pmv@Wb zA#$(cL}xd2EvA_x;qnCBa*Z~t6T|y%l3Er-9$qod zTWmR`2N1)lZ{u4~mxuU+F_qAE)&kNKh~cC?3a$Ce>aYnwI?HQit~J?SG1A$GrB4vw zj8MR^jQWXQ)@*#E4C2lXBSlT@6kU0 z3Fp%s=!rD9xDO7vOH5UrF>UIx@4ahoIX_?r#oG41YQcU^O+HG~5rR;RYn&3D!lOP3 z;p|g^z%L7T+%Z0&IaIc6?~^@u&5>)DNqhS)7rG8!g1|VqlR~U^y`ah*(F=5XMvN0( zd1o&cJp`@J4;OjNQ9K#$!(>IV9jSD~oELO- z0D;kVf->p=y|B}G0Ff^Dc!haUysdrx(eqJmi~Hr? z*-GuXXSkvpgC9i`F7=##8rH+244Z7zs_N=AcK9z2W)$%#1;BVTVoe*@QV#QQ=D#6l zTZ2_bt)K2+maBaz)2lI*H(SKus(SAXLbxKc)n#kMs&IGdsC|@Z-zdSHsWlPz+RfA# zB47Lx^5yg2r~4$P=I5mceTOcz2m5B^)iyZ}Tk?jzJil^o;z3Iu&$e`Ae6}nOrb~wzG1EUOrqHIn#_aLfYCm(L-RZtRaRAT5{24cQ z`uFx%T3t%|Y5qRe86T`Jtvmxj1(>`URO8!n`u9`>Rq234wSJwmfN(hdOe(!Bj4Bue z4)4-nJPJd__(R(e6{F!6h@VI*85L)#`V&Sp--{KZ+k+Rb<`nCC_TbqgWQ81%bzhWAs>mU1Y+vVq$7DSJ^dcD zx!9j-M^_q79>y)=ks^P+pKp%Oe$?$6o7c=l*DDs*080*jyc4{mkjdTonD?x>&>aO9 zF0Xx3RTUfi8w`IrgX<{Srssd`7cs9w+txv@paW5m6nN5gnt+_AaBiKQo7?4r|7XU2 zf>q!q;B_tlj+#N@fkm_iw(BRlI$7*3{2DeLkN$EPbH1D z-xD6CKGj4}qg30fUFxO(oYkgU(C<$d3z9KwV}QCq3@1WG<6Vmvc@)F&a%7%C?%LLN zJuaPTet*089Ye5LTNVm4ngdzsAoT|!C;JNz-%>VbL=#=!cyEadmxt))wwh$XRTD#R z<33Kzq8$ifS_IGeR9WhPW)DfES!pBRd8r|hHghU@f83Z9tZE1@a$C@ zX0#*uv3LVl1&%xx5i+v9B7N#BJX-3tddryHXK-u!J!}|buOZb{B%5q_5-TS10B3K} z9)y^5sxF7;kk$OY<-H)EuQ9T@z@>Obb1UK^*AX;pQM9Ns^3fYRf^sO_(#=;Cfff$6 zpUb#$d&lxNg)rk|DsxZlen(kLYiDh6w-f&!*GIS_QHswee=;9@4eh;7GTh>1Ql&q2 z*z9{g@Y{iH`9Iv8$vcVv?wxdbK5SECZaz?9ncl#`xdE>zE#vR9L2uV`BVGqXR$Q}< zu-1dMUKr+`ic1_|BmX8iYH1RSE>bWtF-hpk5ecE$BV&dhzl?-b7Hc|jjfSJiztru19G4MjzY5nw<;di86DlFvkwugcKq$GkbL*e_A-KE zcjW@r4*ysQ`&4Dji#uJa9sp*mm>s6(;7U?uL5*?P^!+l%t4^n<=~2!JlcgUjFW zl|rIzMFI)h-#r}7qFr;u7yH3@5@J^28vxBVtbh@__b$7oz_ug%4ZRu`nb7MPVV1pR z571nc(@Klr15L+j`_>*uTU^V&=&2B47X>kWU+tXC1s<|uJn&L4Me4QKzxJ!vPSeOVA6 zvis_OWwwZS`E`jcVY>elL;F+xC|R5=;x}?PtBE`aEEW?cshXf`9KJhawgS1!O4X@X zbr&bydV>gzYB8H0jx0ghaff>ardJ?BBc}DI79vHy&ZUpnr|T%?L@j@t@s13BJ;0g_ zK6#Tnknq}||B%VadfA)r1*ThgetIJ})8&hFuFpPDDrY?3*m~*MISIygf00pP3zf`C zwp}NY`&c)bSlvLzb_47F5A~IBw;Ca1mTLyfNC5jHo2Y@qI8{RpzWC=4>ytvs^&oJu zISC?QT2^`mecUFjr6pd`A2W_>=LZ&Bk*>5p`uM5SCYa!GAY?>@VSr_%UWIXQt@Tpv zv#a0&T{BIc6P9H>;1x$2wUOmLmDetp40RQCDc%Cc0&h2FT%_1&r50Os#JB+wM?r{# zxRFu&AFeK774V!+4r4{Tvq1qqAs`lRFZM+BKAjyaxoD~x+m2TX|uSN$K zD8=tDYZY2%Yo>ucq$h?#zZn0y3lj+Lna&VLDEQeFeVc0eaHa|wquteoNW-Nve-aqx z62_cNUoq{{h*rD}e3zlg$s1HQ1~zG*P820q#8((w46bNtr8pnpy<$IYGmIPfb8m3> zyOhHFVP^SFb*CgNP7cO^J8jz9EbEwfZI+TU1qNP6@bIWG=}+XA2Blag$IKi#N7HxB zYsBc)UOWVoCMamSq}L5f;0fSQt}OE7THifnbaqAiX0NoKcCg>e>&P7Q>;Y)pzz@S` zEK*JWQG0D36OFi5WO;;{UcNyKRw)NN5`CT;M{C=vXhox}mG~^{=D}u{FH6L3O(fo> z8@^zzg=iQJSPm}h#0$Xed$T-WZx9;_y~!F-5Rc2eUgRL=Fj_3EDL`XcZUNPE1!aZS zu@7D|5#byyJx`JKAkSuwRi~LS%&V;M4c?Q@AO*Hb@JMPhbk}OZrxbdXu|726EqLdN ziD3vSxt(U?IFgv%dT9RMye1|yXMs|noKu|7PIZ|*84v3&f1J8P{=nj`nT46no2ae0 z$NVU%bxyojNK}gVp+^jn+oZ_>Ry@czDXQTP(igblyJ^HdGXHD!P7?w4oOK8Qb6|Eq zeqRvM^y@h1PPf3(It-14)Li7Yuzus#LV$~b$gS~T?SJx?+jU6$QIS}f>90{VZ__+Z zC^r4lTzgaYC4Te#)KWwf5ql3z(@)q189C)Q1!)bOoG*)w6v#Mm^Uj(E*KrVQi$H^+ zPfv8W=ONSQe*9MVyBeJf3(b$RX9#XqIRqOU+ahEM1$0(LPCwtH3j$%HCjdY!&`1C_ fDShPPx4+0KeHZf>P92Q@Tn|^0SC^{=nTP)$*fJsf literal 0 HcmV?d00001 diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/appPackage/assets/outline.png b/templates/vsc/ts/office-addin-excel-cfshortcut/appPackage/assets/outline.png new file mode 100644 index 0000000000000000000000000000000000000000..e8cb4b6ba4f726d47a2e274f16b6069b9a8041cc GIT binary patch literal 249 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?3oVGw3ym^DWND9BhG z%|CHbgX^da?eHs6`1kLARhjOac zf3;y1Iq~M{LLS3g_M2bU{+PBvomV=FH7$YTy5I%1<5B$=?>3fqI5P%5iajq7)W9SX p;gpazd1JnvZNlx8HB0WjVJ`J~Q+P@%pA+aZ22WQ%mvv4FO#n^cR9FB2 literal 0 HcmV?d00001 diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/appPackage/manifest.json.tpl b/templates/vsc/ts/office-addin-excel-cfshortcut/appPackage/manifest.json.tpl new file mode 100644 index 0000000000..775c0446b9 --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/appPackage/manifest.json.tpl @@ -0,0 +1,247 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.schema.json", + "id": "c1b06178-4084-4a0e-8f1e-7acddec18830", + "manifestVersion": "devPreview", + "version": "1.0.0", + "name": { + "short": "{{appName}}", + "full": "Full name for {{appName}}" + }, + "description": { + "short": "Custom Functions and Shortcuts test", + "full": "This is RichAPI extension with Custom Functions and Shortcuts." + }, + "developer": { + "name": "Contoso", + "websiteUrl": "https://www.contoso.com", + "privacyUrl": "https://www.contoso.com/privacy", + "termsOfUseUrl": "https://www.contoso.com/servicesagreement" + }, + "icons": { + "outline": "assets/outline.png", + "color": "assets/color.png" + }, + "accentColor": "#230201", + "localizationInfo": { + "defaultLanguageTag": "en-us", + "additionalLanguages": [] + }, + "authorization": { + "permissions": { + "resourceSpecific": [ + { + "name": "Document.ReadWrite.User", + "type": "Delegated" + } + ] + } + }, + "extensions": [ + { + "requirements": { + "scopes": ["workbook"] + }, + "runtimes": [ + { + "requirements": { + "capabilities": [{ "name": "SharedRuntime", "minVersion": "1.1" }] + }, + "id": "taskpane", + "type": "general", + "code": { + "page": "https://localhost:3000/taskpane.html" + }, + "lifetime": "long", + "actions": [ + { + "id": "RIBBONTASKPANESHOW", + "type": "openPage", + "pinnable": false, + "view": "dashboard" + }, + { + "id": "SHOWTASKPANE", + "type": "executeFunction", + "displayName": "Open" + }, + { + "id": "HIDETASKPANE", + "type": "executeFunction", + "displayName": "Close" + } + ], + "customFunctions": { + "functions": [ + { + "id": "ADD", + "name": "ADD", + "description": "Adds two numbers.", + "parameters": [ + { + "description": "First number", + "name": "first", + "type": "number", + "dimensionality": "scalar" + }, + { + "description": "Second number", + "name": "second", + "type": "number", + "dimensionality": "scalar" + } + ], + "result": { + "type": "number", + "dimensionality": "scalar" + } + }, + { + "description": "Displays the current time once a second.", + "id": "CLOCK", + "name": "CLOCK", + "parameters": [], + "stream": true, + "result": { + "type": "string" + } + }, + { + "description": "Increments a value once a second.", + "id": "INCREMENT", + "name": "INCREMENT", + "parameters": [ + { + "description": "Amount to increment", + "name": "incrementBy", + "type": "number" + } + ], + "stream": true, + "result": { + "type": "number" + } + } + ], + "namespace": { + "id": "CFNAMESPACE", + "name": "CFNAMESPACE" + }, + "allowCustomDataForDataTypeAny": false + } + } + ], + "keyboardShortcuts": [ + { + "requirements": { + "capabilities": [ + { + "name": "SharedRuntime", + "minVersion": "1.1" + } + ] + }, + "shortcuts": [ + { + "key": { + "default": "Ctrl+Alt+1", + "mac": "Command+Shift+1", + "web": "Ctrl+Alt+1" + }, + "actionId": "SHOWTASKPANE" + }, + { + "key": { + "default": "Ctrl+Alt+2", + "mac": "Command+Shift+2", + "web": "Ctrl+Alt+2" + }, + "actionId": "HIDETASKPANE" + } + ] + } + ], + "ribbons": [ + { + "contexts": [ + "default" + ], + "tabs": [ + { + "builtInTabId": "TabHome", + "groups": [ + { + "id": "msgReadGroup", + "label": "Contoso Add-in", + "icons": [ + { + "size": 16, + "url": "https://localhost:3000/assets/icon-16.png" + }, + { + "size": 32, + "url": "https://localhost:3000/assets/icon-32.png" + }, + { + "size": 80, + "url": "https://localhost:3000/assets/icon-80.png" + } + ], + "controls": [ + { + "id": "msgReadOpenPaneButton", + "type": "button", + "label": "Show Taskpane", + "icons": [ + { + "size": 16, + "url": "https://localhost:3000/assets/icon-16.png" + }, + { + "size": 32, + "url": "https://localhost:3000/assets/icon-32.png" + }, + { + "size": 80, + "url": "https://localhost:3000/assets/icon-80.png" + } + ], + "supertip": { + "title": "Show Taskpane", + "description": "Opens a pane displaying all available properties." + }, + "actionId": "RIBBONTASKPANESHOW" + }, + { + "id": "CloseButton", + "type": "button", + "label": "Close Taskpane", + "icons": [ + { + "size": 16, + "url": "https://localhost:3000/assets/icon-16.png" + }, + { + "size": 32, + "url": "https://localhost:3000/assets/icon-32.png" + }, + { + "size": 80, + "url": "https://localhost:3000/assets/icon-80.png" + } + ], + "supertip": { + "title": "Close Taskpane", + "description": "Close Taskpane when clicked." + }, + "actionId": "HIDETASKPANE" + } + ] + } + ] + } + ] + } + ] + } + ] +} diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/babel.config.json b/templates/vsc/ts/office-addin-excel-cfshortcut/babel.config.json new file mode 100644 index 0000000000..f57bd9a51f --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/babel.config.json @@ -0,0 +1,3 @@ +{ + "presets": ["@babel/preset-typescript"] +} \ No newline at end of file diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/env/.env.dev b/templates/vsc/ts/office-addin-excel-cfshortcut/env/.env.dev new file mode 100644 index 0000000000..8043fefee4 --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/env/.env.dev @@ -0,0 +1,15 @@ +# This file includes environment variables that will be committed to git by default. + +# Built-in environment variables +TEAMSFX_ENV=dev +APP_NAME_SUFFIX=dev + +# Updating AZURE_SUBSCRIPTION_ID or AZURE_RESOURCE_GROUP_NAME after provision may also require an update to RESOURCE_SUFFIX, because some services require a globally unique name across subscriptions/resource groups. +AZURE_SUBSCRIPTION_ID= +AZURE_RESOURCE_GROUP_NAME= +RESOURCE_SUFFIX= + +# Generated during provision, you can also add your own variables. +AZURE_STATIC_WEB_APPS_RESOURCE_ID= +ADDIN_DOMAIN= +ADDIN_ENDPOINT= \ No newline at end of file diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/infra/azure.bicep b/templates/vsc/ts/office-addin-excel-cfshortcut/infra/azure.bicep new file mode 100644 index 0000000000..d8b03bd473 --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/infra/azure.bicep @@ -0,0 +1,25 @@ +@maxLength(20) +@minLength(4) +param resourceBaseName string +param staticWebAppSku string + +param staticWebAppName string = resourceBaseName + +// Azure Static Web Apps that hosts your static web site +resource swa 'Microsoft.Web/staticSites@2022-09-01' = { + name: staticWebAppName + // SWA do not need location setting + location: 'centralus' + sku: { + name: staticWebAppSku + tier: staticWebAppSku + } + properties: {} +} + +var siteDomain = swa.properties.defaultHostname + +// The output will be persisted in .env.{envName}. Visit https://aka.ms/teamsfx-actions/arm-deploy for more details. +output AZURE_STATIC_WEB_APPS_RESOURCE_ID string = swa.id +output ADDIN_DOMAIN string = siteDomain +output ADDIN_ENDPOINT string = 'https://${siteDomain}' diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/infra/azure.parameters.json b/templates/vsc/ts/office-addin-excel-cfshortcut/infra/azure.parameters.json new file mode 100644 index 0000000000..adc251f3de --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/infra/azure.parameters.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "resourceBaseName": { + "value": "tab${{RESOURCE_SUFFIX}}" + }, + "staticWebAppSku": { + "value": "Free" + } + } +} \ No newline at end of file diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/m365agents.yml b/templates/vsc/ts/office-addin-excel-cfshortcut/m365agents.yml new file mode 100644 index 0000000000..46eaa20222 --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/m365agents.yml @@ -0,0 +1,60 @@ +# yaml-language-server: $schema=https://aka.ms/m365-agents-toolkits/v1.9/yaml.schema.json +# Visit https://aka.ms/teamsfx-v5.0-guide for details on this file +# Visit https://aka.ms/teamsfx-actions for details on actions +version: v1.9 + +environmentFolderPath: ./env + +# Triggered when 'teamsapp provision' is executed +provision: + - uses: arm/deploy # Deploy given ARM templates parallelly. + with: + # AZURE_SUBSCRIPTION_ID is a built-in environment variable, + # if its value is empty, TeamsFx will prompt you to select a subscription. + # Referencing other environment variables with empty values + # will skip the subscription selection prompt. + subscriptionId: ${{AZURE_SUBSCRIPTION_ID}} + # AZURE_RESOURCE_GROUP_NAME is a built-in environment variable, + # if its value is empty, TeamsFx will prompt you to select or create one + # resource group. + # Referencing other environment variables with empty values + # will skip the resource group selection prompt. + resourceGroupName: ${{AZURE_RESOURCE_GROUP_NAME}} + templates: + - path: ./infra/azure.bicep # Relative path to this file + # Relative path to this yaml file. + # Placeholders will be replaced with corresponding environment + # variable before ARM deployment. + parameters: ./infra/azure.parameters.json + # Required when deploying ARM template + deploymentName: Create-resources-for-tab + # Microsoft 365 Agents Toolkit will download this bicep CLI version from github for you, + # will use bicep CLI in PATH if you remove this config. + bicepCliVersion: v0.9.1 + +# Triggered when 'teamsapp deploy' is executed +deploy: + # Run npm command + - uses: cli/runNpmCommand + name: install dependencies + with: + args: install + - uses: cli/runNpmCommand + name: build app + with: + args: run build --if-present + # Azure Static Web Apps needs index.html + - uses: cli/runNpxCommand + with: + args: shx touch dist/index.html + # Get the deployment token from Azure Static Web Apps + - uses: azureStaticWebApps/getDeploymentToken + with: + resourceId: ${{AZURE_STATIC_WEB_APPS_RESOURCE_ID}} + # Deploy bits to Azure Static Web Apps + - uses: cli/runNpxCommand + name: deploy to Azure Static Web Apps + with: + args: '@azure/static-web-apps-cli deploy ./dist -d + ${{SECRET_TAB_SWA_DEPLOYMENT_TOKEN}} --env production' +projectId: 47881792-967b-4b57-920c-cb3856631ba3 diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/package.json.tpl b/templates/vsc/ts/office-addin-excel-cfshortcut/package.json.tpl new file mode 100644 index 0000000000..884ea58666 --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/package.json.tpl @@ -0,0 +1,69 @@ +{ + "name": "{{SafeProjectNameLowerCase}}", + "version": "0.0.1", + "repository": { + "type": "git", + "url": "https://github.com/OfficeDev/Office-Addin-TaskPane.git" + }, + "license": "MIT", + "config": { + "app_to_debug": "excel", + "app_type_to_debug": "desktop", + "dev_server_port": 3000 + }, + "engines": { + "node": "18 || 20 || 22" + }, + "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 appPackage/manifest.json", + "start:desktop": "office-addin-debugging start appPackage/manifest.json desktop", + "start:desktop:excel": "office-addin-debugging start appPackage/manifest.json desktop --app excel", + "stop": "office-addin-debugging stop appPackage/manifest.json", + "validate": "office-addin-manifest validate appPackage/manifest.json", + "watch": "webpack --mode development --watch" + }, + "dependencies": { + "core-js": "^3.36.0", + "regenerator-runtime": "^0.14.1" + }, + "devDependencies": { + "@babel/core": "^7.24.0", + "@babel/preset-typescript": "^7.23.3", + "@types/office-js": "^1.0.377", + "@types/office-runtime": "^1.0.35", + "babel-loader": "^9.1.3", + "copy-webpack-plugin": "^12.0.2", + "custom-functions-metadata-plugin": "^1.5.7", + "eslint-plugin-office-addins": "^3.0.2", + "file-loader": "^6.2.0", + "html-loader": "^5.0.0", + "html-webpack-plugin": "^5.6.0", + "office-addin-cli": "^1.6.3", + "office-addin-debugging": "^5.1.4", + "office-addin-dev-certs": "^1.13.3", + "office-addin-lint": "^2.3.3", + "office-addin-manifest": "^1.13.4", + "office-addin-prettier-config": "^1.2.1", + "os-browserify": "^0.3.0", + "process": "^0.11.10", + "source-map-loader": "^5.0.0", + "ts-loader": "^9.5.1", + "typescript": "^5.4.2", + "webpack": "^5.90.3", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "5.0.3" + }, + "prettier": "office-addin-prettier-config", + "browserslist": [ + "last 2 versions", + "ie 11" + ] +} \ No newline at end of file diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/src/taskpane/excel.ts b/templates/vsc/ts/office-addin-excel-cfshortcut/src/taskpane/excel.ts new file mode 100644 index 0000000000..70d3ac1438 --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/src/taskpane/excel.ts @@ -0,0 +1,56 @@ +/* + * 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 */ + +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 = runExcel; + + Office.actions.associate("SHOWTASKPANE", () => { + return Office.addin.showAsTaskpane() + .then(() => { + return; + }) + .catch((error) => { + return error.code; + }); + }); + + Office.actions.associate("HIDETASKPANE", () => { + return Office.addin.hide() + .then(() => { + return; + }) + .catch((error) => { + return error.code; + }); + }); + } +}); + +export async function runExcel() { + 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); + } +} diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/src/taskpane/functions.ts b/templates/vsc/ts/office-addin-excel-cfshortcut/src/taskpane/functions.ts new file mode 100644 index 0000000000..dd01f764e8 --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/src/taskpane/functions.ts @@ -0,0 +1,71 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. + * See LICENSE in the project root for license information. + */ + +/* global CustomFunctions */ + +/** + * Adds two numbers. + * @customfunction + * @param first First number + * @param second Second number + * @returns The sum of the two numbers. + */ +export function add(first: number, second: number): number { + return first + second; +} + +/** + * Displays the current time once a second. + * @customfunction + * @param invocation Custom function handler + */ +export function clock(invocation: CustomFunctions.StreamingInvocation): void { + const timer = setInterval(() => { + const time = currentTime(); + invocation.setResult(time); + }, 1000); + + invocation.onCanceled = () => { + clearInterval(timer); + }; +} + +/** + * Returns the current time. + * @returns String with the current time formatted for the current locale. + */ +export function currentTime(): string { + return new Date().toLocaleTimeString(); +} + +/** + * Increments a value once a second. + * @customfunction + * @param incrementBy Amount to increment + * @param invocation Custom function handler + */ +export function increment(incrementBy: number, invocation: CustomFunctions.StreamingInvocation): void { + let result = 0; + const timer = setInterval(() => { + result += incrementBy; + invocation.setResult(result); + }, 1000); + + invocation.onCanceled = () => { + clearInterval(timer); + }; +} + +/** + * Writes a message to console.log(). + * @customfunction LOG + * @param message String to write. + * @returns String to write. + */ +export function logMessage(message: string): string { + console.log(message); + + return message; +} diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/src/taskpane/taskpane.css b/templates/vsc/ts/office-addin-excel-cfshortcut/src/taskpane/taskpane.css new file mode 100644 index 0000000000..5f78c16066 --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/src/taskpane/taskpane.css @@ -0,0 +1,80 @@ +/* + * 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; +} \ No newline at end of file diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/src/taskpane/taskpane.html b/templates/vsc/ts/office-addin-excel-cfshortcut/src/taskpane/taskpane.html new file mode 100644 index 0000000000..190ed3db57 --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/src/taskpane/taskpane.html @@ -0,0 +1,55 @@ + + + + + + + + + + + Contoso Task Pane Add-in + + + + + + + + + + + + +
+ Contoso +

Welcome

+
+
+

Please sideload your add-in to see app body.

+
+
+

Discover what Office Add-ins can do for you today!

+
    +
  • + + Achieve more with Office integration +
  • +
  • + + Unlock features and functionality +
  • +
  • + + Create and visualize like a pro +
  • +
+

Modify the source files, then click Run.

+
+ Run +
+

+
+ + + diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/src/taskpane/taskpane.ts b/templates/vsc/ts/office-addin-excel-cfshortcut/src/taskpane/taskpane.ts new file mode 100644 index 0000000000..feec6f9bec --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/src/taskpane/taskpane.ts @@ -0,0 +1,2 @@ +import "./excel"; +import "./functions"; \ No newline at end of file diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/tsconfig.json b/templates/vsc/ts/office-addin-excel-cfshortcut/tsconfig.json new file mode 100644 index 0000000000..8845bd0379 --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "allowJs": true, + "baseUrl": ".", + "esModuleInterop": true, + "experimentalDecorators": true, + "jsx": "react", + "noEmitOnError": true, + "outDir": "lib", + "sourceMap": true, + "target": "es5", + "lib": [ + "es2015", + "dom" + ] + }, + "exclude": [ + "node_modules", + "dist", + "lib", + "lib-amd" + ], + "ts-node": { + "files": true + } +} \ No newline at end of file diff --git a/templates/vsc/ts/office-addin-excel-cfshortcut/webpack.config.js b/templates/vsc/ts/office-addin-excel-cfshortcut/webpack.config.js new file mode 100644 index 0000000000..3c946298ed --- /dev/null +++ b/templates/vsc/ts/office-addin-excel-cfshortcut/webpack.config.js @@ -0,0 +1,103 @@ +/* eslint-disable no-undef */ + +const devCerts = require("office-addin-dev-certs"); +const CopyWebpackPlugin = require("copy-webpack-plugin"); +const CustomFunctionsMetadataPlugin = require("custom-functions-metadata-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.ts", "./src/taskpane/taskpane.html"], + functions: "./src/taskpane/functions.ts", + }, + output: { + clean: true, + }, + resolve: { + extensions: [".ts", ".html", ".js"], + }, + module: { + rules: [ + { + test: /\.ts$/, + exclude: /node_modules/, + use: { + loader: "babel-loader", + options: { + presets: ["@babel/preset-typescript"], + }, + }, + }, + { + 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 CustomFunctionsMetadataPlugin({ + output: "functions.json", + input: "./src/taskpane/functions.ts", + }), + new HtmlWebpackPlugin({ + filename: "taskpane.html", + template: "./src/taskpane/taskpane.html", + chunks: ["polyfill", "taskpane"], + }), + new CopyWebpackPlugin({ + patterns: [ + { + from: "appPackage/assets/*", + to: "assets/[name][ext][query]", + }, + { + from: "appPackage/manifest*.json", + to: "[name]" + "[ext]", + transform(content) { + if (dev) { + return content; + } else { + return content.toString().replace(new RegExp(urlDev, "g"), urlProd); + } + }, + }, + ], + }), + ], + 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; +}; From eb8314462fa7ad0e46adaab64c8294a4e23ab6c5 Mon Sep 17 00:00:00 2001 From: quanfuxiao Date: Mon, 8 Sep 2025 16:05:31 +0800 Subject: [PATCH 3/3] feat: add feature gate for CF and shortcut enable --- packages/fx-core/src/common/featureFlags.ts | 5 +++++ .../src/question/scaffold/vsc/officeAddinProjectTypeNode.ts | 4 +++- packages/vscode-extension/package.json | 5 +++++ packages/vscode-extension/src/config.ts | 4 ++++ packages/vscode-extension/src/constants.ts | 1 + 5 files changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/fx-core/src/common/featureFlags.ts b/packages/fx-core/src/common/featureFlags.ts index baaa1c443f..0dd62c224c 100644 --- a/packages/fx-core/src/common/featureFlags.ts +++ b/packages/fx-core/src/common/featureFlags.ts @@ -38,6 +38,7 @@ export class FeatureFlagName { static readonly SandBoxedTeam = "TEAMSFX_SANDBOXED_TEAM"; static readonly SensitivityLabelEnabled = "TEAMSFX_SENSITIVITY_LABEL"; static readonly DAMetaOS = "TEAMSFX_DA_METAOS"; + static readonly CFShortcutMetaOS = "TEAMSFX_CF_SHORTCUT_METAOS"; } export interface FeatureFlag { @@ -133,6 +134,10 @@ export class FeatureFlags { name: FeatureFlagName.DAMetaOS, defaultValue: "false", }; + static readonly CFShortcutMetaOS = { + name: FeatureFlagName.CFShortcutMetaOS, + defaultValue: "false", + }; } export class FeatureFlagManager { diff --git a/packages/fx-core/src/question/scaffold/vsc/officeAddinProjectTypeNode.ts b/packages/fx-core/src/question/scaffold/vsc/officeAddinProjectTypeNode.ts index 4a1893dcf4..79b3b8e86c 100644 --- a/packages/fx-core/src/question/scaffold/vsc/officeAddinProjectTypeNode.ts +++ b/packages/fx-core/src/question/scaffold/vsc/officeAddinProjectTypeNode.ts @@ -67,7 +67,9 @@ export function wxpAddinProjectTypeNode(): IQTreeNode { type: "singleSelect", staticOptions: [ OfficeAddinCapabilityOptions.wxpTaskPane(), - OfficeAddinCapabilityOptions.excelCFShortcut(), + ...(featureFlagManager.getBooleanValue(FeatureFlags.CFShortcutMetaOS) + ? [OfficeAddinCapabilityOptions.excelCFShortcut()] + : []), ...(featureFlagManager.getBooleanValue(FeatureFlags.DAMetaOS) ? [OfficeAddinCapabilityOptions.DAMetaOS()] : []), diff --git a/packages/vscode-extension/package.json b/packages/vscode-extension/package.json index db863279c7..e37d8637e2 100644 --- a/packages/vscode-extension/package.json +++ b/packages/vscode-extension/package.json @@ -1870,6 +1870,11 @@ "type": "boolean", "default": false, "markdownDescription": "Enable Declarative Agent support in Office Add-in project." + }, + "M365AgentsToolkit.enableCustomFunctionShortcutInOfficeAddIn": { + "type": "boolean", + "default": false, + "markdownDescription": "Enable Custom function and Shortcut support in Office Add-in project." } } }, diff --git a/packages/vscode-extension/src/config.ts b/packages/vscode-extension/src/config.ts index c64eebfbb2..ebbb800b70 100644 --- a/packages/vscode-extension/src/config.ts +++ b/packages/vscode-extension/src/config.ts @@ -37,6 +37,10 @@ export class ConfigManager { ConfigurationKey.EnableDAMetaOS, false ).toString(); + process.env[FeatureFlags.CFShortcutMetaOS.name] = this.getConfiguration( + ConfigurationKey.EnableCFShortcutMetaOS, + false + ).toString(); } loadLogLevel() { const logLevel = this.getConfiguration(ConfigurationKey.LogLevel, "Info") as string; diff --git a/packages/vscode-extension/src/constants.ts b/packages/vscode-extension/src/constants.ts index 99154b43f3..77565dd7f1 100644 --- a/packages/vscode-extension/src/constants.ts +++ b/packages/vscode-extension/src/constants.ts @@ -6,6 +6,7 @@ export enum ConfigurationKey { LogLevel = "logLevel", EnableCEA = "enableLaunchAgentForTeamsInCopilot", EnableDAMetaOS = "enableDeclarativeAgentInOfficeAddIn", + EnableCFShortcutMetaOS = "enableCustomFunctionShortcutInOfficeAddIn", } export const AzurePortalUrl = "https://portal.azure.com";