@seed-ship/mcp-ui-solid 6.23.0 → 6.25.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/CHANGELOG.md +167 -0
- package/README.md +154 -2
- package/dist/components/ActionGroupRenderer.cjs +6 -6
- package/dist/components/ActionGroupRenderer.cjs.map +1 -1
- package/dist/components/ActionGroupRenderer.js +6 -6
- package/dist/components/ActionGroupRenderer.js.map +1 -1
- package/dist/components/AutocompleteDropdown.cjs +6 -6
- package/dist/components/AutocompleteDropdown.cjs.map +1 -1
- package/dist/components/AutocompleteDropdown.js +6 -6
- package/dist/components/AutocompleteDropdown.js.map +1 -1
- package/dist/components/AutocompleteFormField.cjs +6 -5
- package/dist/components/AutocompleteFormField.cjs.map +1 -1
- package/dist/components/AutocompleteFormField.d.ts.map +1 -1
- package/dist/components/AutocompleteFormField.js +6 -5
- package/dist/components/AutocompleteFormField.js.map +1 -1
- package/dist/components/ChartJSRenderer.cjs +406 -70
- package/dist/components/ChartJSRenderer.cjs.map +1 -1
- package/dist/components/ChartJSRenderer.d.ts.map +1 -1
- package/dist/components/ChartJSRenderer.js +407 -71
- package/dist/components/ChartJSRenderer.js.map +1 -1
- package/dist/components/ChatPrompt.cjs +5 -5
- package/dist/components/ChatPrompt.cjs.map +1 -1
- package/dist/components/ChatPrompt.js +5 -5
- package/dist/components/ChatPrompt.js.map +1 -1
- package/dist/components/ComponentToolbar.cjs +5 -5
- package/dist/components/ComponentToolbar.cjs.map +1 -1
- package/dist/components/ComponentToolbar.js +5 -5
- package/dist/components/ComponentToolbar.js.map +1 -1
- package/dist/components/DegradedFallback.cjs +35 -3
- package/dist/components/DegradedFallback.cjs.map +1 -1
- package/dist/components/DegradedFallback.d.ts +7 -0
- package/dist/components/DegradedFallback.d.ts.map +1 -1
- package/dist/components/DegradedFallback.js +35 -3
- package/dist/components/DegradedFallback.js.map +1 -1
- package/dist/components/ExpandableWrapper.cjs +10 -7
- package/dist/components/ExpandableWrapper.cjs.map +1 -1
- package/dist/components/ExpandableWrapper.d.ts.map +1 -1
- package/dist/components/ExpandableWrapper.js +10 -7
- package/dist/components/ExpandableWrapper.js.map +1 -1
- package/dist/components/FormFieldRenderer.cjs +13 -12
- package/dist/components/FormFieldRenderer.cjs.map +1 -1
- package/dist/components/FormFieldRenderer.d.ts.map +1 -1
- package/dist/components/FormFieldRenderer.js +13 -12
- package/dist/components/FormFieldRenderer.js.map +1 -1
- package/dist/components/FormRenderer.cjs +1 -1
- package/dist/components/FormRenderer.cjs.map +1 -1
- package/dist/components/FormRenderer.js +1 -1
- package/dist/components/FormRenderer.js.map +1 -1
- package/dist/components/GenerativeUIErrorBoundary.cjs +1 -1
- package/dist/components/GenerativeUIErrorBoundary.cjs.map +1 -1
- package/dist/components/GenerativeUIErrorBoundary.js +1 -1
- package/dist/components/GenerativeUIErrorBoundary.js.map +1 -1
- package/dist/components/GhostText.cjs +2 -2
- package/dist/components/GhostText.cjs.map +1 -1
- package/dist/components/GhostText.js +2 -2
- package/dist/components/GhostText.js.map +1 -1
- package/dist/components/GraphRenderer.cjs +100 -29
- package/dist/components/GraphRenderer.cjs.map +1 -1
- package/dist/components/GraphRenderer.d.ts +1 -1
- package/dist/components/GraphRenderer.d.ts.map +1 -1
- package/dist/components/GraphRenderer.js +100 -29
- package/dist/components/GraphRenderer.js.map +1 -1
- package/dist/components/MissingValue.cjs +43 -0
- package/dist/components/MissingValue.cjs.map +1 -0
- package/dist/components/MissingValue.d.ts +27 -0
- package/dist/components/MissingValue.d.ts.map +1 -0
- package/dist/components/MissingValue.js +43 -0
- package/dist/components/MissingValue.js.map +1 -0
- package/dist/components/ModalRenderer.cjs +39 -31
- package/dist/components/ModalRenderer.cjs.map +1 -1
- package/dist/components/ModalRenderer.d.ts.map +1 -1
- package/dist/components/ModalRenderer.js +40 -32
- package/dist/components/ModalRenderer.js.map +1 -1
- package/dist/components/PortalDropdownMenu.cjs +6 -3
- package/dist/components/PortalDropdownMenu.cjs.map +1 -1
- package/dist/components/PortalDropdownMenu.d.ts.map +1 -1
- package/dist/components/PortalDropdownMenu.js +7 -4
- package/dist/components/PortalDropdownMenu.js.map +1 -1
- package/dist/components/ScratchpadPanel.cjs +38 -44
- package/dist/components/ScratchpadPanel.cjs.map +1 -1
- package/dist/components/ScratchpadPanel.d.ts.map +1 -1
- package/dist/components/ScratchpadPanel.js +38 -44
- package/dist/components/ScratchpadPanel.js.map +1 -1
- package/dist/components/UIResourceRenderer.cjs +255 -217
- package/dist/components/UIResourceRenderer.cjs.map +1 -1
- package/dist/components/UIResourceRenderer.d.ts.map +1 -1
- package/dist/components/UIResourceRenderer.js +255 -217
- package/dist/components/UIResourceRenderer.js.map +1 -1
- package/dist/components/chart-data-table.cjs +30 -5
- package/dist/components/chart-data-table.cjs.map +1 -1
- package/dist/components/chart-data-table.d.ts +18 -1
- package/dist/components/chart-data-table.d.ts.map +1 -1
- package/dist/components/chart-data-table.js +30 -5
- package/dist/components/chart-data-table.js.map +1 -1
- package/dist/components/chart-missing-plugin.cjs +194 -0
- package/dist/components/chart-missing-plugin.cjs.map +1 -0
- package/dist/components/chart-missing-plugin.d.ts +129 -0
- package/dist/components/chart-missing-plugin.d.ts.map +1 -0
- package/dist/components/chart-missing-plugin.js +194 -0
- package/dist/components/chart-missing-plugin.js.map +1 -0
- package/dist/context/MCPUIStringsContext.cjs +12 -1
- package/dist/context/MCPUIStringsContext.cjs.map +1 -1
- package/dist/context/MCPUIStringsContext.d.ts +62 -0
- package/dist/context/MCPUIStringsContext.d.ts.map +1 -1
- package/dist/context/MCPUIStringsContext.js +12 -1
- package/dist/context/MCPUIStringsContext.js.map +1 -1
- package/dist/services/component-registry.cjs +9 -2
- package/dist/services/component-registry.cjs.map +1 -1
- package/dist/services/component-registry.d.ts.map +1 -1
- package/dist/services/component-registry.js +9 -2
- package/dist/services/component-registry.js.map +1 -1
- package/dist/services/validation.cjs +31 -8
- package/dist/services/validation.cjs.map +1 -1
- package/dist/services/validation.d.ts.map +1 -1
- package/dist/services/validation.js +31 -8
- package/dist/services/validation.js.map +1 -1
- package/dist/types/index.d.ts +14 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/degraded-projections.cjs +19 -3
- package/dist/utils/degraded-projections.cjs.map +1 -1
- package/dist/utils/degraded-projections.d.ts +22 -2
- package/dist/utils/degraded-projections.d.ts.map +1 -1
- package/dist/utils/degraded-projections.js +19 -3
- package/dist/utils/degraded-projections.js.map +1 -1
- package/dist/utils/math-markdown.cjs +1 -1
- package/dist/utils/math-markdown.cjs.map +1 -1
- package/dist/utils/math-markdown.js +1 -1
- package/dist/utils/math-markdown.js.map +1 -1
- package/dist/utils/missing-value.cjs +60 -0
- package/dist/utils/missing-value.cjs.map +1 -0
- package/dist/utils/missing-value.d.ts +79 -0
- package/dist/utils/missing-value.d.ts.map +1 -0
- package/dist/utils/missing-value.js +60 -0
- package/dist/utils/missing-value.js.map +1 -0
- package/dist/utils/theme.cjs +91 -0
- package/dist/utils/theme.cjs.map +1 -0
- package/dist/utils/theme.d.ts +11 -0
- package/dist/utils/theme.d.ts.map +1 -0
- package/dist/utils/theme.js +91 -0
- package/dist/utils/theme.js.map +1 -0
- package/docs/SECURITY-DEPENDENCIES.md +81 -0
- package/docs/THEMING.md +205 -0
- package/package.json +7 -5
- package/src/components/ActionGroupRenderer.tsx +6 -6
- package/src/components/AutocompleteDropdown.tsx +17 -17
- package/src/components/AutocompleteFormField.tsx +10 -9
- package/src/components/ChartJSRenderer.tsx +609 -212
- package/src/components/ChatPrompt.tsx +16 -16
- package/src/components/ComponentToolbar.tsx +5 -5
- package/src/components/DegradedFallback.tsx +33 -11
- package/src/components/ExpandableWrapper.tsx +15 -11
- package/src/components/FormFieldRenderer.tsx +52 -51
- package/src/components/FormRenderer.tsx +12 -12
- package/src/components/GenerativeUIErrorBoundary.tsx +6 -6
- package/src/components/GhostText.tsx +7 -7
- package/src/components/GraphRenderer.tsx +83 -67
- package/src/components/MissingValue.tsx +61 -0
- package/src/components/ModalRenderer.tsx +11 -5
- package/src/components/PortalDropdownMenu.tsx +6 -2
- package/src/components/ScratchpadPanel.tsx +144 -141
- package/src/components/UIResourceRenderer.tsx +143 -110
- package/src/components/chart-data-table.ts +50 -6
- package/src/components/chart-missing-plugin.ts +336 -0
- package/src/context/MCPUIStringsContext.tsx +76 -0
- package/src/services/component-registry.ts +10 -2
- package/src/services/validation.ts +44 -9
- package/src/styles/autocomplete.css +53 -53
- package/src/types/index.ts +13 -1
- package/src/utils/degraded-projections.ts +44 -4
- package/src/utils/math-markdown.ts +1 -1
- package/src/utils/missing-value.ts +127 -0
- package/src/utils/theme.ts +101 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.cjs","sources":["../../src/utils/theme.ts"],"sourcesContent":["import { createEffect, onCleanup } from 'solid-js'\n\nconst THEME_PREFIX = '--mcp-ui-'\n\n/** Read only explicitly supplied host tokens; blank values retain engine defaults. */\nexport function readThemeTokens(\n element: Element | undefined,\n names: readonly string[],\n): Record<string, string | undefined> {\n const result: Record<string, string | undefined> = {}\n const view = element?.ownerDocument.defaultView\n const style = element && view ? view.getComputedStyle(element) : undefined\n for (const name of names) result[name] = style?.getPropertyValue(name).trim() || undefined\n return result\n}\n\n/** Observe inherited theme changes, including moving a mounted renderer into a portal. */\nexport function observeTheme(element: HTMLElement, callback: () => void): () => void {\n const view = element.ownerDocument.defaultView\n if (!view?.MutationObserver) return () => {}\n let stopped = false\n let pending = false\n const schedule = () => {\n if (pending || stopped) return\n pending = true\n queueMicrotask(() => {\n pending = false\n if (!stopped) callback()\n })\n }\n const observer = new view.MutationObserver((records) => {\n if (records.some((record) => {\n if (record.type === 'attributes') {\n return record.target instanceof view.Element && record.target.contains(element)\n }\n return [...record.addedNodes, ...record.removedNodes].some(\n (node) => node === element || node.contains(element),\n )\n })) schedule()\n })\n // Attribute names are deliberately not filtered: hosts may use any data-* selector.\n observer.observe(element.ownerDocument.documentElement, {\n attributes: true,\n childList: true,\n subtree: true,\n })\n return () => {\n stopped = true\n observer.disconnect()\n }\n}\n\n/**\n * A body portal cannot inherit a local host theme. Copy only our effective\n * custom properties and dark state from its stationary anchor, and keep them\n * synchronized for the lifetime of the Solid owner. Geometry stays unchanged.\n */\nexport function bindPortalTheme(portal: HTMLElement, source: () => HTMLElement | undefined): void {\n createEffect(() => {\n const anchor = source()\n const view = anchor?.ownerDocument.defaultView\n if (!anchor || !view) return\n const copied = new Set<string>()\n const sync = () => {\n const style = view.getComputedStyle(anchor)\n const inherited = portal.parentElement ? view.getComputedStyle(portal.parentElement) : undefined\n const names = new Set(copied)\n for (const computed of [style, inherited]) {\n if (!computed) continue\n for (let index = 0; index < computed.length; index++) {\n const name = computed.item(index)\n if (name.startsWith(THEME_PREFIX)) names.add(name)\n }\n }\n const values = new Map<string, string>()\n for (const name of names) {\n const value = style.getPropertyValue(name).trim()\n if (value) values.set(name, value)\n // Empty setProperty removes a declaration. Explicitly block a body\n // token when the local scope reset it to the guaranteed-invalid value.\n else if (inherited?.getPropertyValue(name).trim()) values.set(name, 'initial')\n }\n for (const name of copied) {\n if (!values.has(name)) portal.style.removeProperty(name)\n }\n copied.clear()\n for (const [name, value] of values) {\n if (portal.style.getPropertyValue(name) !== value) portal.style.setProperty(name, value)\n copied.add(name)\n }\n portal.classList.toggle('dark', anchor.closest('.dark') !== null)\n }\n sync()\n const stop = observeTheme(anchor, sync)\n onCleanup(() => {\n stop()\n for (const name of copied) portal.style.removeProperty(name)\n portal.classList.remove('dark')\n })\n })\n}\n"],"names":["createEffect","onCleanup"],"mappings":";;;AAEA,MAAM,eAAe;AAGd,SAAS,gBACd,SACA,OACoC;AACpC,QAAM,SAA6C,CAAA;AACnD,QAAM,OAAO,mCAAS,cAAc;AACpC,QAAM,QAAQ,WAAW,OAAO,KAAK,iBAAiB,OAAO,IAAI;AACjE,aAAW,QAAQ,MAAO,QAAO,IAAI,KAAI,+BAAO,iBAAiB,MAAM,WAAU;AACjF,SAAO;AACT;AAGO,SAAS,aAAa,SAAsB,UAAkC;AACnF,QAAM,OAAO,QAAQ,cAAc;AACnC,MAAI,EAAC,6BAAM,kBAAkB,QAAO,MAAM;AAAA,EAAC;AAC3C,MAAI,UAAU;AACd,MAAI,UAAU;AACd,QAAM,WAAW,MAAM;AACrB,QAAI,WAAW,QAAS;AACxB,cAAU;AACV,mBAAe,MAAM;AACnB,gBAAU;AACV,UAAI,CAAC,QAAS,UAAA;AAAA,IAChB,CAAC;AAAA,EACH;AACA,QAAM,WAAW,IAAI,KAAK,iBAAiB,CAAC,YAAY;AACtD,QAAI,QAAQ,KAAK,CAAC,WAAW;AAC3B,UAAI,OAAO,SAAS,cAAc;AAChC,eAAO,OAAO,kBAAkB,KAAK,WAAW,OAAO,OAAO,SAAS,OAAO;AAAA,MAChF;AACA,aAAO,CAAC,GAAG,OAAO,YAAY,GAAG,OAAO,YAAY,EAAE;AAAA,QACpD,CAAC,SAAS,SAAS,WAAW,KAAK,SAAS,OAAO;AAAA,MAAA;AAAA,IAEvD,CAAC,EAAG,UAAA;AAAA,EACN,CAAC;AAED,WAAS,QAAQ,QAAQ,cAAc,iBAAiB;AAAA,IACtD,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,SAAS;AAAA,EAAA,CACV;AACD,SAAO,MAAM;AACX,cAAU;AACV,aAAS,WAAA;AAAA,EACX;AACF;AAOO,SAAS,gBAAgB,QAAqB,QAA6C;AAChGA,UAAAA,aAAa,MAAM;AACjB,UAAM,SAAS,OAAA;AACf,UAAM,OAAO,iCAAQ,cAAc;AACnC,QAAI,CAAC,UAAU,CAAC,KAAM;AACtB,UAAM,6BAAa,IAAA;AACnB,UAAM,OAAO,MAAM;AACjB,YAAM,QAAQ,KAAK,iBAAiB,MAAM;AAC1C,YAAM,YAAY,OAAO,gBAAgB,KAAK,iBAAiB,OAAO,aAAa,IAAI;AACvF,YAAM,QAAQ,IAAI,IAAI,MAAM;AAC5B,iBAAW,YAAY,CAAC,OAAO,SAAS,GAAG;AACzC,YAAI,CAAC,SAAU;AACf,iBAAS,QAAQ,GAAG,QAAQ,SAAS,QAAQ,SAAS;AACpD,gBAAM,OAAO,SAAS,KAAK,KAAK;AAChC,cAAI,KAAK,WAAW,YAAY,EAAG,OAAM,IAAI,IAAI;AAAA,QACnD;AAAA,MACF;AACA,YAAM,6BAAa,IAAA;AACnB,iBAAW,QAAQ,OAAO;AACxB,cAAM,QAAQ,MAAM,iBAAiB,IAAI,EAAE,KAAA;AAC3C,YAAI,MAAO,QAAO,IAAI,MAAM,KAAK;AAAA,iBAGxB,uCAAW,iBAAiB,MAAM,OAAQ,QAAO,IAAI,MAAM,SAAS;AAAA,MAC/E;AACA,iBAAW,QAAQ,QAAQ;AACzB,YAAI,CAAC,OAAO,IAAI,IAAI,EAAG,QAAO,MAAM,eAAe,IAAI;AAAA,MACzD;AACA,aAAO,MAAA;AACP,iBAAW,CAAC,MAAM,KAAK,KAAK,QAAQ;AAClC,YAAI,OAAO,MAAM,iBAAiB,IAAI,MAAM,MAAO,QAAO,MAAM,YAAY,MAAM,KAAK;AACvF,eAAO,IAAI,IAAI;AAAA,MACjB;AACA,aAAO,UAAU,OAAO,QAAQ,OAAO,QAAQ,OAAO,MAAM,IAAI;AAAA,IAClE;AACA,SAAA;AACA,UAAM,OAAO,aAAa,QAAQ,IAAI;AACtCC,YAAAA,UAAU,MAAM;AACd,WAAA;AACA,iBAAW,QAAQ,OAAQ,QAAO,MAAM,eAAe,IAAI;AAC3D,aAAO,UAAU,OAAO,MAAM;AAAA,IAChC,CAAC;AAAA,EACH,CAAC;AACH;;;;"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Read only explicitly supplied host tokens; blank values retain engine defaults. */
|
|
2
|
+
export declare function readThemeTokens(element: Element | undefined, names: readonly string[]): Record<string, string | undefined>;
|
|
3
|
+
/** Observe inherited theme changes, including moving a mounted renderer into a portal. */
|
|
4
|
+
export declare function observeTheme(element: HTMLElement, callback: () => void): () => void;
|
|
5
|
+
/**
|
|
6
|
+
* A body portal cannot inherit a local host theme. Copy only our effective
|
|
7
|
+
* custom properties and dark state from its stationary anchor, and keep them
|
|
8
|
+
* synchronized for the lifetime of the Solid owner. Geometry stays unchanged.
|
|
9
|
+
*/
|
|
10
|
+
export declare function bindPortalTheme(portal: HTMLElement, source: () => HTMLElement | undefined): void;
|
|
11
|
+
//# sourceMappingURL=theme.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/utils/theme.ts"],"names":[],"mappings":"AAIA,sFAAsF;AACtF,wBAAgB,eAAe,CAC7B,OAAO,EAAE,OAAO,GAAG,SAAS,EAC5B,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAMpC;AAED,0FAA0F;AAC1F,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAiCnF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,WAAW,GAAG,SAAS,GAAG,IAAI,CA2ChG"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { createEffect, onCleanup } from "solid-js";
|
|
2
|
+
const THEME_PREFIX = "--mcp-ui-";
|
|
3
|
+
function readThemeTokens(element, names) {
|
|
4
|
+
const result = {};
|
|
5
|
+
const view = element == null ? void 0 : element.ownerDocument.defaultView;
|
|
6
|
+
const style = element && view ? view.getComputedStyle(element) : void 0;
|
|
7
|
+
for (const name of names) result[name] = (style == null ? void 0 : style.getPropertyValue(name).trim()) || void 0;
|
|
8
|
+
return result;
|
|
9
|
+
}
|
|
10
|
+
function observeTheme(element, callback) {
|
|
11
|
+
const view = element.ownerDocument.defaultView;
|
|
12
|
+
if (!(view == null ? void 0 : view.MutationObserver)) return () => {
|
|
13
|
+
};
|
|
14
|
+
let stopped = false;
|
|
15
|
+
let pending = false;
|
|
16
|
+
const schedule = () => {
|
|
17
|
+
if (pending || stopped) return;
|
|
18
|
+
pending = true;
|
|
19
|
+
queueMicrotask(() => {
|
|
20
|
+
pending = false;
|
|
21
|
+
if (!stopped) callback();
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
const observer = new view.MutationObserver((records) => {
|
|
25
|
+
if (records.some((record) => {
|
|
26
|
+
if (record.type === "attributes") {
|
|
27
|
+
return record.target instanceof view.Element && record.target.contains(element);
|
|
28
|
+
}
|
|
29
|
+
return [...record.addedNodes, ...record.removedNodes].some(
|
|
30
|
+
(node) => node === element || node.contains(element)
|
|
31
|
+
);
|
|
32
|
+
})) schedule();
|
|
33
|
+
});
|
|
34
|
+
observer.observe(element.ownerDocument.documentElement, {
|
|
35
|
+
attributes: true,
|
|
36
|
+
childList: true,
|
|
37
|
+
subtree: true
|
|
38
|
+
});
|
|
39
|
+
return () => {
|
|
40
|
+
stopped = true;
|
|
41
|
+
observer.disconnect();
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function bindPortalTheme(portal, source) {
|
|
45
|
+
createEffect(() => {
|
|
46
|
+
const anchor = source();
|
|
47
|
+
const view = anchor == null ? void 0 : anchor.ownerDocument.defaultView;
|
|
48
|
+
if (!anchor || !view) return;
|
|
49
|
+
const copied = /* @__PURE__ */ new Set();
|
|
50
|
+
const sync = () => {
|
|
51
|
+
const style = view.getComputedStyle(anchor);
|
|
52
|
+
const inherited = portal.parentElement ? view.getComputedStyle(portal.parentElement) : void 0;
|
|
53
|
+
const names = new Set(copied);
|
|
54
|
+
for (const computed of [style, inherited]) {
|
|
55
|
+
if (!computed) continue;
|
|
56
|
+
for (let index = 0; index < computed.length; index++) {
|
|
57
|
+
const name = computed.item(index);
|
|
58
|
+
if (name.startsWith(THEME_PREFIX)) names.add(name);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const values = /* @__PURE__ */ new Map();
|
|
62
|
+
for (const name of names) {
|
|
63
|
+
const value = style.getPropertyValue(name).trim();
|
|
64
|
+
if (value) values.set(name, value);
|
|
65
|
+
else if (inherited == null ? void 0 : inherited.getPropertyValue(name).trim()) values.set(name, "initial");
|
|
66
|
+
}
|
|
67
|
+
for (const name of copied) {
|
|
68
|
+
if (!values.has(name)) portal.style.removeProperty(name);
|
|
69
|
+
}
|
|
70
|
+
copied.clear();
|
|
71
|
+
for (const [name, value] of values) {
|
|
72
|
+
if (portal.style.getPropertyValue(name) !== value) portal.style.setProperty(name, value);
|
|
73
|
+
copied.add(name);
|
|
74
|
+
}
|
|
75
|
+
portal.classList.toggle("dark", anchor.closest(".dark") !== null);
|
|
76
|
+
};
|
|
77
|
+
sync();
|
|
78
|
+
const stop = observeTheme(anchor, sync);
|
|
79
|
+
onCleanup(() => {
|
|
80
|
+
stop();
|
|
81
|
+
for (const name of copied) portal.style.removeProperty(name);
|
|
82
|
+
portal.classList.remove("dark");
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
export {
|
|
87
|
+
bindPortalTheme,
|
|
88
|
+
observeTheme,
|
|
89
|
+
readThemeTokens
|
|
90
|
+
};
|
|
91
|
+
//# sourceMappingURL=theme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.js","sources":["../../src/utils/theme.ts"],"sourcesContent":["import { createEffect, onCleanup } from 'solid-js'\n\nconst THEME_PREFIX = '--mcp-ui-'\n\n/** Read only explicitly supplied host tokens; blank values retain engine defaults. */\nexport function readThemeTokens(\n element: Element | undefined,\n names: readonly string[],\n): Record<string, string | undefined> {\n const result: Record<string, string | undefined> = {}\n const view = element?.ownerDocument.defaultView\n const style = element && view ? view.getComputedStyle(element) : undefined\n for (const name of names) result[name] = style?.getPropertyValue(name).trim() || undefined\n return result\n}\n\n/** Observe inherited theme changes, including moving a mounted renderer into a portal. */\nexport function observeTheme(element: HTMLElement, callback: () => void): () => void {\n const view = element.ownerDocument.defaultView\n if (!view?.MutationObserver) return () => {}\n let stopped = false\n let pending = false\n const schedule = () => {\n if (pending || stopped) return\n pending = true\n queueMicrotask(() => {\n pending = false\n if (!stopped) callback()\n })\n }\n const observer = new view.MutationObserver((records) => {\n if (records.some((record) => {\n if (record.type === 'attributes') {\n return record.target instanceof view.Element && record.target.contains(element)\n }\n return [...record.addedNodes, ...record.removedNodes].some(\n (node) => node === element || node.contains(element),\n )\n })) schedule()\n })\n // Attribute names are deliberately not filtered: hosts may use any data-* selector.\n observer.observe(element.ownerDocument.documentElement, {\n attributes: true,\n childList: true,\n subtree: true,\n })\n return () => {\n stopped = true\n observer.disconnect()\n }\n}\n\n/**\n * A body portal cannot inherit a local host theme. Copy only our effective\n * custom properties and dark state from its stationary anchor, and keep them\n * synchronized for the lifetime of the Solid owner. Geometry stays unchanged.\n */\nexport function bindPortalTheme(portal: HTMLElement, source: () => HTMLElement | undefined): void {\n createEffect(() => {\n const anchor = source()\n const view = anchor?.ownerDocument.defaultView\n if (!anchor || !view) return\n const copied = new Set<string>()\n const sync = () => {\n const style = view.getComputedStyle(anchor)\n const inherited = portal.parentElement ? view.getComputedStyle(portal.parentElement) : undefined\n const names = new Set(copied)\n for (const computed of [style, inherited]) {\n if (!computed) continue\n for (let index = 0; index < computed.length; index++) {\n const name = computed.item(index)\n if (name.startsWith(THEME_PREFIX)) names.add(name)\n }\n }\n const values = new Map<string, string>()\n for (const name of names) {\n const value = style.getPropertyValue(name).trim()\n if (value) values.set(name, value)\n // Empty setProperty removes a declaration. Explicitly block a body\n // token when the local scope reset it to the guaranteed-invalid value.\n else if (inherited?.getPropertyValue(name).trim()) values.set(name, 'initial')\n }\n for (const name of copied) {\n if (!values.has(name)) portal.style.removeProperty(name)\n }\n copied.clear()\n for (const [name, value] of values) {\n if (portal.style.getPropertyValue(name) !== value) portal.style.setProperty(name, value)\n copied.add(name)\n }\n portal.classList.toggle('dark', anchor.closest('.dark') !== null)\n }\n sync()\n const stop = observeTheme(anchor, sync)\n onCleanup(() => {\n stop()\n for (const name of copied) portal.style.removeProperty(name)\n portal.classList.remove('dark')\n })\n })\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Security dependency notes (6.25.1)
|
|
2
|
+
|
|
3
|
+
Verified on 2026-10-18. This is a dependency-remediation record, not a claim
|
|
4
|
+
that every renderer or consuming application has been security-audited.
|
|
5
|
+
|
|
6
|
+
## Updates
|
|
7
|
+
|
|
8
|
+
| Dependency | Previous lock | Updated lock | Scope and advisories |
|
|
9
|
+
| --- | --- | --- | --- |
|
|
10
|
+
| DOMPurify | 3.4.13 | 3.4.17 | Required runtime dependency; published floor is `^3.4.17`. [Rawtext root](https://github.com/cure53/DOMPurify/security/advisories/GHSA-6688-9rhm-gjv2), [hook removal](https://github.com/cure53/DOMPurify/security/advisories/GHSA-p98j-92pf-mc4p) and [subsequent hardening](https://github.com/cure53/DOMPurify/releases/tag/3.4.17). |
|
|
11
|
+
| Solid / Seroval / Seroval plugins | 1.9.11 / 1.5.6 / 1.5.0 | 1.9.17 / 1.6.11 / 1.6.11 | Workspace development resolution; Solid remains a required host peer. [Callable invocation](https://github.com/lxsmnsyc/seroval/security/advisories/GHSA-p6vx-979v-rg4c), [TypedArray allocation](https://github.com/lxsmnsyc/seroval/security/advisories/GHSA-jp82-f5mq-hwhp). |
|
|
12
|
+
| source-map-js | 1.2.1 | 1.2.2 | Build/test tooling via PostCSS and css-tree. [Indexed-map denial of service](https://github.com/advisories/GHSA-68fv-2mgg-jv7q). |
|
|
13
|
+
| brace-expansion | 5.0.9 | 5.0.12 | Lint tooling via minimatch. [Rewrite complexity](https://github.com/juliangruber/brace-expansion/security/advisories/GHSA-q2hr-2g5m-vwhr), [nested groups](https://github.com/advisories/GHSA-qhr7-859c-m2p7), [comma recursion](https://github.com/advisories/GHSA-6j4f-fj2g-mc7p). |
|
|
14
|
+
|
|
15
|
+
The sanitizer accepts strings, not live DOM nodes, and the library's profiles
|
|
16
|
+
do not use `IN_PLACE` or node-removing hooks. The two reported DOMPurify
|
|
17
|
+
preconditions were not found in these library paths; this is not a finding
|
|
18
|
+
that every possible host configuration is safe. No renderer policy is relaxed.
|
|
19
|
+
|
|
20
|
+
Solid 1.9.17 requests Seroval and its plugins in the supported `~1.6.8` range.
|
|
21
|
+
The workspace override follows that range instead of forcing Solid's old
|
|
22
|
+
`~1.5.3` resolution. Seroval 1.6.2 alone would not cover the additional
|
|
23
|
+
TypedArray advisory, which was fixed in 1.6.3.
|
|
24
|
+
|
|
25
|
+
## Host applications
|
|
26
|
+
|
|
27
|
+
This package externalizes dependencies. Its DOMPurify manifest floor affects
|
|
28
|
+
the version installed by consumers, but this repository's root pnpm overrides
|
|
29
|
+
and development lockfile do **not** propagate to an application's lockfile.
|
|
30
|
+
The public `solid-js` peer range remains `^1.9.0` for compatibility; it is not
|
|
31
|
+
a guarantee that the host has a corrected Seroval resolution.
|
|
32
|
+
|
|
33
|
+
Update the application lockfile separately. An upstream-aligned option is
|
|
34
|
+
Solid 1.9.17 with its supported Seroval/plugin 1.6.x dependencies; verify the
|
|
35
|
+
resolved versions and audit the application itself. The library's new test
|
|
36
|
+
performs a real asynchronous Solid server render with hydration serialization,
|
|
37
|
+
plus a legitimate Map/Date/typed-array/Promise JSON transport roundtrip through
|
|
38
|
+
Solid's own Seroval. It is a compatibility smoke test, not browser hydration
|
|
39
|
+
proof, an exploitability assessment of the host, or an end-to-end SolidStart test.
|
|
40
|
+
|
|
41
|
+
## Remaining advisory: braces 3.0.3
|
|
42
|
+
|
|
43
|
+
[`GHSA-vfj7-8cjw-p6xm`](https://github.com/advisories/GHSA-vfj7-8cjw-p6xm)
|
|
44
|
+
is HIGH severity and has no patched version listed as of the verification
|
|
45
|
+
date. It concerns stack exhaustion when deeply nested, attacker-controlled
|
|
46
|
+
brace patterns reach recursive glob parsing. `braces` and `brace-expansion`
|
|
47
|
+
are different packages: updating the latter does not fix this advisory.
|
|
48
|
+
|
|
49
|
+
The retained development-only chain is:
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
ESLint / typescript-eslint
|
|
53
|
+
-> typescript-estree -> globby -> fast-glob -> micromatch -> braces 3.0.3
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
No compatible published parent update that removes this chain was identified.
|
|
57
|
+
Even the checked latest globby release still uses fast-glob/micromatch and
|
|
58
|
+
braces. The [upstream issue](https://github.com/micromatch/braces/issues/70)
|
|
59
|
+
remains the follow-up point.
|
|
60
|
+
|
|
61
|
+
Current lint scripts use `eslint src`; repository lint configuration has
|
|
62
|
+
simple static file patterns and no `parserOptions.project` glob. No production
|
|
63
|
+
renderer path to braces was identified. Keep project/glob patterns static and
|
|
64
|
+
reviewed, and do not expose this tooling as a service accepting untrusted
|
|
65
|
+
patterns without a depth guard and isolation. These observations bound the
|
|
66
|
+
known exposure; they do not prove complete unreachability, including through
|
|
67
|
+
every future lint configuration or pull request.
|
|
68
|
+
|
|
69
|
+
The advisory stays visible: no audit suppression, alias, downgrade, vendored
|
|
70
|
+
patch or GitHub dismissal is included. Recheck upstream before claiming it
|
|
71
|
+
resolved. The verified full-workspace audit moves from 9 advisories
|
|
72
|
+
(1 critical, 5 high, 1 moderate, 2 low) to **1 high**; a nonzero full audit
|
|
73
|
+
result is therefore expected until this residual is fixed.
|
|
74
|
+
|
|
75
|
+
## Release boundary
|
|
76
|
+
|
|
77
|
+
The package version bump does not itself start npm staging. The current
|
|
78
|
+
GitHub workflow triggers on an approved `v*.*.*` tag (or manual dispatch),
|
|
79
|
+
then uses `npm stage publish`. A successful upload still requires maintainer
|
|
80
|
+
approval in npm Staged Packages with 2FA before it is public. Do not label a
|
|
81
|
+
merge, a green CI run or an uploaded stage as a public npm release.
|
package/docs/THEMING.md
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# Theming (6.25.0)
|
|
2
|
+
|
|
3
|
+
Set `--mcp-ui-*` custom properties on any ancestor of the library's components.
|
|
4
|
+
They inherit through nested components and can be scoped differently for two
|
|
5
|
+
instances on the same page. No provider, global theme or new dependency is
|
|
6
|
+
required. Continue scanning `src/**/*.{ts,tsx}` with Tailwind v4 and use the
|
|
7
|
+
host's class-based `.dark` variant. Import the existing autocomplete CSS when
|
|
8
|
+
using those components, as before.
|
|
9
|
+
|
|
10
|
+
## Defaults and inheritance
|
|
11
|
+
|
|
12
|
+
The migrated component roots carry `mcp-ui-root`, but **do not assign public
|
|
13
|
+
token defaults** there. Assigning `--mcp-ui-surface: white` on a nested root
|
|
14
|
+
would mask an inherited host value. Instead, each static utility consumes a
|
|
15
|
+
token with the exact old color as its fallback, for example:
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
class="bg-[color:var(--mcp-ui-surface,var(--color-white,#fff))] dark:bg-[color:var(--mcp-ui-surface,var(--color-gray-800,#1f2937))]"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The host's existing Tailwind palette remains authoritative. Fallbacks retain
|
|
22
|
+
the original per-component shades, dark variants and alpha paints; there is
|
|
23
|
+
deliberately no single replacement palette that collapses those distinctions.
|
|
24
|
+
Removing a token restores that original fallback. A custom color replaces the
|
|
25
|
+
whole paint, including opacity, rather than applying the old opacity to it.
|
|
26
|
+
Host tokens apply without a `.dark` class too; that class only selects the
|
|
27
|
+
original dark fallbacks. Normal form text falls back to its inherited color
|
|
28
|
+
when `--mcp-ui-fg` is absent, while muted fields keep their secondary role.
|
|
29
|
+
|
|
30
|
+
## Tokens
|
|
31
|
+
|
|
32
|
+
Values below describe the original fallbacks, not declarations on the root.
|
|
33
|
+
`gray-*`, `blue-*`, etc. refer to the host's Tailwind color variables. Exact
|
|
34
|
+
per-use defaults remain visible in the static source classes. The frozen
|
|
35
|
+
6.24.0 browser fixture records representative resolved light/dark values.
|
|
36
|
+
|
|
37
|
+
| Token | Role | Original default / behavior |
|
|
38
|
+
| --- | --- | --- |
|
|
39
|
+
| `--mcp-ui-bg` | Outer resource-renderer background | Transparent |
|
|
40
|
+
| `--mcp-ui-surface` | Cards, panels, menus, input surfaces | White; dark gray-800, with original local exceptions |
|
|
41
|
+
| `--mcp-ui-surface-muted` | Headers, secondary/disabled surfaces | Original gray-50/100; dark gray-700/900 variants |
|
|
42
|
+
| `--mcp-ui-surface-hover` | Hovered neutral elements | Original gray-50/100; dark gray-700 variants |
|
|
43
|
+
| `--mcp-ui-fg` | Primary text | Original gray-700/900; dark gray-100/300/white |
|
|
44
|
+
| `--mcp-ui-fg-muted` | Descriptions and secondary text | Original gray-500/600; dark gray-400 variants |
|
|
45
|
+
| `--mcp-ui-fg-subtle` | Hints, placeholders, disabled text | Original gray-400 and existing autocomplete RGBA values |
|
|
46
|
+
| `--mcp-ui-border` | Decorative borders and dividers | Original gray-100/200; dark gray-600/700 |
|
|
47
|
+
| `--mcp-ui-border-control` | Form-field outlines | Original gray-300; dark gray-600, with local variants |
|
|
48
|
+
| `--mcp-ui-focus` | Focus ring/border | Original blue-400/500 |
|
|
49
|
+
| `--mcp-ui-accent` | Primary action, link, selected text | Original blue/indigo shades |
|
|
50
|
+
| `--mcp-ui-accent-fg` | Text on a strong action fill | White |
|
|
51
|
+
| `--mcp-ui-accent-hover` | Strong action hover fill | Original blue-700 |
|
|
52
|
+
| `--mcp-ui-accent-soft` | Selection and preview backgrounds | Original blue-50/100; dark blue-900 with original alpha |
|
|
53
|
+
| `--mcp-ui-accent-border` | Accent borders | Original blue-200/300; dark blue-600/800 |
|
|
54
|
+
| `--mcp-ui-danger` | Destructive/error text or strong fill | Original red/rose shades |
|
|
55
|
+
| `--mcp-ui-danger-hover` | Destructive hover fill | Original red-700 |
|
|
56
|
+
| `--mcp-ui-danger-soft` | Error backgrounds | Original red-50/100; dark red-900 with original alpha |
|
|
57
|
+
| `--mcp-ui-danger-border` | Error borders | Original red-200/300; dark red-600/800 |
|
|
58
|
+
| `--mcp-ui-warning` | Warning/loading text or strong fill | Original amber/yellow/orange shades |
|
|
59
|
+
| `--mcp-ui-warning-hover` | Warning action hover | Original amber-700 |
|
|
60
|
+
| `--mcp-ui-warning-soft` | Warning backgrounds | Original amber/yellow/orange soft paints |
|
|
61
|
+
| `--mcp-ui-warning-border` | Warning borders | Original amber/yellow/orange border shades |
|
|
62
|
+
| `--mcp-ui-success` | Successful/completed states | Original green/emerald shades |
|
|
63
|
+
| `--mcp-ui-success-soft` | Success backgrounds | Original green-50/100; dark green-900 with original alpha |
|
|
64
|
+
| `--mcp-ui-info` | Ready/information states | Original cyan/sky/teal shades |
|
|
65
|
+
| `--mcp-ui-info-soft` | Information backgrounds | Original cyan/sky/teal soft paints |
|
|
66
|
+
| `--mcp-ui-info-border` | Information borders | Original cyan/sky/teal border shades |
|
|
67
|
+
| `--mcp-ui-secondary` | Processing/comparison states | Original purple/violet/pink shades |
|
|
68
|
+
| `--mcp-ui-secondary-soft` | Secondary-state backgrounds | Original purple/violet/pink soft paints |
|
|
69
|
+
| `--mcp-ui-highlight` | Table search match | Original yellow-200 and existing dark search paint |
|
|
70
|
+
| `--mcp-ui-overlay` | Dialog backdrop | Black at 50% |
|
|
71
|
+
| `--mcp-ui-shadow` | Full shadow value, not a color | Existing Tailwind shadow or autocomplete box-shadow |
|
|
72
|
+
| `--mcp-ui-scrollbar-thumb` / `--mcp-ui-scrollbar-track` | Scratchpad native scrollbars | Original automatic rendering when unset |
|
|
73
|
+
| `--mcp-ui-chart-1` … `--mcp-ui-chart-8` | Series / graph node / pie-point colors | Engine-managed defaults when absent |
|
|
74
|
+
| `--mcp-ui-chart-grid` | Chart grid / graph edges | Existing engine defaults |
|
|
75
|
+
| `--mcp-ui-chart-axis` | Chart axis border / graph node stroke | Existing engine defaults |
|
|
76
|
+
| `--mcp-ui-chart-label` | Axes, legend, graph labels, no-data text | Existing engine defaults |
|
|
77
|
+
|
|
78
|
+
Typography, spacing and radii are not changed by this release. Native
|
|
79
|
+
scrollbars retain their automatic appearance unless the host supplies tokens.
|
|
80
|
+
Existing non-color `!important` utilities and the pre-existing
|
|
81
|
+
autocomplete selected-state/reduced-motion rules remain; the theme migration
|
|
82
|
+
adds no `!important` rule and no global palette selector.
|
|
83
|
+
|
|
84
|
+
## Host example
|
|
85
|
+
|
|
86
|
+
```css
|
|
87
|
+
.app-theme {
|
|
88
|
+
--mcp-ui-surface: #fff;
|
|
89
|
+
--mcp-ui-surface-muted: #f1f5f9;
|
|
90
|
+
--mcp-ui-surface-hover: #e2e8f0;
|
|
91
|
+
--mcp-ui-fg: #0f172a;
|
|
92
|
+
--mcp-ui-fg-muted: #475569;
|
|
93
|
+
--mcp-ui-fg-subtle: #64748b;
|
|
94
|
+
--mcp-ui-border: #cbd5e1;
|
|
95
|
+
--mcp-ui-border-control: #64748b;
|
|
96
|
+
--mcp-ui-accent: #1d4ed8;
|
|
97
|
+
--mcp-ui-accent-hover: #1e40af;
|
|
98
|
+
--mcp-ui-accent-fg: #fff;
|
|
99
|
+
--mcp-ui-accent-soft: #dbeafe;
|
|
100
|
+
--mcp-ui-focus: #1d4ed8;
|
|
101
|
+
--mcp-ui-chart-1: #1d4ed8;
|
|
102
|
+
--mcp-ui-chart-grid: #cbd5e1;
|
|
103
|
+
--mcp-ui-chart-axis: #64748b;
|
|
104
|
+
--mcp-ui-chart-label: #334155;
|
|
105
|
+
}
|
|
106
|
+
.dark .app-theme {
|
|
107
|
+
--mcp-ui-surface: #0f172a;
|
|
108
|
+
--mcp-ui-surface-muted: #1e293b;
|
|
109
|
+
--mcp-ui-surface-hover: #334155;
|
|
110
|
+
--mcp-ui-fg: #f8fafc;
|
|
111
|
+
--mcp-ui-fg-muted: #cbd5e1;
|
|
112
|
+
--mcp-ui-fg-subtle: #94a3b8;
|
|
113
|
+
--mcp-ui-border: #475569;
|
|
114
|
+
--mcp-ui-border-control: #94a3b8;
|
|
115
|
+
--mcp-ui-accent: #93c5fd;
|
|
116
|
+
--mcp-ui-accent-hover: #60a5fa;
|
|
117
|
+
--mcp-ui-accent-fg: #0f172a;
|
|
118
|
+
--mcp-ui-accent-soft: #1e3a8a;
|
|
119
|
+
--mcp-ui-focus: #93c5fd;
|
|
120
|
+
--mcp-ui-chart-1: #93c5fd;
|
|
121
|
+
--mcp-ui-chart-grid: #475569;
|
|
122
|
+
--mcp-ui-chart-axis: #94a3b8;
|
|
123
|
+
--mcp-ui-chart-label: #e2e8f0;
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Choose semantic status colors and all series colors appropriate for the host.
|
|
128
|
+
Recheck contrast against the actual backgrounds, including hover/disabled
|
|
129
|
+
states and chart series. The example is a starting point, not a full theme
|
|
130
|
+
or an accessibility certification.
|
|
131
|
+
|
|
132
|
+
## Portals and mounted graphics
|
|
133
|
+
|
|
134
|
+
Export menus and dialogs stay mounted under `document.body`. Moving them into
|
|
135
|
+
the host container would restore clipping and transformed-parent positioning
|
|
136
|
+
bugs. The library copies only effective `--mcp-ui-*` values and local dark
|
|
137
|
+
state from the menu trigger or the stationary inline expansion slot. It
|
|
138
|
+
updates them while the portal is open and clears stale values when its
|
|
139
|
+
anchor changes. `ModalRenderer` uses a hidden inline anchor. Fullscreen
|
|
140
|
+
content is still moved, not duplicated.
|
|
141
|
+
|
|
142
|
+
Canvas renderers read computed tokens from their own roots. Ancestor
|
|
143
|
+
class/style/data-attribute changes, token removal and DOM reparenting trigger
|
|
144
|
+
an in-place Chart.js `update('none')` or G6 `setOptions`/`draw`, preserving
|
|
145
|
+
renderer identity and layout/zoom. Explicit dataset, scale, element, tooltip,
|
|
146
|
+
node and edge styles keep precedence. Chart payload objects are shallow-cloned
|
|
147
|
+
before the engine adds its generated colors, allowing one payload in multiple
|
|
148
|
+
independently themed instances.
|
|
149
|
+
Point paint overrides are separate from the line/radar stroke: an explicit
|
|
150
|
+
point outline does not block a host series color on the line itself. Live
|
|
151
|
+
updates also refresh Chart.js's shared point/bar paints without replacing
|
|
152
|
+
their shared option objects or changing non-color settings.
|
|
153
|
+
|
|
154
|
+
Chart.js tooltips use `surface`, `fg` and `border`; G6 tooltip content reads
|
|
155
|
+
those current tokens when shown. An omitted series token leaves its original
|
|
156
|
+
engine color. Chart.js's default cycle is seven colors, while configured host
|
|
157
|
+
tokens address eight repeating slots. No default palette is installed globally.
|
|
158
|
+
|
|
159
|
+
Pure stylesheet/CSSOM or media-query changes without a DOM attribute change
|
|
160
|
+
are not observed. When switching such a host theme, also update the host's
|
|
161
|
+
class or a `data-theme` attribute. The supported light/dark strategy remains
|
|
162
|
+
the `.dark` ancestor class.
|
|
163
|
+
|
|
164
|
+
## Coverage and known accessibility gaps
|
|
165
|
+
|
|
166
|
+
Migrated: `ChatPrompt`, `UIResourceRenderer` (including table chrome and virtual
|
|
167
|
+
rows), `ScratchpadPanel`, `FormFieldRenderer`, `AutocompleteFormField`,
|
|
168
|
+
`AutocompleteDropdown`, `GhostText`, `ComponentToolbar`, `ExpandableWrapper`,
|
|
169
|
+
`PortalDropdownMenu`, `FormRenderer`, `ActionGroupRenderer`, `ModalRenderer`,
|
|
170
|
+
missing-value/degraded/error surfaces, and Chart.js/G6 chrome and canvas colors.
|
|
171
|
+
|
|
172
|
+
Not fully migrated: map/Leaflet chrome and external tiles, code highlighting
|
|
173
|
+
themes, `AgentCard`/`AgentHandoff`/`AgentStatusBadge`, standalone artifact,
|
|
174
|
+
carousel, footer, grid/editor, gallery/video and presentation-feedback chrome,
|
|
175
|
+
and other renderers outside the list above. Native OS select popups are outside
|
|
176
|
+
the library's DOM styling. Hosts should remove old overrides only for migrated
|
|
177
|
+
surfaces and retain targeted overrides for these remaining components.
|
|
178
|
+
|
|
179
|
+
Default colors are preserved even where they miss accessibility targets.
|
|
180
|
+
The frozen Chromium fixture measured these existing gaps:
|
|
181
|
+
|
|
182
|
+
| Measurement | Light | Dark | Target |
|
|
183
|
+
| --- | ---: | ---: | ---: |
|
|
184
|
+
| Required input / form select / scratchpad select border | 1.47:1 | 1.94:1 | 3:1 |
|
|
185
|
+
| Search / choice control border | 1.24:1 | 1.94:1 | 3:1 |
|
|
186
|
+
| Required input visible 2px focus ring | 3.76:1 | 3.90:1 | 3:1 |
|
|
187
|
+
|
|
188
|
+
The represented text measurements reached at least 4.52:1; this is not a full
|
|
189
|
+
audit of every field, state or renderer. Supplementary Chart.js 4.5.1 palette
|
|
190
|
+
measurements on white put six of seven opaque default colors, and all seven
|
|
191
|
+
50%-alpha fills, below 3:1. These are palette measurements, not a rendered
|
|
192
|
+
canvas or adjacent-series audit. Changing those defaults would alter existing
|
|
193
|
+
appearance, so hosts must supply their own accessible theme where needed.
|
|
194
|
+
|
|
195
|
+
See [`tests/theme/README.md`](../tests/theme/README.md) for the exact baseline
|
|
196
|
+
commit, tool versions, measured deficits and limits. The fixture checks actual
|
|
197
|
+
computed styles, not screenshot equality; raster canvas appearance and native
|
|
198
|
+
popup pixels are outside that comparison.
|
|
199
|
+
|
|
200
|
+
## Release handoff
|
|
201
|
+
|
|
202
|
+
This is a Solid-only 6.25.0 release. The MCP schema and Spec/CLI versions are
|
|
203
|
+
unchanged. Hosts can upgrade, bind their design tokens, remove the corresponding
|
|
204
|
+
overrides and run their own browser contrast checks. The public release follows
|
|
205
|
+
review/CI, the `v6.25.0` tag, then maintainer approval of the staged npm package.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seed-ship/mcp-ui-solid",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.25.1",
|
|
4
4
|
"description": "SolidJS components for rendering MCP-generated UI resources",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
"!src/test-setup.ts",
|
|
12
12
|
"README.md",
|
|
13
13
|
"CHANGELOG.md",
|
|
14
|
+
"docs/THEMING.md",
|
|
15
|
+
"docs/SECURITY-DEPENDENCIES.md",
|
|
14
16
|
"LICENSE"
|
|
15
17
|
],
|
|
16
18
|
"sideEffects": [
|
|
@@ -229,9 +231,9 @@
|
|
|
229
231
|
}
|
|
230
232
|
},
|
|
231
233
|
"dependencies": {
|
|
232
|
-
"@seed-ship/mcp-ui-spec": "^5.
|
|
234
|
+
"@seed-ship/mcp-ui-spec": "^5.7.0",
|
|
233
235
|
"@types/dompurify": "^3.0.5",
|
|
234
|
-
"dompurify": "^3.4.
|
|
236
|
+
"dompurify": "^3.4.17",
|
|
235
237
|
"marked": "^16.3.0",
|
|
236
238
|
"zod": "^3.22.4"
|
|
237
239
|
},
|
|
@@ -253,7 +255,7 @@
|
|
|
253
255
|
"katex": "0.18.4",
|
|
254
256
|
"leaflet.markercluster": "^1.5.3",
|
|
255
257
|
"size-limit": "^12.0.0",
|
|
256
|
-
"solid-js": "^1.9.
|
|
258
|
+
"solid-js": "^1.9.17",
|
|
257
259
|
"typescript": "^5.3.3",
|
|
258
260
|
"vite": "^6.4.1",
|
|
259
261
|
"vite-plugin-solid": "^2.11.8",
|
|
@@ -289,7 +291,7 @@
|
|
|
289
291
|
"test:watch": "vitest",
|
|
290
292
|
"test:browser": "vitest --mode browser",
|
|
291
293
|
"lint": "eslint src",
|
|
292
|
-
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.contracts.json",
|
|
294
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.contracts.json && tsc --noEmit -p tsconfig.examples.json",
|
|
293
295
|
"clean": "rm -rf dist",
|
|
294
296
|
"size": "size-limit",
|
|
295
297
|
"size:analyze": "size-limit --why"
|
|
@@ -62,17 +62,17 @@ const ActionButton: Component<{
|
|
|
62
62
|
const variantClass = () => {
|
|
63
63
|
switch (props.action.variant) {
|
|
64
64
|
case 'primary':
|
|
65
|
-
return 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500'
|
|
65
|
+
return 'bg-[color:var(--mcp-ui-accent,var(--color-blue-600))] text-[color:var(--mcp-ui-accent-fg,var(--color-white,#fff))] hover:bg-[color:var(--mcp-ui-accent-hover,var(--color-blue-700))] focus:ring-[color:var(--mcp-ui-focus,var(--color-blue-500))]'
|
|
66
66
|
case 'secondary':
|
|
67
|
-
return 'bg-gray-200 text-gray-800 hover:bg-gray-300 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600 focus:ring-gray-500'
|
|
67
|
+
return 'bg-[color:var(--mcp-ui-surface-muted,var(--color-gray-200,#e5e7eb))] text-[color:var(--mcp-ui-fg,var(--color-gray-800,#1f2937))] hover:bg-[color:var(--mcp-ui-surface-hover,var(--color-gray-300,#d1d5db))] dark:bg-[color:var(--mcp-ui-surface-muted,var(--color-gray-700,#374151))] dark:text-[color:var(--mcp-ui-fg,var(--color-white,#fff))] dark:hover:bg-[color:var(--mcp-ui-surface-hover,var(--color-gray-600,#4b5563))] focus:ring-[color:var(--mcp-ui-focus,var(--color-gray-500,#6b7280))]'
|
|
68
68
|
case 'outline':
|
|
69
|
-
return 'border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 focus:ring-gray-500'
|
|
69
|
+
return 'border border-[color:var(--mcp-ui-border,var(--color-gray-300,#d1d5db))] dark:border-[color:var(--mcp-ui-border,var(--color-gray-600,#4b5563))] text-[color:var(--mcp-ui-fg,var(--color-gray-700,#374151))] dark:text-[color:var(--mcp-ui-fg,var(--color-gray-300,#d1d5db))] hover:bg-[color:var(--mcp-ui-surface-hover,var(--color-gray-50,#f9fafb))] dark:hover:bg-[color:var(--mcp-ui-surface-hover,var(--color-gray-700,#374151))] focus:ring-[color:var(--mcp-ui-focus,var(--color-gray-500,#6b7280))]'
|
|
70
70
|
case 'ghost':
|
|
71
|
-
return 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 focus:ring-gray-500'
|
|
71
|
+
return 'text-[color:var(--mcp-ui-fg,var(--color-gray-700,#374151))] dark:text-[color:var(--mcp-ui-fg,var(--color-gray-300,#d1d5db))] hover:bg-[color:var(--mcp-ui-surface-hover,var(--color-gray-100,#f3f4f6))] dark:hover:bg-[color:var(--mcp-ui-surface-hover,var(--color-gray-700,#374151))] focus:ring-[color:var(--mcp-ui-focus,var(--color-gray-500,#6b7280))]'
|
|
72
72
|
case 'danger':
|
|
73
|
-
return 'bg-red-600 text-white hover:bg-red-700 focus:ring-red-500'
|
|
73
|
+
return 'bg-[color:var(--mcp-ui-danger,var(--color-red-600))] text-[color:var(--mcp-ui-accent-fg,var(--color-white,#fff))] hover:bg-[color:var(--mcp-ui-danger-hover,var(--color-red-700))] focus:ring-[color:var(--mcp-ui-focus,var(--color-red-500))]'
|
|
74
74
|
default:
|
|
75
|
-
return 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500'
|
|
75
|
+
return 'bg-[color:var(--mcp-ui-accent,var(--color-blue-600))] text-[color:var(--mcp-ui-accent-fg,var(--color-white,#fff))] hover:bg-[color:var(--mcp-ui-accent-hover,var(--color-blue-700))] focus:ring-[color:var(--mcp-ui-focus,var(--color-blue-500))]'
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -162,10 +162,10 @@ export const AutocompleteDropdown: Component<AutocompleteDropdownProps> = (props
|
|
|
162
162
|
left: '0',
|
|
163
163
|
right: '0',
|
|
164
164
|
'z-index': '50',
|
|
165
|
-
'background-color': '#ffffff',
|
|
166
|
-
border: '1px solid #e5e7eb',
|
|
165
|
+
'background-color': 'var(--mcp-ui-surface, #ffffff)',
|
|
166
|
+
border: '1px solid var(--mcp-ui-border, #e5e7eb)',
|
|
167
167
|
'border-radius': '6px',
|
|
168
|
-
'box-shadow': '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',
|
|
168
|
+
'box-shadow': 'var(--mcp-ui-shadow, 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06))',
|
|
169
169
|
'max-height': props.maxHeight || '240px',
|
|
170
170
|
overflow: 'auto',
|
|
171
171
|
...positionStyles()
|
|
@@ -174,7 +174,7 @@ export const AutocompleteDropdown: Component<AutocompleteDropdownProps> = (props
|
|
|
174
174
|
return (
|
|
175
175
|
<Show when={props.isOpen}>
|
|
176
176
|
<div
|
|
177
|
-
class={`mcp-autocomplete-dropdown ${props.class || ''}`}
|
|
177
|
+
class={`mcp-ui-root mcp-autocomplete-dropdown ${props.class || ''}`}
|
|
178
178
|
style={containerStyles()}
|
|
179
179
|
role="listbox"
|
|
180
180
|
aria-label={strings.autocompleteSuggestions}
|
|
@@ -185,7 +185,7 @@ export const AutocompleteDropdown: Component<AutocompleteDropdownProps> = (props
|
|
|
185
185
|
class="mcp-autocomplete-loading"
|
|
186
186
|
style={{
|
|
187
187
|
padding: '12px 16px',
|
|
188
|
-
color: '#6b7280',
|
|
188
|
+
color: 'var(--mcp-ui-fg-muted, #6b7280)',
|
|
189
189
|
'font-size': '0.875rem',
|
|
190
190
|
display: 'flex',
|
|
191
191
|
'align-items': 'center',
|
|
@@ -197,8 +197,8 @@ export const AutocompleteDropdown: Component<AutocompleteDropdownProps> = (props
|
|
|
197
197
|
display: 'inline-block',
|
|
198
198
|
width: '14px',
|
|
199
199
|
height: '14px',
|
|
200
|
-
border: '2px solid #e5e7eb',
|
|
201
|
-
'border-top-color': '#3b82f6',
|
|
200
|
+
border: '2px solid var(--mcp-ui-border, #e5e7eb)',
|
|
201
|
+
'border-top-color': 'var(--mcp-ui-accent, #3b82f6)',
|
|
202
202
|
'border-radius': '50%',
|
|
203
203
|
animation: 'mcp-spin 0.6s linear infinite'
|
|
204
204
|
}}
|
|
@@ -213,7 +213,7 @@ export const AutocompleteDropdown: Component<AutocompleteDropdownProps> = (props
|
|
|
213
213
|
class="mcp-autocomplete-empty"
|
|
214
214
|
style={{
|
|
215
215
|
padding: '12px 16px',
|
|
216
|
-
color: '#9ca3af',
|
|
216
|
+
color: 'var(--mcp-ui-fg-subtle, #9ca3af)',
|
|
217
217
|
'font-size': '0.875rem',
|
|
218
218
|
'text-align': 'center'
|
|
219
219
|
}}
|
|
@@ -242,8 +242,8 @@ export const AutocompleteDropdown: Component<AutocompleteDropdownProps> = (props
|
|
|
242
242
|
style={{
|
|
243
243
|
padding: '8px 16px',
|
|
244
244
|
cursor: isDisabled() ? 'not-allowed' : 'pointer',
|
|
245
|
-
'background-color': isSelected() ? '#eff6ff' : 'transparent',
|
|
246
|
-
color: isDisabled() ? '#9ca3af' : '#374151',
|
|
245
|
+
'background-color': isSelected() ? 'var(--mcp-ui-accent-soft, #eff6ff)' : 'transparent',
|
|
246
|
+
color: isDisabled() ? 'var(--mcp-ui-fg-subtle, #9ca3af)' : 'var(--mcp-ui-fg, #374151)',
|
|
247
247
|
'font-size': '0.875rem',
|
|
248
248
|
transition: 'background-color 150ms ease'
|
|
249
249
|
}}
|
|
@@ -283,14 +283,14 @@ export const AutocompleteDropdown: Component<AutocompleteDropdownProps> = (props
|
|
|
283
283
|
class="mcp-autocomplete-footer"
|
|
284
284
|
style={{
|
|
285
285
|
padding: '6px 12px',
|
|
286
|
-
'border-top': '1px solid #e5e7eb',
|
|
287
|
-
'background-color': '#f9fafb',
|
|
286
|
+
'border-top': '1px solid var(--mcp-ui-border, #e5e7eb)',
|
|
287
|
+
'background-color': 'var(--mcp-ui-surface-muted, #f9fafb)',
|
|
288
288
|
'font-size': '0.75rem',
|
|
289
|
-
color: '#6b7280'
|
|
289
|
+
color: 'var(--mcp-ui-fg-muted, #6b7280)'
|
|
290
290
|
}}
|
|
291
291
|
>
|
|
292
292
|
<kbd style={{
|
|
293
|
-
'background-color': '#e5e7eb',
|
|
293
|
+
'background-color': 'var(--mcp-ui-surface-hover, #e5e7eb)',
|
|
294
294
|
padding: '1px 4px',
|
|
295
295
|
'border-radius': '2px',
|
|
296
296
|
'font-family': 'inherit',
|
|
@@ -298,7 +298,7 @@ export const AutocompleteDropdown: Component<AutocompleteDropdownProps> = (props
|
|
|
298
298
|
}}>↑</kbd>
|
|
299
299
|
{' '}
|
|
300
300
|
<kbd style={{
|
|
301
|
-
'background-color': '#e5e7eb',
|
|
301
|
+
'background-color': 'var(--mcp-ui-surface-hover, #e5e7eb)',
|
|
302
302
|
padding: '1px 4px',
|
|
303
303
|
'border-radius': '2px',
|
|
304
304
|
'font-family': 'inherit',
|
|
@@ -306,7 +306,7 @@ export const AutocompleteDropdown: Component<AutocompleteDropdownProps> = (props
|
|
|
306
306
|
}}>↓</kbd>
|
|
307
307
|
{strings.autocompleteHintNavigate}
|
|
308
308
|
<kbd style={{
|
|
309
|
-
'background-color': '#e5e7eb',
|
|
309
|
+
'background-color': 'var(--mcp-ui-surface-hover, #e5e7eb)',
|
|
310
310
|
padding: '1px 4px',
|
|
311
311
|
'border-radius': '2px',
|
|
312
312
|
'font-family': 'inherit',
|
|
@@ -314,7 +314,7 @@ export const AutocompleteDropdown: Component<AutocompleteDropdownProps> = (props
|
|
|
314
314
|
}}>Enter</kbd>
|
|
315
315
|
{strings.autocompleteHintSelect}
|
|
316
316
|
<kbd style={{
|
|
317
|
-
'background-color': '#e5e7eb',
|
|
317
|
+
'background-color': 'var(--mcp-ui-surface-hover, #e5e7eb)',
|
|
318
318
|
padding: '1px 4px',
|
|
319
319
|
'border-radius': '2px',
|
|
320
320
|
'font-family': 'inherit',
|