@nimblebrain/synapse 0.19.0 → 0.20.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 (100) hide show
  1. package/README.md +20 -25
  2. package/dist/check-JEM5PN64.cjs +435 -0
  3. package/dist/check-JEM5PN64.cjs.map +1 -0
  4. package/dist/check-YDZUQT6A.js +429 -0
  5. package/dist/check-YDZUQT6A.js.map +1 -0
  6. package/dist/{chunk-RU64F5RS.cjs → chunk-BNF352SA.cjs} +57 -146
  7. package/dist/chunk-BNF352SA.cjs.map +1 -0
  8. package/dist/{chunk-DRLVEHCD.js → chunk-DJS2FKBN.js} +21 -79
  9. package/dist/chunk-DJS2FKBN.js.map +1 -0
  10. package/dist/chunk-H3INUKA3.js +32 -0
  11. package/dist/chunk-H3INUKA3.js.map +1 -0
  12. package/dist/chunk-HQLSVUOR.cjs +667 -0
  13. package/dist/chunk-HQLSVUOR.cjs.map +1 -0
  14. package/dist/{chunk-NP7ROSH2.cjs → chunk-ISV6HFQE.cjs} +14 -7
  15. package/dist/chunk-ISV6HFQE.cjs.map +1 -0
  16. package/dist/chunk-TR2YNWZ5.js +658 -0
  17. package/dist/chunk-TR2YNWZ5.js.map +1 -0
  18. package/dist/{chunk-OYLZ5OCK.js → chunk-UKP4W6DI.js} +57 -146
  19. package/dist/chunk-UKP4W6DI.js.map +1 -0
  20. package/dist/{chunk-SZWHFTXV.cjs → chunk-YSL7KJNU.cjs} +21 -81
  21. package/dist/chunk-YSL7KJNU.cjs.map +1 -0
  22. package/dist/codegen/cli.cjs +41 -4
  23. package/dist/codegen/cli.cjs.map +1 -1
  24. package/dist/codegen/cli.js +41 -4
  25. package/dist/codegen/cli.js.map +1 -1
  26. package/dist/codegen/index.cjs +0 -1
  27. package/dist/codegen/index.d.cts +1 -1
  28. package/dist/codegen/index.d.ts +1 -1
  29. package/dist/codegen/index.js +0 -1
  30. package/dist/connect.iife.global.js +34 -34
  31. package/dist/{detect-DZMJsyvZ.d.cts → detect-DVGaL2bH.d.cts} +39 -33
  32. package/dist/{detect-DBwc8a21.d.ts → detect-DVGaL2bH.d.ts} +39 -33
  33. package/dist/host/index.cjs +17 -18
  34. package/dist/host/index.d.cts +11 -27
  35. package/dist/host/index.d.ts +11 -27
  36. package/dist/host/index.js +2 -3
  37. package/dist/index.cjs +30 -19
  38. package/dist/index.cjs.map +1 -1
  39. package/dist/index.d.cts +72 -26
  40. package/dist/index.d.ts +72 -26
  41. package/dist/index.js +8 -9
  42. package/dist/index.js.map +1 -1
  43. package/dist/react/index.cjs +9 -10
  44. package/dist/react/index.cjs.map +1 -1
  45. package/dist/react/index.d.cts +36 -8
  46. package/dist/react/index.d.ts +36 -8
  47. package/dist/react/index.js +3 -4
  48. package/dist/react/index.js.map +1 -1
  49. package/dist/schema-reader-776246MJ.js +3 -0
  50. package/dist/{schema-reader-GM4A4L44.js.map → schema-reader-776246MJ.js.map} +1 -1
  51. package/dist/{schema-reader-MWBRQ2TQ.cjs → schema-reader-B6LTGBMJ.cjs} +2 -3
  52. package/dist/{schema-reader-MWBRQ2TQ.cjs.map → schema-reader-B6LTGBMJ.cjs.map} +1 -1
  53. package/dist/{server-LR6T6C44.cjs → server-BR5MNFOB.cjs} +13 -14
  54. package/dist/server-BR5MNFOB.cjs.map +1 -0
  55. package/dist/{server-UHPEE55X.js → server-OOEEWAEG.js} +13 -14
  56. package/dist/server-OOEEWAEG.js.map +1 -0
  57. package/dist/synapse-runtime.iife.global.js +34 -34
  58. package/dist/synapse-ui.iife.global.js +4 -4
  59. package/dist/type-generator-MZ4X4DE5.js +3 -0
  60. package/dist/{type-generator-ZYSVFK3X.js.map → type-generator-MZ4X4DE5.js.map} +1 -1
  61. package/dist/{type-generator-YTPFWLVG.cjs → type-generator-UA3L4OIA.cjs} +2 -3
  62. package/dist/{type-generator-YTPFWLVG.cjs.map → type-generator-UA3L4OIA.cjs.map} +1 -1
  63. package/dist/{types-CG08aO82.d.cts → types-BxPfGHKO.d.cts} +35 -43
  64. package/dist/{types-CG08aO82.d.ts → types-BxPfGHKO.d.ts} +35 -43
  65. package/dist/ui/base.cjs +0 -1
  66. package/dist/ui/base.cjs.map +1 -1
  67. package/dist/ui/base.js +0 -1
  68. package/dist/ui/base.js.map +1 -1
  69. package/dist/ui/index.cjs +0 -1
  70. package/dist/ui/index.cjs.map +1 -1
  71. package/dist/ui/index.js +0 -1
  72. package/dist/ui/index.js.map +1 -1
  73. package/dist/vite/index.cjs +10 -10
  74. package/dist/vite/index.cjs.map +1 -1
  75. package/dist/vite/index.js +6 -6
  76. package/dist/vite/index.js.map +1 -1
  77. package/dist/{writer-HDRCQSX2.cjs → writer-NDK4U6C5.cjs} +3 -3
  78. package/dist/{writer-HDRCQSX2.cjs.map → writer-NDK4U6C5.cjs.map} +1 -1
  79. package/dist/{writer-QNDPMEYD.js → writer-ZNBXYUYC.js} +3 -3
  80. package/dist/{writer-QNDPMEYD.js.map → writer-ZNBXYUYC.js.map} +1 -1
  81. package/package.json +6 -3
  82. package/dist/chunk-54W6IAHM.js +0 -24
  83. package/dist/chunk-54W6IAHM.js.map +0 -1
  84. package/dist/chunk-DRLVEHCD.js.map +0 -1
  85. package/dist/chunk-NP7ROSH2.cjs.map +0 -1
  86. package/dist/chunk-OYLZ5OCK.js.map +0 -1
  87. package/dist/chunk-P5DBJFSQ.js +0 -8459
  88. package/dist/chunk-P5DBJFSQ.js.map +0 -1
  89. package/dist/chunk-PZ5AY32C.js +0 -9
  90. package/dist/chunk-PZ5AY32C.js.map +0 -1
  91. package/dist/chunk-Q7SFCCGT.cjs +0 -11
  92. package/dist/chunk-Q7SFCCGT.cjs.map +0 -1
  93. package/dist/chunk-QXDCA7QW.cjs +0 -8466
  94. package/dist/chunk-QXDCA7QW.cjs.map +0 -1
  95. package/dist/chunk-RU64F5RS.cjs.map +0 -1
  96. package/dist/chunk-SZWHFTXV.cjs.map +0 -1
  97. package/dist/schema-reader-GM4A4L44.js +0 -4
  98. package/dist/server-LR6T6C44.cjs.map +0 -1
  99. package/dist/server-UHPEE55X.js.map +0 -1
  100. package/dist/type-generator-ZYSVFK3X.js +0 -4
@@ -1,13 +1,22 @@
1
1
  'use strict';
2
2
 
3
- var chunkSZWHFTXV_cjs = require('./chunk-SZWHFTXV.cjs');
3
+ var chunkYSL7KJNU_cjs = require('./chunk-YSL7KJNU.cjs');
4
4
 
5
5
  // src/host/theme.ts
6
6
  function applyHostTheme(theme) {
7
7
  if (typeof document !== "undefined") {
8
8
  document.documentElement.setAttribute("data-theme", theme.mode);
9
9
  }
10
- chunkSZWHFTXV_cjs.applyTheme(theme.mode, theme.tokens, theme.fontFaces);
10
+ chunkYSL7KJNU_cjs.applyTheme(theme.mode, theme.tokens);
11
+ }
12
+ var HOST_FONTS_STYLE_ID = "__mcp-host-fonts";
13
+ function injectHostFonts(css) {
14
+ if (typeof document === "undefined") return;
15
+ if (document.getElementById(HOST_FONTS_STYLE_ID)) return;
16
+ const style = document.createElement("style");
17
+ style.id = HOST_FONTS_STYLE_ID;
18
+ style.textContent = css;
19
+ document.head.appendChild(style);
11
20
  }
