agentic-ui-kit 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core.js CHANGED
@@ -1,4 +1,4 @@
1
- import { A as resolveEffort, C as toBlocks, D as estimateTokens, E as awaitingToolCalls, M as parsePartialJson, N as salvageJson, O as outputTokens, S as isActivityPart, T as applyEvent, _ as replacedId, a as readUiValue, b as formatEvent, c as uiMarkupGuide, d as isStateRef, f as matchesSchema, g as uiActionSchema, h as schemaErrors, i as parseUiMarkup, j as settleToolCall, k as reduceEvents, l as uiMarkupLanguage, m as renderUiToolName, n as checkUiMarkupCode, o as showUiTool, p as renderUiTool, r as findUiBlocks, s as showUiToolName, t as checkUiMarkup, u as checkUiTree, v as replacesGuide, w as activityTokens, x as readEventStream, y as replacesProperty } from "./ui-markup-CG9Lednh.js";
1
+ import { A as awaitingToolCalls, C as widgetLoaderMessages, D as toBlocks, E as isActivityPart, F as settleToolCall, I as parsePartialJson, L as salvageJson, M as outputTokens, N as reduceEvents, O as activityTokens, P as resolveEffort, S as widgetFrameLoader, T as readEventStream, _ as replacedId, a as readUiValue, b as resolveReplaces, c as uiMarkupGuide, d as isStateRef, f as matchesSchema, g as uiActionSchema, h as schemaErrors, i as parseUiMarkup, j as estimateTokens, k as applyEvent, l as uiMarkupLanguage, m as renderUiToolName, n as checkUiMarkupCode, o as showUiTool, p as renderUiTool, r as findUiBlocks, s as showUiToolName, t as checkUiMarkup, u as checkUiTree, v as replacesGuide, w as formatEvent, x as withCallId, y as replacesProperty } from "./src-Ce7xTfGU.js";
2
2
  // ../core/src/visualize.ts
