@aiot-nexus/plugin-sdk 3.1.0 → 3.2.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 +20 -13
- package/dev-host/main.js +20 -5
- package/dist/.generated/plugin-framework-version.d.ts +1 -1
- package/dist/.generated/plugin-framework-version.js +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/src/browser/frame-client.d.ts +1 -0
- package/dist/src/browser/frame-client.js +197 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +3 -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/upgrade.js +13 -4
- package/dist/src/web-dev.js +2 -0
- package/package.json +1 -1
- package/templates/minimal/README.md.tmpl +4 -0
- 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 +4 -0
- package/templates/sqlite/src/renderer/app.ts.tmpl +4 -1
- package/templates/sqlite/src/renderer/plugin.css +135 -57
|
@@ -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/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);
|
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.2.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.2.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/upgrade.js
CHANGED
|
@@ -225,7 +225,7 @@ function applyUpgrade(inspection, options, runtime) {
|
|
|
225
225
|
"install",
|
|
226
226
|
saveFlag,
|
|
227
227
|
"--save-exact",
|
|
228
|
-
inspection.normalizedSource.
|
|
228
|
+
inspection.normalizedSource.installSpec,
|
|
229
229
|
], result.projectDirectory);
|
|
230
230
|
if (!commandSucceeded(installResult)) {
|
|
231
231
|
throw new Error(commandFailure("npm install", installResult));
|
|
@@ -455,7 +455,11 @@ function normalizeSdkSource(value, workingDirectory, projectDirectory, runtime,
|
|
|
455
455
|
else {
|
|
456
456
|
addCheck(result, "SDK source", "passed", "Using exact registry version " + version + ".");
|
|
457
457
|
}
|
|
458
|
-
return {
|
|
458
|
+
return {
|
|
459
|
+
display: version,
|
|
460
|
+
dependencySpec: version,
|
|
461
|
+
installSpec: sdkPackageName + "@" + version,
|
|
462
|
+
};
|
|
459
463
|
}
|
|
460
464
|
let localArchivePath;
|
|
461
465
|
if (source.startsWith("file:")) {
|
|
@@ -471,14 +475,18 @@ function normalizeSdkSource(value, workingDirectory, projectDirectory, runtime,
|
|
|
471
475
|
}
|
|
472
476
|
if (!localArchivePath) {
|
|
473
477
|
addCheck(result, "SDK source", "failed", "SDK source must be an exact X.Y.Z version or a local .tgz path.");
|
|
474
|
-
return { display: source, dependencySpec: source };
|
|
478
|
+
return { display: source, dependencySpec: source, installSpec: source };
|
|
475
479
|
}
|
|
476
480
|
try {
|
|
477
481
|
assertRegularFile(localArchivePath, "Local SDK archive", ".tgz");
|
|
478
482
|
}
|
|
479
483
|
catch (error) {
|
|
480
484
|
addCheck(result, "SDK source", "failed", errorMessage(error));
|
|
481
|
-
return {
|
|
485
|
+
return {
|
|
486
|
+
display: localArchivePath,
|
|
487
|
+
dependencySpec: source,
|
|
488
|
+
installSpec: source,
|
|
489
|
+
};
|
|
482
490
|
}
|
|
483
491
|
const archiveInfo = runtime.runNpm([
|
|
484
492
|
"pack",
|
|
@@ -520,6 +528,7 @@ function normalizeSdkSource(value, workingDirectory, projectDirectory, runtime,
|
|
|
520
528
|
return {
|
|
521
529
|
display: localArchivePath,
|
|
522
530
|
dependencySpec: "file:" + vendoredRelativePath,
|
|
531
|
+
installSpec: "file:" + vendoredRelativePath,
|
|
523
532
|
localArchivePath,
|
|
524
533
|
vendoredArchivePath: path.join(projectDirectory, ...vendoredRelativePath.split("/")),
|
|
525
534
|
};
|
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
|
@@ -103,6 +103,10 @@ The renderer calls the trusted-main `demo.echo` operation through `@aiot-nexus/p
|
|
|
103
103
|
|
|
104
104
|
The renderer follows `context.language` and `context.themeMode` from Nexus, 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 Nexus 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
|
+
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
|
+
|
|
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
|
+
|
|
106
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 Nexus repository's plugin IPC and event notification example for the full contract and code.
|
|
107
111
|
|
|
108
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 Nexus context, while `invokeMain` is intentionally unavailable. Pass `-- --no-open` or `-- --port <port>` when needed; use `npm run dev` for Host API and Electron integration.
|
|
@@ -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
|
|
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
:root {
|
|
2
|
+
--bg: var(--nexus-color-background, #eef1f4);
|
|
3
|
+
--panel: var(--nexus-color-surface, #ffffff);
|
|
4
|
+
--raise: var(--nexus-color-surface-raised, #f7f9fa);
|
|
5
|
+
--hover: var(--nexus-color-surface-hover, #eef3f6);
|
|
6
|
+
--line: var(--nexus-color-border, #d7dde3);
|
|
7
|
+
--line-strong: var(--nexus-color-border-strong, #c2cbd3);
|
|
8
|
+
--fg: var(--nexus-color-text, #212a32);
|
|
9
|
+
--fg-dim: var(--nexus-color-text-muted, #5a6772);
|
|
10
|
+
--brand: var(--nexus-color-primary, #8fc31f);
|
|
11
|
+
--brand-text: var(--nexus-color-accent, #6f9a15);
|
|
12
|
+
--on-brand: var(--nexus-color-on-primary, #ffffff);
|
|
13
|
+
--primary-action: var(--nexus-color-primary, #8fc31f);
|
|
14
|
+
--on-primary: var(--nexus-color-on-primary, #ffffff);
|
|
15
|
+
--on-negative: var(--nexus-color-on-negative, #ffffff);
|
|
16
|
+
--focus-ring: var(--nexus-color-accent, #6f9a15);
|
|
17
|
+
--brand-soft: var(--nexus-color-primary-soft, rgba(143, 195, 31, 0.12));
|
|
18
|
+
--positive: var(--nexus-color-positive, #1f9a4e);
|
|
2
19
|
color-scheme: light;
|
|
3
|
-
font-family: "
|
|
4
|
-
|
|
5
|
-
|
|
20
|
+
font-family: "Noto Sans SC", "Source Han Sans SC", "Microsoft YaHei",
|
|
21
|
+
"Segoe UI", system-ui, sans-serif;
|
|
22
|
+
background: var(--bg);
|
|
23
|
+
color: var(--fg);
|
|
24
|
+
font-size: 14px;
|
|
25
|
+
letter-spacing: 0;
|
|
6
26
|
}
|
|
7
27
|
|
|
8
28
|
* {
|
|
@@ -16,7 +36,8 @@
|
|
|
16
36
|
body {
|
|
17
37
|
margin: 0;
|
|
18
38
|
min-height: 0;
|
|
19
|
-
background:
|
|
39
|
+
background: var(--bg);
|
|
40
|
+
color: var(--fg);
|
|
20
41
|
}
|
|
21
42
|
|
|
22
43
|
.plugin-shell {
|
|
@@ -27,9 +48,9 @@ body {
|
|
|
27
48
|
|
|
28
49
|
.plugin-header,
|
|
29
50
|
.echo-panel {
|
|
30
|
-
border: 1px solid
|
|
31
|
-
border-radius:
|
|
32
|
-
background:
|
|
51
|
+
border: 1px solid var(--line);
|
|
52
|
+
border-radius: 10px;
|
|
53
|
+
background: var(--panel);
|
|
33
54
|
}
|
|
34
55
|
|
|
35
56
|
.plugin-header {
|
|
@@ -47,22 +68,24 @@ p {
|
|
|
47
68
|
}
|
|
48
69
|
|
|
49
70
|
h1 {
|
|
50
|
-
font-size:
|
|
71
|
+
font-size: 18px;
|
|
72
|
+
font-weight: 600;
|
|
51
73
|
}
|
|
52
74
|
|
|
53
75
|
h2 {
|
|
54
|
-
font-size:
|
|
76
|
+
font-size: 14.5px;
|
|
77
|
+
font-weight: 600;
|
|
55
78
|
}
|
|
56
79
|
|
|
57
80
|
p,
|
|
58
81
|
.result-row > span {
|
|
59
82
|
margin-top: 4px;
|
|
60
|
-
color:
|
|
83
|
+
color: var(--fg-dim);
|
|
61
84
|
font-size: 12px;
|
|
62
85
|
}
|
|
63
86
|
|
|
64
87
|
.status {
|
|
65
|
-
color:
|
|
88
|
+
color: var(--positive);
|
|
66
89
|
font-size: 12px;
|
|
67
90
|
font-weight: 600;
|
|
68
91
|
}
|
|
@@ -87,24 +110,42 @@ label {
|
|
|
87
110
|
input {
|
|
88
111
|
min-width: 0;
|
|
89
112
|
height: 36px;
|
|
90
|
-
border: 1px solid
|
|
91
|
-
border-radius:
|
|
113
|
+
border: 1px solid var(--line-strong);
|
|
114
|
+
border-radius: 7px;
|
|
92
115
|
padding: 0 10px;
|
|
93
|
-
background:
|
|
94
|
-
color:
|
|
116
|
+
background: var(--panel);
|
|
117
|
+
color: var(--fg);
|
|
95
118
|
font: inherit;
|
|
96
119
|
}
|
|
97
120
|
|
|
98
121
|
button {
|
|
99
122
|
height: 36px;
|
|
100
|
-
border: 1px solid
|
|
101
|
-
border-radius:
|
|
123
|
+
border: 1px solid var(--brand);
|
|
124
|
+
border-radius: 7px;
|
|
102
125
|
padding: 0 16px;
|
|
103
|
-
background:
|
|
104
|
-
color:
|
|
126
|
+
background: var(--brand);
|
|
127
|
+
color: var(--on-brand);
|
|
105
128
|
font: inherit;
|
|
106
129
|
font-weight: 600;
|
|
107
130
|
cursor: pointer;
|
|
131
|
+
transition: filter 0.15s ease, background-color 0.15s ease,
|
|
132
|
+
border-color 0.15s ease, color 0.15s ease;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
button:not(:disabled):hover {
|
|
136
|
+
border-color: var(--primary-action);
|
|
137
|
+
background: var(--primary-action);
|
|
138
|
+
color: var(--on-primary);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
button:focus-visible,
|
|
142
|
+
input:focus-visible {
|
|
143
|
+
outline: 2px solid var(--focus-ring);
|
|
144
|
+
outline-offset: 2px;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
input:hover {
|
|
148
|
+
border-color: var(--brand);
|
|
108
149
|
}
|
|
109
150
|
|
|
110
151
|
button:disabled {
|
|
@@ -117,7 +158,7 @@ button:disabled {
|
|
|
117
158
|
grid-template-columns: 80px minmax(0, 1fr);
|
|
118
159
|
gap: 10px;
|
|
119
160
|
margin-top: 14px;
|
|
120
|
-
border-top: 1px solid
|
|
161
|
+
border-top: 1px solid var(--line);
|
|
121
162
|
padding-top: 12px;
|
|
122
163
|
}
|
|
123
164
|
|
|
@@ -135,37 +176,22 @@ output {
|
|
|
135
176
|
}
|
|
136
177
|
|
|
137
178
|
:root[data-theme="dark"] {
|
|
179
|
+
--bg: var(--nexus-color-background, #101418);
|
|
180
|
+
--panel: var(--nexus-color-surface, #1b222a);
|
|
181
|
+
--raise: var(--nexus-color-surface-raised, #171c22);
|
|
182
|
+
--hover: var(--nexus-color-surface-hover, #222a34);
|
|
183
|
+
--line: var(--nexus-color-border, #2a333e);
|
|
184
|
+
--line-strong: var(--nexus-color-border-strong, #38434f);
|
|
185
|
+
--fg: var(--nexus-color-text, #e2e8ee);
|
|
186
|
+
--fg-dim: var(--nexus-color-text-muted, #97a3af);
|
|
187
|
+
--brand-text: var(--nexus-color-accent, #a5d43e);
|
|
188
|
+
--primary-action: var(--nexus-color-primary, #8fc31f);
|
|
189
|
+
--on-primary: var(--nexus-color-on-primary, #ffffff);
|
|
190
|
+
--on-negative: var(--nexus-color-on-negative, #101418);
|
|
191
|
+
--focus-ring: var(--nexus-color-accent, #a5d43e);
|
|
192
|
+
--brand-soft: var(--nexus-color-primary-soft, rgba(143, 195, 31, 0.14));
|
|
193
|
+
--positive: var(--nexus-color-positive, #46b96e);
|
|
138
194
|
color-scheme: dark;
|
|
139
|
-
background:
|
|
140
|
-
color:
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
:root[data-theme="dark"] body {
|
|
144
|
-
background: #16191d;
|
|
145
|
-
color: #edf0f2;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
:root[data-theme="dark"] .plugin-header,
|
|
149
|
-
:root[data-theme="dark"] .echo-panel {
|
|
150
|
-
border-color: #454b52;
|
|
151
|
-
background: #24282d;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
:root[data-theme="dark"] p,
|
|
155
|
-
:root[data-theme="dark"] .result-row > span {
|
|
156
|
-
color: #aeb7c2;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
:root[data-theme="dark"] input {
|
|
160
|
-
border-color: #59616a;
|
|
161
|
-
background: #191c20;
|
|
162
|
-
color: #edf0f2;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
:root[data-theme="dark"] .result-row {
|
|
166
|
-
border-top-color: #454b52;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
:root[data-theme="dark"] .status {
|
|
170
|
-
color: #75d6a5;
|
|
195
|
+
background: var(--bg);
|
|
196
|
+
color: var(--fg);
|
|
171
197
|
}
|
|
@@ -51,6 +51,10 @@ The table selector lists ordinary user tables from the `main` schema. Dynamic he
|
|
|
51
51
|
|
|
52
52
|
The renderer follows `context.language` and `context.themeMode` from Nexus and preserves unfinished editor values during in-place context updates. The plugin page has no language or theme selector; the controls shown by `npm run dev` belong to the debug host. Below that toolbar, the iframe has no host padding, border, minimum size, or content-driven height and fills the remaining host viewport. This SDK template deliberately has no Quasar dependency, so its data grid uses a native `<table>` as a dependency-minimized template exception. It still provides pagination, contained horizontal scrolling, light/dark themes, `en-US` / `zh-CN`, and busy/empty/error states. Edit renderer messages in `src/renderer/app.ts`; edit host-shell titles and descriptions in `src/manifest.json` resources.
|
|
53
53
|
|
|
54
|
+
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`.
|
|
55
|
+
|
|
56
|
+
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.
|
|
57
|
+
|
|
54
58
|
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 Nexus repository's plugin IPC and event notification example for the full contract and code.
|
|
55
59
|
|
|
56
60
|
Use `npm run dev:web` for renderer-only layout, styling, localization, and responsive checks in the system browser. It runs `build:web`, writes an isolated package under `.nexus-plugin-dev/web-package/`, and does not rebuild or load `better-sqlite3`, execute `mainEntry`, provide Node or Electron main-process APIs, copy native runtime files, or overwrite the deployable `dist` package. It builds with source maps, serves only on `127.0.0.1:4173`, and opens the browser automatically. The page explicitly reports `PluginCapabilityUnsupported`; file selection, table loading, and CRUD require `npm run dev` or a formal Nexus host. Pass `-- --no-open` or `-- --port <port>` when needed.
|