pptb-standard-sample-tool 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # PowerPlatform Example Tool
2
+
3
+ A complete example tool demonstrating the HTML-first architecture with TypeScript for PowerPlatform ToolBox.
4
+
5
+ ## Features
6
+
7
+ - ✅ TypeScript with full type safety
8
+ - ✅ HTML+CSS+JS architecture
9
+ - ✅ Access to ToolBox API via `window.toolboxAPI`
10
+ - ✅ Connection URL and access token handling
11
+ - ✅ Event subscription and handling
12
+ - ✅ Interactive UI with notifications, clipboard, and data display
13
+
14
+ ## Structure
15
+
16
+ ```
17
+ example-tool/
18
+ ├── src/
19
+ │ ├── index.html # Main UI
20
+ │ ├── index.ts # Tool logic (TypeScript)
21
+ │ └── styles.css # Styling
22
+ ├── dist/ # Compiled output (after build)
23
+ │ ├── index.html
24
+ │ ├── index.js
25
+ │ ├── index.js.map
26
+ │ └── styles.css
27
+ ├── package.json
28
+ ├── tsconfig.json
29
+ └── README.md
30
+ ```
31
+
32
+ ## Installation
33
+
34
+ Install dependencies:
35
+
36
+ ```bash
37
+ npm install
38
+ ```
39
+
40
+ ## Development
41
+
42
+ Build the tool:
43
+
44
+ ```bash
45
+ npm run build
46
+ ```
47
+
48
+ Watch mode for development:
49
+
50
+ ```bash
51
+ npm run watch
52
+ ```
53
+
54
+ ## Usage in ToolBox
55
+
56
+ 1. Install the tool in ToolBox:
57
+ ```javascript
58
+ await window.toolboxAPI.installTool('@powerplatform/example-tool');
59
+ ```
60
+
61
+ 2. Load the tool:
62
+ ```javascript
63
+ await window.toolboxAPI.loadTool('@powerplatform/example-tool');
64
+ ```
65
+
66
+ 3. Get the tool's HTML with connection context:
67
+ ```javascript
68
+ const html = await window.toolboxAPI.getToolWebviewHtml(
69
+ '@powerplatform/example-tool',
70
+ 'https://your-env.crm.dynamics.com',
71
+ 'your-access-token'
72
+ );
73
+ ```
74
+
75
+ 4. Render the HTML in a webview.
76
+
77
+ ## API Usage
78
+
79
+ The tool demonstrates various ToolBox API features:
80
+
81
+ ### Getting Connection Context
82
+
83
+ ```typescript
84
+ const context = await window.toolboxAPI.getToolContext();
85
+ console.log(context.connectionUrl);
86
+ console.log(context.accessToken);
87
+ ```
88
+
89
+ ### Showing Notifications
90
+
91
+ ```typescript
92
+ await window.toolboxAPI.showNotification({
93
+ title: 'Success',
94
+ body: 'Operation completed',
95
+ type: 'success'
96
+ });
97
+ ```
98
+
99
+ ### Subscribing to Events
100
+
101
+ ```typescript
102
+ window.toolboxAPI.onToolboxEvent((event, payload) => {
103
+ console.log('Event:', payload.event);
104
+ console.log('Data:', payload.data);
105
+ });
106
+ ```
107
+
108
+ ### Accessing Connections
109
+
110
+ ```typescript
111
+ const connections = await window.toolboxAPI.getConnections();
112
+ const active = await window.toolboxAPI.getActiveConnection();
113
+ ```
114
+
115
+ ## Type Definitions
116
+
117
+ This tool includes TypeScript type definitions in the `types/` folder. The type definitions provide:
118
+
119
+ - Full ToolBoxAPI interface
120
+ - Event type definitions
121
+ - Connection and Tool interfaces
122
+ - Notification options
123
+ - And more...
124
+
125
+ ## TypeScript Configuration
126
+
127
+ The tool uses modern TypeScript configuration:
128
+
129
+ - **Target**: ES2022
130
+ - **Module**: ES2022
131
+ - **Module Resolution**: bundler (modern strategy)
132
+ - **Strict Mode**: Enabled with additional safety checks:
133
+ - `noUnusedLocals`: Warns about unused local variables
134
+ - `noUnusedParameters`: Warns about unused function parameters
135
+ - `noFallthroughCasesInSwitch`: Ensures all switch cases handle control flow
136
+ - `useDefineForClassFields`: Modern class field semantics
137
+ - `moduleDetection`: Force module detection
138
+
139
+ ## License
140
+
141
+ MIT
@@ -0,0 +1,66 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>PowerPlatform Example Tool</title>
7
+ <link rel="stylesheet" href="styles.css">
8
+ </head>
9
+ <body>
10
+ <div class="container">
11
+ <header>
12
+ <h1>🔧 PowerPlatform Example Tool</h1>
13
+ <p>HTML-first architecture with TypeScript</p>
14
+ </header>
15
+
16
+ <section class="info-panel">
17
+ <h2>Connection Information</h2>
18
+ <div class="info-grid">
19
+ <div class="info-item">
20
+ <label>Tool ID:</label>
21
+ <span id="tool-id">Loading...</span>
22
+ </div>
23
+ <div class="info-item">
24
+ <label>Connection URL:</label>
25
+ <span id="connection-url">Loading...</span>
26
+ </div>
27
+ <div class="info-item">
28
+ <label>Access Token:</label>
29
+ <span id="access-token">Loading...</span>
30
+ </div>
31
+ </div>
32
+ </section>
33
+
34
+ <section class="actions-panel">
35
+ <h2>Quick Actions</h2>
36
+ <div class="button-grid">
37
+ <button id="btn-show-notification" class="btn btn-primary">
38
+ Show Notification
39
+ </button>
40
+ <button id="btn-get-connections" class="btn btn-secondary">
41
+ Get Connections
42
+ </button>
43
+ <button id="btn-copy-url" class="btn btn-secondary">
44
+ Copy Connection URL
45
+ </button>
46
+ <button id="btn-get-tools" class="btn btn-secondary">
47
+ List All Tools
48
+ </button>
49
+ </div>
50
+ </section>
51
+
52
+ <section class="events-panel">
53
+ <h2>Event Log</h2>
54
+ <div id="event-log" class="event-log"></div>
55
+ <button id="btn-clear-log" class="btn btn-small">Clear Log</button>
56
+ </section>
57
+
58
+ <section class="output-panel">
59
+ <h2>Output</h2>
60
+ <pre id="output"></pre>
61
+ </section>
62
+ </div>
63
+
64
+ <script type="module" src="index.js"></script>
65
+ </body>
66
+ </html>
package/dist/index.js ADDED
@@ -0,0 +1,191 @@
1
+ /// <reference types="../types" />
2
+ /**
3
+ * PowerPlatform Example Tool - Main Entry Point
4
+ *
5
+ * This tool demonstrates the HTML-first architecture with TypeScript.
6
+ * It accesses the ToolBox API via window.toolboxAPI and displays
7
+ * connection information, handles events, and performs actions.
8
+ */
9
+ // Type the toolbox API
10
+ const toolbox = window.toolboxAPI;
11
+ // State management
12
+ let eventCount = 0;
13
+ /**
14
+ * Listen for TOOLBOX_CONTEXT from parent window (passed via postMessage)
15
+ */
16
+ window.addEventListener("message", (event) => {
17
+ if (event.data && event.data.type === "TOOLBOX_CONTEXT") {
18
+ // Set the context on window object
19
+ window.TOOLBOX_CONTEXT = event.data.data;
20
+ console.log("Received TOOLBOX_CONTEXT:", window.TOOLBOX_CONTEXT);
21
+ // If DOM is already ready, update the display immediately
22
+ if (document.readyState === "complete" || document.readyState === "interactive") {
23
+ displayConnectionContext();
24
+ }
25
+ }
26
+ });
27
+ /**
28
+ * Initialize the tool when DOM is ready
29
+ */
30
+ document.addEventListener("DOMContentLoaded", async () => {
31
+ console.log("Example Tool Initialized");
32
+ // Display connection context
33
+ await displayConnectionContext();
34
+ // Setup event listeners for UI interactions
35
+ setupUIEventListeners();
36
+ // Subscribe to ToolBox events
37
+ subscribeToToolBoxEvents();
38
+ // Log tool startup
39
+ logEvent("Tool started successfully");
40
+ });
41
+ /**
42
+ * Display connection context from TOOLBOX_CONTEXT or getToolContext
43
+ */
44
+ async function displayConnectionContext() {
45
+ try {
46
+ // First, try to get context from injected window.TOOLBOX_CONTEXT
47
+ let context = window.TOOLBOX_CONTEXT;
48
+ // If not available, fetch it via API
49
+ if (!context) {
50
+ context = await toolbox.getToolContext();
51
+ }
52
+ // Update UI
53
+ updateElement("tool-id", context.toolId || "Not set");
54
+ updateElement("connection-url", context.connectionUrl || "Not set");
55
+ updateElement("access-token", context.accessToken ? "***" + context.accessToken.slice(-10) : "Not set");
56
+ logEvent("Connection context loaded", context);
57
+ }
58
+ catch (error) {
59
+ console.error("Failed to load connection context:", error);
60
+ logEvent("Error loading connection context", error);
61
+ }
62
+ }
63
+ /**
64
+ * Setup UI event listeners
65
+ */
66
+ function setupUIEventListeners() {
67
+ // Show Notification button
68
+ document.getElementById("btn-show-notification")?.addEventListener("click", async () => {
69
+ try {
70
+ await toolbox.showNotification({
71
+ title: "Hello from Example Tool!",
72
+ body: "This is a notification from the example tool",
73
+ type: "success",
74
+ duration: 5000,
75
+ });
76
+ logEvent("Notification shown");
77
+ }
78
+ catch (error) {
79
+ logEvent("Error showing notification", error);
80
+ }
81
+ });
82
+ // Get Connections button
83
+ document.getElementById("btn-get-connections")?.addEventListener("click", async () => {
84
+ try {
85
+ const connections = await toolbox.getConnections();
86
+ displayOutput("Connections", connections);
87
+ logEvent(`Retrieved ${connections.length} connections`);
88
+ }
89
+ catch (error) {
90
+ logEvent("Error getting connections", error);
91
+ }
92
+ });
93
+ // Copy Connection URL button
94
+ document.getElementById("btn-copy-url")?.addEventListener("click", async () => {
95
+ try {
96
+ const context = await toolbox.getToolContext();
97
+ if (context.connectionUrl) {
98
+ await toolbox.copyToClipboard(context.connectionUrl);
99
+ logEvent("Connection URL copied to clipboard");
100
+ }
101
+ else {
102
+ logEvent("No connection URL available");
103
+ }
104
+ }
105
+ catch (error) {
106
+ logEvent("Error copying URL", error);
107
+ }
108
+ });
109
+ // List All Tools button
110
+ document.getElementById("btn-get-tools")?.addEventListener("click", async () => {
111
+ try {
112
+ const tools = await toolbox.getAllTools();
113
+ displayOutput("Installed Tools", tools);
114
+ logEvent(`Retrieved ${tools.length} tools`);
115
+ }
116
+ catch (error) {
117
+ logEvent("Error getting tools", error);
118
+ }
119
+ });
120
+ // Clear Log button
121
+ document.getElementById("btn-clear-log")?.addEventListener("click", () => {
122
+ const eventLog = document.getElementById("event-log");
123
+ if (eventLog) {
124
+ eventLog.innerHTML = "";
125
+ eventCount = 0;
126
+ }
127
+ });
128
+ }
129
+ /**
130
+ * Subscribe to ToolBox events
131
+ */
132
+ function subscribeToToolBoxEvents() {
133
+ toolbox.onToolboxEvent((_event, payload) => {
134
+ logEvent(`ToolBox Event: ${payload.event}`, payload.data);
135
+ });
136
+ logEvent("Subscribed to ToolBox events");
137
+ }
138
+ /**
139
+ * Update an element's text content with animation
140
+ */
141
+ function updateElement(id, value) {
142
+ const element = document.getElementById(id);
143
+ if (element) {
144
+ element.textContent = value;
145
+ element.classList.add("status-updated");
146
+ setTimeout(() => element.classList.remove("status-updated"), 500);
147
+ }
148
+ }
149
+ /**
150
+ * Display output in the output panel
151
+ */
152
+ function displayOutput(title, data) {
153
+ const output = document.getElementById("output");
154
+ if (output) {
155
+ output.textContent = `=== ${title} ===\n\n${JSON.stringify(data, null, 2)}`;
156
+ }
157
+ }
158
+ /**
159
+ * Log an event to the event log panel
160
+ */
161
+ function logEvent(message, data) {
162
+ const eventLog = document.getElementById("event-log");
163
+ if (!eventLog)
164
+ return;
165
+ eventCount++;
166
+ const time = new Date().toLocaleTimeString();
167
+ const entry = document.createElement("div");
168
+ entry.className = "event-entry";
169
+ entry.innerHTML = `
170
+ <span class="event-time">[${time}]</span>
171
+ <span class="event-type">#${eventCount}</span>
172
+ <span>${message}</span>
173
+ `;
174
+ eventLog.appendChild(entry);
175
+ eventLog.scrollTop = eventLog.scrollHeight;
176
+ // Also log to console
177
+ if (data) {
178
+ console.log(`[${time}] ${message}`, data);
179
+ }
180
+ else {
181
+ console.log(`[${time}] ${message}`);
182
+ }
183
+ }
184
+ // Export for debugging
185
+ window.exampleTool = {
186
+ displayConnectionContext,
187
+ logEvent,
188
+ displayOutput,
189
+ };
190
+ export {};
191
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAElC;;;;;;GAMG;AAEH,uBAAuB;AACvB,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC;AAElC,mBAAmB;AACnB,IAAI,UAAU,GAAG,CAAC,CAAC;AAEnB;;GAEG;AACH,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;IACzC,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;QACtD,mCAAmC;QACnC,MAAM,CAAC,eAAe,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QACzC,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;QAEjE,0DAA0D;QAC1D,IAAI,QAAQ,CAAC,UAAU,KAAK,UAAU,IAAI,QAAQ,CAAC,UAAU,KAAK,aAAa,EAAE,CAAC;YAC9E,wBAAwB,EAAE,CAAC;QAC/B,CAAC;IACL,CAAC;AACL,CAAC,CAAC,CAAC;AAEH;;GAEG;AACH,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,KAAK,IAAI,EAAE;IACrD,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;IAExC,6BAA6B;IAC7B,MAAM,wBAAwB,EAAE,CAAC;IAEjC,4CAA4C;IAC5C,qBAAqB,EAAE,CAAC;IAExB,8BAA8B;IAC9B,wBAAwB,EAAE,CAAC;IAE3B,mBAAmB;IACnB,QAAQ,CAAC,2BAA2B,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC;AAEH;;GAEG;AACH,KAAK,UAAU,wBAAwB;IACnC,IAAI,CAAC;QACD,iEAAiE;QACjE,IAAI,OAAO,GAAG,MAAM,CAAC,eAAe,CAAC;QAErC,qCAAqC;QACrC,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,OAAO,GAAG,MAAM,OAAO,CAAC,cAAc,EAAE,CAAC;QAC7C,CAAC;QAED,YAAY;QACZ,aAAa,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC;QACtD,aAAa,CAAC,gBAAgB,EAAE,OAAO,CAAC,aAAa,IAAI,SAAS,CAAC,CAAC;QACpE,aAAa,CAAC,cAAc,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,GAAG,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAExG,QAAQ,CAAC,2BAA2B,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,oCAAoC,EAAE,KAAK,CAAC,CAAC;QAC3D,QAAQ,CAAC,kCAAkC,EAAE,KAAK,CAAC,CAAC;IACxD,CAAC;AACL,CAAC;AAED;;GAEG;AACH,SAAS,qBAAqB;IAC1B,2BAA2B;IAC3B,QAAQ,CAAC,cAAc,CAAC,uBAAuB,CAAC,EAAE,gBAAgB,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;QACnF,IAAI,CAAC;YACD,MAAM,OAAO,CAAC,gBAAgB,CAAC;gBAC3B,KAAK,EAAE,0BAA0B;gBACjC,IAAI,EAAE,8CAA8C;gBACpD,IAAI,EAAE,SAAS;gBACf,QAAQ,EAAE,IAAI;aACjB,CAAC,CAAC;YACH,QAAQ,CAAC,oBAAoB,CAAC,CAAC;QACnC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,QAAQ,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;QAClD,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,yBAAyB;IACzB,QAAQ,CAAC,cAAc,CAAC,qBAAqB,CAAC,EAAE,gBAAgB,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;QACjF,IAAI,CAAC;YACD,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,cAAc,EAAE,CAAC;YACnD,aAAa,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;YAC1C,QAAQ,CAAC,aAAa,WAAW,CAAC,MAAM,cAAc,CAAC,CAAC;QAC5D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,QAAQ,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAC;QACjD,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,6BAA6B;IAC7B,QAAQ,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,gBAAgB,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;QAC1E,IAAI,CAAC;YACD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,cAAc,EAAE,CAAC;YAC/C,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;gBACxB,MAAM,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;gBACrD,QAAQ,CAAC,oCAAoC,CAAC,CAAC;YACnD,CAAC;iBAAM,CAAC;gBACJ,QAAQ,CAAC,6BAA6B,CAAC,CAAC;YAC5C,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,QAAQ,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC;QACzC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,wBAAwB;IACxB,QAAQ,CAAC,cAAc,CAAC,eAAe,CAAC,EAAE,gBAAgB,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;QAC3E,IAAI,CAAC;YACD,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC;YAC1C,aAAa,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;YACxC,QAAQ,CAAC,aAAa,KAAK,CAAC,MAAM,QAAQ,CAAC,CAAC;QAChD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,QAAQ,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;QAC3C,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,mBAAmB;IACnB,QAAQ,CAAC,cAAc,CAAC,eAAe,CAAC,EAAE,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;QACrE,MAAM,QAAQ,GAAG,QAAQ,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;QACtD,IAAI,QAAQ,EAAE,CAAC;YACX,QAAQ,CAAC,SAAS,GAAG,EAAE,CAAC;YACxB,UAAU,GAAG,CAAC,CAAC;QACnB,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC;AAED;;GAEG;AACH,SAAS,wBAAwB;IAC7B,OAAO,CAAC,cAAc,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE;QACvC,QAAQ,CAAC,kBAAkB,OAAO,CAAC,KAAK,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,8BAA8B,CAAC,CAAC;AAC7C,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,EAAU,EAAE,KAAa;IAC5C,MAAM,OAAO,GAAG,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;IAC5C,IAAI,OAAO,EAAE,CAAC;QACV,OAAO,CAAC,WAAW,GAAG,KAAK,CAAC;QAC5B,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACxC,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,gBAAgB,CAAC,EAAE,GAAG,CAAC,CAAC;IACtE,CAAC;AACL,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,KAAa,EAAE,IAAS;IAC3C,MAAM,MAAM,GAAG,QAAQ,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;IACjD,IAAI,MAAM,EAAE,CAAC;QACT,MAAM,CAAC,WAAW,GAAG,OAAO,KAAK,WAAW,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;IAChF,CAAC;AACL,CAAC;AAED;;GAEG;AACH,SAAS,QAAQ,CAAC,OAAe,EAAE,IAAU;IACzC,MAAM,QAAQ,GAAG,QAAQ,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;IACtD,IAAI,CAAC,QAAQ;QAAE,OAAO;IAEtB,UAAU,EAAE,CAAC;IACb,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,kBAAkB,EAAE,CAAC;IAE7C,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAC5C,KAAK,CAAC,SAAS,GAAG,aAAa,CAAC;IAChC,KAAK,CAAC,SAAS,GAAG;oCACc,IAAI;oCACJ,UAAU;gBAC9B,OAAO;KAClB,CAAC;IAEF,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IAC5B,QAAQ,CAAC,SAAS,GAAG,QAAQ,CAAC,YAAY,CAAC;IAE3C,sBAAsB;IACtB,IAAI,IAAI,EAAE,CAAC;QACP,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,KAAK,OAAO,EAAE,EAAE,IAAI,CAAC,CAAC;IAC9C,CAAC;SAAM,CAAC;QACJ,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;IACxC,CAAC;AACL,CAAC;AAED,uBAAuB;AACtB,MAAc,CAAC,WAAW,GAAG;IAC1B,wBAAwB;IACxB,QAAQ;IACR,aAAa;CAChB,CAAC"}
@@ -0,0 +1,202 @@
1
+ /* Reset and base styles */
2
+ * {
3
+ margin: 0;
4
+ padding: 0;
5
+ box-sizing: border-box;
6
+ }
7
+
8
+ body {
9
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
10
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
11
+ padding: 20px;
12
+ min-height: 100vh;
13
+ }
14
+
15
+ .container {
16
+ max-width: 1000px;
17
+ margin: 0 auto;
18
+ }
19
+
20
+ header {
21
+ background: white;
22
+ padding: 30px;
23
+ border-radius: 10px;
24
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
25
+ margin-bottom: 20px;
26
+ text-align: center;
27
+ }
28
+
29
+ header h1 {
30
+ color: #667eea;
31
+ font-size: 2em;
32
+ margin-bottom: 10px;
33
+ }
34
+
35
+ header p {
36
+ color: #666;
37
+ font-size: 1.1em;
38
+ }
39
+
40
+ section {
41
+ background: white;
42
+ padding: 25px;
43
+ border-radius: 10px;
44
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
45
+ margin-bottom: 20px;
46
+ }
47
+
48
+ section h2 {
49
+ color: #333;
50
+ margin-bottom: 20px;
51
+ padding-bottom: 10px;
52
+ border-bottom: 2px solid #667eea;
53
+ }
54
+
55
+ .info-grid {
56
+ display: grid;
57
+ gap: 15px;
58
+ }
59
+
60
+ .info-item {
61
+ display: grid;
62
+ grid-template-columns: 150px 1fr;
63
+ gap: 10px;
64
+ align-items: center;
65
+ }
66
+
67
+ .info-item label {
68
+ font-weight: 600;
69
+ color: #555;
70
+ }
71
+
72
+ .info-item span {
73
+ color: #333;
74
+ padding: 8px 12px;
75
+ background: #f5f5f5;
76
+ border-radius: 5px;
77
+ font-family: 'Courier New', monospace;
78
+ word-break: break-all;
79
+ }
80
+
81
+ .button-grid {
82
+ display: grid;
83
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
84
+ gap: 15px;
85
+ }
86
+
87
+ .btn {
88
+ padding: 12px 24px;
89
+ font-size: 1em;
90
+ border: none;
91
+ border-radius: 5px;
92
+ cursor: pointer;
93
+ transition: all 0.3s ease;
94
+ font-weight: 600;
95
+ }
96
+
97
+ .btn-primary {
98
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
99
+ color: white;
100
+ }
101
+
102
+ .btn-primary:hover {
103
+ transform: translateY(-2px);
104
+ box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
105
+ }
106
+
107
+ .btn-secondary {
108
+ background: #f0f0f0;
109
+ color: #333;
110
+ }
111
+
112
+ .btn-secondary:hover {
113
+ background: #e0e0e0;
114
+ transform: translateY(-2px);
115
+ box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
116
+ }
117
+
118
+ .btn-small {
119
+ padding: 8px 16px;
120
+ font-size: 0.9em;
121
+ margin-top: 10px;
122
+ }
123
+
124
+ .event-log {
125
+ background: #f9f9f9;
126
+ border: 1px solid #ddd;
127
+ border-radius: 5px;
128
+ padding: 15px;
129
+ max-height: 300px;
130
+ overflow-y: auto;
131
+ font-family: 'Courier New', monospace;
132
+ font-size: 0.9em;
133
+ margin-bottom: 10px;
134
+ }
135
+
136
+ .event-entry {
137
+ padding: 8px 0;
138
+ border-bottom: 1px solid #eee;
139
+ color: #333;
140
+ }
141
+
142
+ .event-entry:last-child {
143
+ border-bottom: none;
144
+ }
145
+
146
+ .event-time {
147
+ color: #666;
148
+ font-size: 0.85em;
149
+ margin-right: 10px;
150
+ }
151
+
152
+ .event-type {
153
+ font-weight: 600;
154
+ color: #667eea;
155
+ margin-right: 10px;
156
+ }
157
+
158
+ .output-panel pre {
159
+ background: #2d2d2d;
160
+ color: #f8f8f2;
161
+ padding: 15px;
162
+ border-radius: 5px;
163
+ overflow-x: auto;
164
+ font-family: 'Courier New', monospace;
165
+ font-size: 0.9em;
166
+ max-height: 400px;
167
+ overflow-y: auto;
168
+ }
169
+
170
+ /* Scrollbar styling */
171
+ ::-webkit-scrollbar {
172
+ width: 8px;
173
+ height: 8px;
174
+ }
175
+
176
+ ::-webkit-scrollbar-track {
177
+ background: #f1f1f1;
178
+ border-radius: 10px;
179
+ }
180
+
181
+ ::-webkit-scrollbar-thumb {
182
+ background: #667eea;
183
+ border-radius: 10px;
184
+ }
185
+
186
+ ::-webkit-scrollbar-thumb:hover {
187
+ background: #764ba2;
188
+ }
189
+
190
+ /* Animation for status changes */
191
+ .status-updated {
192
+ animation: pulse 0.5s ease-in-out;
193
+ }
194
+
195
+ @keyframes pulse {
196
+ 0%, 100% {
197
+ transform: scale(1);
198
+ }
199
+ 50% {
200
+ transform: scale(1.05);
201
+ }
202
+ }
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "pptb-standard-sample-tool",
3
+ "version": "0.0.5",
4
+ "displayName": "Power Platform Standard Sample Tool",
5
+ "description": "Example tool demonstrating HTML-first architecture with TypeScript",
6
+ "author": "Power Platform ToolBox",
7
+ "license": "MIT",
8
+ "scripts": {
9
+ "build": "tsc && npm run copy-html && npm run copy-css",
10
+ "copy-html": "cp src/index.html dist/",
11
+ "copy-css": "cp src/styles.css dist/",
12
+ "watch": "tsc --watch"
13
+ },
14
+ "devDependencies": {
15
+ "typescript": "^5.6.3"
16
+ }
17
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ES2022",
5
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "moduleResolution": "bundler",
13
+ "declaration": false,
14
+ "sourceMap": true,
15
+ "allowSyntheticDefaultImports": true,
16
+ "isolatedModules": true,
17
+ "useDefineForClassFields": true,
18
+ "moduleDetection": "force",
19
+ "noUnusedLocals": true,
20
+ "noUnusedParameters": true,
21
+ "noFallthroughCasesInSwitch": true
22
+ },
23
+ "include": ["src/**/*"],
24
+ "exclude": ["node_modules", "dist"]
25
+ }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * PowerPlatform ToolBox API Type Definitions
3
+ *
4
+ * Tools running in ToolBox webviews can access the ToolBox API via window.toolboxAPI
5
+ */
6
+
7
+ declare namespace ToolBox {
8
+ /**
9
+ * Tool context containing connection information
10
+ */
11
+ export interface ToolContext {
12
+ toolId: string | null;
13
+ connectionUrl: string | null;
14
+ accessToken: string | null;
15
+ }
16
+
17
+ /**
18
+ * Notification options
19
+ */
20
+ export interface NotificationOptions {
21
+ title: string;
22
+ body: string;
23
+ type?: 'info' | 'success' | 'warning' | 'error';
24
+ duration?: number; // Duration in milliseconds, 0 for persistent
25
+ }
26
+
27
+ /**
28
+ * Event types that can be emitted by the ToolBox
29
+ */
30
+ export type ToolBoxEvent =
31
+ | 'tool:loaded'
32
+ | 'tool:unloaded'
33
+ | 'connection:created'
34
+ | 'connection:updated'
35
+ | 'connection:deleted'
36
+ | 'settings:updated'
37
+ | 'notification:shown';
38
+
39
+ /**
40
+ * Event payload for ToolBox events
41
+ */
42
+ export interface ToolBoxEventPayload {
43
+ event: ToolBoxEvent;
44
+ data: unknown;
45
+ timestamp: string;
46
+ }
47
+
48
+ /**
49
+ * Dataverse connection configuration
50
+ */
51
+ export interface DataverseConnection {
52
+ id: string;
53
+ name: string;
54
+ url: string;
55
+ environment: 'Dev' | 'Test' | 'UAT' | 'Production';
56
+ clientId?: string;
57
+ tenantId?: string;
58
+ createdAt: string;
59
+ lastUsedAt?: string;
60
+ isActive?: boolean;
61
+ }
62
+
63
+ /**
64
+ * Tool information
65
+ */
66
+ export interface Tool {
67
+ id: string;
68
+ name: string;
69
+ version: string;
70
+ description: string;
71
+ author: string;
72
+ icon?: string;
73
+ }
74
+
75
+ /**
76
+ * ToolBox API exposed to tools via window.toolboxAPI
77
+ */
78
+ export interface ToolBoxAPI {
79
+ // Settings
80
+ getUserSettings: () => Promise<any>;
81
+ updateUserSettings: (settings: any) => Promise<void>;
82
+ getSetting: (key: string) => Promise<any>;
83
+ setSetting: (key: string, value: any) => Promise<void>;
84
+
85
+ // Connections
86
+ addConnection: (connection: any) => Promise<void>;
87
+ updateConnection: (id: string, updates: any) => Promise<void>;
88
+ deleteConnection: (id: string) => Promise<void>;
89
+ getConnections: () => Promise<DataverseConnection[]>;
90
+ setActiveConnection: (id: string) => Promise<void>;
91
+ getActiveConnection: () => Promise<DataverseConnection | null>;
92
+ disconnectConnection: () => Promise<void>;
93
+
94
+ // Tools
95
+ getAllTools: () => Promise<Tool[]>;
96
+ getTool: (toolId: string) => Promise<Tool>;
97
+ loadTool: (packageName: string) => Promise<Tool>;
98
+ unloadTool: (toolId: string) => Promise<void>;
99
+ installTool: (packageName: string) => Promise<Tool>;
100
+ uninstallTool: (packageName: string, toolId: string) => Promise<void>;
101
+ getToolWebviewHtml: (
102
+ packageName: string,
103
+ connectionUrl?: string,
104
+ accessToken?: string
105
+ ) => Promise<string | null>;
106
+ getToolContext: () => Promise<ToolContext>;
107
+
108
+ // Tool Settings
109
+ getToolSettings: (toolId: string) => Promise<any>;
110
+ updateToolSettings: (toolId: string, settings: any) => Promise<void>;
111
+
112
+ // Notifications
113
+ showNotification: (options: NotificationOptions) => Promise<void>;
114
+
115
+ // Clipboard
116
+ copyToClipboard: (text: string) => Promise<void>;
117
+
118
+ // File operations
119
+ saveFile: (defaultPath: string, content: any) => Promise<string | null>;
120
+
121
+ // Events
122
+ getEventHistory: (limit?: number) => Promise<ToolBoxEventPayload[]>;
123
+ onToolboxEvent: (
124
+ callback: (event: any, payload: ToolBoxEventPayload) => void
125
+ ) => void;
126
+ removeToolboxEventListener: (
127
+ callback: (event: any, payload: ToolBoxEventPayload) => void
128
+ ) => void;
129
+
130
+ // Auto-update
131
+ checkForUpdates: () => Promise<void>;
132
+ downloadUpdate: () => Promise<void>;
133
+ quitAndInstall: () => Promise<void>;
134
+ getAppVersion: () => Promise<string>;
135
+ onUpdateChecking: (callback: () => void) => void;
136
+ onUpdateAvailable: (callback: (info: any) => void) => void;
137
+ onUpdateNotAvailable: (callback: () => void) => void;
138
+ onUpdateDownloadProgress: (callback: (progress: any) => void) => void;
139
+ onUpdateDownloaded: (callback: (info: any) => void) => void;
140
+ onUpdateError: (callback: (error: string) => void) => void;
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Global window interface extension for ToolBox tools
146
+ */
147
+ declare global {
148
+ interface Window {
149
+ toolboxAPI: ToolBox.ToolBoxAPI;
150
+ TOOLBOX_CONTEXT?: ToolBox.ToolContext;
151
+ }
152
+ }
153
+
154
+ export = ToolBox;
155
+ export as namespace ToolBox;