@nimblebrain/synapse 0.12.2 → 0.14.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 (51) hide show
  1. package/README.md +54 -2
  2. package/dist/{chunk-A4ENX6XW.cjs → chunk-A44KCBNG.cjs} +28 -39
  3. package/dist/chunk-A44KCBNG.cjs.map +1 -0
  4. package/dist/chunk-B7WGYPYP.cjs +192 -0
  5. package/dist/chunk-B7WGYPYP.cjs.map +1 -0
  6. package/dist/{chunk-JVKQNMAP.js → chunk-BMHJKTAM.js} +11 -6
  7. package/dist/chunk-BMHJKTAM.js.map +1 -0
  8. package/dist/{chunk-XGJAADVV.js → chunk-HNGTHBLM.js} +25 -36
  9. package/dist/chunk-HNGTHBLM.js.map +1 -0
  10. package/dist/{chunk-ANUZ7WFQ.cjs → chunk-M7DSXSYG.cjs} +11 -6
  11. package/dist/chunk-M7DSXSYG.cjs.map +1 -0
  12. package/dist/chunk-SFPTRDNS.js +184 -0
  13. package/dist/chunk-SFPTRDNS.js.map +1 -0
  14. package/dist/codegen/index.d.cts +1 -1
  15. package/dist/codegen/index.d.ts +1 -1
  16. package/dist/connect.iife.global.js +30 -25
  17. package/dist/{detect-DSnLT-_m.d.ts → detect-C_qmYsh9.d.cts} +6 -0
  18. package/dist/{detect-DSnLT-_m.d.cts → detect-Dfd0upoz.d.ts} +6 -0
  19. package/dist/host/index.cjs +14 -14
  20. package/dist/host/index.d.cts +10 -3
  21. package/dist/host/index.d.ts +10 -3
  22. package/dist/host/index.js +2 -2
  23. package/dist/index.cjs +13 -9
  24. package/dist/index.d.cts +16 -4
  25. package/dist/index.d.ts +16 -4
  26. package/dist/index.js +3 -3
  27. package/dist/react/index.cjs +5 -5
  28. package/dist/react/index.cjs.map +1 -1
  29. package/dist/react/index.d.cts +1 -1
  30. package/dist/react/index.d.ts +1 -1
  31. package/dist/react/index.js +3 -3
  32. package/dist/react/index.js.map +1 -1
  33. package/dist/synapse-runtime.iife.global.js +30 -25
  34. package/dist/synapse-ui.iife.global.js +6 -1
  35. package/dist/{types-C9utGXv_.d.cts → types-BSZasM8q.d.cts} +39 -1
  36. package/dist/{types-C9utGXv_.d.ts → types-BSZasM8q.d.ts} +39 -1
  37. package/package.json +1 -6
  38. package/dist/chunk-A4ENX6XW.cjs.map +0 -1
  39. package/dist/chunk-ANUZ7WFQ.cjs.map +0 -1
  40. package/dist/chunk-JVKQNMAP.js.map +0 -1
  41. package/dist/chunk-KTXASIFJ.cjs +0 -75
  42. package/dist/chunk-KTXASIFJ.cjs.map +0 -1
  43. package/dist/chunk-VXTMI266.js +0 -73
  44. package/dist/chunk-VXTMI266.js.map +0 -1
  45. package/dist/chunk-XGJAADVV.js.map +0 -1
  46. package/dist/ui/fonts.cjs +0 -19
  47. package/dist/ui/fonts.cjs.map +0 -1
  48. package/dist/ui/fonts.d.cts +0 -30
  49. package/dist/ui/fonts.d.ts +0 -30
  50. package/dist/ui/fonts.js +0 -17
  51. package/dist/ui/fonts.js.map +0 -1
package/README.md CHANGED
@@ -48,7 +48,6 @@ component rendered across ChatGPT, Claude, and NimbleBrain — ships as the
48
48
  | `@nimblebrain/synapse` | Vanilla JS core — `connect()`, `createSynapse()`, `createStore()` |
49
49
  | `@nimblebrain/synapse/react` | React hooks and providers (`AppProvider`, `SynapseProvider`) |
50
50
  | `@nimblebrain/synapse/ui` | Component library — tokens, primitives, components, layouts |
51
- | `@nimblebrain/synapse/ui/fonts` | Side-effect import that loads the brand fonts into the iframe |
52
51
  | `@nimblebrain/synapse/ui/base` | Side-effect import that establishes the root-height chain (`html, body, #root`) the app shell fills. `AppFrame` does this automatically on render; import it in your entry to apply it before first paint |
53
52
  | `@nimblebrain/synapse/vite` | Vite plugin for dev mode |
54
53
  | `@nimblebrain/synapse/codegen` | CLI + programmatic code generation |
@@ -67,9 +66,14 @@ every token and component in light/dark across several themes.
67
66
  These are the durable decisions behind the library; they rarely change.
68
67
 
69
68
  - **The library holds no brand.** Tokens are `var(--token, neutral-fallback)`
70
- references, not hex values. The host injects the real palette/fonts at runtime
69
+ references, not hex values. The host injects the real palette at runtime
71
70
  (the MCP ext-apps `hostContext.styles.variables`), so the same app adopts
72
71
  whatever host it runs in. Standalone, it renders in neutral fallbacks.
