@ggui-ai/iframe-runtime 0.14.0 → 0.16.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.
Files changed (42) hide show
  1. package/dist/channel-transport.d.ts +3 -3
  2. package/dist/channels/channel-payload.d.ts +1 -1
  3. package/dist/channels/data.d.ts +1 -1
  4. package/dist/channels/drain-ack.d.ts +1 -1
  5. package/dist/channels/props-update.d.ts +1 -1
  6. package/dist/channels/render-frame.d.ts +1 -1
  7. package/dist/compiled-validators.d.ts +1 -1
  8. package/dist/connection.d.ts +34 -12
  9. package/dist/connection.js +40 -12
  10. package/dist/context-observer.d.ts +1 -1
  11. package/dist/context-observer.js +1 -1
  12. package/dist/events-polling.d.ts +1 -1
  13. package/dist/host-context-emitter.d.ts +23 -2
  14. package/dist/host-context-emitter.js +38 -5
  15. package/dist/host-fonts.d.ts +10 -0
  16. package/dist/host-fonts.js +113 -0
  17. package/dist/host-palette-bridge.js +20 -15
  18. package/dist/iframe-runtime.js +97 -108
  19. package/dist/iframe-runtime.meta.json +1 -1
  20. package/dist/index.d.ts +1 -1
  21. package/dist/mcp-app-iframe-host.d.ts +15 -0
  22. package/dist/mcp-app-iframe-host.js +5 -1
  23. package/dist/meta-parse.d.ts +9 -0
  24. package/dist/meta-parse.js +15 -3
  25. package/dist/observability.d.ts +73 -14
  26. package/dist/provisional-renderer.d.ts +1 -1
  27. package/dist/react-renderer.d.ts +19 -37
  28. package/dist/react-renderer.js +64 -72
  29. package/dist/registry-subscribe.d.ts +1 -1
  30. package/dist/render-item.d.ts +7 -14
  31. package/dist/runtime-telemetry.js +1 -1
  32. package/dist/runtime.d.ts +48 -56
  33. package/dist/runtime.js +213 -115
  34. package/dist/system-cards/NoCredentialsCard.js +13 -13
  35. package/dist/system-cards/ProtocolProbeCard.js +11 -11
  36. package/dist/system-cards/QrCode.js +1 -1
  37. package/dist/system-cards/index.js +2 -2
  38. package/dist/types.d.ts +1 -1
  39. package/dist/validation.d.ts +1 -1
  40. package/dist/validation.js +1 -1
  41. package/dist/wire-config.d.ts +3 -3
  42. package/package.json +7 -7
@@ -36,7 +36,7 @@ function GguiWordmark() {
36
36
  // off-white in dark). `gray-300` flips too — the gray scale in
37
37
  // `darkTheme` is intentionally inverted, so the wordmark's
38
38
  // contrasting "chrome" tone reads correctly under either theme.
39
- const ink = 'var(--ggui-color-onSurface, #292929)';
39
+ const ink = 'var(--ggui-color-onContainer, #292929)';
40
40
  const chrome = 'var(--ggui-color-neutral-300, #d1d5db)';
41
41
  return (_jsxs("svg", { viewBox: "0 0 224 50", width: "72", height: "16", role: "img", "aria-label": "ggui", style: { display: 'block', flexShrink: 0 }, children: [_jsx("path", { d: "M 0 0 H 50 V 25 H 25 V 50 H 0 Z", fill: chrome }), _jsx("rect", { x: "33", y: "33", width: "17", height: "17", fill: ink }), _jsx("path", { d: "M 58 0 H 108 V 25 H 83 V 50 H 58 Z", fill: ink }), _jsx("rect", { x: "91", y: "33", width: "17", height: "17", fill: chrome }), _jsx("path", { d: "M 141 50 C 154.807 50 166 38.8071 166 25 V 0 H 116 V 25 C 116 38.8071 127.193 50 141 50 Z", fill: ink }), _jsx("rect", { x: "174", y: "0", width: "50", height: "50", fill: chrome })] }));
42
42
  }
