@aiot-nexus/plugin-sdk 3.1.1 → 3.3.0
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 +34 -22
- package/dev-host/main.js +20 -5
- package/dist/.generated/plugin-framework-version.d.ts +2 -1
- package/dist/.generated/plugin-framework-version.js +3 -2
- package/dist/.generated/plugin-manifest.schema.json +1 -1
- package/dist/.generated/theme-contract.d.ts +43 -0
- package/dist/.generated/theme-contract.js +75 -0
- package/dist/browser/frame-client.d.ts +1 -0
- package/dist/browser/frame-client.mjs +197 -0
- package/dist/dev-host-metadata.json +1 -1
- package/dist/src/browser/frame-client.d.ts +1 -0
- package/dist/src/browser/frame-client.js +197 -0
- package/dist/src/cli/run.js +2 -2
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +3 -1
- package/dist/src/nexus-dev-deployment.js +2 -2
- package/dist/src/scaffold.js +1 -1
- package/dist/src/sdk-info.d.ts +1 -1
- package/dist/src/sdk-info.js +1 -1
- package/dist/src/types.d.ts +37 -0
- package/dist/src/web-dev.js +2 -0
- package/package.json +1 -1
- package/templates/minimal/README.md.tmpl +13 -9
- package/templates/minimal/src/renderer/app.ts.tmpl +4 -1
- package/templates/minimal/src/renderer/plugin.css +78 -52
- package/templates/sqlite/README.md.tmpl +13 -9
- package/templates/sqlite/src/renderer/app.ts.tmpl +4 -1
- package/templates/sqlite/src/renderer/plugin.css +135 -57
|
@@ -10,6 +10,7 @@ export interface PluginFrameClientOptions {
|
|
|
10
10
|
initializationTimeoutMs?: number;
|
|
11
11
|
requestTimeoutMs?: number;
|
|
12
12
|
autoResize?: boolean;
|
|
13
|
+
autoApplyHostTheme?: boolean;
|
|
13
14
|
resizeRoot?: HTMLElement;
|
|
14
15
|
}
|
|
15
16
|
export type PluginFrameContextListener = (context: PluginRendererContext) => void;
|
|
@@ -8,6 +8,40 @@ const requestMessageType = "nexus-plugin:request";
|
|
|
8
8
|
const responseMessageType = "nexus-plugin:response";
|
|
9
9
|
const resizeMessageType = "nexus-plugin:resize";
|
|
10
10
|
const defaultInitializationTimeoutMs = 20000;
|
|
11
|
+
const systemThemeMediaQuery = "(prefers-color-scheme: dark)";
|
|
12
|
+
const themeTokenCssVariables = [
|
|
13
|
+
["background", "--nexus-color-background"],
|
|
14
|
+
["surface", "--nexus-color-surface"],
|
|
15
|
+
["surfaceAlt", "--nexus-color-surface-alt"],
|
|
16
|
+
["surfaceRaised", "--nexus-color-surface-raised"],
|
|
17
|
+
["surfaceHover", "--nexus-color-surface-hover"],
|
|
18
|
+
["surfaceInset", "--nexus-color-surface-inset"],
|
|
19
|
+
["border", "--nexus-color-border"],
|
|
20
|
+
["borderSoft", "--nexus-color-border-soft"],
|
|
21
|
+
["borderStrong", "--nexus-color-border-strong"],
|
|
22
|
+
["text", "--nexus-color-text"],
|
|
23
|
+
["textMuted", "--nexus-color-text-muted"],
|
|
24
|
+
["textSoft", "--nexus-color-text-soft"],
|
|
25
|
+
["textFaint", "--nexus-color-text-faint"],
|
|
26
|
+
["primary", "--nexus-color-primary"],
|
|
27
|
+
["accent", "--nexus-color-accent"],
|
|
28
|
+
["onPrimary", "--nexus-color-on-primary"],
|
|
29
|
+
["primarySoft", "--nexus-color-primary-soft"],
|
|
30
|
+
["info", "--nexus-color-info"],
|
|
31
|
+
["infoSoft", "--nexus-color-info-soft"],
|
|
32
|
+
["positive", "--nexus-color-positive"],
|
|
33
|
+
["positiveSoft", "--nexus-color-positive-soft"],
|
|
34
|
+
["warning", "--nexus-color-warning"],
|
|
35
|
+
["warningSoft", "--nexus-color-warning-soft"],
|
|
36
|
+
["negative", "--nexus-color-negative"],
|
|
37
|
+
["negativeSoft", "--nexus-color-negative-soft"],
|
|
38
|
+
["mutedSoft", "--nexus-color-muted-soft"],
|
|
39
|
+
["onInfo", "--nexus-color-on-info"],
|
|
40
|
+
["onPositive", "--nexus-color-on-positive"],
|
|
41
|
+
["onWarning", "--nexus-color-on-warning"],
|
|
42
|
+
["onNegative", "--nexus-color-on-negative"]
|
|
43
|
+
];
|
|
44
|
+
const hostThemeOwners = new WeakMap();
|
|
11
45
|
class PluginFrameClientError extends Error {
|
|
12
46
|
constructor(code, message) {
|
|
13
47
|
super(message);
|
|
@@ -25,6 +59,7 @@ function createPluginFrameClient(options = {}) {
|
|
|
25
59
|
const targetWindow = options.targetWindow || frameWindow.parent;
|
|
26
60
|
const initializationTimeoutMs = options.initializationTimeoutMs === undefined ? defaultInitializationTimeoutMs : options.initializationTimeoutMs;
|
|
27
61
|
const requestTimeoutMs = options.requestTimeoutMs === undefined ? 0 : options.requestTimeoutMs;
|
|
62
|
+
const autoApplyHostTheme = options.autoApplyHostTheme !== false;
|
|
28
63
|
if (!Number.isFinite(initializationTimeoutMs) || initializationTimeoutMs <= 0) {
|
|
29
64
|
throw new PluginFrameClientError("ProtocolError", "initializationTimeoutMs must be a positive finite number.");
|
|
30
65
|
}
|
|
@@ -39,6 +74,16 @@ function createPluginFrameClient(options = {}) {
|
|
|
39
74
|
let contextTimeoutId;
|
|
40
75
|
let resizeTimer;
|
|
41
76
|
let resizeObserver;
|
|
77
|
+
const hostThemeOwner = Symbol("nexus-plugin-host-theme");
|
|
78
|
+
let hostThemeRoot;
|
|
79
|
+
let hostThemeMarker;
|
|
80
|
+
let hostThemePreviousMarker;
|
|
81
|
+
let hostThemeHadPreviousMarker = false;
|
|
82
|
+
const hostThemeAppliedValues = new Map();
|
|
83
|
+
const hostThemePreviousValues = new Map();
|
|
84
|
+
const systemThemeMedia = autoApplyHostTheme && typeof frameWindow.matchMedia === "function"
|
|
85
|
+
? frameWindow.matchMedia(systemThemeMediaQuery)
|
|
86
|
+
: undefined;
|
|
42
87
|
const pendingRequests = new Map();
|
|
43
88
|
const contextListeners = new Set();
|
|
44
89
|
let resolveContext;
|
|
@@ -56,6 +101,14 @@ function createPluginFrameClient(options = {}) {
|
|
|
56
101
|
}
|
|
57
102
|
}, initializationTimeoutMs);
|
|
58
103
|
frameWindow.addEventListener("message", handleMessage);
|
|
104
|
+
if (systemThemeMedia) {
|
|
105
|
+
if (typeof systemThemeMedia.addEventListener === "function") {
|
|
106
|
+
systemThemeMedia.addEventListener("change", handleSystemThemeChange);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
systemThemeMedia.addListener(handleSystemThemeChange);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
59
112
|
if (options.autoResize !== false) {
|
|
60
113
|
frameWindow.addEventListener("resize", scheduleResize);
|
|
61
114
|
const ResizeObserverConstructor = frameWindow.ResizeObserver;
|
|
@@ -128,6 +181,7 @@ function createPluginFrameClient(options = {}) {
|
|
|
128
181
|
const hadContext = contextValue !== undefined;
|
|
129
182
|
hostOrigin = event.origin;
|
|
130
183
|
contextValue = event.data.context;
|
|
184
|
+
applyCurrentHostTheme();
|
|
131
185
|
if (!contextSettled) {
|
|
132
186
|
contextSettled = true;
|
|
133
187
|
if (contextTimeoutId !== undefined) {
|
|
@@ -170,6 +224,99 @@ function createPluginFrameClient(options = {}) {
|
|
|
170
224
|
}
|
|
171
225
|
}
|
|
172
226
|
}
|
|
227
|
+
function handleSystemThemeChange() {
|
|
228
|
+
if (contextValue?.themeMode === "system") {
|
|
229
|
+
applyCurrentHostTheme();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function applyCurrentHostTheme() {
|
|
233
|
+
if (!autoApplyHostTheme || !contextValue) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const root = frameWindow.document.documentElement;
|
|
237
|
+
if (!root?.dataset || !root.style) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
initializeHostThemeOwnership(root);
|
|
241
|
+
const resolvedMode = contextValue.themeMode === "system"
|
|
242
|
+
? (systemThemeMedia?.matches ? "dark" : "light")
|
|
243
|
+
: contextValue.themeMode;
|
|
244
|
+
root.dataset.nexusTheme = resolvedMode;
|
|
245
|
+
hostThemeMarker = resolvedMode;
|
|
246
|
+
const tokens = contextValue.theme?.[resolvedMode];
|
|
247
|
+
for (const [tokenName, cssVariable] of themeTokenCssVariables) {
|
|
248
|
+
if (tokens) {
|
|
249
|
+
const value = tokens[tokenName];
|
|
250
|
+
root.style.setProperty(cssVariable, value);
|
|
251
|
+
hostThemeAppliedValues.set(cssVariable, value);
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
restoreHostThemeVariable(root, cssVariable);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
function initializeHostThemeOwnership(root) {
|
|
259
|
+
if (hostThemeRoot === root) {
|
|
260
|
+
hostThemeOwners.set(root, hostThemeOwner);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (hostThemeRoot) {
|
|
264
|
+
cleanupHostTheme();
|
|
265
|
+
}
|
|
266
|
+
hostThemeRoot = root;
|
|
267
|
+
hostThemeOwners.set(root, hostThemeOwner);
|
|
268
|
+
hostThemePreviousMarker = root.dataset.nexusTheme;
|
|
269
|
+
hostThemeHadPreviousMarker = Object.prototype.hasOwnProperty.call(root.dataset, "nexusTheme");
|
|
270
|
+
hostThemePreviousValues.clear();
|
|
271
|
+
for (const [, cssVariable] of themeTokenCssVariables) {
|
|
272
|
+
hostThemePreviousValues.set(cssVariable, root.style.getPropertyValue(cssVariable));
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function restoreHostThemeVariable(root, cssVariable) {
|
|
276
|
+
const appliedValue = hostThemeAppliedValues.get(cssVariable);
|
|
277
|
+
if (appliedValue === undefined || root.style.getPropertyValue(cssVariable) !== appliedValue) {
|
|
278
|
+
hostThemeAppliedValues.delete(cssVariable);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const previousValue = hostThemePreviousValues.get(cssVariable) || "";
|
|
282
|
+
if (previousValue) {
|
|
283
|
+
root.style.setProperty(cssVariable, previousValue);
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
root.style.removeProperty(cssVariable);
|
|
287
|
+
}
|
|
288
|
+
hostThemeAppliedValues.delete(cssVariable);
|
|
289
|
+
}
|
|
290
|
+
function cleanupHostTheme() {
|
|
291
|
+
const root = hostThemeRoot;
|
|
292
|
+
if (!root) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (hostThemeOwners.get(root) !== hostThemeOwner) {
|
|
296
|
+
hostThemeRoot = undefined;
|
|
297
|
+
hostThemeAppliedValues.clear();
|
|
298
|
+
hostThemePreviousValues.clear();
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
for (const [, cssVariable] of themeTokenCssVariables) {
|
|
302
|
+
restoreHostThemeVariable(root, cssVariable);
|
|
303
|
+
}
|
|
304
|
+
if (hostThemeMarker !== undefined && root.dataset.nexusTheme === hostThemeMarker) {
|
|
305
|
+
if (hostThemeHadPreviousMarker) {
|
|
306
|
+
root.dataset.nexusTheme = hostThemePreviousMarker || "";
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
delete root.dataset.nexusTheme;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
hostThemeOwners.delete(root);
|
|
313
|
+
hostThemeRoot = undefined;
|
|
314
|
+
hostThemeMarker = undefined;
|
|
315
|
+
hostThemePreviousMarker = undefined;
|
|
316
|
+
hostThemeHadPreviousMarker = false;
|
|
317
|
+
hostThemeAppliedValues.clear();
|
|
318
|
+
hostThemePreviousValues.clear();
|
|
319
|
+
}
|
|
173
320
|
function scheduleResize() {
|
|
174
321
|
if (disposed || !contextValue || options.autoResize === false) {
|
|
175
322
|
return;
|
|
@@ -204,6 +351,19 @@ function createPluginFrameClient(options = {}) {
|
|
|
204
351
|
disposed = true;
|
|
205
352
|
frameWindow.removeEventListener("message", handleMessage);
|
|
206
353
|
frameWindow.removeEventListener("resize", scheduleResize);
|
|
354
|
+
if (systemThemeMedia) {
|
|
355
|
+
if (typeof systemThemeMedia.removeEventListener === "function") {
|
|
356
|
+
systemThemeMedia.removeEventListener("change", handleSystemThemeChange);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
systemThemeMedia.removeListener(handleSystemThemeChange);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
cleanupHostTheme();
|
|
363
|
+
if (contextTimeoutId !== undefined) {
|
|
364
|
+
frameWindow.clearTimeout(contextTimeoutId);
|
|
365
|
+
contextTimeoutId = undefined;
|
|
366
|
+
}
|
|
207
367
|
resizeObserver?.disconnect();
|
|
208
368
|
if (resizeTimer !== undefined) {
|
|
209
369
|
frameWindow.clearTimeout(resizeTimer);
|
|
@@ -241,8 +401,45 @@ function isPluginRendererContext(value) {
|
|
|
241
401
|
&& typeof value.contributionId === "string"
|
|
242
402
|
&& typeof value.language === "string"
|
|
243
403
|
&& (value.themeMode === "light" || value.themeMode === "dark" || value.themeMode === "system")
|
|
404
|
+
&& (value.theme === undefined || isPluginRendererTheme(value.theme))
|
|
244
405
|
&& typeof value.hostApiVersion === "string";
|
|
245
406
|
}
|
|
407
|
+
function isPluginRendererTheme(value) {
|
|
408
|
+
if (!hasExactKeys(value, ["light", "dark"])) {
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
return isPluginThemeTokens(value.light) && isPluginThemeTokens(value.dark);
|
|
412
|
+
}
|
|
413
|
+
function isPluginThemeTokens(value) {
|
|
414
|
+
const tokenNames = themeTokenCssVariables.map(([tokenName]) => tokenName);
|
|
415
|
+
return hasExactKeys(value, tokenNames)
|
|
416
|
+
&& tokenNames.every((tokenName) => isSafeCssColorValue(value[tokenName]));
|
|
417
|
+
}
|
|
418
|
+
function hasExactKeys(value, expectedKeys) {
|
|
419
|
+
if (!isRecord(value)) {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
const keys = Object.keys(value);
|
|
423
|
+
return keys.length === expectedKeys.length && keys.every((key) => expectedKeys.includes(key));
|
|
424
|
+
}
|
|
425
|
+
function isSafeCssColorValue(value) {
|
|
426
|
+
if (typeof value !== "string" || value.length === 0 || value.length > 64 || /[;{}]/.test(value)) {
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
if (/^#[0-9a-f]{6}(?:[0-9a-f]{2})?$/i.test(value)) {
|
|
430
|
+
return true;
|
|
431
|
+
}
|
|
432
|
+
const match = /^(rgb|rgba)\(([^)]+)\)$/.exec(value);
|
|
433
|
+
if (!match) {
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
const parts = match[2].split(",").map((part) => part.trim());
|
|
437
|
+
const expectedLength = match[1] === "rgba" ? 4 : 3;
|
|
438
|
+
if (parts.length !== expectedLength || !parts.slice(0, 3).every((part) => /^\d{1,3}$/.test(part) && Number(part) <= 255)) {
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
441
|
+
return expectedLength === 3 || /^(?:0|1|0?\.\d+)$/.test(parts[3]) && Number(parts[3]) <= 1;
|
|
442
|
+
}
|
|
246
443
|
function isNexusResult(value) {
|
|
247
444
|
if (!isRecord(value) || typeof value.ok !== "boolean") {
|
|
248
445
|
return false;
|
package/dist/src/cli/run.js
CHANGED
|
@@ -102,7 +102,7 @@ function usage() {
|
|
|
102
102
|
" nexus-plugin dev:web [plugin-project-directory] [--no-build] [--no-open] [--port <port>]",
|
|
103
103
|
" [--language <culture>] [--theme system|light|dark] [--surface business|tool|settings]",
|
|
104
104
|
" [--contribution-id <id>]",
|
|
105
|
-
" nexus-plugin dev:nexus [plugin-project-directory] --host <
|
|
105
|
+
" nexus-plugin dev:nexus [plugin-project-directory] --host <host-exe-or-directory>",
|
|
106
106
|
" [--inspect[=<port>] | --inspect-brk[=<port>]] [--no-devtools]",
|
|
107
107
|
" [--language <culture>] [--theme system|light|dark] [--surface business|tool|settings]",
|
|
108
108
|
" [--contribution-id <id>]"
|
|
@@ -247,7 +247,7 @@ function runNexusDev(args, io) {
|
|
|
247
247
|
}
|
|
248
248
|
}
|
|
249
249
|
if (!options.hostPath) {
|
|
250
|
-
return usageError(io, "dev:nexus requires --host <
|
|
250
|
+
return usageError(io, "dev:nexus requires --host <host-exe-or-directory>.");
|
|
251
251
|
}
|
|
252
252
|
return (0, nexus_dev_1.runPluginNexusDev)(options, io).catch((error) => {
|
|
253
253
|
if (error instanceof dev_1.PluginDevError) {
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import pluginManifestSchema from "../.generated/plugin-manifest.schema.json";
|
|
2
2
|
export { pluginManifestSchema };
|
|
3
3
|
export * from "../.generated/plugin-framework-version";
|
|
4
|
+
export { NEXUS_PLUGIN_RENDERER_THEME } from "../.generated/theme-contract";
|
|
4
5
|
export * from "./sdk-info";
|
|
5
6
|
export * from "./types";
|
|
6
7
|
export * from "./electron-namespaces";
|
package/dist/src/index.js
CHANGED
|
@@ -17,10 +17,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
17
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.pluginManifestSchema = void 0;
|
|
20
|
+
exports.NEXUS_PLUGIN_RENDERER_THEME = exports.pluginManifestSchema = void 0;
|
|
21
21
|
const plugin_manifest_schema_json_1 = __importDefault(require("../.generated/plugin-manifest.schema.json"));
|
|
22
22
|
exports.pluginManifestSchema = plugin_manifest_schema_json_1.default;
|
|
23
23
|
__exportStar(require("../.generated/plugin-framework-version"), exports);
|
|
24
|
+
var theme_contract_1 = require("../.generated/theme-contract");
|
|
25
|
+
Object.defineProperty(exports, "NEXUS_PLUGIN_RENDERER_THEME", { enumerable: true, get: function () { return theme_contract_1.NEXUS_PLUGIN_RENDERER_THEME; } });
|
|
24
26
|
__exportStar(require("./sdk-info"), exports);
|
|
25
27
|
__exportStar(require("./types"), exports);
|
|
26
28
|
__exportStar(require("./electron-namespaces"), exports);
|
|
@@ -34,9 +34,9 @@ function resolveNexusExecutable(hostPath) {
|
|
|
34
34
|
throw new dev_1.PluginDevError("Nexus host path does not exist: " + resolved);
|
|
35
35
|
}
|
|
36
36
|
const stat = fs.statSync(resolved);
|
|
37
|
-
const executable = stat.isDirectory() ? path.join(resolved, "
|
|
37
|
+
const executable = stat.isDirectory() ? path.join(resolved, "AIoT Toolkits.exe") : resolved;
|
|
38
38
|
if (!fs.existsSync(executable) || !fs.statSync(executable).isFile()) {
|
|
39
|
-
throw new dev_1.PluginDevError("
|
|
39
|
+
throw new dev_1.PluginDevError("AIoT Toolkits executable is missing: " + executable);
|
|
40
40
|
}
|
|
41
41
|
if (process.platform === "win32" && path.extname(executable).toLowerCase() !== ".exe") {
|
|
42
42
|
throw new dev_1.PluginDevError("Nexus host must be a Windows executable: " + executable);
|
package/dist/src/scaffold.js
CHANGED
|
@@ -218,7 +218,7 @@ function createPackageJson(options, sdkSource) {
|
|
|
218
218
|
name: options.pluginId,
|
|
219
219
|
version: "0.1.0",
|
|
220
220
|
private: true,
|
|
221
|
-
description: options.displayName + " independent plugin for AIoT
|
|
221
|
+
description: options.displayName + " independent plugin for AIoT Toolkits.",
|
|
222
222
|
scripts
|
|
223
223
|
};
|
|
224
224
|
if (options.template === "sqlite") {
|
package/dist/src/sdk-info.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const PLUGIN_SDK_VERSION = "3.
|
|
1
|
+
export declare const PLUGIN_SDK_VERSION = "3.3.0";
|
|
2
2
|
export declare const PLUGIN_SDK_NODE_ENGINE = ">=16.20.0 <17";
|
|
3
3
|
export declare const PLUGIN_SDK_NPM_ENGINE = ">=6.13.4";
|
|
4
4
|
export declare const PLUGIN_SDK_ELECTRON_VERSION = "22.3.27";
|
package/dist/src/sdk-info.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getPluginSdkInfo = exports.PLUGIN_SDK_TYPES_NODE_VERSION = exports.PLUGIN_SDK_TYPESCRIPT_VERSION = exports.PLUGIN_SDK_ELECTRON_VERSION = exports.PLUGIN_SDK_NPM_ENGINE = exports.PLUGIN_SDK_NODE_ENGINE = exports.PLUGIN_SDK_VERSION = void 0;
|
|
4
4
|
const plugin_framework_version_1 = require("../.generated/plugin-framework-version");
|
|
5
|
-
exports.PLUGIN_SDK_VERSION = "3.
|
|
5
|
+
exports.PLUGIN_SDK_VERSION = "3.3.0";
|
|
6
6
|
exports.PLUGIN_SDK_NODE_ENGINE = ">=16.20.0 <17";
|
|
7
7
|
exports.PLUGIN_SDK_NPM_ENGINE = ">=6.13.4";
|
|
8
8
|
exports.PLUGIN_SDK_ELECTRON_VERSION = "22.3.27";
|
package/dist/src/types.d.ts
CHANGED
|
@@ -12,6 +12,42 @@ export type NexusResult<T> = {
|
|
|
12
12
|
};
|
|
13
13
|
export type ThemeMode = "light" | "dark" | "system";
|
|
14
14
|
export type LaunchPageParams = Record<string, string>;
|
|
15
|
+
export interface PluginThemeTokens {
|
|
16
|
+
background: string;
|
|
17
|
+
surface: string;
|
|
18
|
+
surfaceAlt: string;
|
|
19
|
+
surfaceRaised: string;
|
|
20
|
+
surfaceHover: string;
|
|
21
|
+
surfaceInset: string;
|
|
22
|
+
border: string;
|
|
23
|
+
borderSoft: string;
|
|
24
|
+
borderStrong: string;
|
|
25
|
+
text: string;
|
|
26
|
+
textMuted: string;
|
|
27
|
+
textSoft: string;
|
|
28
|
+
textFaint: string;
|
|
29
|
+
primary: string;
|
|
30
|
+
accent: string;
|
|
31
|
+
onPrimary: string;
|
|
32
|
+
primarySoft: string;
|
|
33
|
+
info: string;
|
|
34
|
+
infoSoft: string;
|
|
35
|
+
positive: string;
|
|
36
|
+
positiveSoft: string;
|
|
37
|
+
warning: string;
|
|
38
|
+
warningSoft: string;
|
|
39
|
+
negative: string;
|
|
40
|
+
negativeSoft: string;
|
|
41
|
+
mutedSoft: string;
|
|
42
|
+
onInfo: string;
|
|
43
|
+
onPositive: string;
|
|
44
|
+
onWarning: string;
|
|
45
|
+
onNegative: string;
|
|
46
|
+
}
|
|
47
|
+
export interface PluginRendererTheme {
|
|
48
|
+
light: PluginThemeTokens;
|
|
49
|
+
dark: PluginThemeTokens;
|
|
50
|
+
}
|
|
15
51
|
export type PluginPermission = "businessLog" | "settings" | "filesystem" | "udp" | "serial" | "externalProcess" | "dialog";
|
|
16
52
|
export type PluginPermissionSupportState = "implemented" | "routeGuard" | "reserved";
|
|
17
53
|
export interface PluginPermissionSummary {
|
|
@@ -76,6 +112,7 @@ export interface PluginRendererContext {
|
|
|
76
112
|
launchParams?: LaunchPageParams;
|
|
77
113
|
language: string;
|
|
78
114
|
themeMode: ThemeMode;
|
|
115
|
+
theme?: PluginRendererTheme;
|
|
79
116
|
hostApiVersion: string;
|
|
80
117
|
}
|
|
81
118
|
export interface PluginSettingsDocument {
|
package/dist/src/web-dev.js
CHANGED
|
@@ -30,6 +30,7 @@ const http = __importStar(require("http"));
|
|
|
30
30
|
const path = __importStar(require("path"));
|
|
31
31
|
const dev_1 = require("./dev");
|
|
32
32
|
const plugin_framework_version_1 = require("../.generated/plugin-framework-version");
|
|
33
|
+
const theme_contract_1 = require("../.generated/theme-contract");
|
|
33
34
|
const loopbackHost = "127.0.0.1";
|
|
34
35
|
const defaultBodyLimit = 1024 * 1024;
|
|
35
36
|
const browserApiPrefix = "/__nexus_plugin_dev";
|
|
@@ -165,6 +166,7 @@ function pluginContext(target) {
|
|
|
165
166
|
contributionId: target.contributionId,
|
|
166
167
|
language: target.language,
|
|
167
168
|
themeMode: target.themeMode,
|
|
169
|
+
theme: theme_contract_1.NEXUS_PLUGIN_RENDERER_THEME,
|
|
168
170
|
hostApiVersion: plugin_framework_version_1.PLUGIN_FRAMEWORK_VERSION
|
|
169
171
|
};
|
|
170
172
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# __DISPLAY_NAME_MARKDOWN__
|
|
2
2
|
|
|
3
|
-
AIoT
|
|
3
|
+
AIoT Toolkits independent PluginFrame plugin generated from the `minimal` template.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npm install
|
|
@@ -8,14 +8,14 @@ npm run typecheck
|
|
|
8
8
|
npm run build
|
|
9
9
|
npm run dev:web
|
|
10
10
|
npm run dev
|
|
11
|
-
npm run dev:nexus -- --host C:\path\to\
|
|
11
|
+
npm run dev:nexus -- --host "C:\path\to\AIoT Toolkits.exe"
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
SDK checks for a newer `@aiot-nexus/plugin-sdk` in two steps. The package `postinstall` refreshes a user cache; npm 7+ hides dependency lifecycle output by default, so use `npm install --foreground-scripts` to see it. The `nexus-plugin` CLI reads that cache and prints npm-style notices when it is fresh; missing or older-than-24-hours cache data is checked in the background without blocking the command. `npm install --ignore-scripts` skips only the install check, while CLI checks remain available.
|
|
15
15
|
|
|
16
16
|
The check follows the npm-configured registry, falls back to the official npm registry when needed, and silently ignores offline, timeout, malformed-response, or corrupt-cache failures. Use `NEXUS_PLUGIN_SDK_UPDATE_CHECK=0` or `NO_UPDATE_NOTIFIER=1` to disable both checks. Notices never modify the project. When one appears, substitute the exact version printed by the notice for `X.Y.Z`: `npx --package=@aiot-nexus/plugin-sdk@X.Y.Z nexus-plugin upgrade . --check`, then repeat without `--check`. The transaction synchronizes package.json, package-lock.json, and node_modules, runs project checks, and rolls back on failure. A major update additionally requires `--allow-major` and the migration guide linked by the CLI; source and manifest files are never rewritten.
|
|
17
17
|
|
|
18
|
-
The deployable plugin package is `dist/__PLUGIN_ID__/`. For a manual installation, stop
|
|
18
|
+
The deployable plugin package is `dist/__PLUGIN_ID__/`. For a manual installation, stop AIoT Toolkits before replacing `Plugins/__PLUGIN_ID__/`, then start it again. During development, use `npm run dev:nexus` so the CLI coordinates AIoT Toolkits shutdown, atomic replacement, and restart for trusted `mainEntry` changes.
|
|
19
19
|
|
|
20
20
|
## Native Electron file dialogs
|
|
21
21
|
|
|
@@ -97,17 +97,21 @@ export async function chooseSavePath(): Promise<string | undefined> {
|
|
|
97
97
|
}
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
Call these helpers from an operation registered with `context.host.registerRendererMethod`, then let the renderer invoke that narrow operation through `client.invokeMain()`. Return the `NexusResult` from `chooseAndReadText` directly so the renderer receives the stable `FileNotReadable`, `FileTooLarge`, or `FileReadFailed` code. Do not expose Electron objects or unrestricted file paths to the iframe. For larger files, stream or paginate in `mainEntry` and return only bounded chunks or summaries instead of the whole file. `npm run dev:web` is renderer-only and deliberately has no `mainEntry`, Node filesystem, or Electron main-process API; use `npm run dev` or a formal
|
|
100
|
+
Call these helpers from an operation registered with `context.host.registerRendererMethod`, then let the renderer invoke that narrow operation through `client.invokeMain()`. Return the `NexusResult` from `chooseAndReadText` directly so the renderer receives the stable `FileNotReadable`, `FileTooLarge`, or `FileReadFailed` code. Do not expose Electron objects or unrestricted file paths to the iframe. For larger files, stream or paginate in `mainEntry` and return only bounded chunks or summaries instead of the whole file. `npm run dev:web` is renderer-only and deliberately has no `mainEntry`, Node filesystem, or Electron main-process API; use `npm run dev` or a formal AIoT Toolkits host to test these dialogs.
|
|
101
101
|
|
|
102
102
|
The renderer calls the trusted-main `demo.echo` operation through `@aiot-nexus/plugin-sdk`. The main entry also calls the directly imported `app.getName()` during activation, so `npm run dev` verifies the real Electron main-process path. Run `npm run validate` after changing the manifest or output layout.
|
|
103
103
|
|
|
104
|
-
The renderer follows `context.language` and `context.themeMode` from
|
|
104
|
+
The renderer follows `context.language` and `context.themeMode` from AIoT Toolkits, subscribes to in-place context updates without reloading the iframe, and includes `en-US` and `zh-CN` message catalogs. The plugin page has no language or theme selector; the controls shown by `npm run dev` belong to the debug host and simulate AIoT Toolkits context changes. Below that toolbar, the iframe has no host padding, border, minimum size, or content-driven height and fills the remaining host viewport. Add or edit renderer messages in `src/renderer/app.ts`; edit host-shell titles and descriptions in `src/manifest.json` resources.
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
SDK 3.2 automatically maps the active `context.theme` palette to `--nexus-color-*` variables and sets `data-nexus-theme="light|dark"` on the document root. This template consumes those variables with local light/dark fallbacks, so it still renders on an older host. Merely upgrading the SDK does not recolor custom CSS: use the namespaced variables or read the optional palette explicitly, for example `const primary = context.theme?.light.primary`.
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
For a plugin-owned theme, create the client with `createPluginFrameClient({ autoApplyHostTheme: false })`. The palette remains available from `await client.waitForContext()`, but the SDK will not write CSS variables or `data-nexus-theme`. It never overwrites plugin-owned `--bg`, `--accent`, `data-theme`, or `color-scheme` values.
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
The current SDK has no generic business-event subscription API. For a long-running operation, register a fast `job.start` operation that returns a `taskId`, then expose `job.getStatus` and optional `job.cancel` for renderer polling. See the AIoT Toolkits repository's plugin IPC and event notification example for the full contract and code.
|
|
111
|
+
|
|
112
|
+
Use `npm run dev:web` for renderer-only UI work in the system browser. It runs `build:web`, writes an isolated package under `.nexus-plugin-dev/web-package/`, builds with source maps, serves only on `127.0.0.1:4173`, and opens the browser automatically. It does not execute `mainEntry`, provide Node or Electron main-process APIs, or overwrite the deployable `dist` package. Language and theme controls still simulate AIoT Toolkits context, while `invokeMain` is intentionally unavailable. Pass `-- --no-open` or `-- --port <port>` when needed; use `npm run dev` for Host API and Electron integration.
|
|
113
|
+
|
|
114
|
+
Use `npm run dev:nexus -- --host "C:\path\to\AIoT Toolkits.exe"` for live development against a compiled, packaged AIoT Toolkits. The CLI atomically deploys this plugin to `<exeDir>/Plugins/__PLUGIN_ID__/`. Renderer changes rebuild and reload the iframe without changing the AIoT Toolkits PID; safe manifest changes rescan contributions in the same process; `mainEntry`, dependency, native-module, or restart-sensitive manifest changes stop AIoT Toolkits, replace the complete package, and start a new AIoT Toolkits process because trusted modules cannot be unloaded safely from Node's module cache. Failed builds keep the previous deployment; launch or readiness failures restore the previous package and host. Add `--no-devtools`, `--inspect[=<port>]`, `--inspect-brk[=<port>]`, `--surface`, or `--contribution-id` after the second `--` as needed.
|
|
111
115
|
|
|
112
116
|
When this project was created from a local SDK archive, `vendor/aiot-nexus-plugin-sdk.tgz` is part of the source project and must be kept when the project is moved or committed. An explicit local upgrade copies the target archive to `vendor/aiot-nexus-plugin-sdk-<version>.tgz` and updates the exact file dependency.
|
|
113
117
|
|
|
@@ -115,4 +119,4 @@ For a local SDK archive, the tgz version remains the plugin's current SDK versio
|
|
|
115
119
|
|
|
116
120
|
For trusted-main breakpoints, open this plugin directory in VS Code, set breakpoints in `src/main.ts`, and run `Debug Nexus plugin`. To attach manually, start `npm run dev:debug` and select `Attach Nexus plugin main process (9229)`. Renderer DevTools opens automatically, with `src/renderer/app.ts` available through the development source map. `npm run dev` rebuilds before launch; use `npm run dev -- --no-build` only when intentionally reusing the current `dist` output.
|
|
117
121
|
|
|
118
|
-
The independent host stores data under `.nexus-plugin-dev/` and mirrors the PluginFrame protocol and current Host API surface. Its simulated
|
|
122
|
+
The independent host stores data under `.nexus-plugin-dev/` and mirrors the PluginFrame protocol and current Host API surface. Its simulated AIoT Toolkits application version defaults to the version recorded by the SDK build; pass `npm run dev -- --host-version <X.Y.Z>` to exercise another compatibility branch. Run the deployable `dist/__PLUGIN_ID__/` in AIoT Toolkits before delivery because the standalone host does not reproduce the full application shell or routing state.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createPluginFrameClient } from "@aiot-nexus/plugin-sdk/browser";
|
|
2
|
+
import type { PluginThemeTokens } from "@aiot-nexus/plugin-sdk";
|
|
2
3
|
|
|
3
4
|
interface EchoResponse {
|
|
4
5
|
value: string;
|
|
@@ -139,9 +140,11 @@ function applyLanguage(nextLanguage: SupportedLanguage): void {
|
|
|
139
140
|
client.notifyResize();
|
|
140
141
|
}
|
|
141
142
|
|
|
142
|
-
function applyContext(context: { language: string; themeMode: ThemeMode }): void {
|
|
143
|
+
function applyContext(context: { language: string; themeMode: ThemeMode; theme?: { light: PluginThemeTokens; dark: PluginThemeTokens } }): void {
|
|
143
144
|
applyLanguage(resolveLanguage(context.language));
|
|
144
145
|
applyTheme(context.themeMode);
|
|
146
|
+
const resolvedMode = context.themeMode === "dark" || (context.themeMode === "system" && systemDarkMode.matches) ? "dark" : "light";
|
|
147
|
+
contextOutput.dataset.hostPrimary = context.theme?.[resolvedMode].primary || "";
|
|
145
148
|
revealPluginContent();
|
|
146
149
|
}
|
|
147
150
|
|