72
+ - **That includes typography — the SDK ships no fonts.** Font fallbacks are
73
+ web-safe system stacks, so an app renders correctly with no host, no network,
74
+ and no font files. A host that wants its own typeface sends `@font-face`
75
+ descriptors on the theme; the library fetches nothing on its own. See
76
+ [Host fonts](#host-fonts).
73
77
  - **Theme via CSS, not React.** Components style with token-driven inline-style
74
78
  objects whose values are those `var()` refs, so theming — including light/dark —
75
79
  resolves in CSS with no re-render. `ensureStyle` injects keyframes and
@@ -87,6 +91,54 @@ These are the durable decisions behind the library; they rarely change.
87
91
  throws there. The scrim, Tab focus trap, focus-in/restore, scroll-lock, and
88
92
  Escape are hand-rolled.
89
93
 
94
+ ### Host fonts
95
+
96
+ A CSS custom property can *name* a font family but cannot *load* one, and an app
97
+ iframe is its own document — it inherits no `@font-face` from the host page. So a
98
+ host that sends only tokens is naming a typeface the app has no way to render.
99
+ `SynapseTheme.fontFaces` closes that gap: the host sends the faces alongside the
100
+ tokens, and the SDK loads them into the app document.
101
+
102
+ ```ts
103
+ // Host side — sent as the `synapse/fontFaces` host-context extension.
104
+ {
105
+ mode: "dark",
106
+ tokens: { "--font-sans": "'Your Sans', system-ui, sans-serif" },
107
+ fontFaces: [
108
+ { family: "Your Sans", src: "url('/fonts/your-sans.woff2') format('woff2')", weight: "400 700" },
109
+ ],
110
+ }
111
+ ```
112
+
113
+ | Field | Notes |
114
+ |---|---|
115
+ | `family` | Must match the family named in the host's `--font-*` token value. |
116
+ | `src` | Any CSS `src` descriptor — relative (`url('/fonts/x.woff2')`), absolute (`url('https://cdn.example/x.woff2')`), or `data:`. |
117
+ | `weight` | Single weight (`400`) or a variable range (`400 700`). Optional. |
118
+ | `style` | `normal`, `italic`, … Optional. |
119
+ | `display` | Defaults to `swap`, so text paints in the fallback rather than blocking. Optional. |
120
+
121
+ Three things worth knowing:
122
+
123
+ - **Sending no fonts is a supported configuration, not a degraded one.** Omit
124
+ `fontFaces` and the app renders in the web-safe fallbacks (`system-ui`,
125
+ `ui-monospace`, `Georgia`).
126
+ - **Give every `--font-*` token value a web-safe tail.** A bare family name with
127
+ no matching face falls through to the browser default, not to your intended
128
+ stack. `"'Your Sans', system-ui, sans-serif"`, never `"'Your Sans'"`.
129
+ - **The URL must satisfy the app iframe's CSP — and `'self'` may not mean what
130
+ you expect.** A host that mounts apps in a `srcdoc` iframe *without*
131
+ `allow-same-origin` (the usual choice, since granting it to third-party app
132
+ HTML is a sandbox escape) gives the frame an **opaque origin**, and CSP
133
+ `'self'` then matches nothing. Under a typical `font-src 'self' data:`, a
134
+ `data:` URI is the only form that works unconditionally; any http(s) URL —
135
+ self-hosted or CDN — needs the host to add that origin to the frame's CSP.
136
+ Check the host's policy before choosing between them.
137
+
138
+ Faces are applied through the same funnel as the token variables
139
+ (`applyTheme`), so a theme change can never leave an app with the host's palette
140
+ under the wrong typeface.
141
+
90
142
  ## Quick Start
91
143
 
92
144
  ### Vanilla JS
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkKTXASIFJ_cjs = require('./chunk-KTXASIFJ.cjs');
3
+ var chunkB7WGYPYP_cjs = require('./chunk-B7WGYPYP.cjs');
4
4
  var extApps = require('@modelcontextprotocol/ext-apps');
5
5
 
6
6
  // src/content-parser.ts
@@ -254,9 +254,11 @@ async function connect(options) {
254
254
  };
255
255
  const ctx = result.hostContext;
256
256
  if (ctx) {
257
+ const initialFontFaces = chunkB7WGYPYP_cjs.extractFontFaces(ctx);
257
258
  currentTheme = {
258
259
  mode: ctx.theme === "dark" ? "dark" : "light",
259
- tokens: ctx.styles?.variables && typeof ctx.styles.variables === "object" ? ctx.styles.variables : {}
260
+ tokens: ctx.styles?.variables && typeof ctx.styles.variables === "object" ? ctx.styles.variables : {},
261
+ ...initialFontFaces ? { fontFaces: initialFontFaces } : {}
260
262
  };
261
263
  if (ctx.toolInfo && typeof ctx.toolInfo === "object") {
262
264
  toolInfo = { tool: ctx.toolInfo.tool ?? {} };
@@ -264,7 +266,7 @@ async function connect(options) {
264
266
  if (ctx.containerDimensions && typeof ctx.containerDimensions === "object") {
265
267
  containerDimensions = ctx.containerDimensions;
266
268
  }
267
- chunkKTXASIFJ_cjs.applyThemeVariables(currentTheme.mode, currentTheme.tokens);
269
+ chunkB7WGYPYP_cjs.applyTheme(currentTheme.mode, currentTheme.tokens, currentTheme.fontFaces);
268
270
  }
269
271
  }
270
272
  transport.onMessage(extApps.HOST_CONTEXT_CHANGED_METHOD, (params) => {
@@ -273,8 +275,9 @@ async function connect(options) {
273
275
  const mode = ctx.theme === "dark" ? "dark" : "light";
274
276
  const variables = ctx.styles?.variables;
275
277
  const tokens = variables && typeof variables === "object" ? variables : currentTheme.tokens;
276
- currentTheme = { mode, tokens };
277
- chunkKTXASIFJ_cjs.applyThemeVariables(mode, tokens);
278
+ const fontFaces = chunkB7WGYPYP_cjs.foldFontFaces(currentTheme.fontFaces, ctx);
279
+ currentTheme = { mode, tokens, ...fontFaces ? { fontFaces } : {} };
280
+ chunkB7WGYPYP_cjs.applyTheme(mode, tokens, fontFaces);
278
281
  const set = handlers.get(extApps.HOST_CONTEXT_CHANGED_METHOD);
279
282
  if (set) {
280
283
  for (const handler of set) handler(currentTheme);
@@ -398,30 +401,6 @@ async function connect(options) {
398
401
  return app;
399
402
  }
400
403
 
401
- // src/detection.ts
402
- var DEFAULT_THEME = {
403
- mode: "light",
404
- primaryColor: "#6366f1",
405
- tokens: {}
406
- };
407
- function detectHost(initResponse) {
408
- const resp = initResponse;
409
- const hostName = resp?.hostInfo?.name ?? "unknown";
410
- const protocolVersion = resp?.protocolVersion ?? "unknown";
411
- return {
412
- isNimbleBrain: hostName === "nimblebrain",
413
- serverName: hostName,
414
- protocolVersion
415
- };
416
- }
417
- function extractTheme(ctx) {
418
- if (!ctx) return { ...DEFAULT_THEME };
419
- const mode = ctx.theme === "light" || ctx.theme === "dark" ? ctx.theme : DEFAULT_THEME.mode;
420
- const variables = ctx.styles?.variables;
421
- const tokens = variables && typeof variables === "object" && !Array.isArray(variables) ? variables : {};
422
- return { mode, primaryColor: DEFAULT_THEME.primaryColor, tokens };
423
- }
424
-
425
404
  // src/keyboard.ts
426
405
  var KeyboardForwarder = class {
427
406
  listener;
@@ -610,6 +589,15 @@ function createSynapse(options) {
610
589
  const transport = new SynapseTransport();
611
590
  let hostInfo = null;
612
591
  let currentHostContext = {};
592
+ let currentFontFaces;
593
+ function syncFontFaces(ctx) {
594
+ currentFontFaces = chunkB7WGYPYP_cjs.foldFontFaces(currentFontFaces, ctx);
595
+ return currentFontFaces;
596
+ }
597
+ function resolveTheme(ctx) {
598
+ const theme = chunkB7WGYPYP_cjs.extractTheme(ctx);
599
+ return currentFontFaces ? { ...theme, fontFaces: currentFontFaces } : theme;
600
+ }
613
601
  let hostTasksCapability = null;
614
602
  let destroyed = false;
615
603
  const taskStatusRouter = createTaskStatusRouter(transport);
@@ -627,14 +615,14 @@ function createSynapse(options) {
627
615
  appCapabilities
628
616
  };
629
617
  const ready = transport.request(extApps.INITIALIZE_METHOD, initParams).then((result) => {
630
- hostInfo = detectHost(result);
618
+ hostInfo = chunkB7WGYPYP_cjs.detectHost(result);
631
619
  currentHostContext = result?.hostContext ?? {};
632
620
  const initResult = result;
633
621
  const rawTasks = initResult?.hostCapabilities?.tasks;
634
622
  hostTasksCapability = rawTasks && typeof rawTasks === "object" && !Array.isArray(rawTasks) ? rawTasks : void 0;
635
623
  {
636
- const theme = extractTheme(currentHostContext);
637
- chunkKTXASIFJ_cjs.applyThemeVariables(theme.mode, theme.tokens);
624
+ const theme = chunkB7WGYPYP_cjs.extractTheme(currentHostContext);
625
+ chunkB7WGYPYP_cjs.applyTheme(theme.mode, theme.tokens, syncFontFaces(currentHostContext));
638
626
  }
639
627
  for (const cb of hostContextCallbacks) cb(currentHostContext);
640
628
  transport.send(extApps.INITIALIZED_METHOD, {});
@@ -642,8 +630,8 @@ function createSynapse(options) {
642
630
  });
643
631
  const unsubHostContext = transport.onMessage(extApps.HOST_CONTEXT_CHANGED_METHOD, (params) => {
644
632
  currentHostContext = params ?? {};
645
- const theme = extractTheme(currentHostContext);
646
- chunkKTXASIFJ_cjs.applyThemeVariables(theme.mode, theme.tokens);
633
+ const theme = chunkB7WGYPYP_cjs.extractTheme(currentHostContext);
634
+ chunkB7WGYPYP_cjs.applyTheme(theme.mode, theme.tokens, syncFontFaces(currentHostContext));
647
635
  for (const cb of hostContextCallbacks) cb(currentHostContext);
648
636
  });
649
637
  const hostContextCallbacks = /* @__PURE__ */ new Set();
@@ -734,7 +722,7 @@ function createSynapse(options) {
734
722
  };
735
723
  },
736
724
  getTheme() {
737
- return extractTheme(currentHostContext);
725
+ return resolveTheme(currentHostContext);
738
726
  },
739
727
  // Selector over `onHostContextChanged`: only fires when the *derived*
740
728
  // theme actually changes, so theme subscribers don't see spurious
@@ -750,9 +738,9 @@ function createSynapse(options) {
750
738
  // current derived theme, so a workspace-only `host-context-changed`
751
739
  // notification correctly filters as a no-op.
752
740
  onThemeChanged(callback) {
753
- let prev = hostInfo !== null ? extractTheme(currentHostContext) : null;
741
+ let prev = hostInfo !== null ? resolveTheme(currentHostContext) : null;
754
742
  const wrapped = (ctx) => {
755
- const next = extractTheme(ctx);
743
+ const next = resolveTheme(ctx);
756
744
  if (prev !== null && themesEqual(prev, next)) return;
757
745
  prev = next;
758
746
  callback(next);
@@ -870,6 +858,7 @@ function validateFileResult(value) {
870
858
  }
871
859
  function themesEqual(a, b) {
872
860
  if (a.mode !== b.mode || a.primaryColor !== b.primaryColor) return false;
861
+ if (chunkB7WGYPYP_cjs.fontFacesKey(a.fontFaces) !== chunkB7WGYPYP_cjs.fontFacesKey(b.fontFaces)) return false;
873
862
  const aKeys = Object.keys(a.tokens);
874
863
  const bKeys = Object.keys(b.tokens);
875
864
  if (aKeys.length !== bKeys.length) return false;
@@ -881,5 +870,5 @@ function themesEqual(a, b) {
881
870
 
882
871
  exports.connect = connect;
883
872
  exports.createSynapse = createSynapse;
884
- //# sourceMappingURL=chunk-A4ENX6XW.cjs.map
885
- //# sourceMappingURL=chunk-A4ENX6XW.cjs.map
873
+ //# sourceMappingURL=chunk-A44KCBNG.cjs.map
874
+ //# sourceMappingURL=chunk-A44KCBNG.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/content-parser.ts","../src/event-map.ts","../src/resize.ts","../src/result-parser.ts","../src/transport.ts","../src/connect.ts","../src/keyboard.ts","../src/task-handle.ts","../src/core.ts"],"names":["TOOL_RESULT_METHOD","TOOL_INPUT_METHOD","TOOL_INPUT_PARTIAL_METHOD","TOOL_CANCELLED_METHOD","HOST_CONTEXT_CHANGED_METHOD","RESOURCE_TEARDOWN_METHOD","LATEST_PROTOCOL_VERSION","INITIALIZE_METHOD","extractFontFaces","applyTheme","foldFontFaces","INITIALIZED_METHOD","OPEN_LINK_METHOD","MESSAGE_METHOD","raw","READ_RESOURCE_METHOD","extractTheme","detectHost","options","fontFacesKey"],"mappings":"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","file":"chunk-A44KCBNG.cjs","sourcesContent":["import type { ToolResultData } from \"./types.js\";\n\nexport type { ToolResultData };\n\n/**\n * Parse inbound `ui/notifications/tool-result` notification params into\n * a consistent `ToolResultData` shape.\n *\n * Implements the 5-step parsing algorithm from the RFC:\n * 1. If `params.structuredContent` exists → use it as content.\n * 2. Else if `params.content` is an array of `{type:\"text\", text}` blocks →\n * join the text values and try JSON.parse.\n * 3. Else if `params.content` is a string → try JSON.parse.\n * 4. If JSON.parse fails in steps 2 or 3 → deliver the raw string.\n * 5. Return `{ content, structuredContent, raw }`.\n */\nexport function parseToolResultParams(params: Record<string, unknown> | undefined): ToolResultData {\n const raw = params ?? {};\n const structuredContent = raw.structuredContent ?? null;\n\n // Step 1: If structuredContent exists, use it as content\n if (structuredContent != null) {\n return { content: structuredContent, structuredContent, raw };\n }\n\n const rawContent = raw.content;\n\n // Step 2: If content is array of {type:\"text\", text} blocks, join texts and try JSON.parse\n if (Array.isArray(rawContent)) {\n const texts = rawContent\n .filter(\n (block: unknown) =>\n block != null &&\n typeof block === \"object\" &&\n (block as Record<string, unknown>).type === \"text\" &&\n typeof (block as Record<string, unknown>).text === \"string\",\n )\n .map((block: unknown) => (block as Record<string, unknown>).text as string);\n\n if (texts.length > 0) {\n const joined = texts.join(\"\");\n try {\n return { content: JSON.parse(joined), structuredContent: null, raw };\n } catch {\n return { content: joined, structuredContent: null, raw };\n }\n }\n\n // No text blocks — return raw content array\n return { content: rawContent, structuredContent: null, raw };\n }\n\n // Step 3: If content is string, try JSON.parse\n if (typeof rawContent === \"string\") {\n try {\n return { content: JSON.parse(rawContent), structuredContent: null, raw };\n } catch {\n return { content: rawContent, structuredContent: null, raw };\n }\n }\n\n // Fallback: return content as-is (could be null, object, etc.)\n return { content: rawContent ?? null, structuredContent: null, raw };\n}\n","import {\n HOST_CONTEXT_CHANGED_METHOD,\n RESOURCE_TEARDOWN_METHOD,\n TOOL_CANCELLED_METHOD,\n TOOL_INPUT_METHOD,\n TOOL_INPUT_PARTIAL_METHOD,\n TOOL_RESULT_METHOD,\n} from \"@modelcontextprotocol/ext-apps\";\n\n/**\n * Maps short event names used in App.on() to full MCP method names.\n * Uses canonical constants from @modelcontextprotocol/ext-apps to stay\n * in sync with the spec — if the spec changes a method name, this breaks\n * at compile time, not silently at runtime.\n */\nconst EVENT_MAP: Record<string, string> = {\n \"tool-result\": TOOL_RESULT_METHOD,\n \"tool-input\": TOOL_INPUT_METHOD,\n \"tool-input-partial\": TOOL_INPUT_PARTIAL_METHOD,\n \"tool-cancelled\": TOOL_CANCELLED_METHOD,\n \"theme-changed\": HOST_CONTEXT_CHANGED_METHOD,\n teardown: RESOURCE_TEARDOWN_METHOD,\n};\n\n/**\n * Resolve a short event name to a full MCP method name.\n * Returns the mapped method if found, otherwise passes through as-is.\n */\nexport function resolveEventMethod(name: string): string {\n return EVENT_MAP[name] ?? name;\n}\n","type SendFn = (method: string, params: Record<string, unknown>) => void;\n\ninterface Resizer {\n resize(width?: number, height?: number): void;\n measureAndSend(): void;\n destroy(): void;\n}\n\nexport function createResizer(send: SendFn, autoResize: boolean): Resizer {\n let destroyed = false;\n let observer: ResizeObserver | null = null;\n let rafId: number | null = null;\n\n function measureAndSend(): void {\n if (destroyed) return;\n const width = document.body.scrollWidth;\n const height = document.body.scrollHeight;\n send(\"ui/notifications/size-changed\", { width, height });\n }\n\n function resize(width?: number, height?: number): void {\n if (destroyed) return;\n if (width !== undefined && height !== undefined) {\n send(\"ui/notifications/size-changed\", { width, height });\n } else {\n measureAndSend();\n }\n }\n\n // Auto mode: attach ResizeObserver, debounced at 16ms via requestAnimationFrame\n if (autoResize && typeof ResizeObserver !== \"undefined\") {\n observer = new ResizeObserver(() => {\n if (destroyed) return;\n if (rafId !== null) cancelAnimationFrame(rafId);\n rafId = requestAnimationFrame(() => {\n rafId = null;\n measureAndSend();\n });\n });\n observer.observe(document.body);\n }\n\n function destroy(): void {\n if (destroyed) return;\n destroyed = true;\n if (rafId !== null) cancelAnimationFrame(rafId);\n observer?.disconnect();\n observer = null;\n }\n\n return { resize, measureAndSend, destroy };\n}\n","import type { ToolCallResult } from \"./types.js\";\n\n/**\n * Normalize a raw tool call response into a consistent `ToolCallResult`.\n *\n * Handles three shapes:\n * 1. MCP `CallToolResult` — has a `content` array with typed blocks.\n * 2. Raw JSON object (NimbleBrain bridge) — used as-is.\n * 3. Null / undefined — returns `{ data: null, isError: false }`.\n *\n * `_meta` on a `CallToolResult` is preserved on the parsed output as a\n * whole-object passthrough — notably `io.modelcontextprotocol/related-task`\n * (`{ taskId }`) for MCP 2025-11-25 task-augmented results, but any other\n * namespaced `_meta` keys propagate for free.\n */\nexport function parseToolResult(raw: unknown): ToolCallResult {\n if (raw == null) {\n return { data: null, isError: false };\n }\n\n if (isCallToolResult(raw)) {\n return parseCallToolResult(raw);\n }\n\n // Raw JSON object — pass through.\n return { data: raw, isError: false };\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\ninterface McpTextBlock {\n type: \"text\";\n text: string;\n}\n\ninterface McpCallToolResult {\n content: unknown[];\n isError?: boolean;\n _meta?: { [key: string]: unknown };\n}\n\nfunction isCallToolResult(value: unknown): value is McpCallToolResult {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n return false;\n }\n return Array.isArray((value as Record<string, unknown>).content);\n}\n\nfunction isTextBlock(block: unknown): block is McpTextBlock {\n if (block === null || typeof block !== \"object\" || Array.isArray(block)) {\n return false;\n }\n const obj = block as Record<string, unknown>;\n return obj.type === \"text\" && typeof obj.text === \"string\";\n}\n\n/**\n * Extract `_meta` as a shallow-copied object if present and object-shaped.\n *\n * Key-preserving by design: every `_meta` entry propagates without\n * selective copying, so future spec additions flow through without code\n * changes. Returns `undefined` when the input has no meaningful `_meta`.\n */\nfunction extractMeta(result: McpCallToolResult): { [key: string]: unknown } | undefined {\n const meta = result._meta;\n if (!meta || typeof meta !== \"object\" || Array.isArray(meta)) return undefined;\n // Shallow spread so mutations on either side don't leak. Preserves the\n // full key set including `io.modelcontextprotocol/related-task`.\n return { ...meta };\n}\n\nfunction parseCallToolResult(result: McpCallToolResult): ToolCallResult {\n const isError = result.isError === true;\n const content = result.content;\n const meta = extractMeta(result);\n\n if (content.length === 0) {\n return { data: null, isError, content, ...(meta && { _meta: meta }) };\n }\n\n const firstText = content.find(isTextBlock);\n\n if (!firstText) {\n // No text blocks — return the full content array so callers can inspect it.\n return { data: content, isError, content, ...(meta && { _meta: meta }) };\n }\n\n // Try to parse JSON from the text block.\n try {\n return { data: JSON.parse(firstText.text), isError, content, ...(meta && { _meta: meta }) };\n } catch {\n // Invalid JSON — return the raw string.\n return { data: firstText.text, isError, content, ...(meta && { _meta: meta }) };\n }\n}\n","import type {\n JsonRpcMessage,\n JsonRpcNotification,\n JsonRpcRequest,\n JsonRpcResponse,\n} from \"./types.js\";\n\ntype PendingEntry = {\n resolve: (value: unknown) => void;\n reject: (reason: unknown) => void;\n};\n\ntype MessageHandler = (params: Record<string, unknown> | undefined) => void;\n\nexport class SynapseTransport {\n private counter = 0;\n private destroyed = false;\n private pending = new Map<string, PendingEntry>();\n private handlers = new Map<string, Set<MessageHandler>>();\n private listener: (event: MessageEvent) => void;\n\n constructor() {\n this.listener = (event: MessageEvent) => this.handleMessage(event);\n window.addEventListener(\"message\", this.listener);\n }\n\n send(method: string, params?: Record<string, unknown>): void {\n if (this.destroyed) return;\n\n const msg: JsonRpcNotification = {\n jsonrpc: \"2.0\",\n method,\n ...(params !== undefined && { params }),\n };\n window.parent.postMessage(msg, \"*\");\n }\n\n request(method: string, params?: Record<string, unknown>): Promise<unknown> {\n if (this.destroyed) {\n return Promise.reject(new Error(\"Transport destroyed\"));\n }\n\n const id = `syn-${++this.counter}`;\n const msg: JsonRpcRequest = {\n jsonrpc: \"2.0\",\n method,\n id,\n ...(params !== undefined && { params }),\n };\n\n return new Promise<unknown>((resolve, reject) => {\n this.pending.set(id, { resolve, reject });\n window.parent.postMessage(msg, \"*\");\n });\n }\n\n onMessage(method: string, callback: MessageHandler): () => void {\n if (!this.handlers.has(method)) {\n this.handlers.set(method, new Set());\n }\n this.handlers.get(method)?.add(callback);\n\n return () => {\n const set = this.handlers.get(method);\n if (set) {\n set.delete(callback);\n if (set.size === 0) {\n this.handlers.delete(method);\n }\n }\n };\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n\n window.removeEventListener(\"message\", this.listener);\n\n const error = new Error(\"Transport destroyed\");\n for (const entry of this.pending.values()) {\n entry.reject(error);\n }\n this.pending.clear();\n this.handlers.clear();\n }\n\n private handleMessage(event: MessageEvent): void {\n if (this.destroyed) return;\n\n const data = event.data as JsonRpcMessage;\n if (!data || data.jsonrpc !== \"2.0\") return;\n\n // Response to a pending request\n if (\"id\" in data && data.id && !(\"method\" in data)) {\n const response = data as JsonRpcResponse;\n const entry = this.pending.get(response.id);\n if (!entry) return;\n this.pending.delete(response.id);\n\n if (response.error) {\n const err = new Error(response.error.message);\n (err as any).code = response.error.code;\n (err as any).data = response.error.data;\n entry.reject(err);\n } else {\n entry.resolve(response.result);\n }\n return;\n }\n\n // Incoming notification\n if (\"method\" in data && !(\"id\" in data && data.id)) {\n const notification = data as JsonRpcNotification;\n const set = this.handlers.get(notification.method);\n if (set) {\n for (const handler of set) {\n handler(notification.params);\n }\n }\n }\n }\n}\n","import type {\n McpUiHostContext,\n McpUiHostContextChangedNotification,\n McpUiInitializeRequest,\n McpUiInitializeResult,\n McpUiMessageRequest,\n McpUiOpenLinkRequest,\n McpUiUpdateModelContextRequest,\n} from \"@modelcontextprotocol/ext-apps\";\nimport {\n HOST_CONTEXT_CHANGED_METHOD,\n INITIALIZE_METHOD,\n INITIALIZED_METHOD,\n LATEST_PROTOCOL_VERSION,\n MESSAGE_METHOD,\n OPEN_LINK_METHOD,\n TOOL_RESULT_METHOD,\n} from \"@modelcontextprotocol/ext-apps\";\nimport type {\n CallToolRequest,\n ReadResourceRequest,\n ReadResourceResult,\n TextContent,\n} from \"@modelcontextprotocol/sdk/types.js\";\n\nimport { parseToolResultParams } from \"./content-parser.js\";\nimport { extractFontFaces, foldFontFaces } from \"./detection.js\";\nimport { resolveEventMethod } from \"./event-map.js\";\nimport { createResizer } from \"./resize.js\";\nimport { parseToolResult } from \"./result-parser.js\";\nimport { applyTheme } from \"./theme-defaults.js\";\nimport { SynapseTransport } from \"./transport.js\";\nimport type { App, ConnectOptions, Dimensions, Theme, ToolCallResult } from \"./types.js\";\n\n// Derived locally because `@modelcontextprotocol/ext-apps` only exports\n// METHOD constants for ext-apps specific ui/* methods. Typing the literal\n// with the spec's request `method` field still produces a compile error if\n// upstream renames it.\nconst READ_RESOURCE_METHOD: ReadResourceRequest[\"method\"] = \"resources/read\";\n\n/**\n * Connect to a MCP Apps host.\n *\n * Owns the full ext-apps handshake, content parsing, resize management,\n * and event routing. Returns a ready-to-use `App` object.\n */\nexport async function connect(options: ConnectOptions): Promise<App> {\n const { name, version, autoResize = false } = options;\n\n const transport = new SynapseTransport();\n let destroyed = false;\n\n // --- Mutable state ---\n let currentTheme: Theme = { mode: \"light\", tokens: {} };\n let hostInfo: { name: string; version: string } = { name: \"unknown\", version: \"unknown\" };\n let toolInfo: { tool: Record<string, unknown> } | null = null;\n let containerDimensions: Dimensions | null = null;\n\n // --- Event handlers ---\n const handlers = new Map<string, Set<(params: unknown) => void>>();\n\n // --- Step 1: Set up message listener (handled by SynapseTransport constructor) ---\n\n // --- Step 2: Send initial size ---\n const resizer = createResizer((method, params) => transport.send(method, params), autoResize);\n resizer.measureAndSend();\n\n // --- Steps 3-4: Send ui/initialize and wait for response ---\n //\n // `connect()` returns an `App` with no task-augmented call surface, so\n // we don't advertise the `tasks` capability here. Per MCP 2025-11-25,\n // requestors MUST NOT advertise capabilities they can't use — and\n // doing so confuses hosts that allocate state on the strength of the\n // advertisement. If `App` ever grows `callToolAsTask`, re-add the\n // capability here at the same time.\n const initParams: McpUiInitializeRequest[\"params\"] = {\n protocolVersion: LATEST_PROTOCOL_VERSION,\n appInfo: { name, version },\n appCapabilities: {},\n };\n\n const result = (await transport.request(\n INITIALIZE_METHOD,\n initParams as unknown as Record<string, unknown>,\n )) as McpUiInitializeResult | null;\n\n // --- Step 5: Extract theme, hostInfo, toolInfo, containerDimensions ---\n if (result) {\n hostInfo = {\n name: result.hostInfo?.name ?? \"unknown\",\n version: result.hostInfo?.version ?? \"unknown\",\n };\n\n const ctx: McpUiHostContext | undefined = result.hostContext;\n if (ctx) {\n const initialFontFaces = extractFontFaces(ctx);\n currentTheme = {\n mode: ctx.theme === \"dark\" ? \"dark\" : \"light\",\n tokens:\n ctx.styles?.variables && typeof ctx.styles.variables === \"object\"\n ? (ctx.styles.variables as Record<string, string>)\n : {},\n ...(initialFontFaces ? { fontFaces: initialFontFaces } : {}),\n };\n\n if (ctx.toolInfo && typeof ctx.toolInfo === \"object\") {\n toolInfo = { tool: (ctx.toolInfo.tool as unknown as Record<string, unknown>) ?? {} };\n }\n if (ctx.containerDimensions && typeof ctx.containerDimensions === \"object\") {\n containerDimensions = ctx.containerDimensions as Dimensions;\n }\n\n // Inject the theme into the DOM: the host's values go inline, and the\n // neutral defaults for the mode go in a cascade layer, where they back any\n // var nobody declares and lose to every var that is declared. Any\n // host-supplied font faces load alongside — a token names a family, it\n // cannot load one.\n applyTheme(currentTheme.mode, currentTheme.tokens, currentTheme.fontFaces);\n }\n }\n\n // --- Route incoming notifications to registered handlers ---\n\n // Special handling for host-context-changed: update internal theme state\n transport.onMessage(HOST_CONTEXT_CHANGED_METHOD, (params) => {\n if (destroyed || !params) return;\n const ctx = params as Partial<McpUiHostContextChangedNotification[\"params\"]>;\n const mode = ctx.theme === \"dark\" ? \"dark\" : \"light\";\n const variables = ctx.styles?.variables;\n const tokens =\n variables && typeof variables === \"object\"\n ? (variables as Record<string, string>)\n : currentTheme.tokens;\n // A host-context-changed carries only the fields that changed; `foldFontFaces`\n // owns what an absent or unusable list means (see its doc).\n const fontFaces = foldFontFaces(currentTheme.fontFaces, ctx);\n currentTheme = { mode, tokens, ...(fontFaces ? { fontFaces } : {}) };\n applyTheme(mode, tokens, fontFaces);\n const set = handlers.get(HOST_CONTEXT_CHANGED_METHOD);\n if (set) {\n for (const handler of set) handler(currentTheme);\n }\n });\n\n // Helper to ensure transport subscription exists for a method\n const subscribedMethods = new Set<string>([HOST_CONTEXT_CHANGED_METHOD]);\n\n function ensureTransportSub(method: string): void {\n if (subscribedMethods.has(method)) return;\n subscribedMethods.add(method);\n\n const isToolResult = method === TOOL_RESULT_METHOD;\n\n transport.onMessage(method, (params) => {\n if (destroyed) return;\n const set = handlers.get(method);\n if (!set) return;\n for (const handler of set) {\n if (isToolResult) {\n handler(parseToolResultParams(params));\n } else {\n handler(params);\n }\n }\n });\n }\n\n // --- Step 6: Pre-register handlers from options.on, then send initialized ---\n if (options.on) {\n for (const [event, handler] of Object.entries(options.on)) {\n if (typeof handler === \"function\") {\n const method = resolveEventMethod(event);\n if (!handlers.has(method)) handlers.set(method, new Set());\n handlers.get(method)?.add(handler);\n ensureTransportSub(method);\n }\n }\n }\n transport.send(INITIALIZED_METHOD, {});\n\n // --- Step 7: Build and return the App object ---\n const app: App = {\n get theme() {\n return { ...currentTheme };\n },\n get hostInfo() {\n return { ...hostInfo };\n },\n get toolInfo() {\n return toolInfo;\n },\n get containerDimensions() {\n return containerDimensions;\n },\n\n on(event: string, handler: (params: any) => void): () => void {\n const method = resolveEventMethod(event);\n\n if (!handlers.has(method)) {\n handlers.set(method, new Set());\n }\n handlers.get(method)?.add(handler);\n ensureTransportSub(method);\n\n return () => {\n const set = handlers.get(method);\n if (set) {\n set.delete(handler);\n if (set.size === 0) handlers.delete(method);\n }\n };\n },\n\n resize(width?: number, height?: number): void {\n resizer.resize(width, height);\n },\n\n openLink(url: string): void {\n if (destroyed) return;\n const params: McpUiOpenLinkRequest[\"params\"] = { url };\n // Spec: ui/open-link is a request (expects a response), not a notification\n transport\n .request(OPEN_LINK_METHOD, params as unknown as Record<string, unknown>)\n .catch(() => {});\n },\n\n updateModelContext(state: Record<string, unknown>, summary?: string): void {\n if (destroyed) return;\n const params: McpUiUpdateModelContextRequest[\"params\"] = {\n structuredContent: state,\n ...(summary !== undefined && {\n content: [{ type: \"text\", text: summary } satisfies TextContent],\n }),\n };\n transport.send(\"ui/update-model-context\", params as unknown as Record<string, unknown>);\n },\n\n async callTool(toolName: string, args?: Record<string, unknown>): Promise<ToolCallResult> {\n const params: CallToolRequest[\"params\"] = {\n name: toolName,\n arguments: args ?? {},\n };\n const raw = await transport.request(\n \"tools/call\",\n params as unknown as Record<string, unknown>,\n );\n return parseToolResult(raw);\n },\n\n async readServerResource(params: ReadResourceRequest[\"params\"]): Promise<ReadResourceResult> {\n const raw = await transport.request(\n READ_RESOURCE_METHOD,\n params as unknown as Record<string, unknown>,\n );\n return raw as ReadResourceResult;\n },\n\n sendMessage(text: string, context?: { action?: string; entity?: string }): void {\n if (destroyed) return;\n const textBlock: TextContent = {\n type: \"text\",\n text,\n ...(context && { _meta: { context } }),\n };\n const params: McpUiMessageRequest[\"params\"] = {\n role: \"user\",\n content: [textBlock],\n };\n transport.send(MESSAGE_METHOD, params as unknown as Record<string, unknown>);\n },\n\n destroy(): void {\n if (destroyed) return;\n destroyed = true;\n resizer.destroy();\n handlers.clear();\n transport.destroy();\n },\n };\n\n return app;\n}\n","import type { SynapseTransport } from \"./transport.js\";\nimport type { KeyForwardConfig } from \"./types.js\";\n\n/**\n * Forward keyboard shortcuts from the iframe document to the host.\n *\n * By default, forwards all Ctrl/Cmd+key combos and Escape.\n * Apps can customize via `forwardKeys` config.\n */\nexport class KeyboardForwarder {\n private listener: (event: KeyboardEvent) => void;\n private destroyed = false;\n\n constructor(transport: SynapseTransport, customKeys?: KeyForwardConfig[]) {\n const config = customKeys ?? null; // null = default behavior\n\n this.listener = (event: KeyboardEvent) => {\n if (this.destroyed) return;\n if (this.shouldForward(event, config)) {\n event.preventDefault();\n transport.send(\"synapse/keydown\", {\n key: event.key,\n ctrlKey: event.ctrlKey,\n metaKey: event.metaKey,\n shiftKey: event.shiftKey,\n altKey: event.altKey,\n });\n }\n };\n\n document.addEventListener(\"keydown\", this.listener);\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n document.removeEventListener(\"keydown\", this.listener);\n }\n\n private shouldForward(event: KeyboardEvent, config: KeyForwardConfig[] | null): boolean {\n // Empty array = forwarding disabled\n if (config && config.length === 0) return false;\n\n // Custom config: match exactly\n if (config) {\n return config.some(\n (k) =>\n event.key.toLowerCase() === k.key.toLowerCase() &&\n (k.ctrl === undefined || event.ctrlKey === k.ctrl) &&\n (k.meta === undefined || event.metaKey === k.meta) &&\n (k.shift === undefined || event.shiftKey === k.shift) &&\n (k.alt === undefined || event.altKey === k.alt),\n );\n }\n\n // Default: forward all Ctrl/Cmd combos + Escape,\n // EXCEPT clipboard shortcuts (c, v, x, a) which the browser must handle natively.\n if (event.key === \"Escape\") return true;\n if (event.ctrlKey || event.metaKey) {\n const key = event.key.toLowerCase();\n if (key === \"c\" || key === \"v\" || key === \"x\" || key === \"a\") return false;\n return true;\n }\n return false;\n }\n}\n","import type {\n CallToolRequest,\n CancelTaskRequest,\n CancelTaskResult,\n CreateTaskResult,\n GetTaskPayloadRequest,\n GetTaskPayloadResult,\n GetTaskRequest,\n GetTaskResult,\n Task,\n TaskStatus,\n TaskStatusNotification,\n TaskStatusNotificationParams,\n} from \"@modelcontextprotocol/sdk/types.js\";\n\nimport { parseToolResult } from \"./result-parser.js\";\nimport type { SynapseTransport } from \"./transport.js\";\nimport type { CallToolAsTaskOptions, TaskHandle, ToolCallResult } from \"./types.js\";\n\n// -----------------------------------------------------------------------------\n// Spec method constants\n// -----------------------------------------------------------------------------\n//\n// The MCP SDK publishes task-method strings only inside Zod `z.literal(...)`s,\n// not as top-level `*_METHOD` constants. Derive each from its request's\n// `method` type so an upstream rename surfaces here as a compile error\n// (same pattern as `READ_RESOURCE_METHOD` in core.ts / connect.ts).\n//\n// When adding a new method here, also mirror it in `src/_shims/ext-apps.ts`\n// per the IIFE build instructions in CLAUDE.md — the shim must export the\n// same string constants any source file consumes.\n\nexport const TOOLS_CALL_METHOD: CallToolRequest[\"method\"] = \"tools/call\";\nexport const TASKS_GET_METHOD: GetTaskRequest[\"method\"] = \"tasks/get\";\nexport const TASKS_RESULT_METHOD: GetTaskPayloadRequest[\"method\"] = \"tasks/result\";\nexport const TASKS_CANCEL_METHOD: CancelTaskRequest[\"method\"] = \"tasks/cancel\";\nexport const TASKS_STATUS_NOTIFICATION_METHOD: TaskStatusNotification[\"method\"] =\n \"notifications/tasks/status\";\n\n// -----------------------------------------------------------------------------\n// Status router\n// -----------------------------------------------------------------------------\n\n/**\n * Notification fields the spec guarantees on `notifications/tasks/status`.\n * Deliberately narrower than `Task` — the wire notification omits\n * `createdAt`, `lastUpdatedAt`, `ttl`, `pollInterval`. Consumers receive\n * a merged full `Task` via `TaskHandle.onStatus`; the handle is the only\n * party with access to the initial `CreateTaskResult.task` needed to fill\n * the missing fields.\n */\nexport interface TaskStatusUpdate {\n taskId: string;\n status: TaskStatus;\n statusMessage?: string;\n}\n\n/**\n * Per-taskId callback registry. One registry is shared across all\n * `TaskHandle` instances on a given transport so the transport-level\n * `notifications/tasks/status` handler registers exactly once — multiple\n * handles route in-memory by taskId, not by wire subscription.\n */\nexport interface TaskStatusRouter {\n subscribe(taskId: string, cb: (update: TaskStatusUpdate) => void): () => void;\n dispose(): void;\n}\n\nexport function createTaskStatusRouter(transport: SynapseTransport): TaskStatusRouter {\n const listeners = new Map<string, Set<(update: TaskStatusUpdate) => void>>();\n\n // Single transport-level subscription. All per-handle listeners filter\n // in-memory by taskId off this one wire handler.\n const unsub = transport.onMessage(TASKS_STATUS_NOTIFICATION_METHOD, (rawParams) => {\n if (!rawParams) return;\n const params = rawParams as unknown as TaskStatusNotificationParams;\n const taskId = params.taskId;\n if (typeof taskId !== \"string\") return;\n\n const set = listeners.get(taskId);\n if (!set || set.size === 0) return;\n\n // Forward the spec-guaranteed fields verbatim. We deliberately don't\n // fabricate `createdAt`/`lastUpdatedAt`/`ttl` here — the handle merges\n // these from its initial `CreateTaskResult.task` so consumers always\n // see a real Task with no placeholder strings.\n const update: TaskStatusUpdate = {\n taskId: params.taskId,\n status: params.status,\n ...(params.statusMessage !== undefined && { statusMessage: params.statusMessage }),\n };\n\n for (const cb of set) cb(update);\n });\n\n return {\n subscribe(taskId, cb) {\n let set = listeners.get(taskId);\n if (!set) {\n set = new Set();\n listeners.set(taskId, set);\n }\n set.add(cb);\n return () => {\n const s = listeners.get(taskId);\n if (!s) return;\n s.delete(cb);\n if (s.size === 0) listeners.delete(taskId);\n };\n },\n dispose() {\n listeners.clear();\n unsub();\n },\n };\n}\n\n// -----------------------------------------------------------------------------\n// Caller-facing factory\n// -----------------------------------------------------------------------------\n\n/**\n * Dependencies injected into `callToolAsTask`. The call site (in\n * `core.ts`) provides the transport, the shared status router, a factory\n * for the `tools/call` params (so `internal` / `name` handling stays in\n * one place), and the current `_hostTasksCapability` value.\n */\nexport interface CallToolAsTaskDeps {\n transport: SynapseTransport;\n router: TaskStatusRouter;\n /**\n * `null` before the `ui/initialize` handshake resolves, `undefined`\n * afterward if the host did not advertise `tasks`, or the captured\n * capability shape if it did. `callToolAsTask` throws when the nested\n * `requests.tools.call` is not present.\n */\n getHostTasksCapability: () =>\n | { requests?: { tools?: { call?: Record<string, never> } } }\n | undefined\n | null;\n /** App name for `params.server` when `internal: true`. */\n appName: string;\n /** Whether the enclosing Synapse is running in internal-apps mode. */\n internalApp: boolean;\n}\n\nexport async function callToolAsTask<TOutput = unknown>(\n deps: CallToolAsTaskDeps,\n toolName: string,\n args?: unknown,\n options?: CallToolAsTaskOptions,\n): Promise<TaskHandle<TOutput>> {\n const hostTasks = deps.getHostTasksCapability();\n if (!hostTasks?.requests?.tools?.call) {\n throw new Error(\n \"callToolAsTask: host did not advertise tasks.requests.tools.call in its capabilities. \" +\n \"Per MCP 2025-11-25 §, requestors MUST NOT task-augment a tools/call without \" +\n \"matching receiver capability. Fall back to `synapse.callTool`.\",\n );\n }\n\n // Per spec, `task` is an object even when the caller provides no hints —\n // its presence is the signal to the receiver that augmentation is\n // requested. An empty `{}` is valid.\n const taskParam: { ttl?: number } = {};\n if (options?.ttl !== undefined) taskParam.ttl = options.ttl;\n\n // `internal` resolution:\n // - explicit `options.internal === true` → cross-server, add `server`\n // - explicit `options.internal === false` → never add `server`\n // - omitted → inherit from app-level\n // `internal` flag (same\n // semantics as `callTool`)\n const crossServer = options?.internal ?? deps.internalApp;\n\n // Build `tools/call` params. We layer our own shape on the SDK's\n // `CallToolRequest[\"params\"]` via `satisfies` so any rename upstream\n // (`name` → `toolName`, `arguments` → `args`, etc.) trips tsc. The\n // `server` extension is a NimbleBrain bridge convention, not an MCP\n // spec field.\n const callParams = {\n name: toolName,\n arguments: (args as Record<string, unknown> | undefined) ?? {},\n task: taskParam,\n ...(crossServer ? { server: deps.appName } : {}),\n } satisfies CallToolRequest[\"params\"] & { server?: string };\n\n const raw = await deps.transport.request(\n TOOLS_CALL_METHOD,\n callParams as unknown as Record<string, unknown>,\n );\n\n // Spec: task-augmented `tools/call` returns a `CreateTaskResult`. If a\n // receiver that advertised the capability returns a bare\n // `CallToolResult` instead, that's a protocol violation — surface it.\n const createResult = raw as CreateTaskResult | null | undefined;\n const initialTask = createResult?.task;\n if (!initialTask || typeof initialTask !== \"object\" || typeof initialTask.taskId !== \"string\") {\n throw new Error(\n \"callToolAsTask: receiver returned a response without `task` per CreateTaskResult \" +\n \"(expected shape: `{ task: { taskId, status, ... } }`). Receiver may not honor \" +\n \"the advertised tasks capability.\",\n );\n }\n\n const taskId = initialTask.taskId;\n\n // Preserve the Set-semantic dedup contract on `onStatus` (matches\n // `SynapseTransport.onMessage`): registering the same callback twice\n // collapses to one wire subscription, and either returned unsub\n // releases it. Without this, every `onStatus(cb)` would create a\n // fresh wrapper that the router treats as distinct.\n const localCallbacks = new Map<(task: Task) => void, () => void>();\n\n const handle: TaskHandle<TOutput> = {\n task: initialTask,\n\n async result(): Promise<ToolCallResult<TOutput>> {\n const params = { taskId } satisfies GetTaskPayloadRequest[\"params\"];\n const rawResult = await deps.transport.request(\n TASKS_RESULT_METHOD,\n params as unknown as Record<string, unknown>,\n );\n // Per spec §: `tasks/result` returns exactly what the non-task\n // response would return. Parse through the shared tool-result\n // parser so `_meta[\"io.modelcontextprotocol/related-task\"]`\n // propagates unchanged (key-preserving spread).\n //\n // Type note: `GetTaskPayloadResult` is a union of result shapes\n // (one per augmentable request type). For `tools/call` tasks,\n // the runtime shape is `CallToolResult`, which `parseToolResult`\n // already handles.\n const typed = rawResult as GetTaskPayloadResult;\n return parseToolResult(typed) as ToolCallResult<TOutput>;\n },\n\n async refresh(): Promise<Task> {\n const params = { taskId } satisfies GetTaskRequest[\"params\"];\n const raw = await deps.transport.request(\n TASKS_GET_METHOD,\n params as unknown as Record<string, unknown>,\n );\n // Per spec, `tasks/get` returns the task shape FLAT (taskId, status,\n // ttl, createdAt, lastUpdatedAt, ... at the top level) — NOT\n // wrapped in a `{ task }` field (unlike `CreateTaskResult`).\n return projectTask(raw as GetTaskResult);\n },\n\n async cancel(): Promise<Task> {\n const params = { taskId } satisfies CancelTaskRequest[\"params\"];\n const raw = await deps.transport.request(\n TASKS_CANCEL_METHOD,\n params as unknown as Record<string, unknown>,\n );\n // `CancelTaskResult` is flat like `GetTaskResult`.\n return projectTask(raw as CancelTaskResult);\n },\n\n onStatus(cb) {\n // Idempotent-in-callback: if the same `cb` is already subscribed,\n // return the existing unsub.\n const existing = localCallbacks.get(cb);\n if (existing) return existing;\n\n // Merge the notification's spec-guaranteed fields (taskId, status,\n // statusMessage) with the handle's initial `CreateTaskResult.task`\n // so the consumer always sees a full valid `Task`. `lastUpdatedAt`\n // is stamped at SDK-receive time (an approximation of the host's\n // update time — the spec doesn't wire the host's timestamp through\n // the notification, so this is the closest signal we have).\n // `createdAt`, `ttl`, `pollInterval` come from the initial task\n // because they don't change over a task's lifetime.\n const wireUnsub = deps.router.subscribe(taskId, (update) => {\n const merged: Task = {\n taskId: update.taskId,\n status: update.status,\n ttl: initialTask.ttl,\n createdAt: initialTask.createdAt,\n lastUpdatedAt: new Date().toISOString(),\n ...(initialTask.pollInterval !== undefined && {\n pollInterval: initialTask.pollInterval,\n }),\n ...(update.statusMessage !== undefined && { statusMessage: update.statusMessage }),\n };\n cb(merged);\n });\n\n const unsub = () => {\n localCallbacks.delete(cb);\n wireUnsub();\n };\n localCallbacks.set(cb, unsub);\n return unsub;\n },\n };\n\n return handle;\n}\n\n// -----------------------------------------------------------------------------\n// Internal helpers\n// -----------------------------------------------------------------------------\n\n/**\n * Project a `GetTaskResult` / `CancelTaskResult` down to the canonical\n * `Task` shape. Both result types are structurally `Task` with an\n * optional `_meta` field on top; we project explicitly so consumers\n * don't see protocol-level `_meta` leaking onto the task state they\n * get back from `refresh()` / `cancel()`.\n *\n * Asymmetry note: `parseToolResult` (for `tools/call` / `tasks/result`\n * payloads) is key-preserving — `_meta` flows through end-to-end, which\n * matters for `io.modelcontextprotocol/related-task` correlation. Here\n * we drop `_meta` because no downstream consumer reads it on `Task` state\n * today. If a host starts stamping useful keys on `tasks/get`/`tasks/cancel`\n * responses, switch to a key-preserving projection.\n */\nfunction projectTask(raw: GetTaskResult | CancelTaskResult): Task {\n return {\n taskId: raw.taskId,\n status: raw.status,\n ttl: raw.ttl,\n createdAt: raw.createdAt,\n lastUpdatedAt: raw.lastUpdatedAt,\n ...(raw.pollInterval !== undefined && { pollInterval: raw.pollInterval }),\n ...(raw.statusMessage !== undefined && { statusMessage: raw.statusMessage }),\n };\n}\n","import type {\n McpUiHostContext,\n McpUiInitializeRequest,\n McpUiInitializeResult,\n McpUiMessageRequest,\n McpUiOpenLinkRequest,\n McpUiUpdateModelContextRequest,\n} from \"@modelcontextprotocol/ext-apps\";\nimport {\n HOST_CONTEXT_CHANGED_METHOD,\n INITIALIZE_METHOD,\n INITIALIZED_METHOD,\n LATEST_PROTOCOL_VERSION,\n MESSAGE_METHOD,\n OPEN_LINK_METHOD,\n} from \"@modelcontextprotocol/ext-apps\";\nimport type {\n ReadResourceRequest,\n ReadResourceResult,\n TextContent,\n} from \"@modelcontextprotocol/sdk/types.js\";\n\nimport { detectHost, extractTheme, foldFontFaces } from \"./detection.js\";\nimport { KeyboardForwarder } from \"./keyboard.js\";\nimport { parseToolResult } from \"./result-parser.js\";\nimport { callToolAsTask as callToolAsTaskImpl, createTaskStatusRouter } from \"./task-handle.js\";\nimport { applyTheme, fontFacesKey } from \"./theme-defaults.js\";\nimport { SynapseTransport } from \"./transport.js\";\nimport type {\n AgentAction,\n CallToolAsTaskOptions,\n DataChangedEvent,\n FileResult,\n FontFaceDescriptor,\n HostInfo,\n RequestFileOptions,\n Synapse,\n SynapseOptions,\n SynapseTheme,\n TaskHandle,\n TasksCapability,\n ToolCallResult,\n} from \"./types.js\";\n\n// `@modelcontextprotocol/ext-apps` only exports METHOD constants for ext-apps\n// specific ui/* methods, not standard MCP methods. Deriving the constant\n// locally with the spec request's `method` type still fails compilation if\n// upstream renames it.\nconst READ_RESOURCE_METHOD: ReadResourceRequest[\"method\"] = \"resources/read\";\n\n/**\n * Create a Synapse instance.\n *\n * Wraps the ext-apps protocol handshake via `SynapseTransport` and provides\n * a typed, framework-agnostic API for calling tools, reacting to data changes,\n * dispatching actions, and pushing LLM-visible state.\n *\n * In non-NimbleBrain hosts, NB-specific methods degrade to no-ops.\n */\nexport function createSynapse(options: SynapseOptions): Synapse {\n const { name, version, internal = false, forwardKeys } = options;\n\n const transport = new SynapseTransport();\n let hostInfo: HostInfo | null = null;\n // Single source of truth for ext-apps host context. `theme`, `styles`,\n // `displayMode`, `toolInfo` are spec-standardized fields; the open\n // `[key: string]: unknown` lets hosts publish extensions (e.g. NimbleBrain\n // populates a `workspace` field). `getTheme()` and any other typed view\n // is derived from this object — no parallel state.\n let currentHostContext: McpUiHostContext = {};\n // Font faces are the one derived value that must NOT be recomputed from a\n // replaced context. A `host-context-changed` carries only the fields that\n // changed (per the ext-apps `McpUiHostContextChangedNotification` params\n // doc), so a bare `{ theme: \"dark\" }` toggle would otherwise re-derive\n // \"no fonts\" and unload the host's typeface mid-session — far more visible\n // than the equivalent for a colour token. Sticky here, cleared only by an\n // explicit empty list from the host.\n let currentFontFaces: FontFaceDescriptor[] | undefined;\n\n /** Fold a (possibly partial) context's faces into the sticky set. */\n function syncFontFaces(ctx: McpUiHostContext): FontFaceDescriptor[] | undefined {\n currentFontFaces = foldFontFaces(currentFontFaces, ctx);\n return currentFontFaces;\n }\n\n /**\n * The theme as reported to consumers: derived from the context, with the\n * sticky faces folded back in. Every public view goes through here —\n * `getTheme()`, the `onThemeChanged` payload, and its equality filter — so\n * they cannot disagree with each other or with what is actually loaded.\n */\n function resolveTheme(ctx: McpUiHostContext): SynapseTheme {\n const theme = extractTheme(ctx);\n return currentFontFaces ? { ...theme, fontFaces: currentFontFaces } : theme;\n }\n // Module-private store for the host's declared `tasks` capability. Kept\n // in the closure (not on Synapse) so `callToolAsTask` reads it without\n // expanding the public surface. `null` before the handshake completes;\n // `undefined` after a handshake where the host did not advertise `tasks`.\n let hostTasksCapability: TasksCapability | undefined | null = null;\n let destroyed = false;\n\n // Shared router for `notifications/tasks/status`. Created eagerly so\n // the transport-level listener registers exactly once — every\n // `TaskHandle.onStatus` subscriber filters off this single wire\n // subscription by taskId. Disposed in `destroy()`.\n const taskStatusRouter = createTaskStatusRouter(transport);\n\n // --- Debounce for setVisibleState ---\n let stateTimer: ReturnType<typeof setTimeout> | null = null;\n\n // --- Keyboard forwarding ---\n let keyboard: KeyboardForwarder | null = null;\n\n // --- ext-apps handshake ---\n //\n // `appCapabilities` is typed as `McpUiAppCapabilities` by the ext-apps\n // spec package, which does not yet model the MCP 2025-11-25 tasks\n // utility. We extend structurally via `TasksCapability` (re-exported\n // from the SDK's `ClientCapabilities.tasks` / `ServerTasksCapability`\n // shape) and `satisfies` the extension so the nested objects match the\n // spec literally — empty objects `{}` as presence flags, NOT booleans.\n const appCapabilities = {\n tasks: {\n cancel: {},\n requests: { tools: { call: {} } },\n } satisfies TasksCapability,\n };\n const initParams: McpUiInitializeRequest[\"params\"] = {\n protocolVersion: LATEST_PROTOCOL_VERSION,\n appInfo: { name, version },\n appCapabilities:\n appCapabilities as unknown as McpUiInitializeRequest[\"params\"][\"appCapabilities\"],\n };\n\n const ready = transport\n .request(INITIALIZE_METHOD, initParams as unknown as Record<string, unknown>)\n .then((result) => {\n hostInfo = detectHost(result);\n currentHostContext = ((result as McpUiInitializeResult | null)?.hostContext ??\n {}) as McpUiHostContext;\n\n // Capture the host's declared `tasks` capability from the init\n // response. The ext-apps `McpUiHostCapabilities` type lacks a\n // `tasks` field (the SDK extension post-dates it), so we read via\n // the result's index signature. `undefined` when absent.\n const initResult = result as McpUiInitializeResult | null;\n const rawTasks = (initResult?.hostCapabilities as Record<string, unknown> | undefined)?.tasks;\n hostTasksCapability =\n rawTasks && typeof rawTasks === \"object\" && !Array.isArray(rawTasks)\n ? (rawTasks as TasksCapability)\n : undefined;\n\n // Inject the theme into :root so plain-CSS styles consume it via\n // `var(--…)` without reading theme.tokens imperatively. The host's values\n // go inline; the neutral defaults go in a cascade layer, so they back any\n // var nobody declares and lose to every var that is declared.\n {\n const theme = extractTheme(currentHostContext);\n applyTheme(theme.mode, theme.tokens, syncFontFaces(currentHostContext));\n }\n\n // Notify subscribers so React hooks (useTheme, useHostContext) and\n // custom listeners reflect the handshake-provided context (not just\n // subsequent host-context-changed notifications).\n for (const cb of hostContextCallbacks) cb(currentHostContext);\n\n transport.send(INITIALIZED_METHOD, {});\n\n keyboard = new KeyboardForwarder(transport, forwardKeys);\n });\n\n // Listen for host context changes (ext-apps spec). The notification's params\n // are a PARTIAL update — only the fields that changed — so `currentHostContext`\n // being replaced wholesale here means any field the host omits is re-derived as\n // absent. That is long-standing behaviour for the spec fields (a subscriber\n // reads the notification as sent); fonts opt out via `syncFontFaces` because\n // unloading a typeface on an unrelated toggle is a visible break, not a\n // no-op. Making the whole context merge is a wider change than this one\n // concerns: it also wipes the host's tokens, and fixing it changes what every\n // subscriber observes. Tracked in #46.\n const unsubHostContext = transport.onMessage(HOST_CONTEXT_CHANGED_METHOD, (params) => {\n currentHostContext = (params ?? {}) as McpUiHostContext;\n const theme = extractTheme(currentHostContext);\n applyTheme(theme.mode, theme.tokens, syncFontFaces(currentHostContext));\n for (const cb of hostContextCallbacks) cb(currentHostContext);\n });\n\n const hostContextCallbacks = new Set<(ctx: McpUiHostContext) => void>();\n const dataCallbacks = new Set<(event: DataChangedEvent) => void>();\n const actionCallbacks = new Set<(action: AgentAction) => void>();\n\n // Listen for data change events\n const unsubData = transport.onMessage(\"synapse/data-changed\", (params) => {\n if (!params) return;\n const event: DataChangedEvent = {\n source: \"agent\",\n server: (params.server as string) ?? \"\",\n tool: (params.tool as string) ?? \"\",\n };\n for (const cb of dataCallbacks) cb(event);\n });\n\n // Listen for agent actions (typed, declarative commands from the server)\n const unsubAction = transport.onMessage(\"synapse/action\", (params) => {\n if (!params || typeof params.type !== \"string\") return;\n const action: AgentAction = {\n type: params.type as string,\n payload: (params.payload as Record<string, unknown>) ?? {},\n requiresConfirmation: params.requiresConfirmation === true,\n label: typeof params.label === \"string\" ? params.label : undefined,\n };\n for (const cb of actionCallbacks) cb(action);\n });\n\n const isNB = () => hostInfo?.isNimbleBrain === true;\n\n const synapse: Synapse = {\n get ready() {\n return ready;\n },\n\n get isNimbleBrainHost() {\n return isNB();\n },\n\n get destroyed() {\n return destroyed;\n },\n\n async callTool<TInput = Record<string, unknown>, TOutput = unknown>(\n toolName: string,\n args?: TInput,\n ): Promise<ToolCallResult<TOutput>> {\n const params: Record<string, unknown> = {\n name: toolName,\n arguments: args ?? {},\n };\n // Internal apps can cross-call\n if (internal) {\n params.server = name;\n }\n const raw = await transport.request(\"tools/call\", params);\n return parseToolResult(raw) as ToolCallResult<TOutput>;\n },\n\n async callToolAsTask<TInput = Record<string, unknown>, TOutput = unknown>(\n toolName: string,\n args?: TInput,\n options?: CallToolAsTaskOptions,\n ): Promise<TaskHandle<TOutput>> {\n return callToolAsTaskImpl<TOutput>(\n {\n transport,\n router: taskStatusRouter,\n getHostTasksCapability: () => hostTasksCapability,\n appName: name,\n internalApp: internal,\n },\n toolName,\n args,\n options,\n );\n },\n\n async readResource(uri: string): Promise<ReadResourceResult> {\n const params: ReadResourceRequest[\"params\"] = { uri };\n const raw = await transport.request(\n READ_RESOURCE_METHOD,\n params as unknown as Record<string, unknown>,\n );\n return raw as ReadResourceResult;\n },\n\n onDataChanged(callback: (event: DataChangedEvent) => void): () => void {\n dataCallbacks.add(callback);\n return () => {\n dataCallbacks.delete(callback);\n };\n },\n\n onAction(callback: (action: AgentAction) => void): () => void {\n actionCallbacks.add(callback);\n return () => {\n actionCallbacks.delete(callback);\n };\n },\n\n getHostContext(): McpUiHostContext {\n return currentHostContext;\n },\n\n onHostContextChanged(callback: (ctx: McpUiHostContext) => void): () => void {\n hostContextCallbacks.add(callback);\n return () => {\n hostContextCallbacks.delete(callback);\n };\n },\n\n getTheme(): SynapseTheme {\n return resolveTheme(currentHostContext);\n },\n\n // Selector over `onHostContextChanged`: only fires when the *derived*\n // theme actually changes, so theme subscribers don't see spurious\n // updates when other host-context fields (e.g. workspace) change.\n //\n // Subscriber timing matters:\n // - Subscribed BEFORE handshake: `prev = null` sentinel. The first\n // fire (the handshake dispatch) always invokes the callback,\n // even if the host's theme happens to derive to the default.\n // Otherwise consumers using `onThemeChanged` as their init\n // signal would silently miss it.\n // - Subscribed AFTER handshake: `prev` is pre-seeded with the\n // current derived theme, so a workspace-only `host-context-changed`\n // notification correctly filters as a no-op.\n onThemeChanged(callback: (theme: SynapseTheme) => void): () => void {\n let prev: SynapseTheme | null = hostInfo !== null ? resolveTheme(currentHostContext) : null;\n const wrapped = (ctx: McpUiHostContext) => {\n const next = resolveTheme(ctx);\n if (prev !== null && themesEqual(prev, next)) return;\n prev = next;\n callback(next);\n };\n hostContextCallbacks.add(wrapped);\n return () => {\n hostContextCallbacks.delete(wrapped);\n };\n },\n\n action(action: string, params?: Record<string, unknown>): void {\n if (!isNB()) return;\n transport.send(\"synapse/action\", { action, ...params });\n },\n\n chat(message: string, context?: { action?: string; entity?: string }): void {\n const textBlock: TextContent = {\n type: \"text\",\n text: message,\n ...(isNB() && context && { _meta: { context } }),\n };\n const params: McpUiMessageRequest[\"params\"] = {\n role: \"user\",\n content: [textBlock],\n };\n transport.send(MESSAGE_METHOD, params as unknown as Record<string, unknown>);\n },\n\n setVisibleState(state: Record<string, unknown>, summary?: string): void {\n // Debounce: 250ms\n if (stateTimer) clearTimeout(stateTimer);\n stateTimer = setTimeout(() => {\n const params: McpUiUpdateModelContextRequest[\"params\"] = {\n structuredContent: state,\n ...(summary !== undefined && {\n content: [{ type: \"text\", text: summary } satisfies TextContent],\n }),\n };\n transport.send(\"ui/update-model-context\", params as unknown as Record<string, unknown>);\n stateTimer = null;\n }, 250);\n },\n\n downloadFile(filename: string, content: string | Blob, mimeType?: string): void {\n // Precedence: explicit mimeType arg > Blob's intrinsic type > octet-stream fallback.\n // Empty-string mimeType falls through (a \"\" MIME is effectively \"no type\").\n const resolvedMime =\n mimeType || (content instanceof Blob ? content.type : \"\") || \"application/octet-stream\";\n const blob = content instanceof Blob ? content : new Blob([content], { type: resolvedMime });\n transport.send(\"synapse/download-file\", {\n data: blob,\n filename,\n mimeType: resolvedMime,\n });\n },\n\n openLink(url: string): void {\n const params: McpUiOpenLinkRequest[\"params\"] = { url };\n // Spec: ui/open-link is a request (expects a response), not a notification\n transport\n .request(OPEN_LINK_METHOD, params as unknown as Record<string, unknown>)\n .catch(() => {\n // Fallback: if host doesn't respond, open directly\n window.open(url, \"_blank\", \"noopener\");\n });\n },\n\n async pickFile(options?: RequestFileOptions): Promise<FileResult | null> {\n if (!isNB()) {\n throw new Error(\"pickFile is not supported in this host\");\n }\n const result = await requestFile(transport, options, false);\n if (result === null) return null;\n // Server may have returned an array even for single-pick — take the\n // first entry to preserve the documented `FileResult | null` shape.\n return Array.isArray(result) ? (result[0] ?? null) : result;\n },\n\n async pickFiles(options?: RequestFileOptions): Promise<FileResult[]> {\n if (!isNB()) {\n throw new Error(\"pickFiles is not supported in this host\");\n }\n const result = await requestFile(transport, options, true);\n if (result === null) return [];\n return Array.isArray(result) ? result : [result];\n },\n\n _onMessage(\n method: string,\n callback: (params: Record<string, unknown> | undefined) => void,\n ): () => void {\n return transport.onMessage(method, callback);\n },\n\n _request(method: string, params?: Record<string, unknown>): Promise<unknown> {\n return transport.request(method, params);\n },\n\n get _hostTasksCapability(): TasksCapability | undefined | null {\n return hostTasksCapability;\n },\n\n destroy(): void {\n if (destroyed) return;\n destroyed = true;\n\n if (stateTimer) clearTimeout(stateTimer);\n keyboard?.destroy();\n unsubHostContext();\n unsubData();\n unsubAction();\n taskStatusRouter.dispose();\n hostContextCallbacks.clear();\n dataCallbacks.clear();\n actionCallbacks.clear();\n transport.destroy();\n },\n };\n\n return synapse;\n}\n\n/**\n * Shared request path for `pickFile` / `pickFiles`. Centralizes:\n *\n * - The wire method name `synapse/request-file` (which is what the\n * NimbleBrain bridge actually handles — earlier SDK versions sent\n * `synapse/pick-file` and the call would silently time out).\n * - Runtime shape validation. Older hosts (pre-`POST /v1/resources`)\n * still respond to `synapse/request-file` but return the legacy\n * `{ base64Data, … }` shape. The TS type promises a string `id`;\n * without this guard, consumers would silently get `undefined.id`\n * and fail downstream in confusing ways. Failing loud at the\n * boundary tells the developer exactly which side is mismatched.\n */\nasync function requestFile(\n transport: SynapseTransport,\n options: RequestFileOptions | undefined,\n multiple: boolean,\n): Promise<FileResult | FileResult[] | null> {\n const result = await transport.request(\"synapse/request-file\", {\n accept: options?.accept,\n maxSize: options?.maxSize ?? 26_214_400,\n multiple,\n });\n if (result == null) return null;\n if (Array.isArray(result)) {\n return result.map(validateFileResult);\n }\n return validateFileResult(result);\n}\n\nfunction validateFileResult(value: unknown): FileResult {\n if (\n typeof value !== \"object\" ||\n value === null ||\n typeof (value as { id?: unknown }).id !== \"string\"\n ) {\n throw new Error(\n \"synapse/request-file returned a result without a string `id`. \" +\n \"The host appears to be on a version older than this SDK targets — \" +\n \"@nimblebrain/synapse 0.8.0+ requires a host with POST /v1/resources \" +\n \"(NimbleBrain ≥ the version that ships PR #93).\",\n );\n }\n return value as FileResult;\n}\n\n/** Shallow equality for `SynapseTheme` — used by `onThemeChanged` to filter\n * host-context changes that don't actually move the theme (e.g. a workspace\n * switch that leaves theme/styles untouched). Cheap; tokens are ~40 entries.\n * Typography counts: a host can swap typeface without touching mode or tokens,\n * and that must still reach subscribers. */\nfunction themesEqual(a: SynapseTheme, b: SynapseTheme): boolean {\n if (a.mode !== b.mode || a.primaryColor !== b.primaryColor) return false;\n if (fontFacesKey(a.fontFaces) !== fontFacesKey(b.fontFaces)) return false;\n const aKeys = Object.keys(a.tokens);\n const bKeys = Object.keys(b.tokens);\n if (aKeys.length !== bKeys.length) return false;\n // Iterating only over `a`'s keys is sufficient: under equal length, any\n // key in `a` missing from `b` reads `b[k] === undefined`, which fails the\n // strict-inequality check. Symmetric difference is covered.\n for (const k of aKeys) {\n if (a.tokens[k] !== b.tokens[k]) return false;\n }\n return true;\n}\n"]}
@@ -0,0 +1,192 @@
1
+ 'use strict';
2
+
3
+ // src/theme-defaults.ts
4
+ var FONT_DISPLAY_VALUES = /* @__PURE__ */ new Set([
5
+ "auto",
6
+ "block",
7
+ "swap",
8
+ "fallback",
9
+ "optional"
10
+ ]);
11
+ var LIGHT = {
12
+ // ── Surfaces ──
13
+ "--color-background-primary": "#ffffff",
14
+ "--color-background-secondary": "#fafafa",
15
+ "--color-background-tertiary": "#f3f4f6",
16
+ // ── Text ──
17
+ "--color-text-primary": "#111827",
18
+ "--color-text-secondary": "#6b7280",
19
+ "--color-text-tertiary": "#9ca3af",
20
+ "--color-text-accent": "#2563eb",
21
+ "--nb-color-accent-foreground": "#ffffff",
22
+ // ── Border / ring ──
23
+ "--color-border-primary": "#e5e7eb",
24
+ "--color-border-secondary": "#d1d5db",
25
+ "--color-ring-primary": "#2563eb",
26
+ // ── Status / brand semantics ──
27
+ "--nb-color-danger": "#dc2626",
28
+ "--nb-color-success": "#059669",
29
+ "--nb-color-warning": "#f59e0b",
30
+ "--nb-color-warm": "#d4620a",
31
+ "--nb-color-warm-light": "#fef5ee",
32
+ "--nb-color-processing": "#7c3aed",
33
+ "--nb-color-processing-light": "#f3eeff",
34
+ "--nb-color-info-light": "#eef4ff"
35
+ };
36
+ var DARK = {
37
+ // ── Surfaces (base → lifted) ──
38
+ "--color-background-primary": "#18181b",
39
+ "--color-background-secondary": "#27272a",
40
+ "--color-background-tertiary": "#2f2f34",
41
+ // ── Text ──
42
+ "--color-text-primary": "#fafafa",
43
+ "--color-text-secondary": "#a1a1aa",
44
+ "--color-text-tertiary": "#71717a",
45
+ "--color-text-accent": "#818cf8",
46
+ "--nb-color-accent-foreground": "#ffffff",
47
+ // ── Border / ring (lighter than surfaces so they remain visible) ──
48
+ "--color-border-primary": "#3f3f46",
49
+ "--color-border-secondary": "#52525b",
50
+ "--color-ring-primary": "#818cf8",
51
+ // ── Status / brand semantics (brightened for contrast on dark) ──
52
+ "--nb-color-danger": "#f87171",
53
+ "--nb-color-success": "#34d399",
54
+ "--nb-color-warning": "#fbbf24",
55
+ "--nb-color-warm": "#fb923c",
56
+ "--nb-color-warm-light": "#3a2a1e",
57
+ "--nb-color-processing": "#a78bfa",
58
+ "--nb-color-processing-light": "#2a2440",
59
+ "--nb-color-info-light": "#1e2a44"
60
+ };
61
+ var DEFAULT_THEME_VARS = {
62
+ light: LIGHT,
63
+ dark: DARK
64
+ };
65
+ var DEFAULTS_LAYER_NAME = "synapse-defaults";
66
+ var DEFAULTS_STYLE_ID = "synapse-theme-defaults";
67
+ var appliedInlineKeys = /* @__PURE__ */ new Set();
68
+ function applyDefaultThemeLayer(mode) {
69
+ const declarations = Object.entries(DEFAULT_THEME_VARS[mode]).map(([k, v]) => ` ${k}: ${v};`).join("\n");
70
+ const css = `@layer ${DEFAULTS_LAYER_NAME} {
71
+ :root {
72
+ ${declarations}
73
+ }
74
+ }`;
75
+ const existing = document.getElementById(DEFAULTS_STYLE_ID);
76
+ if (existing) {
77
+ if (existing.textContent !== css) existing.textContent = css;
78
+ return;
79
+ }
80
+ const el = document.createElement("style");
81
+ el.id = DEFAULTS_STYLE_ID;
82
+ el.textContent = css;
83
+ document.head.prepend(el);
84
+ }
85
+ function applyThemeVariables(mode, hostVars) {
86
+ if (typeof document === "undefined") return;
87
+ applyDefaultThemeLayer(mode);
88
+ const incoming = {};
89
+ if (hostVars && typeof hostVars === "object") {
90
+ for (const [k, v] of Object.entries(hostVars)) {
91
+ if (typeof k === "string" && typeof v === "string") incoming[k] = v;
92
+ }
93
+ }
94
+ const root = document.documentElement.style;
95
+ for (const k of appliedInlineKeys) {
96
+ if (!(k in incoming)) root.removeProperty(k);
97
+ }
98
+ for (const [k, v] of Object.entries(incoming)) root.setProperty(k, v);
99
+ appliedInlineKeys = new Set(Object.keys(incoming));
100
+ }
101
+ var managedFaces = /* @__PURE__ */ new Set();
102
+ var appliedFontKey = "";
103
+ function fontFacesKey(faces) {
104
+ if (!Array.isArray(faces)) return "";
105
+ return JSON.stringify(faces.map((d) => [d.family, d.src, d.weight, d.style, d.display]));
106
+ }
107
+ function normalizeFontFaces(raw) {
108
+ if (!Array.isArray(raw)) return void 0;
109
+ const usable = raw.filter(isFontFaceDescriptor);
110
+ if (raw.length > 0 && usable.length === 0) return void 0;
111
+ return usable;
112
+ }
113
+ function applyThemeFontFaces(faces) {
114
+ const next = normalizeFontFaces(faces);
115
+ if (next === void 0) return;
116
+ if (typeof document === "undefined") return;
117
+ if (!document.fonts || typeof FontFace === "undefined") return;
118
+ const key = fontFacesKey(next);
119
+ if (key === appliedFontKey) return;
120
+ appliedFontKey = key;
121
+ for (const face of managedFaces) {
122
+ try {
123
+ document.fonts.delete(face);
124
+ } catch {
125
+ }
126
+ }
127
+ managedFaces.clear();
128
+ for (const d of next) {
129
+ try {
130
+ const face = new FontFace(d.family, d.src, {
131
+ weight: d.weight,
132
+ style: d.style,
133
+ // Wire data is untyped: ignore an unrecognised `display` rather than let
134
+ // the constructor reject the descriptor and cost the app this face.
135
+ display: d.display && FONT_DISPLAY_VALUES.has(d.display) ? d.display : "swap"
136
+ });
137
+ document.fonts.add(face);
138
+ managedFaces.add(face);
139
+ } catch {
140
+ }
141
+ }
142
+ }
143
+ function isFontFaceDescriptor(value) {
144
+ if (!value || typeof value !== "object") return false;
145
+ const d = value;
146
+ return typeof d.family === "string" && typeof d.src === "string";
147
+ }
148
+ function applyTheme(mode, hostVars, fontFaces) {
149
+ applyThemeVariables(mode, hostVars);
150
+ applyThemeFontFaces(fontFaces);
151
+ }
152
+
153
+ // src/detection.ts
154
+ var FONT_FACES_CONTEXT_KEY = "synapse/fontFaces";
155
+ var DEFAULT_THEME = {
156
+ mode: "light",
157
+ primaryColor: "#6366f1",
158
+ tokens: {}
159
+ };
160
+ function detectHost(initResponse) {
161
+ const resp = initResponse;
162
+ const hostName = resp?.hostInfo?.name ?? "unknown";
163
+ const protocolVersion = resp?.protocolVersion ?? "unknown";
164
+ return {
165
+ isNimbleBrain: hostName === "nimblebrain",
166
+ serverName: hostName,
167
+ protocolVersion
168
+ };
169
+ }
170
+ function extractTheme(ctx) {
171
+ if (!ctx) return { ...DEFAULT_THEME };
172
+ const mode = ctx.theme === "light" || ctx.theme === "dark" ? ctx.theme : DEFAULT_THEME.mode;
173
+ const variables = ctx.styles?.variables;
174
+ const tokens = variables && typeof variables === "object" && !Array.isArray(variables) ? variables : {};
175
+ return { mode, primaryColor: DEFAULT_THEME.primaryColor, tokens };
176
+ }
177
+ function extractFontFaces(ctx) {
178
+ return normalizeFontFaces(ctx?.[FONT_FACES_CONTEXT_KEY]);
179
+ }
180
+ function foldFontFaces(prev, ctx) {
181
+ return extractFontFaces(ctx) ?? prev;
182
+ }
183
+
184
+ exports.FONT_FACES_CONTEXT_KEY = FONT_FACES_CONTEXT_KEY;
185
+ exports.applyTheme = applyTheme;
186
+ exports.detectHost = detectHost;
187
+ exports.extractFontFaces = extractFontFaces;
188
+ exports.extractTheme = extractTheme;
189
+ exports.foldFontFaces = foldFontFaces;
190
+ exports.fontFacesKey = fontFacesKey;
191
+ //# sourceMappingURL=chunk-B7WGYPYP.cjs.map
192
+ //# sourceMappingURL=chunk-B7WGYPYP.cjs.map