3
3
  /**
4
4
  * The visualize preset: a tool the model calls to draw an interactive visual inline (like claude.ai's
@@ -100,4 +100,4 @@ function checkWidgetCode(html, allow = {}, toolName = visualizeTool.name) {
100
100
  feedback: ok && !warnings.length ? "Shown to the user." : ok ? `Shown to the user, and it works: do not draw it again now. Next time, to fit the chat better:\n${lines}` : `Shown, but it will not work as written. Fix these and call ${toolName} again with "replaces" set to this call's id, without remarking on the fix to the user:\n${lines}`
101
101
  };
102
102
  }
103
- export { activityTokens, applyEvent, awaitingToolCalls, checkUiMarkup, checkUiMarkupCode, checkUiTree, checkWidgetCode, estimateTokens, findUiBlocks, formatEvent, isActivityPart, isStateRef, matchesSchema, outputTokens, parsePartialJson, parseUiMarkup, readEventStream, readUiValue, reduceEvents, renderUiTool, renderUiToolName, replacedId, replacesGuide, replacesProperty, resolveEffort, salvageJson, schemaErrors, settleToolCall, showUiTool, showUiToolName, toBlocks, uiActionSchema, uiMarkupGuide, uiMarkupLanguage, visualizeGuide, visualizeTool };
103
+ export { activityTokens, applyEvent, awaitingToolCalls, checkUiMarkup, checkUiMarkupCode, checkUiTree, checkWidgetCode, estimateTokens, findUiBlocks, formatEvent, isActivityPart, isStateRef, matchesSchema, outputTokens, parsePartialJson, parseUiMarkup, readEventStream, readUiValue, reduceEvents, renderUiTool, renderUiToolName, replacedId, replacesGuide, replacesProperty, resolveEffort, resolveReplaces, salvageJson, schemaErrors, settleToolCall, showUiTool, showUiToolName, toBlocks, uiActionSchema, uiMarkupGuide, uiMarkupLanguage, visualizeGuide, visualizeTool, widgetFrameLoader, widgetLoaderMessages, withCallId };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { C as toBlocks, E as awaitingToolCalls, N as salvageJson, T as applyEvent, _ as replacedId, d as isStateRef, f as matchesSchema, g as uiActionSchema, i as parseUiMarkup, j as settleToolCall, w as activityTokens, x as readEventStream } from "./ui-markup-CG9Lednh.js";
2
+ import { A as awaitingToolCalls, C as widgetLoaderMessages, D as toBlocks, F as settleToolCall, L as salvageJson, O as activityTokens, T as readEventStream, _ as replacedId, b as resolveReplaces, d as isStateRef, f as matchesSchema, g as uiActionSchema, i as parseUiMarkup, k as applyEvent } from "./src-Ce7xTfGU.js";
3
3
  import { clsx } from "clsx";
4
4
  import { Autocomplete, Button as Button$1, Dialog, DialogTrigger, Disclosure as Disclosure$1, DisclosurePanel, Focusable, Header, Heading, Input, Keyboard, Link, Menu as Menu$1, MenuItem as MenuItem$1, MenuSection as MenuSection$1, MenuTrigger, MenuTrigger as MenuTrigger$1, Modal as Modal$1, ModalOverlay, Popover as Popover$1, SearchField, Separator, SubmenuTrigger, Text, ToggleButton, ToggleButtonGroup, Tooltip as Tooltip$1, TooltipTrigger, useFilter } from "react-aria-components";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -1282,12 +1282,34 @@ const useSidebar = () => useContext(SidebarContext);
1282
1282
  const isReady = (p) => p.status !== "streaming" && p.status !== "input" && p.status !== "error";
1283
1283
  const callsOf = (m) => m.parts.filter((p) => p.type === "tool_call");
1284
1284
  const replaces = (p) => replacedId(p.input ?? p.partialInput);
1285
+ const titleOf = (p) => {
1286
+ const t = (p.input ?? p.partialInput)?.title;
1287
+ return typeof t === "string" && t ? t : void 0;
1288
+ };
1289
+ /** Ids and titles of the calls seen so far, to resolve `replaces` (an id, or a title as a fallback). */
1290
+ function seen() {
1291
+ const ids = /* @__PURE__ */ new Set();
1292
+ const titles = /* @__PURE__ */ new Map();
1293
+ return {
1294
+ resolve: (call) => {
1295
+ const r = replaces(call);
1296
+ return r === void 0 ? void 0 : resolveReplaces(r, ids, titles);
1297
+ },
1298
+ add: (call) => {
1299
+ ids.add(call.id);
1300
+ const t = titleOf(call);
1301
+ if (t) titles.set(t, call.id);
1302
+ }
1303
+ };
1304
+ }
1285
1305
  /** One reply's chains: each call's first call (where the chain is drawn) and each first call's chain, in order. */