12
21
  function preferredMode(win) {
13
22
  try {
@@ -27,13 +36,27 @@ var HostUnsupportedError = class extends Error {
27
36
  this.name = "HostUnsupportedError";
28
37
  }
29
38
  };
39
+ var ToolCallError = class extends Error {
40
+ result;
41
+ constructor(name, result) {
42
+ super(toolErrorText(result) ?? `tool "${name}" returned an error`);
43
+ this.name = "ToolCallError";
44
+ this.result = result;
45
+ }
46
+ };
47
+ function toolErrorText(result) {
48
+ const content = result?.content;
49
+ if (!Array.isArray(content)) return void 0;
50
+ for (const block of content) {
51
+ const b = block;
52
+ if (b?.type === "text" && typeof b.text === "string" && b.text !== "") return b.text;
53
+ }
54
+ return void 0;
55
+ }
56
+ function isToolError(result) {
57
+ return result != null && typeof result === "object" && result.isError === true;
58
+ }
30
59
  var SYNAPSE_DATA_ELEMENT_ID = "synapse-ui-data";
31
- var MCPUI_READY = "ui-lifecycle-iframe-ready";
32
- var MCPUI_RENDER_DATA = "ui-lifecycle-iframe-render-data";
33
- var MCPUI_SIZE_CHANGE = "ui-size-change";
34
- var MCPUI_LINK = "link";
35
- var MCPUI_PROMPT = "prompt";
36
- var OPENAI_SET_GLOBALS = "openai:set_globals";
37
60
  var MCPAPP_PROTOCOL_VERSION = "2026-01-26";
38
61
  var MCPAPP_INITIALIZE = "ui/initialize";
39
62
  var MCPAPP_INITIALIZED = "ui/notifications/initialized";
@@ -45,84 +68,6 @@ var MCPAPP_MESSAGE = "ui/message";
45
68
  var MCPAPP_TEARDOWN = "ui/resource-teardown";
46
69
  var MCP_TOOLS_CALL = "tools/call";
47
70
 
48
- // src/host/adapters/chatgpt.ts
49
- function createChatGPTAdapter(win, _options) {
50
- const openai = () => win.openai;
51
- let currentData = openai()?.toolOutput ?? null;
52
- let currentTheme = {
53
- mode: coerceMode(openai()?.theme, "light"),
54
- tokens: {}
55
- };
56
- const dataCbs = /* @__PURE__ */ new Set();
57
- const themeCbs = /* @__PURE__ */ new Set();
58
- let destroyed = false;
59
- const onSetGlobals = (event) => {
60
- if (destroyed) return;
61
- const globals = event.detail?.globals;
62
- if (!globals) return;
63
- if ("toolOutput" in globals && globals.toolOutput != null) {
64
- currentData = globals.toolOutput;
65
- for (const cb of dataCbs) cb(currentData);
66
- }
67
- if ("theme" in globals && globals.theme != null) {
68
- const mode = coerceMode(globals.theme, currentTheme.mode);
69
- if (mode !== currentTheme.mode) {
70
- currentTheme = { mode, tokens: {} };
71
- for (const cb of themeCbs) cb(currentTheme);
72
- }
73
- }
74
- };
75
- return {
76
- host: "chatgpt",
77
- getData: () => currentData,
78
- onData(cb) {
79
- dataCbs.add(cb);
80
- return () => dataCbs.delete(cb);
81
- },
82
- getTheme: () => currentTheme,
83
- onTheme(cb) {
84
- themeCbs.add(cb);
85
- return () => themeCbs.delete(cb);
86
- },
87
- async callTool(name, args) {
88
- const call = openai()?.callTool;
89
- if (!call) throw new HostUnsupportedError("callTool", "chatgpt");
90
- return await call(name, args ?? {});
91
- },
92
- sendPrompt(text) {
93
- const o = openai();
94
- const send = o?.sendFollowUpMessage ?? o?.sendFollowupMessage;
95
- send?.({ prompt: text });
96
- },
97
- openLink(url) {
98
- const open = openai()?.openExternal;
99
- if (open) open({ href: url });
100
- else win.open(url, "_blank", "noopener,noreferrer");
101
- },
102
- resize() {
103
- },
104
- capabilities() {
105
- const o = openai();
106
- return {
107
- pull: typeof o?.callTool === "function",
108
- sendPrompt: typeof o?.sendFollowUpMessage === "function" || typeof o?.sendFollowupMessage === "function",
109
- openLink: true
110
- };
111
- },
112
- start() {
113
- win.addEventListener(OPENAI_SET_GLOBALS, onSetGlobals, { passive: true });
114
- currentData = openai()?.toolOutput ?? currentData;
115
- },
116
- destroy() {
117
- if (destroyed) return;
118
- destroyed = true;
119
- win.removeEventListener(OPENAI_SET_GLOBALS, onSetGlobals);
120
- dataCbs.clear();
121
- themeCbs.clear();
122
- }
123
- };
124
- }
125
-
126
71
  // src/host/data.ts
127
72
  function readInlineData(doc, elementId) {
128
73
  if (!doc) return null;
@@ -136,15 +81,6 @@ function readInlineData(doc, elementId) {
136
81
  return null;
137
82
  }
138
83
  }
139
- function unwrapRenderData(payload) {
140
- if (payload == null || typeof payload !== "object") return null;
141
- const rec = payload;
142
- const nested = rec.renderData;
143
- const source = nested != null && typeof nested === "object" ? nested : rec;
144
- if (source.toolOutput != null) return source.toolOutput;
145
- if (source.structuredContent != null) return source.structuredContent;
146
- return source;
147
- }
148
84
 
149
85
  // src/host/adapters/inline.ts
150
86
  function createInlineAdapter(win, options) {
@@ -215,7 +151,7 @@ function createMcpAppsAdapter(win, options) {
215
151
  const dataCbs = /* @__PURE__ */ new Set();
216
152
  const themeCbs = /* @__PURE__ */ new Set();
217
153
  let destroyed = false;
218
- let standardConfirmed = false;
154
+ let initialized = false;
219
155
  let nextId = 1;
220
156
  const pending = /* @__PURE__ */ new Map();
221
157
  let lastReportedHeight = -1;
@@ -225,10 +161,6 @@ function createMcpAppsAdapter(win, options) {
225
161
  function post(message) {
226
162
  parent().postMessage(message, "*");
227
163
  }
228
- function postLegacy(message) {
229
- if (standardConfirmed) return;
230
- post(message);
231
- }
232
164
  function notify(method, params) {
233
165
  post({ jsonrpc: "2.0", method, params: params ?? {} });
234
166
  }
@@ -250,7 +182,9 @@ function createMcpAppsAdapter(win, options) {
250
182
  }
251
183
  function applyHostContext(ctx) {
252
184
  if (!ctx || typeof ctx !== "object") return;
253
- let { mode, tokens, fontFaces } = currentTheme;
185
+ const fontCss = chunkYSL7KJNU_cjs.extractHostFontCss(ctx);
186
+ if (fontCss !== void 0) injectHostFonts(fontCss);
187
+ let { mode, tokens } = currentTheme;
254
188
  let changed = false;
255
189
  if (ctx.theme != null) {
256
190
  const next = coerceMode(ctx.theme, mode);
@@ -264,23 +198,17 @@ function createMcpAppsAdapter(win, options) {
264
198
  tokens = { ...tokens, ...styles.variables };
265
199
  changed = true;
266
200
  }
267
- const nextFaces = chunkSZWHFTXV_cjs.foldFontFaces(fontFaces, ctx);
268
- if (chunkSZWHFTXV_cjs.fontFacesKey(nextFaces) !== chunkSZWHFTXV_cjs.fontFacesKey(fontFaces)) {
269
- fontFaces = nextFaces;
270
- changed = true;
271
- }
272
201
  if (changed) {
273
- currentTheme = { mode, tokens, ...fontFaces ? { fontFaces } : {} };
202
+ currentTheme = { mode, tokens };
274
203
  for (const cb of themeCbs) cb(currentTheme);
275
204
  }
276
205
  }
277
206
  function reportSize(height) {
278
- if (destroyed) return;
207
+ if (destroyed || !initialized) return;
279
208
  const h = typeof height === "number" ? height : Math.ceil(win.document.body.scrollHeight);
280
209
  if (h === lastReportedHeight) return;
281
210
  lastReportedHeight = h;
282
- if (standardConfirmed) notify(MCPAPP_SIZE_CHANGED, { height: h });
283
- postLegacy({ type: MCPUI_SIZE_CHANGE, payload: { height: h } });
211
+ notify(MCPAPP_SIZE_CHANGED, { height: h });
284
212
  }
285
213
  function handleResponse(d) {
286
214
  const id = Number(d.id);
@@ -298,7 +226,7 @@ function createMcpAppsAdapter(win, options) {
298
226
  function handleNotification(method, params) {
299
227
  if (method === MCPAPP_TOOL_RESULT) {
300
228
  const structured = params.structuredContent;
301
- emitData(structured != null ? structured : unwrapRenderData(params));
229
+ emitData(structured != null ? structured : params);
302
230
  } else if (method === MCPAPP_HOST_CONTEXT_CHANGED) {
303
231
  applyHostContext(params);
304
232
  }
@@ -308,22 +236,11 @@ function createMcpAppsAdapter(win, options) {
308
236
  post({ jsonrpc: "2.0", id: d.id, result: {} });
309
237
  }
310
238
  }
311
- function handleLegacy(d) {
312
- if (d.type === MCPUI_RENDER_DATA || d.type === "renderData") {
313
- const { theme, ...rest } = d.payload ?? {};
314
- if (theme != null) applyHostContext({ theme });
315
- if (Object.keys(rest).length > 0) emitData(unwrapRenderData(rest));
316
- }
317
- }
318
239
  const onMessage = (event) => {
319
240
  if (destroyed) return;
320
241
  if (event.source && event.source !== parent()) return;
321
242
  const d = event.data;
322
- if (!d || typeof d !== "object") return;
323
- if (d.jsonrpc !== "2.0") {
324
- handleLegacy(d);
325
- return;
326
- }
243
+ if (!d || typeof d !== "object" || d.jsonrpc !== "2.0") return;
327
244
  if (d.id != null && ("result" in d || "error" in d)) {
328
245
  handleResponse(d);
329
246
  } else if (typeof d.method === "string") {
@@ -340,7 +257,7 @@ function createMcpAppsAdapter(win, options) {
340
257
  }
341
258
  }
342
259
  return {
343
- host: "claude",
260
+ host: "mcp-apps",
344
261
  getData: () => currentData,
345
262
  onData(cb) {
346
263
  dataCbs.add(cb);
@@ -359,12 +276,10 @@ function createMcpAppsAdapter(win, options) {
359
276
  () => {
360
277
  }
361
278
  );
362
- postLegacy({ type: MCPUI_PROMPT, payload: { prompt: text } });
363
279
  },
364
280
  openLink(url) {
365
281
  void request(MCPAPP_OPEN_LINK, { url }).catch(() => {
366
282
  });
367
- postLegacy({ type: MCPUI_LINK, payload: { url } });
368
283
  },
369
284
  resize(height) {
370
285
  reportSize(height);
@@ -376,21 +291,18 @@ function createMcpAppsAdapter(win, options) {
376
291
  win.addEventListener("message", onMessage);
377
292
  currentData = readInlineData(win.document, dataElementId);
378
293
  if (autoResize) setupResize();
379
- post({ type: MCPUI_READY });
380
294
  request(MCPAPP_INITIALIZE, {
381
295
  appInfo: { name: options.name ?? "synapse-ui", version: options.version ?? "0.0.0" },
382
296
  appCapabilities: { availableDisplayModes: ["inline"] },
383
297
  protocolVersion: MCPAPP_PROTOCOL_VERSION
384
298
  }).then((result) => {
385
299
  if (destroyed) return;
386
- standardConfirmed = true;
387
300
  applyHostContext(result?.hostContext);
388
301
  notify(MCPAPP_INITIALIZED, {});
389
- lastReportedHeight = -1;
302
+ initialized = true;
390
303
  reportSize();
391
304
  }).catch(() => {
392
305
  });
393
- reportSize();
394
306
  },
395
307
  destroy() {
396
308
  if (destroyed) return;
@@ -413,24 +325,17 @@ function createMcpAppsAdapter(win, options) {
413
325
 
414
326
  // src/host/detect.ts
415
327
  function detectHostKind(win) {
416
- if (win.openai != null) return "chatgpt";
417
328
  try {
418
- if (win.parent != null && win.parent !== win) return "claude";
329
+ if (win.parent != null && win.parent !== win) return "mcp-apps";
419
330
  } catch {
420
- return "claude";
331
+ return "mcp-apps";
421
332
  }
422
333
  return "generic";
423
334
  }
424
335
  function adapterForKind(kind, win, options) {
425
- switch (kind) {
426
- case "chatgpt":
427
- return createChatGPTAdapter(win);
428
- case "claude":
429
- case "nimblebrain":
430
- return createMcpAppsAdapter(win, options);
431
- default:
432
- return createInlineAdapter(win, options);
433
- }
336
+ if (kind === "mcp-apps") return createMcpAppsAdapter(win, options);
337
+ if (kind === "generic") return createInlineAdapter(win, options);
338
+ throw new TypeError(`unknown host "${String(kind)}": expected "mcp-apps" or "generic"`);
434
339
  }
435
340
  function selectAdapter(win, options) {
436
341
  const kind = options.host ?? detectHostKind(win);
@@ -450,7 +355,13 @@ function connectUI(options = {}) {
450
355
  onData: (cb) => adapter.onData(cb),
451
356
  theme: () => adapter.getTheme(),
452
357
  onTheme: (cb) => adapter.onTheme(cb),
453
- callTool: (name, args) => adapter.callTool(name, args),
358
+ // Decided here rather than per adapter: every host reports a failed or
359
+ // refused call inside the result, because that is how MCP defines one.
360
+ async callTool(name, args) {
361
+ const result = await adapter.callTool(name, args);
362
+ if (isToolError(result)) throw new ToolCallError(name, result);
363
+ return result;
364
+ },
454
365
  sendPrompt: (text) => adapter.sendPrompt(text),
455
366
  openLink: (url) => adapter.openLink(url),
456
367
  resize: (height) => adapter.resize(height),
@@ -467,15 +378,15 @@ function connectUI(options = {}) {
467
378
 
468
379
  exports.HostUnsupportedError = HostUnsupportedError;
469
380
  exports.SYNAPSE_DATA_ELEMENT_ID = SYNAPSE_DATA_ELEMENT_ID;
381
+ exports.ToolCallError = ToolCallError;
470
382
  exports.adapterForKind = adapterForKind;
471
383
  exports.applyHostTheme = applyHostTheme;
472
384
  exports.coerceMode = coerceMode;
473
385
  exports.connectUI = connectUI;
474
- exports.createChatGPTAdapter = createChatGPTAdapter;
475
386
  exports.createInlineAdapter = createInlineAdapter;
476
387
  exports.createMcpAppsAdapter = createMcpAppsAdapter;
477
388
  exports.detectHostKind = detectHostKind;
478
389
  exports.preferredMode = preferredMode;
479
390
  exports.selectAdapter = selectAdapter;
480
- //# sourceMappingURL=chunk-RU64F5RS.cjs.map
481
- //# sourceMappingURL=chunk-RU64F5RS.cjs.map
391
+ //# sourceMappingURL=chunk-BNF352SA.cjs.map
392
+ //# sourceMappingURL=chunk-BNF352SA.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/host/theme.ts","../src/host/types.ts","../src/host/data.ts","../src/host/adapters/inline.ts","../src/host/adapters/mcpapps.ts","../src/host/detect.ts","../src/host/connect.ts"],"names":["applyTheme","extractHostFontCss"],"mappings":"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","file":"chunk-BNF352SA.cjs","sourcesContent":["import { applyTheme } from \"../theme-defaults.js\";\nimport type { SynapseUITheme } from \"./types.js\";\n\n/**\n * Apply a resolved theme to the DOM for the cross-host client.\n *\n * Two conventions coexist across Synapse components, so the client drives both:\n *\n * - `document.documentElement[data-theme=\"light\"|\"dark\"]` — how self-contained\n * HTML components (Bassethound's report) gate their `--var` palettes, and the\n * lever a host's light/dark signal actually flips.\n * - CSS custom properties via {@link applyTheme} — how the\n * `@nimblebrain/synapse/ui` token components consume theme, backed by the\n * SDK's neutral defaults so every referenced var resolves in both modes.\n *\n * Setting both means an app can use either convention (and a host that supplies\n * only a mode string still themes correctly). SSR-safe.\n *\n * Fonts travel separately, as the host's `@font-face` CSS: see\n * {@link injectHostFonts}.\n */\nexport function applyHostTheme(theme: SynapseUITheme): void {\n if (typeof document !== \"undefined\") {\n document.documentElement.setAttribute(\"data-theme\", theme.mode);\n }\n applyTheme(theme.mode, theme.tokens);\n}\n\n/** The style element the host's font CSS goes in — the same id the spec's\n * `applyHostFonts` uses, so both clients agree on one element. */\nexport const HOST_FONTS_STYLE_ID = \"__mcp-host-fonts\";\n\n/**\n * Load the host's `@font-face` CSS (the spec's `hostContext.styles.css.fonts`)\n * into the document.\n *\n * Mirrors ext-apps `applyHostFonts`, which this client cannot import without\n * pulling the ext-apps runtime into its IIFE: the CSS is injected once and left\n * in place, so a later host-context change without `css` keeps the typeface\n * loaded. A host that sends none leaves the web-safe fallbacks in force.\n * SSR-safe.\n */\nexport function injectHostFonts(css: string): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(HOST_FONTS_STYLE_ID)) return;\n const style = document.createElement(\"style\");\n style.id = HOST_FONTS_STYLE_ID;\n style.textContent = css;\n document.head.appendChild(style);\n}\n\n/** Read the OS-level color scheme as a sane default for hosts that don't push a\n * theme until the handshake completes, or ever (standalone). */\nexport function preferredMode(win: Window | undefined): \"light\" | \"dark\" {\n try {\n if (win?.matchMedia?.(\"(prefers-color-scheme: dark)\").matches) return \"dark\";\n } catch {\n // matchMedia unavailable (older test envs) — fall through to light.\n }\n return \"light\";\n}\n\n/** Coerce an arbitrary host-supplied theme signal to a mode. Accepts the string\n * form (`\"dark\"`) both hosts use; anything else falls back. */\nexport function coerceMode(value: unknown, fallback: \"light\" | \"dark\"): \"light\" | \"dark\" {\n return value === \"light\" || value === \"dark\" ? value : fallback;\n}\n","/**\n * Cross-host UI client — types and wire constants.\n *\n * The cross-host client (`connectUI`) renders one Synapse-authored component in\n * any MCP Apps host (ChatGPT, Claude, NimbleBrain) and standalone. Apps code\n * against `synapse.*` and never touch the bridge. This is a **push-first** surface: the tool output that spawned\n * the widget is delivered at render (`data()` / `onData()`); `callTool()` is the\n * pull escape hatch, advertised per host via `capabilities()`.\n *\n * This layer intentionally has ZERO dependency on `@modelcontextprotocol/*` — the\n * bridge is JSON-RPC over `postMessage`, spoken by hand, so the IIFE that apps\n * inline stays tiny (no Zod, no ext-apps schemas).\n */\n\n/**\n * The bridge the client resolved to, as reported by `synapse.host()`:\n * `\"mcp-apps\"` in a frame, `\"generic\"` standalone. It names the bridge, not the\n * product — every framing host speaks the same one. Apps should rarely branch on\n * it; `capabilities()` is the supported way to feature-detect.\n */\nexport type HostKind = \"mcp-apps\" | \"generic\";\n\n/** Resolved theme. `mode` always resolves to light or dark. `tokens` are CSS\n * custom properties the host publishes — the MCP Apps adapter reads them from\n * `hostContext.styles.variables`; where a host sends none they stay empty and the\n * SDK's neutral defaults back them. */\nexport interface SynapseUITheme {\n mode: \"light\" | \"dark\";\n tokens: Record<string, string>;\n}\n\n/** What the active host actually supports. `data()`/`onData()`/`theme()`/\n * `resize()` work everywhere; these three vary. */\nexport interface HostCapabilities {\n /** `callTool()` can reach the server (widget→server fetch). */\n pull: boolean;\n /** `sendPrompt()` reaches the agent conversation. */\n sendPrompt: boolean;\n /** `openLink()` opens an external URL through the host. */\n openLink: boolean;\n}\n\n/** Thrown by `callTool()` when the active host offers no widget→server call. */\nexport class HostUnsupportedError extends Error {\n constructor(feature: string, host: HostKind) {\n super(`\"${feature}\" is not supported by the \"${host}\" host`);\n this.name = \"HostUnsupportedError\";\n }\n}\n\n/**\n * Thrown by `callTool()` when the tool result says the call failed\n * (`isError: true`). MCP reports a tool failure inside the result rather than as\n * a JSON-RPC error, and a host reports a refusal the same way — ChatGPT answers a\n * call to a tool the app may not see with an `isError` result — so without this a\n * failure would resolve as if it were data. The message is the result's first\n * text block; the whole result is on `result`.\n */\nexport class ToolCallError extends Error {\n readonly result: unknown;\n constructor(name: string, result: unknown) {\n super(toolErrorText(result) ?? `tool \"${name}\" returned an error`);\n this.name = \"ToolCallError\";\n this.result = result;\n }\n}\n\nfunction toolErrorText(result: unknown): string | undefined {\n const content = (result as { content?: unknown } | null)?.content;\n if (!Array.isArray(content)) return undefined;\n for (const block of content) {\n const b = block as { type?: unknown; text?: unknown } | null;\n if (b?.type === \"text\" && typeof b.text === \"string\" && b.text !== \"\") return b.text;\n }\n return undefined;\n}\n\n/** True when a tool result reports failure, as MCP defines it. */\nexport function isToolError(result: unknown): boolean {\n return (\n result != null &&\n typeof result === \"object\" &&\n (result as { isError?: unknown }).isError === true\n );\n}\n\nexport interface ConnectUIOptions {\n /** App name — informational; forwarded to hosts that accept an appInfo. */\n name?: string;\n /** App semver — informational. */\n version?: string;\n /**\n * Force a host adapter instead of auto-detecting. Used by preview harnesses,\n * SSR, and tests; production apps omit it and let the SDK feature-detect.\n * `\"mcp-apps\"` → MCP Apps standard adapter, `\"generic\"` → inline adapter.\n */\n host?: HostKind;\n /**\n * `id` of the `<script type=\"application/json\">` element carrying pushed data\n * baked into the HTML (the SSR / standalone path). Defaults to\n * {@link SYNAPSE_DATA_ELEMENT_ID}.\n */\n dataElementId?: string;\n /**\n * Auto-report content height to the host on layout changes (MCP Apps only).\n * Defaults to `true`. Set `false` to size manually via `resize()`.\n */\n autoResize?: boolean;\n /** Window to bind to. Defaults to the global `window`. Injectable for tests. */\n window?: Window & typeof globalThis;\n}\n\n/**\n * The public cross-host client. Bound to `synapse` by convention.\n *\n * ```ts\n * const synapse = connectUI();\n * synapse.onData(render); // future pushes/updates\n * render(synapse.data()); // current value (may be null → empty state)\n * ```\n */\nexport interface SynapseUIClient {\n /** The current pushed data, or `null` before anything has been delivered. */\n data<T = unknown>(): T | null;\n /** Subscribe to data updates (NOT replayed — read `data()` for the current\n * value). Returns an unsubscribe. */\n onData<T = unknown>(cb: (data: T) => void): () => void;\n\n /** The current resolved theme. */\n theme(): SynapseUITheme;\n /** Subscribe to theme changes. The client already applies the theme to the DOM\n * (`data-theme` attribute + CSS variables) before this fires. */\n onTheme(cb: (theme: SynapseUITheme) => void): () => void;\n\n /** Widget→server tool call. Resolves with the tool result. Rejects with\n * {@link ToolCallError} when the result reports failure (`isError: true`), and\n * with {@link HostUnsupportedError} where the host advertises no pull\n * (`capabilities().pull === false`). */\n callTool<O = unknown>(name: string, args?: Record<string, unknown>): Promise<O>;\n /** Send a follow-up message to the agent conversation. No-op where unsupported. */\n sendPrompt(text: string): void;\n /** Open an external URL through the host (falls back to `window.open`). */\n openLink(url: string): void;\n /** Report content height to the host. Omit `height` to measure `document.body`. */\n resize(height?: number): void;\n\n /** What the active host supports. */\n capabilities(): HostCapabilities;\n /** The resolved host — an escape hatch; prefer `capabilities()`. */\n host(): HostKind;\n /** Tear down listeners/observers. */\n destroy(): void;\n}\n\n/**\n * Internal adapter contract. One per host bridge; the client is a thin façade\n * over the selected adapter.\n */\nexport interface HostAdapter {\n readonly host: HostKind;\n getData<T = unknown>(): T | null;\n onData<T = unknown>(cb: (data: T) => void): () => void;\n getTheme(): SynapseUITheme;\n onTheme(cb: (theme: SynapseUITheme) => void): () => void;\n callTool<O = unknown>(name: string, args?: Record<string, unknown>): Promise<O>;\n sendPrompt(text: string): void;\n openLink(url: string): void;\n resize(height?: number): void;\n capabilities(): HostCapabilities;\n /** Begin listening / send the ready handshake / read baked-in data. Called once\n * by `connectUI` synchronously so `getData()` is populated on return. */\n start(): void;\n destroy(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Wire constants — the bridge message shapes. Centralized so a\n// host protocol tweak is a one-line change, not a grep-and-pray.\n// ---------------------------------------------------------------------------\n\n/** Default `id` of the baked-in data `<script type=\"application/json\">`. */\nexport const SYNAPSE_DATA_ELEMENT_ID = \"synapse-ui-data\";\n\n// ---------------------------------------------------------------------------\n// MCP Apps standard (SEP-1865) — the bridge every framing host speaks. The View iframe is an MCP client and the\n// host an MCP server; they exchange raw JSON-RPC 2.0 objects over `postMessage`\n// (no wrapper envelope). Method names are canonical to the ext-apps spec.\n// ---------------------------------------------------------------------------\n\n/** Protocol version exchanged in the `ui/initialize` handshake. */\nexport const MCPAPP_PROTOCOL_VERSION = \"2026-01-26\";\n/** View → host handshake request; the result carries the host context. */\nexport const MCPAPP_INITIALIZE = \"ui/initialize\";\n/** View → host notification sent once after the init result — the host holds all\n * pushes until it arrives. */\nexport const MCPAPP_INITIALIZED = \"ui/notifications/initialized\";\n/** Host → view notification carrying the tool output. `params` IS the\n * `CallToolResult` (data at `params.structuredContent`) — no `result` wrapper. */\nexport const MCPAPP_TOOL_RESULT = \"ui/notifications/tool-result\";\n/** Host → view notification carrying a partial host context (theme, styles, …). */\nexport const MCPAPP_HOST_CONTEXT_CHANGED = \"ui/notifications/host-context-changed\";\n/** View → host notification reporting intrinsic size (`{ width?, height? }`). A\n * host may keep the frame hidden until it receives one, so it is sent promptly. */\nexport const MCPAPP_SIZE_CHANGED = \"ui/notifications/size-changed\";\n/** View → host request: open an external URL (`{ url }`). */\nexport const MCPAPP_OPEN_LINK = \"ui/open-link\";\n/** View → host request: send a follow-up to the conversation\n * (`{ role: \"user\", content: ContentBlock[] }`). */\nexport const MCPAPP_MESSAGE = \"ui/message\";\n/** Host → view request: graceful teardown; the view replies `{}`. */\nexport const MCPAPP_TEARDOWN = \"ui/resource-teardown\";\n/** Standard MCP method the View may call over the same channel (pull). */\nexport const MCP_TOOLS_CALL = \"tools/call\";\n","/**\n * Read data baked into the HTML as `<script type=\"application/json\" id=…>`.\n *\n * This is how a server delivers tool output with no round-trip, for first paint\n * and for a standalone render: the server helper escapes the payload\n * and substitutes it into the element (see the Python `SynapseUI.render_html`).\n * A data-free template leaves the marker comment in place, which fails\n * `JSON.parse` and reads back as `null` — exactly the \"no data yet\" state.\n */\nexport function readInlineData<T = unknown>(\n doc: Document | undefined,\n elementId: string,\n): T | null {\n if (!doc) return null;\n const el = doc.getElementById(elementId);\n const text = el?.textContent;\n if (!text) return null;\n try {\n const parsed = JSON.parse(text);\n return (parsed ?? null) as T | null;\n } catch {\n // Unreplaced marker or malformed blob — treat as no data.\n return null;\n }\n}\n","import { readInlineData } from \"../data.js\";\nimport { coerceMode, preferredMode } from \"../theme.js\";\nimport {\n type ConnectUIOptions,\n type HostAdapter,\n type HostCapabilities,\n HostUnsupportedError,\n SYNAPSE_DATA_ELEMENT_ID,\n type SynapseUITheme,\n} from \"../types.js\";\n\n/**\n * Inline / standalone fallback adapter.\n *\n * No live host bridge — used for SSR, previews, and a plain browser render. Data\n * comes only from the baked-in `<script type=\"application/json\">` blob; there is\n * nothing to push, so `onData` never fires after `start()`. Theme follows the OS\n * color scheme and tracks `prefers-color-scheme` changes. `openLink` opens a new\n * tab; `sendPrompt`/`callTool` have no destination.\n */\nexport function createInlineAdapter(\n win: Window & typeof globalThis,\n options: ConnectUIOptions,\n): HostAdapter {\n const dataElementId = options.dataElementId ?? SYNAPSE_DATA_ELEMENT_ID;\n\n let currentData: unknown = null;\n let currentTheme: SynapseUITheme = { mode: preferredMode(win), tokens: {} };\n\n const themeCbs = new Set<(t: SynapseUITheme) => void>();\n let destroyed = false;\n let media: MediaQueryList | null = null;\n\n const onSchemeChange = (event: MediaQueryListEvent) => {\n if (destroyed) return;\n const mode = coerceMode(event.matches ? \"dark\" : \"light\", currentTheme.mode);\n if (mode === currentTheme.mode) return;\n currentTheme = { mode, tokens: {} };\n for (const cb of themeCbs) cb(currentTheme);\n };\n\n return {\n host: \"generic\",\n getData: <T>() => currentData as T | null,\n onData() {\n // Static render — no updates after the initial baked-in read.\n return () => {};\n },\n getTheme: () => currentTheme,\n onTheme(cb) {\n themeCbs.add(cb);\n return () => themeCbs.delete(cb);\n },\n async callTool<O>(_name: string): Promise<O> {\n throw new HostUnsupportedError(\"callTool\", \"generic\");\n },\n sendPrompt() {\n // No agent to reach in a standalone render.\n },\n openLink(url: string) {\n win.open(url, \"_blank\", \"noopener,noreferrer\");\n },\n resize() {\n // No host to size for.\n },\n capabilities(): HostCapabilities {\n return { pull: false, sendPrompt: false, openLink: true };\n },\n start() {\n currentData = readInlineData(win.document, dataElementId);\n try {\n media = win.matchMedia?.(\"(prefers-color-scheme: dark)\") ?? null;\n media?.addEventListener?.(\"change\", onSchemeChange);\n } catch {\n media = null;\n }\n },\n destroy() {\n if (destroyed) return;\n destroyed = true;\n media?.removeEventListener?.(\"change\", onSchemeChange);\n media = null;\n themeCbs.clear();\n },\n };\n}\n","// `detection` is type-only against `@modelcontextprotocol/*`, so importing it\n// keeps this adapter free of the ext-apps runtime (and Zod) that the lean\n// `window.SynapseUI` IIFE deliberately excludes.\nimport { extractHostFontCss } from \"../../detection.js\";\nimport { readInlineData } from \"../data.js\";\nimport { coerceMode, injectHostFonts, preferredMode } from \"../theme.js\";\nimport {\n type ConnectUIOptions,\n type HostAdapter,\n type HostCapabilities,\n MCP_TOOLS_CALL,\n MCPAPP_HOST_CONTEXT_CHANGED,\n MCPAPP_INITIALIZE,\n MCPAPP_INITIALIZED,\n MCPAPP_MESSAGE,\n MCPAPP_OPEN_LINK,\n MCPAPP_PROTOCOL_VERSION,\n MCPAPP_SIZE_CHANGED,\n MCPAPP_TEARDOWN,\n MCPAPP_TOOL_RESULT,\n SYNAPSE_DATA_ELEMENT_ID,\n type SynapseUITheme,\n} from \"../types.js\";\n\n/** How long a widget→host request (`callTool`, `openLink`, `sendPrompt`) waits\n * for its response before rejecting, so a silent host never hangs a promise. */\nconst REQUEST_TIMEOUT_MS = 30_000;\n\ninterface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (reason: unknown) => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\n/**\n * MCP Apps standard (SEP-1865) adapter — the bridge for every framed host.\n * ChatGPT, Claude and NimbleBrain all implement the standard, so there is one.\n *\n * The View iframe is an MCP client speaking JSON-RPC 2.0 to `window.parent`:\n *\n * 1. posts `ui/initialize` and awaits the host context (theme, style variables);\n * 2. posts `ui/notifications/initialized`, then a `size-changed` — a host keeps\n * the frame hidden until it has both the handshake and a size;\n * 3. receives data via `ui/notifications/tool-result` (`params` IS the\n * `CallToolResult`, so data is at `params.structuredContent`) and theme via\n * `ui/notifications/host-context-changed`.\n *\n * Actions go up as requests: `ui/open-link`, `ui/message` (follow-up), and\n * `tools/call` (pull).\n */\nexport function createMcpAppsAdapter(\n win: Window & typeof globalThis,\n options: ConnectUIOptions,\n): HostAdapter {\n const dataElementId = options.dataElementId ?? SYNAPSE_DATA_ELEMENT_ID;\n const autoResize = options.autoResize !== false;\n\n let currentData: unknown = null;\n let currentTheme: SynapseUITheme = { mode: preferredMode(win), tokens: {} };\n\n const dataCbs = new Set<(d: unknown) => void>();\n const themeCbs = new Set<(t: SynapseUITheme) => void>();\n let destroyed = false;\n // Set once the host has answered `ui/initialize` and been told `initialized`.\n let initialized = false;\n let nextId = 1;\n const pending = new Map<number, PendingRequest>();\n let lastReportedHeight = -1;\n let resizeObserver: ResizeObserver | null = null;\n let onWindowResize: (() => void) | null = null;\n\n const parent = () => win.parent ?? win;\n\n function post(message: Record<string, unknown>): void {\n parent().postMessage(message, \"*\");\n }\n\n function notify(method: string, params?: Record<string, unknown>): void {\n post({ jsonrpc: \"2.0\", method, params: params ?? {} });\n }\n\n function request<T = unknown>(method: string, params?: Record<string, unknown>): Promise<T> {\n const id = nextId++;\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n pending.delete(id);\n reject(new Error(`\"${method}\" timed out`));\n }, REQUEST_TIMEOUT_MS);\n pending.set(id, { resolve: resolve as (v: unknown) => void, reject, timer });\n post({ jsonrpc: \"2.0\", id, method, params: params ?? {} });\n });\n }\n\n function emitData(next: unknown): void {\n if (next == null) return;\n currentData = next;\n for (const cb of dataCbs) cb(next);\n }\n\n /** Merge a full or partial host context into the resolved theme (mode, tokens),\n * and load the host's `@font-face` CSS from the spec's `styles.css.fonts`. */\n function applyHostContext(ctx: Record<string, unknown> | null | undefined): void {\n if (!ctx || typeof ctx !== \"object\") return;\n const fontCss = extractHostFontCss(ctx);\n if (fontCss !== undefined) injectHostFonts(fontCss);\n let { mode, tokens } = currentTheme;\n let changed = false;\n if (ctx.theme != null) {\n const next = coerceMode(ctx.theme, mode);\n if (next !== mode) {\n mode = next;\n changed = true;\n }\n }\n const styles = ctx.styles as { variables?: Record<string, string> } | undefined;\n if (styles?.variables && typeof styles.variables === \"object\") {\n tokens = { ...tokens, ...styles.variables };\n changed = true;\n }\n if (changed) {\n currentTheme = { mode, tokens };\n for (const cb of themeCbs) cb(currentTheme);\n }\n }\n\n function reportSize(height?: number): void {\n // `ui/initialize` is the app's first word on this bridge, and a strict host\n // drops anything that arrives before it — leaving the frame hidden. The\n // handshake reports a size as soon as it completes, so nothing is lost.\n if (destroyed || !initialized) return;\n const h = typeof height === \"number\" ? height : Math.ceil(win.document.body.scrollHeight);\n if (h === lastReportedHeight) return;\n lastReportedHeight = h;\n notify(MCPAPP_SIZE_CHANGED, { height: h });\n }\n\n function handleResponse(d: Record<string, unknown>): void {\n // Normalize the echoed id: JSON-RPC requires a same-type echo, but a lax host\n // that returned \"1\" for 1 would otherwise miss the numeric-keyed pending map.\n const id = Number(d.id);\n const p = pending.get(id);\n if (!p) return;\n pending.delete(id);\n clearTimeout(p.timer);\n if (d.error != null) {\n const err = d.error as { message?: string };\n p.reject(new Error(err.message ?? \"request failed\"));\n } else {\n p.resolve(d.result);\n }\n }\n\n function handleNotification(method: string, params: Record<string, unknown>): void {\n if (method === MCPAPP_TOOL_RESULT) {\n // `params` is the CallToolResult: the render data lives at structuredContent.\n const structured = params.structuredContent;\n emitData(structured != null ? structured : params);\n } else if (method === MCPAPP_HOST_CONTEXT_CHANGED) {\n applyHostContext(params);\n }\n }\n\n function handleRequest(d: Record<string, unknown>): void {\n // The one host→view request we honor: acknowledge teardown so the host can\n // dispose the frame cleanly.\n if (d.method === MCPAPP_TEARDOWN) {\n post({ jsonrpc: \"2.0\", id: d.id, result: {} });\n }\n }\n\n const onMessage = (event: MessageEvent) => {\n if (destroyed) return;\n // Accept only frames from the host window when the browser sets a source.\n if (event.source && event.source !== parent()) return;\n const d = event.data as Record<string, unknown> | null | undefined;\n if (!d || typeof d !== \"object\" || d.jsonrpc !== \"2.0\") return;\n if (d.id != null && (\"result\" in d || \"error\" in d)) {\n handleResponse(d);\n } else if (typeof d.method === \"string\") {\n if (d.id != null) handleRequest(d);\n else handleNotification(d.method, (d.params ?? {}) as Record<string, unknown>);\n }\n };\n\n function setupResize(): void {\n onWindowResize = () => reportSize();\n win.addEventListener(\"resize\", onWindowResize);\n if (typeof win.ResizeObserver !== \"undefined\") {\n resizeObserver = new win.ResizeObserver(() => reportSize());\n resizeObserver.observe(win.document.body);\n }\n }\n\n return {\n host: \"mcp-apps\",\n getData: <T>() => currentData as T | null,\n onData(cb) {\n dataCbs.add(cb as (d: unknown) => void);\n return () => dataCbs.delete(cb as (d: unknown) => void);\n },\n getTheme: () => currentTheme,\n onTheme(cb) {\n themeCbs.add(cb);\n return () => themeCbs.delete(cb);\n },\n async callTool<O>(name: string, args?: Record<string, unknown>): Promise<O> {\n return (await request(MCP_TOOLS_CALL, { name, arguments: args ?? {} })) as O;\n },\n sendPrompt(text: string) {\n void request(MCPAPP_MESSAGE, { role: \"user\", content: [{ type: \"text\", text }] }).catch(\n () => {},\n );\n },\n openLink(url: string) {\n void request(MCPAPP_OPEN_LINK, { url }).catch(() => {});\n },\n resize(height?: number) {\n reportSize(height);\n },\n capabilities(): HostCapabilities {\n // An MCP Apps host answers `tools/call`, `ui/message` and `ui/open-link`\n // over this bridge.\n return { pull: true, sendPrompt: true, openLink: true };\n },\n start() {\n win.addEventListener(\"message\", onMessage as EventListener);\n\n // Baked-in data → available on first paint, before the handshake resolves.\n currentData = readInlineData(win.document, dataElementId);\n\n if (autoResize) setupResize();\n\n request<{ hostContext?: Record<string, unknown> }>(MCPAPP_INITIALIZE, {\n appInfo: { name: options.name ?? \"synapse-ui\", version: options.version ?? \"0.0.0\" },\n appCapabilities: { availableDisplayModes: [\"inline\"] },\n protocolVersion: MCPAPP_PROTOCOL_VERSION,\n })\n .then((result) => {\n if (destroyed) return;\n applyHostContext(result?.hostContext);\n notify(MCPAPP_INITIALIZED, {});\n initialized = true;\n // A host keeps the frame hidden until it gets a size after init, and\n // this is the first point at which sending one is allowed.\n reportSize();\n })\n .catch(() => {\n // The parent never answered: it is not an MCP Apps host, and nothing\n // reaches the component over this bridge. Baked-in data still renders.\n });\n },\n destroy() {\n if (destroyed) return;\n destroyed = true;\n win.removeEventListener(\"message\", onMessage as EventListener);\n if (onWindowResize) win.removeEventListener(\"resize\", onWindowResize);\n onWindowResize = null;\n resizeObserver?.disconnect();\n resizeObserver = null;\n for (const p of pending.values()) {\n clearTimeout(p.timer);\n p.reject(new Error(\"adapter destroyed\"));\n }\n pending.clear();\n dataCbs.clear();\n themeCbs.clear();\n },\n };\n}\n","import { createInlineAdapter } from \"./adapters/inline.js\";\nimport { createMcpAppsAdapter } from \"./adapters/mcpapps.js\";\nimport type { ConnectUIOptions, HostAdapter, HostKind } from \"./types.js\";\n\n/**\n * Minimal window shape the detector inspects. Kept structural so detection is\n * unit-testable with a plain object — no full `Window` fake required.\n */\ninterface DetectableWindow {\n parent?: unknown;\n}\n\n/**\n * Detect the bridge from the browsing context.\n *\n * - a nested browsing context (`parent !== self`, or a cross-origin access that\n * throws) → **mcp-apps**: every host that frames a component speaks the MCP\n * Apps standard.\n * - top-level document → **generic** (inline / standalone).\n *\n * Nothing on the window names the host. `window.openai` in particular does not:\n * ChatGPT injects it into every frame it serves, whatever the component is.\n * Which host answered is in the handshake, not here.\n */\nexport function detectHostKind(win: DetectableWindow): HostKind {\n try {\n if (win.parent != null && win.parent !== win) return \"mcp-apps\";\n } catch {\n // Cross-origin parent access throws → we are framed by another origin.\n return \"mcp-apps\";\n }\n return \"generic\";\n}\n\n/** Build the adapter for an explicit host kind (`options.host` or a detected one). */\nexport function adapterForKind(\n kind: HostKind,\n win: Window & typeof globalThis,\n options: ConnectUIOptions,\n): HostAdapter {\n if (kind === \"mcp-apps\") return createMcpAppsAdapter(win, options);\n if (kind === \"generic\") return createInlineAdapter(win, options);\n // A script-tag caller has no compiler to reject a value outside `HostKind`, and\n // treating one as standalone inside a frame sends no handshake: the host keeps\n // the frame hidden and the component is blank with nothing to say why.\n throw new TypeError(`unknown host \"${String(kind)}\": expected \"mcp-apps\" or \"generic\"`);\n}\n\n/** Select and construct the host adapter, honoring `options.host` when set. */\nexport function selectAdapter(\n win: Window & typeof globalThis,\n options: ConnectUIOptions,\n): HostAdapter {\n const kind = options.host ?? detectHostKind(win as unknown as DetectableWindow);\n return adapterForKind(kind, win, options);\n}\n","import { selectAdapter } from \"./detect.js\";\nimport { applyHostTheme } from \"./theme.js\";\nimport {\n type ConnectUIOptions,\n isToolError,\n type SynapseUIClient,\n ToolCallError,\n} from \"./types.js\";\n\n/**\n * Connect a Synapse-authored component to whatever host it renders in — any MCP\n * Apps host, or a plain/standalone page — behind one push-first API. Detects\n * whether it is framed, selects an adapter, and applies the\n * host theme to the DOM before returning.\n *\n * Synchronous: `data()` is populated from baked-in data (where present) on\n * return, and pushed updates arrive via `onData`. Bind the result to `synapse`:\n *\n * ```ts\n * const synapse = connectUI({ name: \"my-widget\", version: \"1.0.0\" });\n * synapse.onData(render); // future pushes/updates\n * render(synapse.data()); // current value (null → empty state)\n * ```\n */\nexport function connectUI(options: ConnectUIOptions = {}): SynapseUIClient {\n const win = options.window ?? (globalThis as unknown as Window & typeof globalThis);\n const adapter = selectAdapter(win, options);\n\n // The client owns theme application: adapters resolve mode/tokens; this puts\n // them on the DOM (data-theme + CSS vars) so apps never wire theme by hand.\n applyHostTheme(adapter.getTheme());\n const unsubTheme = adapter.onTheme(applyHostTheme);\n\n adapter.start();\n\n let destroyed = false;\n\n return {\n data: <T>() => adapter.getData<T>(),\n onData: <T>(cb: (data: T) => void) => adapter.onData<T>(cb),\n theme: () => adapter.getTheme(),\n onTheme: (cb) => adapter.onTheme(cb),\n // Decided here rather than per adapter: every host reports a failed or\n // refused call inside the result, because that is how MCP defines one.\n async callTool<O>(name: string, args?: Record<string, unknown>): Promise<O> {\n const result = await adapter.callTool<O>(name, args);\n if (isToolError(result)) throw new ToolCallError(name, result);\n return result;\n },\n sendPrompt: (text: string) => adapter.sendPrompt(text),\n openLink: (url: string) => adapter.openLink(url),\n resize: (height?: number) => adapter.resize(height),\n capabilities: () => adapter.capabilities(),\n host: () => adapter.host,\n destroy() {\n if (destroyed) return;\n destroyed = true;\n unsubTheme();\n adapter.destroy();\n },\n };\n}\n"]}
@@ -1,11 +1,21 @@
1
+ // src/detection.ts
2
+ var DEFAULT_THEME = {
3
+ mode: "light",
4
+ tokens: {}
5
+ };
6
+ function extractTheme(ctx) {
7
+ if (!ctx) return { ...DEFAULT_THEME };
8
+ const mode = ctx.theme === "light" || ctx.theme === "dark" ? ctx.theme : DEFAULT_THEME.mode;
9
+ const variables = ctx.styles?.variables;
10
+ const tokens = variables && typeof variables === "object" && !Array.isArray(variables) ? variables : {};
11
+ return { mode, tokens };
12
+ }
13
+ function extractHostFontCss(ctx) {
14
+ const fonts = ctx?.styles?.css?.fonts;
15
+ return typeof fonts === "string" && fonts !== "" ? fonts : void 0;
16
+ }
17
+
1
18
  // src/theme-defaults.ts
2
- var FONT_DISPLAY_VALUES = /* @__PURE__ */ new Set([
3
- "auto",
4
- "block",
5
- "swap",
6
- "fallback",
7
- "optional"
8
- ]);
9
19
  var LIGHT = {
10
20
  // ── Surfaces ──
11
21
  "--color-background-primary": "#ffffff",
@@ -101,78 +111,10 @@ function applyThemeVariables(mode, hostVars) {
101
111
  for (const [k, v] of Object.entries(incoming)) root.setProperty(k, v);
102
112
  appliedInlineKeys = new Set(Object.keys(incoming));
103
113
  }
104
- var managedFaces = /* @__PURE__ */ new Set();
105
- var appliedFontKey = "";
106
- function fontFacesKey(faces) {
107
- if (!Array.isArray(faces)) return "";
108
- return JSON.stringify(faces.map((d) => [d.family, d.src, d.weight, d.style, d.display]));
109
- }
110
- function normalizeFontFaces(raw) {
111
- if (!Array.isArray(raw)) return void 0;
112
- const usable = raw.filter(isFontFaceDescriptor);
113
- if (raw.length > 0 && usable.length === 0) return void 0;
114
- return usable;
115
- }
116
- function applyThemeFontFaces(faces) {
117
- const next = normalizeFontFaces(faces);
118
- if (next === void 0) return;
119
- if (typeof document === "undefined") return;
120
- if (!document.fonts || typeof FontFace === "undefined") return;
121
- const key = fontFacesKey(next);
122
- if (key === appliedFontKey) return;
123
- appliedFontKey = key;
124
- for (const face of managedFaces) {
125
- try {
126
- document.fonts.delete(face);
127
- } catch {
128
- }
129
- }
130
- managedFaces.clear();
131
- for (const d of next) {
132
- try {
133
- const face = new FontFace(d.family, d.src, {
134
- weight: d.weight,
135
- style: d.style,
136
- // Wire data is untyped: ignore an unrecognised `display` rather than let
137
- // the constructor reject the descriptor and cost the app this face.
138
- display: d.display && FONT_DISPLAY_VALUES.has(d.display) ? d.display : "swap"
139
- });
140
- document.fonts.add(face);
141
- managedFaces.add(face);
142
- } catch {
143
- }
144
- }
145
- }
146
- function isFontFaceDescriptor(value) {
147
- if (!value || typeof value !== "object") return false;
148
- const d = value;
149
- return typeof d.family === "string" && typeof d.src === "string";
150
- }
151
- function applyTheme(mode, hostVars, fontFaces) {
114
+ function applyTheme(mode, hostVars) {
152
115
  applyThemeVariables(mode, hostVars);
153
- applyThemeFontFaces(fontFaces);
154
- }
155
-
156
- // src/detection.ts
157
- var FONT_FACES_CONTEXT_KEY = "synapse/fontFaces";
158
- var DEFAULT_THEME = {
159
- mode: "light",
160
- tokens: {}
161
- };
162
- function extractTheme(ctx) {
163
- if (!ctx) return { ...DEFAULT_THEME };
164
- const mode = ctx.theme === "light" || ctx.theme === "dark" ? ctx.theme : DEFAULT_THEME.mode;
165
- const variables = ctx.styles?.variables;
166
- const tokens = variables && typeof variables === "object" && !Array.isArray(variables) ? variables : {};
167
- return { mode, tokens };
168
- }
169
- function extractFontFaces(ctx) {
170
- return normalizeFontFaces(ctx?.[FONT_FACES_CONTEXT_KEY]);
171
- }
172
- function foldFontFaces(prev, ctx) {
173
- return extractFontFaces(ctx) ?? prev;
174
116
  }
175
117
 
176
- export { FONT_FACES_CONTEXT_KEY, applyTheme, extractTheme, foldFontFaces, fontFacesKey };
177
- //# sourceMappingURL=chunk-DRLVEHCD.js.map
178
- //# sourceMappingURL=chunk-DRLVEHCD.js.map
118
+ export { applyTheme, extractHostFontCss, extractTheme };
119
+ //# sourceMappingURL=chunk-DJS2FKBN.js.map
120
+ //# sourceMappingURL=chunk-DJS2FKBN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/detection.ts","../src/theme-defaults.ts"],"names":[],"mappings":"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","file":"chunk-DJS2FKBN.js","sourcesContent":["import type { McpUiHostContext } from \"@modelcontextprotocol/ext-apps\";\nimport type { Theme } from \"./types\";\n\nconst DEFAULT_THEME: Theme = {\n mode: \"light\",\n tokens: {},\n};\n\nexport function extractTheme(ctx: Partial<McpUiHostContext> | undefined): Theme {\n if (!ctx) return { ...DEFAULT_THEME };\n\n // Spec: theme is a string (\"light\" | \"dark\")\n const mode = ctx.theme === \"light\" || ctx.theme === \"dark\" ? ctx.theme : DEFAULT_THEME.mode;\n\n // Spec: tokens live under styles.variables\n const variables = ctx.styles?.variables;\n const tokens =\n variables && typeof variables === \"object\" && !Array.isArray(variables)\n ? (variables as Record<string, string>)\n : {};\n\n return { mode, tokens };\n}\n\n/**\n * The host's `@font-face` CSS, from the spec's `styles.css.fonts`, or\n * `undefined` when this context carries none.\n */\nexport function extractHostFontCss(ctx: Partial<McpUiHostContext> | undefined): string | undefined {\n const fonts = ctx?.styles?.css?.fonts;\n return typeof fonts === \"string\" && fonts !== \"\" ? fonts : undefined;\n}\n","/**\n * Theme-aware default backing for the `@nimblebrain/synapse/ui` token contract.\n *\n * `tokens` (see `ui/tokens.ts`) are `var(--token, fallback)` references. A CSS\n * `var()` fallback is a *static literal* — it cannot branch on light vs. dark.\n * So any var the host does NOT inject resolves to its single hardcoded (light)\n * fallback in BOTH themes. Pair such a token with a theme-aware one and you get\n * white-on-white in dark mode: it looks correct in light, passes `tsc`/build,\n * and only breaks when the theme is toggled.\n *\n * This module closes that gap with a default layer the SDK controls and that\n * CAN branch on theme. {@link applyThemeVariables} puts the neutral defaults for\n * the active `mode` in a cascade layer and the host's variables inline — so:\n * - the host's (brand) values always win for the keys it provides, through\n * either the protocol or a stylesheet it injects, and\n * - any var nobody declares still resolves to a theme-correct neutral value.\n *\n * It's the `var()` fallback, but able to branch on theme. The values stay\n * neutral grays, a generic blue, and generic semantic hues for danger, success,\n * warning and processing — never a *brand* value. Brand arrives only by host\n * injection, mirroring `ui/tokens.ts`. `__tests__/ui/tokens.test.ts` holds the\n * line: every hex here must be in the sanctioned set. This keeps\n * the library host-agnostic while guaranteeing every token resolves correctly\n * in both themes even against an incomplete host, a standalone `connect()`\n * widget, or a third-party host.\n *\n * Font *faces* are the one part of theming CSS variables cannot carry — a token\n * names a family, it cannot load one. The spec carries them separately, as\n * `@font-face` CSS in `hostContext.styles.css.fonts`, and each client applies\n * that string itself. The CSS is injected as the host sent it: it is trusted,\n * because the host already controls the frame it renders into.\n *\n * Only theme-sensitive (color) vars are listed here. Theme-invariant vars\n * (radii, type scale, shadows, font stacks, weights, border widths) look the\n * same in both themes, so their static `var()` fallback is already correct —\n * they are intentionally absent. The `tokens` regression test enforces this\n * with a TOTAL partition: every referenced var must be either declared\n * theme-invariant (an explicit allowlist) or defined in both maps below, so a\n * newly added theme-sensitive token can't slip through unbacked.\n */\n\n/**\n * Light-theme neutral defaults. Values match the (light) fallbacks baked into\n * `tokens` — applying them changes nothing observable in light mode; they exist\n * so the light path flows through the same code as dark and so a standalone\n * render with an explicit `light` theme is backed identically to its fallbacks.\n */\nconst LIGHT: Record<string, string> = {\n // ── Surfaces ──\n \"--color-background-primary\": \"#ffffff\",\n \"--color-background-secondary\": \"#fafafa\",\n \"--color-background-tertiary\": \"#f3f4f6\",\n // ── Text ──\n \"--color-text-primary\": \"#111827\",\n \"--color-text-secondary\": \"#6b7280\",\n \"--color-text-tertiary\": \"#9ca3af\",\n \"--color-text-accent\": \"#2563eb\",\n \"--nb-color-accent-foreground\": \"#ffffff\",\n // ── Border / ring ──\n \"--color-border-primary\": \"#e5e7eb\",\n \"--color-border-secondary\": \"#d1d5db\",\n \"--color-ring-primary\": \"#2563eb\",\n // ── Status / brand semantics ──\n \"--nb-color-danger\": \"#dc2626\",\n \"--nb-color-danger-foreground\": \"#ffffff\",\n \"--nb-color-success\": \"#059669\",\n \"--nb-color-warning\": \"#f59e0b\",\n \"--nb-color-processing\": \"#7c3aed\",\n \"--nb-color-processing-light\": \"#f3eeff\",\n \"--nb-color-info-light\": \"#eef4ff\",\n};\n\n/**\n * Dark-theme neutral defaults. A monotonic neutral ladder — the three surface\n * tiers are the darkest, borders sit a step lighter so they stay visible\n * against every surface, and text inverts to light. \"Subtle\"/\"strong\" keep\n * their light-mode semantics: `bgSubtle` reads as a lifted hover/inset tint and\n * `borderStrong` is more prominent than `border`.\n */\nconst DARK: Record<string, string> = {\n // ── Surfaces (base → lifted) ──\n \"--color-background-primary\": \"#18181b\",\n \"--color-background-secondary\": \"#27272a\",\n \"--color-background-tertiary\": \"#2f2f34\",\n // ── Text ──\n \"--color-text-primary\": \"#fafafa\",\n \"--color-text-secondary\": \"#a1a1aa\",\n \"--color-text-tertiary\": \"#71717a\",\n \"--color-text-accent\": \"#818cf8\",\n \"--nb-color-accent-foreground\": \"#ffffff\",\n // ── Border / ring (lighter than surfaces so they remain visible) ──\n \"--color-border-primary\": \"#3f3f46\",\n \"--color-border-secondary\": \"#52525b\",\n \"--color-ring-primary\": \"#818cf8\",\n // ── Status / brand semantics (brightened for contrast on dark) ──\n \"--nb-color-danger\": \"#f87171\",\n // Dark text on the brightened red: white on #f87171 is under 3:1.\n \"--nb-color-danger-foreground\": \"#18181b\",\n \"--nb-color-success\": \"#34d399\",\n \"--nb-color-warning\": \"#fbbf24\",\n \"--nb-color-processing\": \"#a78bfa\",\n \"--nb-color-processing-light\": \"#2a2440\",\n \"--nb-color-info-light\": \"#1e2a44\",\n};\n\n/**\n * The neutral default theme, keyed by mode. Exported for the regression test\n * that asserts every color var referenced by `tokens` is backed in both modes.\n */\nexport const DEFAULT_THEME_VARS: Record<\"light\" | \"dark\", Record<string, string>> = {\n light: LIGHT,\n dark: DARK,\n};\n\n/** Cascade layer holding the neutral defaults. Named so an app can order it. */\nexport const DEFAULTS_LAYER_NAME = \"synapse-defaults\";\n\n/** `id` of the single `<style>` element this module owns. */\nexport const DEFAULTS_STYLE_ID = \"synapse-theme-defaults\";\n\n/** Custom properties this module last wrote inline, so a later apply clears only\n * what it owns and never an inline property the app set itself. */\nlet appliedInlineKeys = new Set<string>();\n\n/** Test seam — forget what was written inline so a fresh apply is observable. */\nexport function resetAppliedInlineKeys(): void {\n appliedInlineKeys = new Set();\n}\n\n/**\n * Whether this document can host the defaults stylesheet.\n *\n * Not the same question as `typeof document !== \"undefined\"`. An embedder or a\n * test harness may install a *partial* `document` — `documentElement.style` plus\n * listeners and nothing else — which is everything this module needed while the\n * defaults were inline properties. Moving them into a stylesheet added three new\n * requirements (`getElementById`, `createElement`, `head.prepend`), and reaching\n * for them on such a document throws.\n *\n * A throw here is not contained: it unwinds through `applyTheme` into the\n * handshake, so an app in that environment never finishes connecting. The\n * default layer is a rendering nicety and the connection is not, so a document\n * that cannot carry a stylesheet gets no layer rather than no session.\n */\nfunction canInstallStylesheet(): boolean {\n return (\n typeof document !== \"undefined\" &&\n typeof document.getElementById === \"function\" &&\n typeof document.createElement === \"function\" &&\n typeof document.head?.prepend === \"function\"\n );\n}\n\n/**\n * Install the neutral defaults for `mode` as a cascade layer.\n *\n * A layer, not inline properties on `documentElement`, and that distinction is\n * the whole point: **a default has to lose.** An inline style outranks every\n * author stylesheet, so writing defaults there made them beat any rule a host or\n * app had written for the same var — the opposite of a fallback.\n *\n * A host cannot always deliver a var through the protocol.\n * `hostContext.styles.variables` is a closed enum, so a host whose design system\n * is larger than that enum has to put the remainder in a stylesheet it injects\n * into the app document. Those declarations are ordinary unlayered author rules.\n * Unlayered beats layered, so they now win — while a var nobody declares still\n * resolves to a neutral default, which is what this map is for.\n *\n * An app's own `:root` rule wins for the same reason, so overriding a default\n * needs no `!important` and no knowledge of this module.\n *\n * One element, replaced in place, so a mode flip swaps the whole map atomically\n * and repeat calls are cheap. SSR-safe.\n */\nfunction applyDefaultThemeLayer(mode: \"light\" | \"dark\"): void {\n if (!canInstallStylesheet()) return;\n\n // Safe to concatenate ONLY because every key and value here is module-local\n // (`DEFAULT_THEME_VARS`). A host-supplied value containing `}` would escape\n // the rule, so never fold host or app input into this template; host\n // variables go through `setProperty`, which cannot escape.\n const declarations = Object.entries(DEFAULT_THEME_VARS[mode])\n .map(([k, v]) => ` ${k}: ${v};`)\n .join(\"\\n\");\n const css = `@layer ${DEFAULTS_LAYER_NAME} {\\n :root {\\n${declarations}\\n }\\n}`;\n\n const existing = document.getElementById(DEFAULTS_STYLE_ID);\n if (existing) {\n // Avoid a needless style invalidation when the mode hasn't changed.\n if (existing.textContent !== css) existing.textContent = css;\n return;\n }\n const el = document.createElement(\"style\");\n el.id = DEFAULTS_STYLE_ID;\n el.textContent = css;\n // Prepended so this is the first layer the document declares, which puts it\n // first in layer order — an app that declares its own layers sorts after, and\n // therefore wins. Unlayered app rules win regardless of DOM order.\n document.head.prepend(el);\n}\n\n/**\n * Apply theme CSS custom properties to the app document.\n *\n * The host's variables go inline on `documentElement`, where they outrank\n * everything; the neutral defaults for `mode` go into a cascade layer, where\n * they lose to any rule that actually declares the var. So a host value beats\n * this module's default through *either* channel — the protocol or a stylesheet\n * it injects — and a var nobody declares still resolves to a theme-correct\n * default. Only the protocol channel also outranks the app: a host's injected\n * stylesheet and the app's own `:root` are both unlayered rules of equal\n * specificity, so document order decides between them, not this module.\n *\n * A key this module wrote on a previous call and the incoming set does NOT carry\n * is *removed* inline rather than left behind, because a host may legitimately\n * narrow its key set: stop sending a var and it must stop applying, or it stays\n * pinned inline forever where the layer, the host's own stylesheet and the app's\n * own rules all cannot reach it. Removing lets the cascade resolve it against\n * those instead, which is the property this module exists to establish, and is\n * strictly less destructive than overwriting the key with our own default.\n *\n * Tracked as the set of keys last written, not as `DEFAULT_THEME_VARS`'s keys.\n * Those are two different sets: a host can send any spec-enum var, and ~25\n * theme-sensitive ones (`--color-text-danger`, `--color-background-inverse`, …)\n * have no neutral default, so keying the removal off the default map would pin\n * exactly those at the previous mode's value. Tracking what we wrote also means\n * this module never clears an inline property it did not set — an app writing its\n * own `documentElement.style` is left alone.\n *\n * Today an empty var set also arrives for a reason that is a bug rather than a\n * narrowing — `core.ts` replaces the host context wholesale, so a partial\n * `host-context-changed` that omits `styles` reads as \"no tokens\" (#46;\n * `connect.ts` already carries them forward). Fixing #46 is the better outcome —\n * it keeps the host's brand across a mode flip instead of collapsing to our\n * neutral — and does not make this loop unnecessary.\n *\n * SSR-safe (no-ops when `document` is unavailable). Idempotent — re-applying on\n * every theme change is correct and cheap.\n *\n * Prefer {@link applyTheme}: it applies variables *and* font faces together, so\n * a caller cannot wire up half a theme.\n */\nexport function applyThemeVariables(\n mode: \"light\" | \"dark\",\n hostVars: Record<string, string> | undefined | null,\n): void {\n if (typeof document === \"undefined\") return;\n applyDefaultThemeLayer(mode);\n\n // ONE definition of \"the host provided this key\", read by both loops below.\n // Wire data is untyped, so a key can arrive with a non-string value; that is\n // not a provided value, so the clear loop must reach it. Two predicates —\n // `k in hostVars` to clear, `typeof v === \"string\"` to write — leave such a key\n // in the gap: neither removed nor written, so the previous theme's value stays\n // pinned inline where the layer, the host's stylesheet and the app's `:root`\n // all cannot reach it. That is the un-self-healing pin this module exists to\n // eliminate, re-entered by a different door.\n const incoming: Record<string, string> = {};\n if (hostVars && typeof hostVars === \"object\") {\n for (const [k, v] of Object.entries(hostVars)) {\n if (typeof k === \"string\" && typeof v === \"string\") incoming[k] = v;\n }\n }\n\n const root = document.documentElement.style;\n // `removeProperty` is feature-checked and `setProperty` deliberately is not.\n // The setter is the one capability this module has always required, so a\n // document lacking it was never supported and gets no new promise here; the\n // clear pass arrived with the defaults layer, so a `style` carrying only the\n // setter is a shape that used to work. Dropping the clear pass for it is far\n // better than throwing out of the loop and leaving the host's variables\n // unwritten entirely.\n if (typeof root.removeProperty === \"function\") {\n for (const k of appliedInlineKeys) {\n if (!(k in incoming)) root.removeProperty(k);\n }\n }\n for (const [k, v] of Object.entries(incoming)) root.setProperty(k, v);\n appliedInlineKeys = new Set(Object.keys(incoming));\n}\n\n/**\n * Apply a resolved theme's variables to the app document.\n *\n * The single path by which theme variables reach the DOM: the handshake and\n * `host-context-changed` both funnel through here.\n */\nexport function applyTheme(\n mode: \"light\" | \"dark\",\n hostVars: Record<string, string> | undefined | null,\n): void {\n applyThemeVariables(mode, hostVars);\n}\n"]}
@@ -0,0 +1,32 @@
1
+ import { RESOURCE_TEARDOWN_METHOD, TOOL_CANCELLED_METHOD, TOOL_INPUT_PARTIAL_METHOD, TOOL_INPUT_METHOD, TOOL_RESULT_METHOD } from '@modelcontextprotocol/ext-apps';
2
+
3
+ // src/event-map.ts
4
+ var TOOLS_CALL_METHOD = "tools/call";
5
+ var RESOURCE_LIST_CHANGED_METHOD = "notifications/resources/list_changed";
6
+ var READ_RESOURCE_METHOD = "resources/read";
7
+ var LIST_RESOURCES_METHOD = "resources/list";
8
+ var ACTION_METHOD = "ai.nimblebrain/action";
9
+ var REQUEST_FILE_METHOD = "ai.nimblebrain/request-file";
10
+ var KEYDOWN_METHOD = "ai.nimblebrain/keydown";
11
+ var NIMBLEBRAIN_EXTENSIONS = {
12
+ /** App → host notification: run a host action (navigate, open a panel). */
13
+ action: { method: ACTION_METHOD, capability: ACTION_METHOD },
14
+ /** App → host request: the host's file picker, answered `{ files }`. */
15
+ requestFile: { method: REQUEST_FILE_METHOD, capability: REQUEST_FILE_METHOD },
16
+ /** App → host notification: a keyboard shortcut pressed inside the frame. */
17
+ keydown: { method: KEYDOWN_METHOD, capability: KEYDOWN_METHOD }
18
+ };
19
+ var EVENT_MAP = {
20
+ "tool-result": TOOL_RESULT_METHOD,
21
+ "tool-input": TOOL_INPUT_METHOD,
22
+ "tool-input-partial": TOOL_INPUT_PARTIAL_METHOD,
23
+ "tool-cancelled": TOOL_CANCELLED_METHOD,
24
+ teardown: RESOURCE_TEARDOWN_METHOD
25
+ };
26
+ function resolveEventMethod(name) {
27
+ return EVENT_MAP[name] ?? name;
28
+ }
29
+
30
+ export { KEYDOWN_METHOD, LIST_RESOURCES_METHOD, NIMBLEBRAIN_EXTENSIONS, READ_RESOURCE_METHOD, RESOURCE_LIST_CHANGED_METHOD, TOOLS_CALL_METHOD, resolveEventMethod };
31
+ //# sourceMappingURL=chunk-H3INUKA3.js.map
32
+ //# sourceMappingURL=chunk-H3INUKA3.js.map