@@ -87,10 +87,10 @@ export function NoCredentialsCard({ settingsUrl, intent, }) {
87
87
  // INVERTED in darkTheme so `gray-100` is light-on-dark when in
88
88
  // dark mode and dark-on-light when in light mode).
89
89
  //
90
- // - `--ggui-color-surface` — opaque card surface (white in light,
90
+ // - `--ggui-color-container` — opaque card surface (white in light,
91
91
  // slate-800 in dark). Used for the URL block + QR backdrop.
92
- // - `--ggui-color-onSurface` — high-contrast body text.
93
- // - `--ggui-color-onSurfaceVariant` — muted captions/headings.
92
+ // - `--ggui-color-onContainer` — high-contrast body text.
93
+ // - `--ggui-color-onSunken` — muted captions/headings.
94
94
  // - `--ggui-color-neutral-100` — subtle tinted background that
95
95
  // auto-flips. Used for the inner "details" panel — slightly
96
96
  // differentiated from outer transparent canvas without being
@@ -103,7 +103,7 @@ export function NoCredentialsCard({ settingsUrl, intent, }) {
103
103
  // background creates a card-on-card look. Padding stays so content
104
104
  // doesn't crowd the iframe edges. Outside Claude (preview iframe,
105
105
  // console inspector), the ThemeProvider's body background is the
106
- // theme's `--ggui-color-surface`, so a transparent card just
106
+ // theme's `--ggui-color-container`, so a transparent card just
107
107
  // shows the theme's canvas behind it — also fine.
108
108
  _jsx(Card, { padding: "lg", border: false, shadow: "none", style: { backgroundColor: 'transparent' }, children: _jsxs(Stack, { gap: "lg", children: [_jsxs(Stack, { gap: "md", children: [_jsxs("div", { style: {
109
109
  display: 'flex',
@@ -118,11 +118,11 @@ export function NoCredentialsCard({ settingsUrl, intent, }) {
118
118
  }, children: [_jsx(Heading, { level: 3, children: "Connect an LLM provider" }), _jsx("code", { style: {
119
119
  fontFamily: 'var(--ggui-font-family-mono, ui-monospace, SFMono-Regular, Menlo, monospace)',
120
120
  fontSize: 'var(--ggui-font-size-xs, 11px)',
121
- color: 'var(--ggui-color-onSurfaceVariant, #71717a)',
121
+ color: 'var(--ggui-color-onSunken, #71717a)',
122
122
  opacity: 0.7,
123
123
  userSelect: 'all',
124
124
  }, title: "iframe-runtime build identifier \u2014 useful for diagnosing stale caches", children: BUILD_ID })] }), _jsx(Text, { style: {
125
- color: 'var(--ggui-color-onSurfaceVariant, #52525b)',
125
+ color: 'var(--ggui-color-onSunken, #52525b)',
126
126
  lineHeight: '1.55',
127
127
  }, children: "ggui needs an LLM key to render UIs. Open this server\u2019s settings page to paste an Anthropic, OpenAI, Google, or OpenRouter API key." })] }), _jsx("div", { style: {
128
128
  display: 'flex',
@@ -148,7 +148,7 @@ export function NoCredentialsCard({ settingsUrl, intent, }) {
148
148
  borderRadius: 'var(--ggui-shape-radius-sm, 6px)',
149
149
  lineHeight: 0,
150
150
  }, children: _jsx(QrCode, { value: settingsUrl, size: 120, foreground: "#000000", background: "#ffffff" }) }), _jsx(Text, { size: "xs", style: {
151
- color: 'var(--ggui-color-onSurfaceVariant, #52525b)',
151
+ color: 'var(--ggui-color-onSunken, #52525b)',
152
152
  fontSize: 'var(--ggui-font-size-xs, 11px)',
153
153
  textAlign: 'center',
154
154
  }, children: "Scan with your phone" })] }), _jsxs("div", { style: {
@@ -158,7 +158,7 @@ export function NoCredentialsCard({ settingsUrl, intent, }) {
158
158
  flex: '1 1 240px',
159
159
  minWidth: '200px',
160
160
  }, children: [_jsx(Text, { size: "xs", style: {
161
- color: 'var(--ggui-color-onSurfaceVariant, #52525b)',
161
+ color: 'var(--ggui-color-onSunken, #52525b)',
162
162
  fontWeight: 'var(--ggui-font-weight-semibold, 600)',
163
163
  textTransform: 'uppercase',
164
164
  letterSpacing: '0.06em',
@@ -166,11 +166,11 @@ export function NoCredentialsCard({ settingsUrl, intent, }) {
166
166
  }, children: "Or open this URL in your browser" }), _jsx("div", { style: {
167
167
  fontFamily: 'var(--ggui-font-family-mono, ui-monospace, SFMono-Regular, Menlo, monospace)',
168
168
  fontSize: 'var(--ggui-font-size-sm, 13px)',
169
- color: 'var(--ggui-color-onSurface, #111)',
169
+ color: 'var(--ggui-color-onContainer, #111)',
170
170
  wordBreak: 'break-all',
171
171
  userSelect: 'all',
172
172
  padding: 'var(--ggui-spacing-3, 12px)',
173
- background: 'var(--ggui-color-surface, #fff)',
173
+ background: 'var(--ggui-color-container, #fff)',
174
174
  border: '1px solid var(--ggui-color-neutral-200, #e4e4e7)',
175
175
  borderRadius: 'var(--ggui-shape-radius-sm, 6px)',
176
176
  lineHeight: '1.45',
@@ -180,10 +180,10 @@ export function NoCredentialsCard({ settingsUrl, intent, }) {
180
180
  gap: 'var(--ggui-spacing-3, 12px)',
181
181
  flexWrap: 'wrap',
182
182
  }, children: [_jsx(Button, { variant: "primary", size: "md", onClick: onPrimary, disabled: ctaDisabled, children: ctaLabel }), ctaState === 'idle' || ctaState === 'working' ? (_jsx(Text, { size: "xs", style: {
183
- color: 'var(--ggui-color-onSurfaceVariant, #52525b)',
183
+ color: 'var(--ggui-color-onSunken, #52525b)',
184
184
  opacity: 0.85,
185
185
  }, children: "or tap-and-hold the URL above to copy" })) : null] })] })] }) }), intent ? (_jsxs(Text, { size: "xs", style: {
186
- color: 'var(--ggui-color-onSurfaceVariant, #52525b)',
186
+ color: 'var(--ggui-color-onSunken, #52525b)',
187
187
  opacity: 0.7,
188
188
  }, children: ["Pending prompt: ", intent] })) : null] }) }));