1286
1306
  function chainsOf(message) {
1287
1307
  const first = /* @__PURE__ */ new Map();
1288
1308
  const chains = /* @__PURE__ */ new Map();
1309
+ const known = seen();
1289
1310
  for (const call of callsOf(message)) {
1290
- const r = replaces(call);
1311
+ const r = known.resolve(call);
1312
+ known.add(call);
1291
1313
  const root = r !== void 0 && first.has(r) ? first.get(r) : call.id;
1292
1314
  first.set(call.id, root);
1293
1315
  chains.set(root, [...chains.get(root) ?? [], call]);
@@ -1300,13 +1322,16 @@ function chainsOf(message) {
1300
1322
  /** Calls replaced by a finished call in a later reply: replaced id → the newest such call. */
1301
1323
  function laterReplacements(messages) {
1302
1324
  const later = /* @__PURE__ */ new Map();
1325
+ const known = seen();
1303
1326
  for (const m of messages) {
1304
1327
  if (m.role !== "assistant") continue;
1305
- const own = new Set(callsOf(m).map((c) => c.id));
1306
- for (const call of callsOf(m)) {
1307
- const r = replaces(call);
1328
+ const calls = callsOf(m);
1329
+ const own = new Set(calls.map((c) => c.id));
1330
+ for (const call of calls) {
1331
+ const r = known.resolve(call);
1308
1332
  if (r !== void 0 && !own.has(r) && isReady(call)) later.set(r, call);
1309
1333
  }
1334
+ calls.forEach(known.add);
1310
1335
  }
1311
1336
  return later;
1312
1337
  }
@@ -1524,8 +1549,10 @@ function watchTheme(el, onChange) {
1524
1549
  media?.removeEventListener?.("change", onChange);
1525
1550
  };
1526
1551
  }
1527
- function WidgetFrame({ html, document: isDocument = false, streaming = false, title, loadingMessages, allow, csp: request, onPrompt, onOpenLink, onCallTool, onError, toolInput, toolResult, cancelled, className }) {
1552
+ function WidgetFrame({ html, document: isDocument = false, streaming = false, title, loadingMessages, allow, csp: request, onPrompt, onOpenLink, onCallTool, onError, toolInput, toolResult, cancelled, frameUrl: ownFrameUrl, className }) {
1528
1553
  const labels = useLabels();
1554
+ const hostFrameUrl = useWidgetHost().frameUrl;
1555
+ const frameUrl = ownFrameUrl ?? hostFrameUrl;
1529
1556
  const frame = useRef(null);
1530
1557
  const host = useRef(null);
1531
1558
  const [ready, setReady] = useState(false);
@@ -1547,7 +1574,8 @@ function WidgetFrame({ html, document: isDocument = false, streaming = false, ti
1547
1574
  onCallTool,
1548
1575
  onError,
1549
1576
  policy,
1550
- effective
1577
+ effective,
1578
+ doc
1551
1579
  });
1552
1580
  latest.current = {
1553
1581
  onPrompt,
@@ -1555,7 +1583,16 @@ function WidgetFrame({ html, document: isDocument = false, streaming = false, ti
1555
1583
  onCallTool,
1556
1584
  onError,
1557
1585
  policy,
1558
- effective
1586
+ effective,
1587
+ doc
1588
+ };
1589
+ const generation = useRef({
1590
+ doc,
1591
+ n: 0
1592
+ });
1593
+ if (generation.current.doc !== doc) generation.current = {
1594
+ doc,
1595
+ n: generation.current.n + 1
1559
1596
  };
1560
1597
  const send = (message) => frame.current?.contentWindow?.postMessage({
1561
1598
  jsonrpc: "2.0",
@@ -1605,6 +1642,14 @@ function WidgetFrame({ html, document: isDocument = false, streaming = false, ti
1605
1642
  });
1606
1643
  const onMessage = async (e) => {
1607
1644
  if (!frame.current || e.source !== frame.current.contentWindow) return;
1645
+ if (e.data?.type === widgetLoaderMessages.ready) {
1646
+ const html = latest.current.doc;
1647
+ if (html) frame.current.contentWindow?.postMessage({
1648
+ type: widgetLoaderMessages.document,
1649
+ html
1650
+ }, "*");
1651
+ return;
1652
+ }
1608
1653
  const m = e.data ?? {};
1609
1654
  if (typeof m !== "object" || typeof m.method !== "string") return;
1610
1655
  const { onPrompt, onOpenLink, onCallTool, effective } = latest.current;
@@ -1723,13 +1768,13 @@ function WidgetFrame({ html, document: isDocument = false, streaming = false, ti
1723
1768
  ref: frame,
1724
1769
  title: title ?? labels.widget,
1725
1770
  sandbox: "allow-scripts",
1726
- srcDoc: doc,
1771
+ ...frameUrl ? { src: frameUrl } : { srcDoc: doc },
1727
1772
  style: {
1728
1773
  height: height || (streaming ? 120 : 0),
1729
1774
  transition: "height 150ms ease-out"
1730
1775
  },
1731
1776
  className: "block w-full border-0 bg-transparent"
1732
- })]
1777
+ }, frameUrl ? generation.current.n : "srcdoc")]
1733
1778
  });
1734
1779
  }
1735
1780
  /** Pulls the host's abilities from context into frame props. */
@@ -4079,12 +4124,13 @@ function ChatTranscript({ messages, renderPart, widgets, header, notice, footer,
4079
4124
  ...contentPadding && { "--chat-content-padding": contentPadding }
4080
4125
  };
4081
4126
  const host = useMemo(() => {
4082
- const { allow, readResource } = { ...widgets };
4127
+ const { allow, readResource, frameUrl } = { ...widgets };
4083
4128
  const onOpenLink = widgets && "onOpenLink" in widgets ? widgets.onOpenLink : openInNewTab;
4084
4129
  return {
4085
4130
  ...allow && { allow },
4086
4131
  ...onOpenLink && { onOpenLink },
4087
- ...readResource && { readResource }
4132
+ ...readResource && { readResource },
4133
+ ...frameUrl && { frameUrl }
4088
4134
  };
4089
4135
  }, [widgets]);
4090
4136
  return /* @__PURE__ */ jsxs("div", {
@@ -422,6 +422,34 @@ function parseMessage(text) {
422
422
  if (data.length === 0) return void 0;
423
423
  return JSON.parse(data.join("\n"));
424
424
  }
425
+ // ../core/src/widget-loader.ts
426
+ /**
427
+ * The frame page for `WidgetHost.frameUrl`. Serve this HTML from your own origin (e.g. at /widget-frame.html)
428
+ * when your app's CSP blocks inline scripts: an `srcdoc` frame inherits the page's CSP, a frame loaded from
429
+ * a URL gets the CSP of its own response. The kit loads it sandboxed (opaque origin, no access to your app),
430
+ * posts the widget's document to it, and the loader writes it in. The kit's CSP <meta> inside that document
431
+ * still limits what the widget may load.
432
+ *
433
+ * Its response needs a CSP that lets the loader and the widget run, and no stricter than the kit's, e.g.
434
+ * `Content-Security-Policy: default-src 'none'; script-src 'unsafe-inline' https:; style-src 'unsafe-inline' https:; img-src data: blob: https:; font-src data: https:; connect-src https:`
435
+ * (or no CSP header on that path). Don't set X-Frame-Options or frame-ancestors that exclude your app.
436
+ */
437
+ /** Message types between the kit and the loader. */
438
+ const widgetLoaderMessages = {
439
+ /** Loader → kit: ready for the document. */
440
+ ready: "agentic-ui-kit:frame-ready",
441
+ /** Kit → loader: `{ type, html }`, the widget's whole document. */
442
+ document: "agentic-ui-kit:frame-document"
443
+ };
444
+ const widgetFrameLoader = `<!doctype html><html><head><meta charset="utf-8"></head><body><script>
445
+ addEventListener("message", function load(e) {
446
+ if (e.source !== parent || !e.data || e.data.type !== "${widgetLoaderMessages.document}" || typeof e.data.html !== "string") return;
447
+ removeEventListener("message", load);
448
+ document.open(); document.write(e.data.html); document.close();
449
+ });
450
+ parent.postMessage({ type: "${widgetLoaderMessages.ready}" }, "*");
451
+ <\/script></body></html>
452
+ `;
425
453
  // ../core/src/replaces.ts
426
454
  /**
427
455
  * Updating a visual in place: the drawing tools (show_widget, show_ui, render_ui) take an optional
@@ -432,15 +460,27 @@ function parseMessage(text) {
432
460
  /** The `replaces` input property, shared by the drawing tools. */
433
461
  const replacesProperty = {
434
462
  type: "string",
435
- description: "Id of an earlier call of this tool whose visual this one updates in place: after a fix, or when the user asks for a change. Leave it out for a new visual."
463
+ description: "The id (or title) of an earlier visual of this tool that this one updates in place: after a fix, or when the user asks for a change. Its tool result says \"Drawn as <id>\". Leave it out for a new visual."
436
464
  };
437
465
  /** One line for a drawing tool's description or guide. */
438
- const replacesGuide = "To change a visual you already drew (a fix, or the user asks for a change), call the tool again with `replaces` set to that call's id: it is redrawn in place. Never draw a second copy without it.";
466
+ const replacesGuide = "To change a visual you already drew (a fix, or the user asks for a change), call the tool again with `replaces` set to that visual's id (its tool result says Drawn as <id>): it is redrawn in place. Never draw a second copy without it.";
439
467
  /** The id a drawing tool call replaces, from its (streaming) input; undefined if none. */
440
468
  function replacedId(input) {
441
469
  const r = input?.replaces;
442
470
  return typeof r === "string" && r ? r : void 0;
443
471
  }
472
+ /**
473
+ * The tool result for a drawing call: the check's feedback, prefixed with the call's id, so the model can
474
+ * pass it as `replaces` later. Use it when the model may not see tool call ids (many chat templates drop
475
+ * them): `withCallId(call.id, checkWidgetCode(...).feedback)`.
476
+ */
477
+ function withCallId(id, feedback) {
478
+ return `Drawn as ${id}. ${feedback}`;
479
+ }
480
+ /** What `replaces` points at: the id of a call, or else the title of the latest call with that title. */
481
+ function resolveReplaces(ref, ids, titles) {
482
+ return ids.has(ref) ? ref : titles.get(ref);
483
+ }
444
484
  // ../core/src/ui-tree.ts
445
485
  /**
446
486
  * Host-registered components: instead of HTML, the model writes a small tree of components the host
@@ -1103,4 +1143,4 @@ When a visual helps more than text (options to compare, a plan, a checklist, num
1103
1143
  ${markupRules(components)}
1104
1144
  - Keep explaining in text around the block. One block per answer, unless the user asks for more.`;
1105
1145
  }
1106
- export { resolveEffort as A, toBlocks as C, estimateTokens as D, awaitingToolCalls as E, parsePartialJson as M, salvageJson as N, outputTokens as O, isActivityPart as S, applyEvent as T, replacedId as _, readUiValue as a, formatEvent as b, uiMarkupGuide as c, isStateRef as d, matchesSchema as f, uiActionSchema as g, schemaErrors as h, parseUiMarkup as i, settleToolCall as j, reduceEvents as k, uiMarkupLanguage as l, renderUiToolName as m, checkUiMarkupCode as n, showUiTool as o, renderUiTool as p, findUiBlocks as r, showUiToolName as s, checkUiMarkup as t, checkUiTree as u, replacesGuide as v, activityTokens as w, readEventStream as x, replacesProperty as y };
1146
+ export { awaitingToolCalls as A, widgetLoaderMessages as C, toBlocks as D, isActivityPart as E, settleToolCall as F, parsePartialJson as I, salvageJson as L, outputTokens as M, reduceEvents as N, activityTokens as O, resolveEffort as P, widgetFrameLoader as S, readEventStream as T, replacedId as _, readUiValue as a, resolveReplaces as b, uiMarkupGuide as c, isStateRef as d, matchesSchema as f, uiActionSchema as g, schemaErrors as h, parseUiMarkup as i, estimateTokens as j, applyEvent as k, uiMarkupLanguage as l, renderUiToolName as m, checkUiMarkupCode as n, showUiTool as o, renderUiTool as p, findUiBlocks as r, showUiToolName as s, checkUiMarkup as t, checkUiTree as u, replacesGuide as v, formatEvent as w, withCallId as x, replacesProperty as y };
@@ -5,7 +5,8 @@ export { toBlocks, isActivityPart } from "./blocks";
5
5
  export type { Block, ActivityPart } from "./blocks";
6
6
  export { readEventStream, formatEvent } from "./sse";
7
7
  export { parsePartialJson, salvageJson } from "./partial-json";
8
- export { replacesProperty, replacesGuide, replacedId } from "./replaces";
8
+ export { widgetFrameLoader, widgetLoaderMessages } from "./widget-loader";
9
+ export { replacesProperty, replacesGuide, replacedId, resolveReplaces, withCallId } from "./replaces";
9
10
  export { visualizeTool, visualizeGuide, checkWidgetCode } from "./visualize";
10
11
  export type { VisualizeInput, WidgetCodeCheck } from "./visualize";
11
12
  export { matchesSchema, schemaErrors, checkUiTree, isStateRef, renderUiTool, renderUiToolName, uiActionSchema } from "./ui-tree";
@@ -7,9 +7,17 @@
7
7
  /** The `replaces` input property, shared by the drawing tools. */
8
8
  export declare const replacesProperty: {
9
9
  readonly type: "string";
10
- readonly description: "Id of an earlier call of this tool whose visual this one updates in place: after a fix, or when the user asks for a change. Leave it out for a new visual.";
10
+ readonly description: "The id (or title) of an earlier visual of this tool that this one updates in place: after a fix, or when the user asks for a change. Its tool result says \"Drawn as <id>\". Leave it out for a new visual.";
11
11
  };
12
12
  /** One line for a drawing tool's description or guide. */
13
- export declare const replacesGuide = "To change a visual you already drew (a fix, or the user asks for a change), call the tool again with `replaces` set to that call's id: it is redrawn in place. Never draw a second copy without it.";
13
+ export declare const replacesGuide = "To change a visual you already drew (a fix, or the user asks for a change), call the tool again with `replaces` set to that visual's id (its tool result says Drawn as <id>): it is redrawn in place. Never draw a second copy without it.";
14
14
  /** The id a drawing tool call replaces, from its (streaming) input; undefined if none. */
15
15
  export declare function replacedId(input: unknown): string | undefined;
16
+ /**
17
+ * The tool result for a drawing call: the check's feedback, prefixed with the call's id, so the model can
18
+ * pass it as `replaces` later. Use it when the model may not see tool call ids (many chat templates drop
19
+ * them): `withCallId(call.id, checkWidgetCode(...).feedback)`.
20
+ */
21
+ export declare function withCallId(id: string, feedback: string): string;
22
+ /** What `replaces` points at: the id of a call, or else the title of the latest call with that title. */
23
+ export declare function resolveReplaces(ref: string, ids: ReadonlySet<string>, titles: ReadonlyMap<string, string>): string | undefined;
@@ -80,7 +80,7 @@ export declare function showUiTool(components: readonly UiComponentSpec[], name?
80
80
  };
81
81
  readonly replaces: {
82
82
  readonly type: "string";
83
- readonly description: "Id of an earlier call of this tool whose visual this one updates in place: after a fix, or when the user asks for a change. Leave it out for a new visual.";
83
+ readonly description: "The id (or title) of an earlier visual of this tool that this one updates in place: after a fix, or when the user asks for a change. Its tool result says \"Drawn as <id>\". Leave it out for a new visual.";
84
84
  };
85
85
  };
86
86
  readonly required: readonly ["markup"];
@@ -87,7 +87,7 @@ export declare function renderUiTool(components: readonly UiComponentSpec[]): {
87
87
  };
88
88
  replaces: {
89
89
  readonly type: "string";
90
- readonly description: "Id of an earlier call of this tool whose visual this one updates in place: after a fix, or when the user asks for a change. Leave it out for a new visual.";
90
+ readonly description: "The id (or title) of an earlier visual of this tool that this one updates in place: after a fix, or when the user asks for a change. Its tool result says \"Drawn as <id>\". Leave it out for a new visual.";
91
91
  };
92
92
  };
93
93
  required: string[];
@@ -37,14 +37,14 @@ export declare const visualizeTool: {
37
37
  };
38
38
  readonly replaces: {
39
39
  readonly type: "string";
40
- readonly description: "Id of an earlier call of this tool whose visual this one updates in place: after a fix, or when the user asks for a change. Leave it out for a new visual.";
40
+ readonly description: "The id (or title) of an earlier visual of this tool that this one updates in place: after a fix, or when the user asks for a change. Its tool result says \"Drawn as <id>\". Leave it out for a new visual.";
41
41
  };
42
42
  };
43
43
  readonly required: readonly ["widget_code"];
44
44
  };
45
45
  };
46
46
  /** Design guide for the system prompt when the visualize tool is on. */
47
- export declare const visualizeGuide = "## Visuals (show_widget)\nUse show_widget when a visual says it better than text: data to compare, a plan to adjust, a place to see, a\nprocess to step through. Keep explaining in text around it; the widget is not the whole answer.\n\nHow the frame works\n- widget_code is a fragment: no <!doctype>, <html>, <head> or <body>. Put <style> first, then markup, then <script>.\n- It renders while you write it; scripts run once it is complete, in order. Put scripts last.\n- No network unless the host allows it: no fetch, no external images or fonts. Inline your data.\n- The background is transparent and the frame sizes itself to your content. Add no outer margin or padding,\n no outer border or card around the whole widget, and no fixed height on the root.\n- Two globals: sendPrompt(text) sends a message as the user (for \"Add these to my calendar\", \"Explain this\"),\n openLink(url) opens an http(s) link. Prefer local state (JavaScript in the widget) over round trips: filter,\n toggle, recalculate and animate in place; use sendPrompt only for what needs you.\n- To change a visual you already drew (a fix, or the user asks for a change), call the tool again with `replaces` set to that call's id: it is redrawn in place. Never draw a second copy without it.\n\nLook\n- Use the chat's theme variables, never hard-coded colors or fonts: var(--chat-fg) text, var(--chat-muted) and\n var(--chat-subtle) secondary text, var(--chat-surface) and var(--chat-raised) fills, var(--chat-border) and\n var(--chat-divider) lines, var(--chat-hover) hover, var(--chat-accent) with var(--chat-accent-fg) for the one\n thing that matters most, var(--chat-check) good, var(--chat-warning), var(--chat-danger) bad;\n var(--chat-font-sans), var(--chat-font-mono); var(--chat-ui-font-size), var(--chat-ui-font-size-sm);\n var(--chat-radius-sm), var(--chat-radius-md), var(--chat-radius-lg).\n- Quiet by default: thin borders, few fills, one accent. Readable labels, aligned numbers, no decoration.\n- Set apart what you added or changed from what was already there: your proposals in the accent with a small\n \"New\" (or \"Changed\") tag, existing data quiet. The user must see at a glance what is yours.\n- Make it work on a narrow screen (down to 320px wide) and with a keyboard: real <button>s, visible focus.";
47
+ export declare const visualizeGuide = "## Visuals (show_widget)\nUse show_widget when a visual says it better than text: data to compare, a plan to adjust, a place to see, a\nprocess to step through. Keep explaining in text around it; the widget is not the whole answer.\n\nHow the frame works\n- widget_code is a fragment: no <!doctype>, <html>, <head> or <body>. Put <style> first, then markup, then <script>.\n- It renders while you write it; scripts run once it is complete, in order. Put scripts last.\n- No network unless the host allows it: no fetch, no external images or fonts. Inline your data.\n- The background is transparent and the frame sizes itself to your content. Add no outer margin or padding,\n no outer border or card around the whole widget, and no fixed height on the root.\n- Two globals: sendPrompt(text) sends a message as the user (for \"Add these to my calendar\", \"Explain this\"),\n openLink(url) opens an http(s) link. Prefer local state (JavaScript in the widget) over round trips: filter,\n toggle, recalculate and animate in place; use sendPrompt only for what needs you.\n- To change a visual you already drew (a fix, or the user asks for a change), call the tool again with `replaces` set to that visual's id (its tool result says Drawn as <id>): it is redrawn in place. Never draw a second copy without it.\n\nLook\n- Use the chat's theme variables, never hard-coded colors or fonts: var(--chat-fg) text, var(--chat-muted) and\n var(--chat-subtle) secondary text, var(--chat-surface) and var(--chat-raised) fills, var(--chat-border) and\n var(--chat-divider) lines, var(--chat-hover) hover, var(--chat-accent) with var(--chat-accent-fg) for the one\n thing that matters most, var(--chat-check) good, var(--chat-warning), var(--chat-danger) bad;\n var(--chat-font-sans), var(--chat-font-mono); var(--chat-ui-font-size), var(--chat-ui-font-size-sm);\n var(--chat-radius-sm), var(--chat-radius-md), var(--chat-radius-lg).\n- Quiet by default: thin borders, few fills, one accent. Readable labels, aligned numbers, no decoration.\n- Set apart what you added or changed from what was already there: your proposals in the accent with a small\n \"New\" (or \"Changed\") tag, existing data quiet. The user must see at a glance what is yours.\n- Make it work on a narrow screen (down to 320px wide) and with a keyboard: real <button>s, visible focus.";
48
48
  /** What `checkWidgetCode` found. */
49
49
  export interface WidgetCodeCheck {
50
50
  /** Nothing will break: no errors (warnings may remain). */
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The frame page for `WidgetHost.frameUrl`. Serve this HTML from your own origin (e.g. at /widget-frame.html)
3
+ * when your app's CSP blocks inline scripts: an `srcdoc` frame inherits the page's CSP, a frame loaded from
4
+ * a URL gets the CSP of its own response. The kit loads it sandboxed (opaque origin, no access to your app),
5
+ * posts the widget's document to it, and the loader writes it in. The kit's CSP <meta> inside that document
6
+ * still limits what the widget may load.
7
+ *
8
+ * Its response needs a CSP that lets the loader and the widget run, and no stricter than the kit's, e.g.
9
+ * `Content-Security-Policy: default-src 'none'; script-src 'unsafe-inline' https:; style-src 'unsafe-inline' https:; img-src data: blob: https:; font-src data: https:; connect-src https:`
10
+ * (or no CSP header on that path). Don't set X-Frame-Options or frame-ancestors that exclude your app.
11
+ */
12
+ /** Message types between the kit and the loader. */
13
+ export declare const widgetLoaderMessages: {
14
+ /** Loader → kit: ready for the document. */
15
+ readonly ready: "agentic-ui-kit:frame-ready";
16
+ /** Kit → loader: `{ type, html }`, the widget's whole document. */
17
+ readonly document: "agentic-ui-kit:frame-document";
18
+ };
19
+ export declare const widgetFrameLoader: string;
@@ -1,4 +1,4 @@
1
- import type { ToolCallPart, WidgetPart } from "../../../core/src/index.js";
1
+ import { type ToolCallPart, type WidgetPart } from "../../../core/src/index.js";
2
2
  import { type ReactNode } from "react";
3
3
  /** Origins a widget may load from, e.g. "https://unpkg.com". Entries that are not plain origins are dropped. */
4
4
  export interface WidgetAllow {
@@ -34,6 +34,11 @@ export interface WidgetHost {
34
34
  partId: string;
35
35
  message: string;
36
36
  }) => void) | undefined;
37
+ /**
38
+ * Load widgets from this page (core's `widgetFrameLoader`, served by you) instead of `srcdoc`. Set it when
39
+ * your page's CSP blocks inline scripts: srcdoc frames inherit that CSP, so widget scripts would not run.
40
+ */
41
+ frameUrl?: string;
37
42
  }
