@colixsystems/widget-sdk 0.276.0 → 0.277.1
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 +11 -1
- package/dist/bundled-assets.cjs +6 -2
- package/dist/bundled-assets.js +0 -1
- package/dist/contract.cjs +5 -4
- package/dist/contract.js +2 -2
- package/dist/define-widget.js +8 -1
- package/dist/index.d.ts +2 -3
- package/dist/linter.cjs +290 -212
- package/dist/linter.js +11 -2
- package/dist/lucideIconName.cjs +14 -5
- package/dist/manifest.cjs +7 -15
- package/dist/manifest.js +10 -69
- package/dist/navigation.cjs +31 -11
- package/dist/source-mask.cjs +169 -0
- package/dist/theme-appearance.cjs +7 -5
- package/dist/theme-appearance.js +1 -1
- package/dist/theme-background.cjs +3 -8
- package/dist/theme-components.cjs +13 -10
- package/package.json +2 -2
- package/src/contract.js +2 -2
package/dist/linter.js
CHANGED
|
@@ -1584,13 +1584,22 @@ function _rawUseMemoIntoDatastoreQueryRules(source) {
|
|
|
1584
1584
|
// `.roles` re-implements the table ACL client-side and drifts from it the
|
|
1585
1585
|
// moment the ACL changes; `useCanWrite(tableId)` reads the same rule the
|
|
1586
1586
|
// write endpoint enforces. Flags only when the file has NOT already reached
|
|
1587
|
-
// for it.
|
|
1587
|
+
// for it, and only on useUser()'s own value: `theme.typography.roles` is a
|
|
1588
|
+
// style read (sc-15204).
|
|
1589
|
+
function _readsUserRoles(code) {
|
|
1590
|
+
const named = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*useUser\s*\(\s*\)/.exec(code);
|
|
1591
|
+
if (named && new RegExp(`\\b${named[1].replace(/\$/g, "\\$")}\\??\\.(groupIds|roles)\\b`).test(code)) return true;
|
|
1592
|
+
const destructured = /\b(?:const|let|var)\s*\{([^}]*)\}\s*=\s*useUser\s*\(\s*\)/.exec(code);
|
|
1593
|
+
if (destructured && /\b(groupIds|roles)\b/.test(destructured[1])) return true;
|
|
1594
|
+
return /\buseUser\s*\(\s*\)\s*\??\.(groupIds|roles)\b/.test(code);
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1588
1597
|
function _handRolledWriteGateRules(source) {
|
|
1589
1598
|
const code = _stripNonCode(source);
|
|
1590
1599
|
if (/\buseCanWrite\s*\(/.test(code)) return [];
|
|
1591
1600
|
const userCall = /\buseUser\s*\(\s*\)/.exec(code);
|
|
1592
1601
|
if (!userCall) return [];
|
|
1593
|
-
if (
|
|
1602
|
+
if (!_readsUserRoles(code)) return [];
|
|
1594
1603
|
const hasWrite =
|
|
1595
1604
|
/\.(create|update)\s*\(/.test(code) ||
|
|
1596
1605
|
/\buseDatastoreMutation\s*\(/.test(code);
|
package/dist/lucideIconName.cjs
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
// algorithm, same behaviour. See lucideIconName.js for the rationale.
|
|
1
|
+
// GENERATED from lucideIconName.js by scripts/generate-cjs.js — DO NOT EDIT.
|
|
2
|
+
// Change lucideIconName.js, then run `npm run generate:cjs` in packages/widget-sdk.
|
|
4
3
|
"use strict";
|
|
5
4
|
|
|
5
|
+
// Normalize a human-typed Lucide icon name to the PascalCase form Lucide
|
|
6
|
+
// exports its components under. The lucide.dev gallery shows kebab-case
|
|
7
|
+
// (`arrow-right`) and its copy button hands you kebab-case, but
|
|
8
|
+
// `lucide-react` / `lucide-react-native` key their components in PascalCase
|
|
9
|
+
// (`ArrowRight`). This bridges the two so any of kebab / snake / spaced /
|
|
10
|
+
// camel / PascalCase resolves: `arrow-right`, `arrow_right`, `arrow right`,
|
|
11
|
+
// `arrowRight`, and `ArrowRight` all become `ArrowRight`; `building-2` becomes
|
|
12
|
+
// `Building2`. Non-strings and blanks return "" so callers fall back to a
|
|
13
|
+
// placeholder. Already-PascalCase names pass through unchanged.
|
|
6
14
|
function normalizeLucideIconName(name) {
|
|
7
15
|
if (typeof name !== "string") return "";
|
|
8
16
|
const trimmed = name.trim();
|
|
@@ -13,5 +21,6 @@ function normalizeLucideIconName(name) {
|
|
|
13
21
|
.map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
|
|
14
22
|
.join("");
|
|
15
23
|
}
|
|
16
|
-
|
|
17
|
-
|
|
24
|
+
module.exports = {
|
|
25
|
+
normalizeLucideIconName,
|
|
26
|
+
};
|
package/dist/manifest.cjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
// re-exports the same functions defined here so there is exactly one
|
|
5
|
-
// implementation.
|
|
1
|
+
// GENERATED from manifest.js by scripts/generate-cjs.js — DO NOT EDIT.
|
|
2
|
+
// Change manifest.js, then run `npm run generate:cjs` in packages/widget-sdk.
|
|
3
|
+
"use strict";
|
|
6
4
|
|
|
7
5
|
const { CONTRACT } = require("./contract.cjs");
|
|
8
6
|
|
|
7
|
+
// Structural validation of a widget manifest, shared by the CLI, hosts and backend.
|
|
8
|
+
|
|
9
|
+
|
|
9
10
|
const PAYLOAD_VALUE_TYPES = CONTRACT.payloadValueTypes;
|
|
10
11
|
|
|
11
12
|
// sc-4505 — a payload / input field map: { <field>: { type, label? } }. This is
|
|
@@ -120,16 +121,7 @@ function validateManifestTranslations(translations, manifestId, errors) {
|
|
|
120
121
|
}
|
|
121
122
|
}
|
|
122
123
|
|
|
123
|
-
// REQ-MKT-ACTION (sc-4879):
|
|
124
|
-
//
|
|
125
|
-
// `manifest.actions` was how an automation shipped before Actions existed as
|
|
126
|
-
// their own marketplace deliverable — a widget was two products in one package,
|
|
127
|
-
// a component that renders and a script that never renders at all. It is now
|
|
128
|
-
// REFUSED rather than ignored: an author who declares it believes the
|
|
129
|
-
// automation will ship, and silence would publish that misunderstanding.
|
|
130
|
-
//
|
|
131
|
-
// The replacement is a marketplace Action (`@colixsystems/action-sdk`), which
|
|
132
|
-
// the workspace installs and configures on its own.
|
|
124
|
+
// REQ-MKT-ACTION (sc-4879): refused, not ignored — the author expects it to ship.
|
|
133
125
|
function validateManifestActions(_actions, errors) {
|
|
134
126
|
errors.push(
|
|
135
127
|
"manifest.actions is no longer supported — ship the automation as a " +
|
package/dist/manifest.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
//
|
|
2
|
-
// `validateManifest(...)` without a dynamic-import detour. The two files
|
|
3
|
-
// share their constants by mutual import via createRequire — manifest.js
|
|
4
|
-
// re-exports the same functions defined here so there is exactly one
|
|
5
|
-
// implementation.
|
|
1
|
+
// Structural validation of a widget manifest, shared by the CLI, hosts and backend.
|
|
6
2
|
|
|
7
|
-
import { CONTRACT
|
|
3
|
+
import { CONTRACT } from "./contract.js";
|
|
8
4
|
|
|
9
5
|
const PAYLOAD_VALUE_TYPES = CONTRACT.payloadValueTypes;
|
|
10
6
|
|
|
@@ -39,20 +35,6 @@ const VALID_CATEGORIES = new Set([
|
|
|
39
35
|
]);
|
|
40
36
|
const VALID_PLATFORMS = new Set(["web", "native"]);
|
|
41
37
|
|
|
42
|
-
// REQ-WIDGET-ACTION — structural validation for manifest-declared server
|
|
43
|
-
// actions. Mirrors the backend action.service caps; the runner re-validates
|
|
44
|
-
// the cron expression (node-cron) and binds the tenant-local API key + table
|
|
45
|
-
// at enable time, so we only check shape + size here (cross-env: no Buffer).
|
|
46
|
-
const VALID_ACTION_TRIGGERS = new Set([
|
|
47
|
-
"schedule",
|
|
48
|
-
"record_created",
|
|
49
|
-
"record_updated",
|
|
50
|
-
"record_deleted",
|
|
51
|
-
]);
|
|
52
|
-
const ACTION_SCRIPT_MAX_BYTES = 200 * 1024;
|
|
53
|
-
const ACTION_TIMEOUT_MIN_MS = 100;
|
|
54
|
-
const ACTION_TIMEOUT_MAX_MS = 5 * 60 * 1000;
|
|
55
|
-
|
|
56
38
|
// REQ-L10N-WIDGET — caps + patterns for manifest-declared translations.
|
|
57
39
|
// The relative key pattern is deliberately tighter than the dictionary's
|
|
58
40
|
// own KEY_RE: once the host namespaces it (`widget.<id>.<key>`) the result
|
|
@@ -134,54 +116,13 @@ function validateManifestTranslations(translations, manifestId, errors) {
|
|
|
134
116
|
}
|
|
135
117
|
}
|
|
136
118
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (a === null || typeof a !== "object") {
|
|
145
|
-
errors.push("manifest.actions entries must be objects");
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
if (!isNonEmptyString(a.key)) {
|
|
149
|
-
errors.push("manifest.actions[].key must be a non-empty string");
|
|
150
|
-
} else if (seenKeys.has(a.key)) {
|
|
151
|
-
errors.push(`manifest.actions[].key "${a.key}" is duplicated`);
|
|
152
|
-
} else {
|
|
153
|
-
seenKeys.add(a.key);
|
|
154
|
-
}
|
|
155
|
-
pushIf(errors, isNonEmptyString(a.name), "manifest.actions[].name must be a non-empty string");
|
|
156
|
-
const triggerTypes = normaliseActionTriggerTypes(a);
|
|
157
|
-
if (triggerTypes === null) {
|
|
158
|
-
errors.push(
|
|
159
|
-
`manifest.actions[].triggerTypes must be a non-empty array of unique values from ${[...VALID_ACTION_TRIGGERS].join(", ")}`,
|
|
160
|
-
);
|
|
161
|
-
} else if (triggerTypes.includes("schedule")) {
|
|
162
|
-
pushIf(
|
|
163
|
-
errors,
|
|
164
|
-
isNonEmptyString(a.scheduleCron),
|
|
165
|
-
"manifest.actions[].scheduleCron is required when triggerTypes contains 'schedule'",
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
if (!isNonEmptyString(a.scriptSource)) {
|
|
169
|
-
errors.push("manifest.actions[].scriptSource must be a non-empty string");
|
|
170
|
-
} else if (utf8ByteLength(a.scriptSource) > ACTION_SCRIPT_MAX_BYTES) {
|
|
171
|
-
errors.push("manifest.actions[].scriptSource exceeds 200 KiB");
|
|
172
|
-
}
|
|
173
|
-
if (a.timeoutMs !== undefined) {
|
|
174
|
-
const t = Number(a.timeoutMs);
|
|
175
|
-
if (!Number.isFinite(t) || t < ACTION_TIMEOUT_MIN_MS || t > ACTION_TIMEOUT_MAX_MS) {
|
|
176
|
-
errors.push("manifest.actions[].timeoutMs must be between 100 and 300000");
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
if (a.triggerTableId !== undefined || a.apiKeyId !== undefined) {
|
|
180
|
-
errors.push(
|
|
181
|
-
"manifest.actions[] must not include triggerTableId or apiKeyId — those are tenant-local and bound after install",
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
119
|
+
// REQ-MKT-ACTION (sc-4879): refused, not ignored — the author expects it to ship.
|
|
120
|
+
function validateManifestActions(_actions, errors) {
|
|
121
|
+
errors.push(
|
|
122
|
+
"manifest.actions is no longer supported — ship the automation as a " +
|
|
123
|
+
"marketplace Action (@colixsystems/action-sdk) instead of declaring it " +
|
|
124
|
+
"on a widget",
|
|
125
|
+
);
|
|
185
126
|
}
|
|
186
127
|
|
|
187
128
|
function canonicalCategory(c) {
|
|
@@ -361,7 +302,7 @@ function validateManifest(m) {
|
|
|
361
302
|
}
|
|
362
303
|
}
|
|
363
304
|
|
|
364
|
-
//
|
|
305
|
+
// Present at all is now an error (sc-4879) — see validateManifestActions.
|
|
365
306
|
if (manifest.actions !== undefined) {
|
|
366
307
|
validateManifestActions(manifest.actions, errors);
|
|
367
308
|
}
|
package/dist/navigation.cjs
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
// The BODY below is copied VERBATIM from navigation.js; only the module syntax
|
|
7
|
-
// differs. navigation-parity.test.js pins both facts — identical bodies AND
|
|
8
|
-
// identical behaviour over a shared case table — so drift fails CI.
|
|
1
|
+
// GENERATED from navigation.js by scripts/generate-cjs.js — DO NOT EDIT.
|
|
2
|
+
// Change navigation.js, then run `npm run generate:cjs` in packages/widget-sdk.
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const { CONTRACT, isHexColor, contrastRatio, mixHex, readableTextColor } = require("./contract.cjs");
|
|
9
6
|
|
|
10
7
|
// REQ-NAV-STRUCTURE — the host side of an app's navigation SHAPE and the
|
|
11
8
|
// tokens its footer strip is painted with.
|
|
@@ -25,7 +22,6 @@
|
|
|
25
22
|
// the SAME literal, so what a planner may persist and what a host draws cannot
|
|
26
23
|
// diverge.
|
|
27
24
|
|
|
28
|
-
const { CONTRACT, isHexColor, contrastRatio, mixHex, readableTextColor } = require("./contract.cjs");
|
|
29
25
|
|
|
30
26
|
// Absent or unknown resolves here, so every app authored before menu types
|
|
31
27
|
// existed renders and compiles byte-identically.
|
|
@@ -895,5 +891,29 @@ function borderWidthOr(...values) {
|
|
|
895
891
|
}
|
|
896
892
|
return BORDER_WIDTH_MIN;
|
|
897
893
|
}
|
|
898
|
-
|
|
899
|
-
|
|
894
|
+
module.exports = {
|
|
895
|
+
normaliseNavigation,
|
|
896
|
+
menuItemCap,
|
|
897
|
+
resolveFooterTokens,
|
|
898
|
+
FOOTER_SIZES,
|
|
899
|
+
clampFooterSize,
|
|
900
|
+
FOOTER_HOME_LINE_CLEARANCE_PX,
|
|
901
|
+
resolveSidebarTokens,
|
|
902
|
+
TOP_STRIPE_SLOTS,
|
|
903
|
+
TOP_STRIPE_ALWAYS_SHOWN_SLOTS,
|
|
904
|
+
MIN_TOP_STRIPE_HEIGHT_PX,
|
|
905
|
+
MAX_TOP_STRIPE_HEIGHT_PX,
|
|
906
|
+
MIN_TOP_STRIPE_PADDING_X_PX,
|
|
907
|
+
MAX_TOP_STRIPE_PADDING_X_PX,
|
|
908
|
+
MAX_TOP_STRIPE_OFFSET_Y_PX,
|
|
909
|
+
DEFAULT_TOP_STRIPE_HEIGHT_PX,
|
|
910
|
+
DEFAULT_TOP_STRIPE_PADDING_X_PX,
|
|
911
|
+
topStripeShowKey,
|
|
912
|
+
resolveTopStripeLayout,
|
|
913
|
+
isDarkSurface,
|
|
914
|
+
resolveAppLogo,
|
|
915
|
+
resolveTopBarTokens,
|
|
916
|
+
resolveSidebarUnfold,
|
|
917
|
+
resolveLocalNavModel,
|
|
918
|
+
drawsLocalNavAnywhere,
|
|
919
|
+
};
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// GENERATED from source-mask.js by scripts/generate-cjs.js — DO NOT EDIT.
|
|
2
|
+
// Change source-mask.js, then run `npm run generate:cjs` in packages/widget-sdk.
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
// sc-6086: the shared source masker, extracted verbatim from linter.js so the
|
|
6
|
+
// linter, the packer's import scanner, and the dev-server guard all agree on
|
|
7
|
+
// what is code and what is a comment or string literal (CLAUDE.md §3).
|
|
8
|
+
//
|
|
9
|
+
// Length-preserving: every blanked character becomes a space and newlines are
|
|
10
|
+
// kept, so an index into the mask is the SAME index in the original source.
|
|
11
|
+
// That is what lets a caller match against the mask and splice the original.
|
|
12
|
+
|
|
13
|
+
// Replace the *content* of comments and string / template literals with
|
|
14
|
+
// spaces so the banned-identifier scan only ever sees executable code. A
|
|
15
|
+
// banned host-escape identifier (`window`, `document`, `eval`, `process`, …)
|
|
16
|
+
// is only dangerous as a real identifier reference — never as prose in a
|
|
17
|
+
// `//` comment or as character data inside a string — so matching the bare
|
|
18
|
+
// word there is a false positive that blocks an otherwise-clean widget (a
|
|
19
|
+
// comment that reads "the hour window the grid renders" must not trip
|
|
20
|
+
// `no-window`).
|
|
21
|
+
//
|
|
22
|
+
// Newlines are preserved verbatim so reported line numbers still line up
|
|
23
|
+
// with the original source. Template-literal `${ … }` expression holes are
|
|
24
|
+
// left intact: real code lives there and must still be scanned (`${window}`
|
|
25
|
+
// is a genuine escape). Backslash escapes inside strings/templates are
|
|
26
|
+
// consumed so an escaped quote (`"\""`) doesn't end the literal early.
|
|
27
|
+
function stripNonCode(
|
|
28
|
+
source,
|
|
29
|
+
{ keepStrings = false, keepTemplates = keepStrings } = {},
|
|
30
|
+
) {
|
|
31
|
+
let out = "";
|
|
32
|
+
const n = source.length;
|
|
33
|
+
let mode = "code"; // code | line | block | sq | dq | tmpl
|
|
34
|
+
// Brace depth, plus a stack of the depths at which an enclosing template
|
|
35
|
+
// literal resumes — lets a `${ … }` hole (which may itself contain `{}`,
|
|
36
|
+
// strings, or nested templates) be told apart from the literal text.
|
|
37
|
+
let braceDepth = 0;
|
|
38
|
+
const tmplStack = [];
|
|
39
|
+
const keep = (ch) => {
|
|
40
|
+
out += ch;
|
|
41
|
+
};
|
|
42
|
+
const blank = (ch) => {
|
|
43
|
+
out += ch === "\n" || ch === "\r" ? ch : " ";
|
|
44
|
+
};
|
|
45
|
+
// String / template CONTENT: blanked for the banned-identifier scan
|
|
46
|
+
// (prose must not trip `no-window`), kept for the host-API-URL scan,
|
|
47
|
+
// whose whole job is to find a URL literal.
|
|
48
|
+
const str = keepStrings ? keep : blank;
|
|
49
|
+
// An import specifier is always a quoted string, never a template literal —
|
|
50
|
+
// so the import scanner keeps quoted content and blanks templates, which is
|
|
51
|
+
// what stops an `import … from "./x"` line inside a backtick block from
|
|
52
|
+
// reading as a real statement.
|
|
53
|
+
const tmplStr = keepTemplates ? keep : blank;
|
|
54
|
+
let i = 0;
|
|
55
|
+
while (i < n) {
|
|
56
|
+
const ch = source[i];
|
|
57
|
+
const nx = source[i + 1];
|
|
58
|
+
if (mode === "code") {
|
|
59
|
+
if (ch === "/" && nx === "/") {
|
|
60
|
+
mode = "line";
|
|
61
|
+
blank(ch);
|
|
62
|
+
blank(nx);
|
|
63
|
+
i += 2;
|
|
64
|
+
} else if (ch === "/" && nx === "*") {
|
|
65
|
+
mode = "block";
|
|
66
|
+
blank(ch);
|
|
67
|
+
blank(nx);
|
|
68
|
+
i += 2;
|
|
69
|
+
} else if (ch === "'") {
|
|
70
|
+
mode = "sq";
|
|
71
|
+
str(ch);
|
|
72
|
+
i += 1;
|
|
73
|
+
} else if (ch === '"') {
|
|
74
|
+
mode = "dq";
|
|
75
|
+
str(ch);
|
|
76
|
+
i += 1;
|
|
77
|
+
} else if (ch === "`") {
|
|
78
|
+
mode = "tmpl";
|
|
79
|
+
tmplStr(ch);
|
|
80
|
+
i += 1;
|
|
81
|
+
} else if (ch === "{") {
|
|
82
|
+
braceDepth += 1;
|
|
83
|
+
keep(ch);
|
|
84
|
+
i += 1;
|
|
85
|
+
} else if (ch === "}") {
|
|
86
|
+
braceDepth -= 1;
|
|
87
|
+
if (
|
|
88
|
+
tmplStack.length > 0 &&
|
|
89
|
+
tmplStack[tmplStack.length - 1] === braceDepth
|
|
90
|
+
) {
|
|
91
|
+
tmplStack.pop();
|
|
92
|
+
mode = "tmpl";
|
|
93
|
+
str(ch);
|
|
94
|
+
} else {
|
|
95
|
+
keep(ch);
|
|
96
|
+
}
|
|
97
|
+
i += 1;
|
|
98
|
+
} else {
|
|
99
|
+
keep(ch);
|
|
100
|
+
i += 1;
|
|
101
|
+
}
|
|
102
|
+
} else if (mode === "line") {
|
|
103
|
+
if (ch === "\n") {
|
|
104
|
+
mode = "code";
|
|
105
|
+
keep(ch);
|
|
106
|
+
} else {
|
|
107
|
+
blank(ch);
|
|
108
|
+
}
|
|
109
|
+
i += 1;
|
|
110
|
+
} else if (mode === "block") {
|
|
111
|
+
if (ch === "*" && nx === "/") {
|
|
112
|
+
mode = "code";
|
|
113
|
+
blank(ch);
|
|
114
|
+
blank(nx);
|
|
115
|
+
i += 2;
|
|
116
|
+
} else {
|
|
117
|
+
blank(ch);
|
|
118
|
+
i += 1;
|
|
119
|
+
}
|
|
120
|
+
} else if (mode === "sq" || mode === "dq") {
|
|
121
|
+
const quote = mode === "sq" ? "'" : '"';
|
|
122
|
+
if (ch === "\\") {
|
|
123
|
+
str(ch);
|
|
124
|
+
if (i + 1 < n) str(nx);
|
|
125
|
+
i += 2;
|
|
126
|
+
} else if (ch === quote) {
|
|
127
|
+
mode = "code";
|
|
128
|
+
str(ch);
|
|
129
|
+
i += 1;
|
|
130
|
+
} else if (ch === "\n") {
|
|
131
|
+
// A bare newline terminates an unterminated string in JS; bail back
|
|
132
|
+
// to code so malformed input can't blank the rest of the file.
|
|
133
|
+
mode = "code";
|
|
134
|
+
keep(ch);
|
|
135
|
+
i += 1;
|
|
136
|
+
} else {
|
|
137
|
+
str(ch);
|
|
138
|
+
i += 1;
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
// mode === "tmpl"
|
|
142
|
+
if (ch === "\\") {
|
|
143
|
+
tmplStr(ch);
|
|
144
|
+
if (i + 1 < n) tmplStr(nx);
|
|
145
|
+
i += 2;
|
|
146
|
+
} else if (ch === "`") {
|
|
147
|
+
mode = "code";
|
|
148
|
+
tmplStr(ch);
|
|
149
|
+
i += 1;
|
|
150
|
+
} else if (ch === "$" && nx === "{") {
|
|
151
|
+
// Enter an expression hole. Remember the brace depth the template
|
|
152
|
+
// resumes at, then count the `{` so its matching `}` is recognised.
|
|
153
|
+
tmplStack.push(braceDepth);
|
|
154
|
+
braceDepth += 1;
|
|
155
|
+
mode = "code";
|
|
156
|
+
keep(ch);
|
|
157
|
+
keep(nx);
|
|
158
|
+
i += 2;
|
|
159
|
+
} else {
|
|
160
|
+
tmplStr(ch);
|
|
161
|
+
i += 1;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return out;
|
|
166
|
+
}
|
|
167
|
+
module.exports = {
|
|
168
|
+
stripNonCode,
|
|
169
|
+
};
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
|
|
1
|
+
// GENERATED from theme-appearance.js by scripts/generate-cjs.js — DO NOT EDIT.
|
|
2
|
+
// Change theme-appearance.js, then run `npm run generate:cjs` in packages/widget-sdk.
|
|
3
|
+
"use strict";
|
|
4
4
|
|
|
5
5
|
const { isHexColor, APPEARANCE_PREFERENCES } = require("./contract.cjs");
|
|
6
6
|
|
|
7
|
+
// APPEARANCE_PREFERENCES: what an app opens on and a user may choose.
|
|
8
|
+
|
|
9
|
+
|
|
7
10
|
// REQ-THEME-APPEARANCE (sc-12335) — the ONE implementation of an app's light and
|
|
8
11
|
// dark palettes, and of which one is showing. Host surface, not author API.
|
|
9
12
|
|
|
@@ -181,10 +184,9 @@ function themeInBothModes(theme, mode, twin) {
|
|
|
181
184
|
if (mode !== "dark") return withDarkPalette(base, twinPalette);
|
|
182
185
|
return withDarkPalette(overlayPalette(base, twinPalette), recolourablePalette(base));
|
|
183
186
|
}
|
|
184
|
-
|
|
185
187
|
module.exports = {
|
|
186
|
-
APPEARANCE_MODES,
|
|
187
188
|
APPEARANCE_PREFERENCES,
|
|
189
|
+
APPEARANCE_MODES,
|
|
188
190
|
themePaletteOf,
|
|
189
191
|
normaliseAppearance,
|
|
190
192
|
resolveAppearancePreference,
|
package/dist/theme-appearance.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
// APPEARANCE_PREFERENCES: what an app opens on and a user may choose.
|
|
1
2
|
import { isHexColor, APPEARANCE_PREFERENCES } from "./contract.js";
|
|
2
3
|
|
|
3
|
-
// What an app opens on and a user may choose; one list, in the contract.
|
|
4
4
|
export { APPEARANCE_PREFERENCES };
|
|
5
5
|
|
|
6
6
|
// REQ-THEME-APPEARANCE (sc-12335) — the ONE implementation of an app's light and
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
//
|
|
5
|
-
// The BODY below is copied VERBATIM from theme-background.js; only the module
|
|
6
|
-
// syntax differs. theme-background-parity.test.js pins both facts — identical
|
|
7
|
-
// bodies AND identical behaviour over a shared case table — so drift fails CI.
|
|
1
|
+
// GENERATED from theme-background.js by scripts/generate-cjs.js — DO NOT EDIT.
|
|
2
|
+
// Change theme-background.js, then run `npm run generate:cjs` in packages/widget-sdk.
|
|
3
|
+
"use strict";
|
|
8
4
|
|
|
9
5
|
// REQ-THEME-08 / REQ-PLR-05-CUSTOM — the ONE implementation of the app's
|
|
10
6
|
// background gradient and of the sign-in screen's design, shared by both hosts.
|
|
@@ -169,7 +165,6 @@ function resolveLoginTextVisibility(login) {
|
|
|
169
165
|
subheading: own.hideSubheading !== true,
|
|
170
166
|
};
|
|
171
167
|
}
|
|
172
|
-
|
|
173
168
|
module.exports = {
|
|
174
169
|
LOGIN_TEXT_MAX_LENGTH,
|
|
175
170
|
normaliseThemeGradient,
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
// syntax differs. theme-components-parity.test.js pins both facts — identical
|
|
7
|
-
// bodies AND identical behaviour over a shared case table — so drift fails CI.
|
|
1
|
+
// GENERATED from theme-components.js by scripts/generate-cjs.js — DO NOT EDIT.
|
|
2
|
+
// Change theme-components.js, then run `npm run generate:cjs` in packages/widget-sdk.
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const { CONTRACT, normaliseComponentGradient } = require("./contract.cjs");
|
|
8
6
|
|
|
9
7
|
// REQ-THEME-15 (sc-1497) — the host side of per-component style tokens.
|
|
10
8
|
//
|
|
@@ -23,7 +21,6 @@
|
|
|
23
21
|
// widget reads. The Mason build runner validates `set_theme` against the SAME
|
|
24
22
|
// literal, so what a planner may emit and what a host applies cannot diverge.
|
|
25
23
|
|
|
26
|
-
const { CONTRACT, normaliseComponentGradient } = require("./contract.cjs");
|
|
27
24
|
|
|
28
25
|
// 3-, 6- and 8-digit hex, matching what the build runner persists — the host
|
|
29
26
|
// must never drop a colour the runner already accepted.
|
|
@@ -402,5 +399,11 @@ function applyThemeComponentStyle(manifestId, theme, props, styleSchema) {
|
|
|
402
399
|
const authored = isPlainObject(base.style) ? base.style : null;
|
|
403
400
|
return { ...base, style: { ...themed, ...authored } };
|
|
404
401
|
}
|
|
405
|
-
|
|
406
|
-
|
|
402
|
+
module.exports = {
|
|
403
|
+
normaliseThemeComponents,
|
|
404
|
+
MAX_STYLE_STRING_LENGTH,
|
|
405
|
+
styleValueBytes,
|
|
406
|
+
normaliseWidgetStyleFields,
|
|
407
|
+
normaliseWidgetStyles,
|
|
408
|
+
applyThemeComponentStyle,
|
|
409
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colixsystems/widget-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.277.1",
|
|
4
4
|
"description": "Common widget interface for AppStudio. Implements WidgetManifest, WidgetContext, property schema, and helper hooks.",
|
|
5
5
|
"homepage": "https://github.com/Colix-AB/AppStudio",
|
|
6
6
|
"type": "module",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
],
|
|
62
62
|
"scripts": {
|
|
63
63
|
"build": "node scripts/build.js",
|
|
64
|
-
"generate:
|
|
64
|
+
"generate:cjs": "node scripts/generate-cjs.js",
|
|
65
65
|
"test": "node --test \"src/__tests__/*.test.js\""
|
|
66
66
|
},
|
|
67
67
|
"engines": {
|
package/src/contract.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The widget SDK contract: the ONE source the linter, the AI agents, the
|
|
2
2
|
// compiler and both hosts read. `contract.cjs` is GENERATED from `contract.js`
|
|
3
|
-
// (`npm run generate:
|
|
4
|
-
//
|
|
3
|
+
// (`npm run generate:cjs`) for CommonJS consumers — never edit it by hand;
|
|
4
|
+
// cjs-generated.test.js fails when it is stale.
|
|
5
5
|
|
|
6
6
|
// REQ-AI-AGENT-DESIGN (sc-3696): the ONE cross-platform elevation vocabulary.
|
|
7
7
|
// `shadow*` applies on iOS + react-native-web, `elevation` drives Android.
|