189
189
  }
@@ -372,7 +372,7 @@ function ProbeButtonRow({ method, description, call, }) {
372
372
  padding: 'var(--ggui-spacing-3, 12px)',
373
373
  borderRadius: 'var(--ggui-shape-radius-md, 10px)',
374
374
  border: '1px solid var(--ggui-color-neutral-200, #e4e4e7)',
375
- background: 'var(--ggui-color-surface, #fff)',
375
+ background: 'var(--ggui-color-container, #fff)',
376
376
  }, children: [_jsxs("div", { style: {
377
377
  display: 'flex',
378
378
  alignItems: 'center',
@@ -382,10 +382,10 @@ function ProbeButtonRow({ method, description, call, }) {
382
382
  }, children: [_jsx("code", { style: {
383
383
  fontFamily: 'var(--ggui-font-family-mono, ui-monospace, SFMono-Regular, Menlo, monospace)',
384
384
  fontSize: 'var(--ggui-font-size-sm, 13px)',
385
- color: 'var(--ggui-color-onSurface, #111)',
385
+ color: 'var(--ggui-color-onContainer, #111)',
386
386
  fontWeight: 'var(--ggui-font-weight-semibold, 600)',
387
387
  }, children: method }), statusBadge] }), _jsx(Text, { size: "xs", style: {
388
- color: 'var(--ggui-color-onSurfaceVariant, #52525b)',
388
+ color: 'var(--ggui-color-onSunken, #52525b)',
389
389
  fontSize: 'var(--ggui-font-size-xs, 11px)',
390
390
  }, children: description }), _jsx("div", { style: { display: 'flex', gap: 'var(--ggui-spacing-2, 8px)' }, children: _jsx(Button, { variant: "primary", size: "sm", onClick: onClick, disabled: outcome.state === 'pending', children: buttonLabel }) }), outcome.state === 'success' || outcome.state === 'error' ? (_jsx("pre", { style: {
391
391
  margin: 0,
@@ -393,7 +393,7 @@ function ProbeButtonRow({ method, description, call, }) {
393
393
  fontSize: 'var(--ggui-font-size-xs, 11px)',
394
394
  color: outcome.state === 'error'
395
395
  ? 'var(--ggui-color-warning-500, #b45309)'
396
- : 'var(--ggui-color-onSurfaceVariant, #52525b)',
396
+ : 'var(--ggui-color-onSunken, #52525b)',
397
397
  background: 'var(--ggui-color-neutral-100, #f4f4f5)',
398
398
  padding: 'var(--ggui-spacing-2, 8px)',
399
399
  borderRadius: 'var(--ggui-shape-radius-sm, 6px)',
@@ -414,7 +414,7 @@ export function ProtocolProbeCard({ intent, }) {
414
414
  gap: 'var(--ggui-spacing-3, 12px)',
415
415
  flexWrap: 'wrap',
416
416
  }, children: [_jsx(Heading, { level: 3, children: "MCP Apps protocol probe" }), _jsx(Badge, { variant: "info", children: "diagnostic" })] }), _jsxs(Text, { style: {
417
- color: 'var(--ggui-color-onSurfaceVariant, #52525b)',
417
+ color: 'var(--ggui-color-onSunken, #52525b)',
418
418
  lineHeight: '1.55',
419
419
  }, children: ["One button per primary host effect emitted by the iframe runtime's gesture surfaces (submit-action bridge / native- idiom interceptors). Click each to verify which methods this host honors. The", _jsx("code", { children: "tools/call" }), " row chains the full submit-action bridge (audit + context + consent), reproducing the production ", _jsx("code", { children: "dispatchSubmitAction" }), " envelope shape verbatim. The receive log below captures every notification the host emits while this card is mounted."] })] }), _jsxs(Stack, { gap: "md", children: [_jsx(ProbeButtonRow, { method: "ui/message", description: "Submit-action bridge step 3 \u2014 consent-gated user authorization. Host pre-fills + waits for user confirm before the LLM acts on the paired pending-action context.", call: call }), _jsx(ProbeButtonRow, { method: "ui/update-model-context", description: "Submit-action bridge step 2 \u2014 silent structured `[ggui:pending-action]` context drop. LLM honors on next turn. No immediate UI.", call: call }), _jsx(ProbeButtonRow, { method: "ui/open-link", description: "Native-idiom anchor click interceptor primary effect. Opens ggui.ai in the host's browser / new tab. Real iframe code triggers via `<a href target='_blank'>` or cross-origin href.", call: call }), _jsx(ProbeButtonRow, { method: "ui/request-display-mode", description: "Native-idiom fullscreen interceptor primary effect. Request fullscreen \u2014 real iframe code calls `el.requestFullscreen()`; the runtime overrides the prototype to route through here.", call: call }), _jsx(ProbeButtonRow, { method: "tools/call", description: "Full submit-action bridge: `tools/call ggui_runtime_submit_action` (audit, kind:'dispatch') + `ui/update-model-context` + `ui/message`. Spec \u00A7401 requires `_meta.ui.visibility:['app']` on the receiving tool.", call: call })] }), _jsxs(Stack, { gap: "sm", children: [_jsxs("div", { style: {
420
420
  display: 'flex',
@@ -422,10 +422,10 @@ export function ProtocolProbeCard({ intent, }) {
422
422
  justifyContent: 'space-between',
423
423
  gap: 'var(--ggui-spacing-3, 12px)',
424
424
  }, children: [_jsx(Heading, { level: 4, children: "Receive log" }), _jsxs(Text, { size: "xs", style: {
425
- color: 'var(--ggui-color-onSurfaceVariant, #52525b)',
425
+ color: 'var(--ggui-color-onSunken, #52525b)',
426
426
  fontSize: 'var(--ggui-font-size-xs, 11px)',
427
427
  }, children: [log.length, " ", log.length === 1 ? 'event' : 'events'] })] }), log.length === 0 ? (_jsx(Text, { size: "xs", style: {
428
- color: 'var(--ggui-color-onSurfaceVariant, #52525b)',
428
+ color: 'var(--ggui-color-onSunken, #52525b)',
429
429
  fontStyle: 'italic',
430
430
  }, children: "No host notifications observed yet. Click a probe button or wait for the host to emit one." })) : (_jsx("div", { style: {
431
431
  display: 'flex',
@@ -443,17 +443,17 @@ export function ProtocolProbeCard({ intent, }) {
443
443
  }, children: [_jsxs("summary", { style: {
444
444
  cursor: 'pointer',
445
445
  padding: 'var(--ggui-spacing-1, 4px) 0',
446
- color: 'var(--ggui-color-onSurface, #111)',
446
+ color: 'var(--ggui-color-onContainer, #111)',
447
447
  }, children: [_jsx("span", { style: {
448
- color: 'var(--ggui-color-onSurfaceVariant, #52525b)',
448
+ color: 'var(--ggui-color-onSunken, #52525b)',
449
449
  marginRight: '8px',
450
450
  }, children: entry.tsIso.slice(11, 19) }), _jsx("span", { style: { fontWeight: 600 }, children: entry.method })] }), _jsx("pre", { style: {
451
451
  margin: '4px 0 0 16px',
452
452
  whiteSpace: 'pre-wrap',
453
453
  wordBreak: 'break-word',
454
- color: 'var(--ggui-color-onSurfaceVariant, #52525b)',
454
+ color: 'var(--ggui-color-onSunken, #52525b)',
455
455
  }, children: JSON.stringify(entry.params, null, 2) })] }, entry.id))) }))] }), intent ? (_jsxs(Text, { size: "xs", style: {
456
- color: 'var(--ggui-color-onSurfaceVariant, #52525b)',
456
+ color: 'var(--ggui-color-onSunken, #52525b)',
457
457
  opacity: 0.7,
458
458
  }, children: ["Triggered by: ", intent] })) : null] }) }));
