@ggui-ai/iframe-runtime 0.10.0 → 0.12.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/dist/host-capabilities.d.ts +6 -4
- package/dist/host-palette-bridge.d.ts +43 -0
- package/dist/host-palette-bridge.js +107 -0
- package/dist/iframe-runtime.js +83 -72
- package/dist/iframe-runtime.meta.json +1 -1
- package/dist/mcp-app-iframe-host.js +7 -2
- package/dist/react-renderer.d.ts +48 -6
- package/dist/react-renderer.js +54 -13
- package/dist/render-item.d.ts +17 -0
- package/dist/render-item.js +4 -0
- package/dist/runtime.d.ts +82 -1
- package/dist/runtime.js +214 -62
- package/dist/ui-feedback-chrome.d.ts +16 -0
- package/dist/ui-feedback-chrome.js +20 -0
- package/dist/wire-config.d.ts +7 -3
- package/package.json +7 -7
|
@@ -9,10 +9,12 @@
|
|
|
9
9
|
*
|
|
10
10
|
* FAIL-SAFE BY CONTRACT: absence of a capability MUST NOT stop the
|
|
11
11
|
* runtime from attempting the operation. Hosts under-advertise in
|
|
12
|
-
* practice — ggui's own embed host (`./mcp-app-iframe-host.ts`)
|
|
13
|
-
* `tools/call` while advertising nothing
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* practice — ggui's own embed host (`./mcp-app-iframe-host.ts`)
|
|
13
|
+
* historically proxied `tools/call` while advertising nothing (it now
|
|
14
|
+
* advertises `serverTools: {}`, but version-skewed embeds still
|
|
15
|
+
* under-advertise) — so treating silence as "cannot" would break
|
|
16
|
+
* working paths. These accessors exist to explain a failure that
|
|
17
|
+
* already happened, never to pre-empt an attempt.
|
|
16
18
|
*
|
|
17
19
|
* PRESENCE, NOT VALUE: on `McpUiHostCapabilities` both fields are
|
|
18
20
|
* optional OBJECTS (`serverTools?: { listChanged?: boolean }`,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec→ggui token bridge (ggui#572).
|
|
3
|
+
*
|
|
4
|
+
* MCP Apps hosts announce their palette as spec `--color-*` custom
|
|
5
|
+
* properties in `hostContext.styles.variables`. The canonical
|
|
6
|
+
* `@modelcontextprotocol/ext-apps` helper applies them as inline
|
|
7
|
+
* custom properties on the iframe `<html>` — which is correct spec
|
|
8
|
+
* behavior but repaints nothing ggui renders: generated UI, the
|
|
9
|
+
* design system, and the shell chrome consume exclusively `--ggui-*`
|
|
10
|
+
* tokens, and the renderer's scoped in-body token block shadows
|
|
11
|
+
* root-level inheritance for every token it defines anyway.
|
|
12
|
+
*
|
|
13
|
+
* This module translates the spec vocabulary onto the ggui token
|
|
14
|
+
* ladder so the renderer can merge the host's palette INTO the scoped
|
|
15
|
+
* block — as the fallback layer beneath the app's own theme (#573
|
|
16
|
+
* ruling: the slice's stamped theme wins; the host palette fills the
|
|
17
|
+
* slots no operator repainted).
|
|
18
|
+
*
|
|
19
|
+
* Coverage is deliberately PARTIAL in both directions:
|
|
20
|
+
*
|
|
21
|
+
* - Spec keys with no faithful ggui counterpart (ghost/disabled/
|
|
22
|
+
* inverse variants, ring colors, fonts, radii, shadows) are
|
|
23
|
+
* dropped — they keep applying via the ext-apps inline-DOM path
|
|
24
|
+
* for any non-ggui content, and inventing a mapping would repaint
|
|
25
|
+
* tokens with the WRONG semantic role.
|
|
26
|
+
* - ggui tokens the spec doesn't express (the primary accent scale,
|
|
27
|
+
* containers beyond error, the neutral ladder) keep their
|
|
28
|
+
* mode-resolved base values — the host-announced color MODE
|
|
29
|
+
* (ggui#551) already selects the coherent light/dark ladder these
|
|
30
|
+
* slots resolve from.
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* Map a host-announced `styles.variables` record onto `--ggui-*`
|
|
34
|
+
* declarations ready for the renderer's scoped token block.
|
|
35
|
+
*
|
|
36
|
+
* Unknown keys and unsafe/blank values are dropped silently (the same
|
|
37
|
+
* tolerant posture as the rest of host-context handling — a partial
|
|
38
|
+
* palette is a partial repaint, never an error). Returns `undefined`
|
|
39
|
+
* when nothing survives so callers can spread-skip the option.
|
|
40
|
+
*
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
export declare function mapHostPaletteToGguiVars(variables: Readonly<Record<string, string | undefined>> | undefined): Readonly<Record<string, string>> | undefined;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec→ggui token bridge (ggui#572).
|
|
3
|
+
*
|
|
4
|
+
* MCP Apps hosts announce their palette as spec `--color-*` custom
|
|
5
|
+
* properties in `hostContext.styles.variables`. The canonical
|
|
6
|
+
* `@modelcontextprotocol/ext-apps` helper applies them as inline
|
|
7
|
+
* custom properties on the iframe `<html>` — which is correct spec
|
|
8
|
+
* behavior but repaints nothing ggui renders: generated UI, the
|
|
9
|
+
* design system, and the shell chrome consume exclusively `--ggui-*`
|
|
10
|
+
* tokens, and the renderer's scoped in-body token block shadows
|
|
11
|
+
* root-level inheritance for every token it defines anyway.
|
|
12
|
+
*
|
|
13
|
+
* This module translates the spec vocabulary onto the ggui token
|
|
14
|
+
* ladder so the renderer can merge the host's palette INTO the scoped
|
|
15
|
+
* block — as the fallback layer beneath the app's own theme (#573
|
|
16
|
+
* ruling: the slice's stamped theme wins; the host palette fills the
|
|
17
|
+
* slots no operator repainted).
|
|
18
|
+
*
|
|
19
|
+
* Coverage is deliberately PARTIAL in both directions:
|
|
20
|
+
*
|
|
21
|
+
* - Spec keys with no faithful ggui counterpart (ghost/disabled/
|
|
22
|
+
* inverse variants, ring colors, fonts, radii, shadows) are
|
|
23
|
+
* dropped — they keep applying via the ext-apps inline-DOM path
|
|
24
|
+
* for any non-ggui content, and inventing a mapping would repaint
|
|
25
|
+
* tokens with the WRONG semantic role.
|
|
26
|
+
* - ggui tokens the spec doesn't express (the primary accent scale,
|
|
27
|
+
* containers beyond error, the neutral ladder) keep their
|
|
28
|
+
* mode-resolved base values — the host-announced color MODE
|
|
29
|
+
* (ggui#551) already selects the coherent light/dark ladder these
|
|
30
|
+
* slots resolve from.
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* Spec `--color-*` key → ggui token, one entry per slot whose semantic
|
|
34
|
+
* role matches 1:1. Semantic status text maps to the ladder's `-500`
|
|
35
|
+
* stop — the "live" stop tone slots and Alert actually read; a flat
|
|
36
|
+
* `--ggui-color-error` target would repaint nothing (no theme emits
|
|
37
|
+
* flat semantic tokens).
|
|
38
|
+
*/
|
|
39
|
+
const SPEC_TO_GGUI = {
|
|
40
|
+
'--color-background-primary': '--ggui-color-surface',
|
|
41
|
+
'--color-background-secondary': '--ggui-color-surfaceVariant',
|
|
42
|
+
'--color-background-tertiary': '--ggui-color-container',
|
|
43
|
+
'--color-background-danger': '--ggui-color-errorContainer',
|
|
44
|
+
'--color-text-primary': '--ggui-color-onSurface',
|
|
45
|
+
'--color-text-secondary': '--ggui-color-onSurfaceVariant',
|
|
46
|
+
'--color-text-tertiary': '--ggui-color-neutral-500',
|
|
47
|
+
'--color-text-danger': '--ggui-color-error-500',
|
|
48
|
+
'--color-text-success': '--ggui-color-success-500',
|
|
49
|
+
'--color-text-warning': '--ggui-color-warning-500',
|
|
50
|
+
'--color-text-info': '--ggui-color-info-500',
|
|
51
|
+
'--color-border-primary': '--ggui-color-outline',
|
|
52
|
+
'--color-border-secondary': '--ggui-color-outlineVariant',
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Host palette values arrive over postMessage and are string-joined
|
|
56
|
+
* into a `<style>` element by the renderer — unlike the app's own
|
|
57
|
+
* theme overlay they are NOT pre-validated by the wire parser, so
|
|
58
|
+
* this is the sanitization gate. Conservative deny-list: anything
|
|
59
|
+
* that could close the declaration (`;`), the rule (`{`/`}`), or the
|
|
60
|
+
* style element (`<`/`>`), smuggle strings/escapes, comment out the
|
|
61
|
+
* rest of the sheet (`/*`), or trigger a fetch (`url(`) drops the
|
|
62
|
+
* value. Plain color syntax — hex, rgb()/hsl()/oklch()/color-mix(),
|
|
63
|
+
* named colors — passes untouched.
|
|
64
|
+
*/
|
|
65
|
+
function isSafeCssValue(value) {
|
|
66
|
+
if (value.length === 0 || value.length > 256)
|
|
67
|
+
return false;
|
|
68
|
+
if (/[;{}<>"'`\\]/.test(value))
|
|
69
|
+
return false;
|
|
70
|
+
if (/\/\*/.test(value))
|
|
71
|
+
return false;
|
|
72
|
+
if (/url\s*\(/i.test(value))
|
|
73
|
+
return false;
|
|
74
|
+
// Control characters (incl. newlines) have no place in a color value.
|
|
75
|
+
// eslint-disable-next-line no-control-regex
|
|
76
|
+
if (/[\u0000-\u001f\u007f]/.test(value))
|
|
77
|
+
return false;
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Map a host-announced `styles.variables` record onto `--ggui-*`
|
|
82
|
+
* declarations ready for the renderer's scoped token block.
|
|
83
|
+
*
|
|
84
|
+
* Unknown keys and unsafe/blank values are dropped silently (the same
|
|
85
|
+
* tolerant posture as the rest of host-context handling — a partial
|
|
86
|
+
* palette is a partial repaint, never an error). Returns `undefined`
|
|
87
|
+
* when nothing survives so callers can spread-skip the option.
|
|
88
|
+
*
|
|
89
|
+
* @public
|
|
90
|
+
*/
|
|
91
|
+
export function mapHostPaletteToGguiVars(variables) {
|
|
92
|
+
if (variables === undefined)
|
|
93
|
+
return undefined;
|
|
94
|
+
const mapped = {};
|
|
95
|
+
let count = 0;
|
|
96
|
+
for (const [specKey, gguiKey] of Object.entries(SPEC_TO_GGUI)) {
|
|
97
|
+
const raw = variables[specKey];
|
|
98
|
+
if (typeof raw !== 'string')
|
|
99
|
+
continue;
|
|
100
|
+
const value = raw.trim();
|
|
101
|
+
if (!isSafeCssValue(value))
|
|
102
|
+
continue;
|
|
103
|
+
mapped[gguiKey] = value;
|
|
104
|
+
count += 1;
|
|
105
|
+
}
|
|
106
|
+
return count > 0 ? mapped : undefined;
|
|
107
|
+
}
|