38
43
  /** Sets what widgets below may do. ChatView and ChatTranscript set it from their `widgets` prop. */
39
44
  export declare function WidgetHostProvider({ host, children }: {
@@ -69,7 +74,7 @@ export declare const WIDGET_MAX_HEIGHT = 2000;
69
74
  export declare function intersectAllow(request: WidgetAllow, allow?: WidgetAllow): WidgetAllow;
70
75
  /** The Content-Security-Policy of a widget's document. */
71
76
  export declare function widgetCsp(allow?: WidgetAllow): string;
72
- export declare function WidgetFrame({ html, document: isDocument, streaming, title, loadingMessages, allow, csp: request, onPrompt, onOpenLink, onCallTool, onError, toolInput, toolResult, cancelled, className, }: WidgetFrameProps): import("react").JSX.Element;
77
+ export declare function WidgetFrame({ html, document: isDocument, streaming, title, loadingMessages, allow, csp: request, onPrompt, onOpenLink, onCallTool, onError, toolInput, toolResult, cancelled, frameUrl: ownFrameUrl, className, }: WidgetFrameProps): import("react").JSX.Element;
73
78
  /** The default look of a `widget` part: inline html, or an app UI the host reads from `resourceUri`. */
74
79
  export declare function WidgetPartView({ part }: {
75
80
  part: WidgetPart;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-ui-kit",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "React UI kit for LLM chat and agentic apps: streaming messages, tool calls, approvals, themable with CSS variables.",
5
5
  "license": "MIT",
6
6
  "type": "module",