459
459
  }
@@ -22,7 +22,7 @@ import qrcodeFactory from 'qrcode-generator';
22
22
  * fails (e.g., URL too long for any error-correction level) so the
23
23
  * caller can render a fallback.
24
24
  */
25
- export function QrCode({ value, size = 128, errorCorrection = 'M', foreground = 'var(--ggui-color-onSurface, #18181b)', background = 'var(--ggui-color-surface, #ffffff)', ariaLabel, }) {
25
+ export function QrCode({ value, size = 128, errorCorrection = 'M', foreground = 'var(--ggui-color-onContainer, #18181b)', background = 'var(--ggui-color-container, #ffffff)', ariaLabel, }) {
26
26
  const moduleMatrix = React.useMemo(() => {
27
27
  try {
28
28
  // typeNumber=0 lets the encoder pick the smallest fit. The
@@ -50,8 +50,8 @@ function UnknownSystemCard({ kind, props, }) {
50
50
  border: '1px solid var(--ggui-color-outlineVariant, #e4e4e7)',
51
51
  borderRadius: 'var(--ggui-shape-radius-md, 10px)',
52
52
  fontFamily: 'var(--ggui-font-family-sans, -apple-system, BlinkMacSystemFont, sans-serif)',
53
- background: 'var(--ggui-color-surfaceVariant, #f4f4f5)',
54
- color: 'var(--ggui-color-onSurfaceVariant, #52525b)',
53
+ background: 'var(--ggui-color-sunken, #f4f4f5)',
54
+ color: 'var(--ggui-color-onSunken, #52525b)',
55
55
  },
56
56
  }, React.createElement('div', { style: { fontWeight: 600, marginBottom: 8 } }, `Unknown system card: ${kind}`), React.createElement('div', {
57
57
  style: {
package/dist/types.d.ts CHANGED
@@ -15,7 +15,7 @@
15
15
  * declared the manager's constructor bag has been retired.
16
16
  */
17
17
  import type { McpAppAiGguiRenderMeta } from '@ggui-ai/protocol/integrations/mcp-apps';
18
- import type { HostContextProjection, ComponentGguiSession, SystemGguiSession } from '@ggui-ai/protocol';
18
+ import type { HostContextProjection, ComponentGguiSession, SystemGguiSession } from '@ggui-ai/protocol/wire';
19
19
  /**
20
20
  * The four server-assigned ledger fields on every wire-delivered
21
21
  * {@link GguiSession}. They're stamped by the server's per-render event
@@ -26,7 +26,7 @@
26
26
  * host wrapper explicitly does NOT run this path — its only contract is
27
27
  * the postMessage bridge to the renderer iframe.
28
28
  */
29
- import type { ActionSpec, ActionEnvelope, ContextSpec, JsonObject, PropsSpec, ReservedChannelValidator, StreamSpec, ValidationResult } from '@ggui-ai/protocol';
29
+ import type { ActionSpec, ActionEnvelope, ContextSpec, JsonObject, PropsSpec, ReservedChannelValidator, StreamSpec, ValidationResult } from '@ggui-ai/protocol/wire';
30
30
  import type { CompiledValidatorSet } from './compiled-validators.js';
31
31
  /**
32
32
  * Install the precompiled validators loaded from the bootstrap. Called
@@ -1,4 +1,4 @@
1
- import { isKnownReservedChannel, validateActionEnvelope, validateContextData, validatePropsData, validateStreamData, } from '@ggui-ai/protocol';
1
+ import { isKnownReservedChannel, validateActionEnvelope, validateContextData, validatePropsData, validateStreamData, } from '@ggui-ai/protocol/wire';
2
2
  // =============================================================================
3
3
  // Active precompiled-validator set
4
4
  // =============================================================================
@@ -16,7 +16,7 @@
16
16
  * ProtocolError dual-emission policy, and the tools/call-vs-WS
17
17
  * dispatch transport.
18
18
  */
19
- import type { ActionEnvelope, GguiSession } from '@ggui-ai/protocol';
19
+ import type { ActionEnvelope, GguiSession } from '@ggui-ai/protocol/wire';
20
20
  import type { GguiSessionSeedInput } from './types.js';
21
21
  import { ClientContractViolationError, StreamBus, type WireConfig } from '@ggui-ai/wire';
22
22
  import type { WebSocketMessage } from '@ggui-ai/protocol/transport/websocket';
@@ -98,8 +98,8 @@ export interface BuildRootWireConfigOptions {
98
98
  * pending-events pipe. First-party `createGguiServer` bridges it
99
99
  * ONLY when composed with DEFAULT handlers (`opts.handlers`
100
100
  * undefined); a custom `handlers` list drains its own pipe, so the
101
- * channel deliberately does not bridge — the hosted pod is such a
102
- * caller, and on it a WS gesture lands on the retained ledger only.
101
+ * channel deliberately does not bridge — a deployment composing its own
102
+ * `handlers` is such a caller, and on it a WS gesture lands on the retained ledger only.
103
103
  * See `GguiSessionChannelOptions.pendingEventConsumer`. The default
104
104
  * send remains the seam tests and direct-WS callers exercise.
105
105
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ggui-ai/iframe-runtime",
3
- "version": "0.14.0",
3
+ "version": "0.16.0",
4
4
  "description": "The iframe-local ggui runtime: a self-contained ESM bundle that boots inside an MCP Apps host iframe, runs the protocol-version handshake, opens the live-channel WebSocket, and mounts the rendered UI. Bundles React, ReactDOM, and the ggui design system so generated component code has a single seam to read from.",
5
5
  "keywords": [
6
6
  "ggui",
@@ -41,12 +41,12 @@
41
41
  "react": "^19.0.0",
42
42
  "react-dom": "^19.0.0",
43
43
  "zod": "^4.3.6",
44
- "@ggui-ai/gadgets": "0.14.0",
45
- "@ggui-ai/preview-a2ui": "0.14.0",
46
- "@ggui-ai/live-channel": "0.14.0",
47
- "@ggui-ai/protocol": "0.14.0",
48
- "@ggui-ai/design": "0.14.0",
49
- "@ggui-ai/wire": "0.14.0"
44
+ "@ggui-ai/design": "0.16.0",
45
+ "@ggui-ai/live-channel": "0.16.0",
46
+ "@ggui-ai/preview-a2ui": "0.16.0",
47
+ "@ggui-ai/gadgets": "0.16.0",
48
+ "@ggui-ai/protocol": "0.16.0",
49
+ "@ggui-ai/wire": "0.16.0"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "@modelcontextprotocol/sdk": "^1.27.0"