@assistant-ui/react 0.15.25 → 0.15.27
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/hooks/useToolCallElapsed.d.ts.map +1 -1
- package/dist/hooks/useToolCallElapsed.js +3 -2
- package/dist/hooks/useToolCallElapsed.js.map +1 -1
- package/dist/index.d.ts +31 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/mcp-apps/McpAppRenderer.d.ts.map +1 -1
- package/dist/mcp-apps/McpAppRenderer.js +3 -2
- package/dist/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/mcp-apps/app-frame.d.ts.map +1 -1
- package/dist/mcp-apps/app-frame.js +58 -48
- package/dist/mcp-apps/app-frame.js.map +1 -1
- package/dist/mcp-apps/bridge.d.ts.map +1 -1
- package/dist/mcp-apps/bridge.js +28 -3
- package/dist/mcp-apps/bridge.js.map +1 -1
- package/dist/mcp-apps/csp.d.ts +4 -0
- package/dist/mcp-apps/csp.d.ts.map +1 -0
- package/dist/mcp-apps/csp.js +93 -0
- package/dist/mcp-apps/csp.js.map +1 -0
- package/dist/mcp-apps/index.d.ts +1 -1
- package/dist/mcp-apps/index.d.ts.map +1 -1
- package/dist/mcp-apps/types.d.ts +19 -1
- package/dist/mcp-apps/types.d.ts.map +1 -1
- package/dist/mcp-apps/types.js.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalRoot.d.ts +1 -0
- package/dist/primitives/assistantModal/AssistantModalRoot.d.ts.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalRoot.js.map +1 -1
- package/dist/primitives/attachment.d.ts +3 -1
- package/dist/primitives/attachment.d.ts.map +1 -1
- package/dist/primitives/attachment.js.map +1 -1
- package/dist/primitives/composer/ComposerAttachmentDropzone.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerAttachmentDropzone.js +14 -12
- package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -1
- package/dist/primitives/composer/ComposerCancel.d.ts +5 -1
- package/dist/primitives/composer/ComposerCancel.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerCancel.js +14 -1
- package/dist/primitives/composer/ComposerCancel.js.map +1 -1
- package/dist/primitives/composer/ComposerInput.d.ts +14 -0
- package/dist/primitives/composer/ComposerInput.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerInput.js +4 -3
- package/dist/primitives/composer/ComposerInput.js.map +1 -1
- package/dist/primitives/composer/ComposerRoot.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerRoot.js +24 -19
- package/dist/primitives/composer/ComposerRoot.js.map +1 -1
- package/dist/primitives/composer/composerInputSelector.d.ts +3 -0
- package/dist/primitives/composer/composerInputSelector.d.ts.map +1 -0
- package/dist/primitives/composer/composerInputSelector.js +7 -0
- package/dist/primitives/composer/composerInputSelector.js.map +1 -0
- package/dist/primitives/composer/useComposerCancelWithFocus.d.ts +2 -0
- package/dist/primitives/composer/useComposerCancelWithFocus.d.ts.map +1 -0
- package/dist/primitives/composer/useComposerCancelWithFocus.js +42 -0
- package/dist/primitives/composer/useComposerCancelWithFocus.js.map +1 -0
- package/dist/primitives/composer.d.ts +35 -14
- package/dist/primitives/composer.d.ts.map +1 -1
- package/dist/primitives/composer.js.map +1 -1
- package/dist/primitives/message/MessagePartsGrouped.d.ts +8 -1
- package/dist/primitives/message/MessagePartsGrouped.d.ts.map +1 -1
- package/dist/primitives/message/MessagePartsGrouped.js +58 -278
- package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarRoot.d.ts.map +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js +5 -2
- package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js.map +1 -1
- package/dist/primitives/thread/ThreadViewport.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadViewport.js +4 -3
- package/dist/primitives/thread/ThreadViewport.js.map +1 -1
- package/dist/primitives/thread/topAnchor/useTopAnchorReserve.js +2 -2
- package/dist/primitives/thread/topAnchor/useTopAnchorReserve.js.map +1 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.d.ts.map +1 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.js +90 -21
- package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
- package/dist/sandbox-host/SandboxHost.d.ts.map +1 -1
- package/dist/sandbox-host/SandboxHost.js +3 -2
- package/dist/sandbox-host/SandboxHost.js.map +1 -1
- package/dist/unstable/useComposerInput.d.ts +9 -4
- package/dist/unstable/useComposerInput.d.ts.map +1 -1
- package/dist/unstable/useComposerInput.js +6 -4
- package/dist/unstable/useComposerInput.js.map +1 -1
- package/dist/unstable/useComposerInputHistory.d.ts +3 -2
- package/dist/unstable/useComposerInputHistory.d.ts.map +1 -1
- package/dist/unstable/useComposerInputHistory.js +2 -2
- package/dist/unstable/useComposerInputHistory.js.map +1 -1
- package/dist/unstable/useLiveCompletionAdapter.d.ts +3 -2
- package/dist/unstable/useLiveCompletionAdapter.d.ts.map +1 -1
- package/dist/unstable/useLiveCompletionAdapter.js +8 -7
- package/dist/unstable/useLiveCompletionAdapter.js.map +1 -1
- package/dist/unstable/useMentionAdapter.d.ts +12 -3
- package/dist/unstable/useMentionAdapter.d.ts.map +1 -1
- package/dist/unstable/useMentionAdapter.js +2 -2
- package/dist/unstable/useMentionAdapter.js.map +1 -1
- package/dist/unstable/useMessageStallDetection.d.ts +4 -2
- package/dist/unstable/useMessageStallDetection.d.ts.map +1 -1
- package/dist/unstable/useMessageStallDetection.js +2 -2
- package/dist/unstable/useMessageStallDetection.js.map +1 -1
- package/dist/unstable/useSlashCommandAdapter.d.ts +5 -2
- package/dist/unstable/useSlashCommandAdapter.d.ts.map +1 -1
- package/dist/unstable/useSlashCommandAdapter.js +5 -4
- package/dist/unstable/useSlashCommandAdapter.js.map +1 -1
- package/dist/unstable/webmcp/useWebMcpProvider.d.ts +4 -0
- package/dist/unstable/webmcp/useWebMcpProvider.d.ts.map +1 -1
- package/dist/unstable/webmcp/useWebMcpProvider.js +2 -0
- package/dist/unstable/webmcp/useWebMcpProvider.js.map +1 -1
- package/dist/utils/getSelectionMessageId.d.ts.map +1 -1
- package/dist/utils/getSelectionMessageId.js +31 -8
- package/dist/utils/getSelectionMessageId.js.map +1 -1
- package/dist/utils/hooks/useOnResizeContent.d.ts.map +1 -1
- package/dist/utils/hooks/useOnResizeContent.js +2 -22
- package/dist/utils/hooks/useOnResizeContent.js.map +1 -1
- package/dist/utils/useIsomorphicLayoutEffect.d.ts +3 -0
- package/dist/utils/useIsomorphicLayoutEffect.d.ts.map +1 -0
- package/dist/utils/useIsomorphicLayoutEffect.js +6 -0
- package/dist/utils/useIsomorphicLayoutEffect.js.map +1 -0
- package/package.json +10 -10
- package/src/hooks/useToolCallElapsed.test.tsx +4 -2
- package/src/hooks/useToolCallElapsed.ts +3 -2
- package/src/index.ts +20 -2
- package/src/mcp-apps/McpAppRenderer.test.tsx +7 -2
- package/src/mcp-apps/McpAppRenderer.tsx +3 -9
- package/src/mcp-apps/app-frame.test.tsx +102 -6
- package/src/mcp-apps/app-frame.tsx +7 -10
- package/src/mcp-apps/bridge.test.ts +154 -0
- package/src/mcp-apps/bridge.ts +51 -9
- package/src/mcp-apps/csp.test.ts +112 -0
- package/src/mcp-apps/csp.ts +124 -0
- package/src/mcp-apps/index.ts +1 -0
- package/src/mcp-apps/types.ts +16 -1
- package/src/model-context/frame/AssistantFrame.test.ts +45 -0
- package/src/model-context/frame/SPEC_AssistantFrame.md +1 -0
- package/src/primitives/assistantModal/AssistantModalRoot.tsx +1 -0
- package/src/primitives/attachment.ts +4 -1
- package/src/primitives/composer/ComposerAttachmentDropzone.test.tsx +32 -1
- package/src/primitives/composer/ComposerAttachmentDropzone.tsx +14 -12
- package/src/primitives/composer/ComposerCancel.focus.test.tsx +307 -0
- package/src/primitives/composer/ComposerCancel.ts +8 -1
- package/src/primitives/composer/ComposerDisabledThread.test.tsx +121 -0
- package/src/primitives/composer/ComposerInput.tsx +19 -1
- package/src/primitives/composer/ComposerRoot.tsx +6 -5
- package/src/primitives/composer/composerInputSelector.ts +4 -0
- package/src/primitives/composer/useComposerCancelWithFocus.ts +57 -0
- package/src/primitives/composer.ts +56 -21
- package/src/primitives/message/MessagePartsGrouped.test.tsx +106 -2
- package/src/primitives/message/MessagePartsGrouped.tsx +36 -134
- package/src/primitives/selectionToolbar/SelectionToolbarRoot.test.tsx +20 -12
- package/src/primitives/selectionToolbar/SelectionToolbarRoot.tsx +6 -2
- package/src/primitives/thread/ThreadViewport.tsx +3 -3
- package/src/primitives/thread/topAnchor/useTopAnchorReserve.ts +2 -2
- package/src/primitives/thread/useThreadViewportAutoScroll.test.tsx +666 -63
- package/src/primitives/thread/useThreadViewportAutoScroll.ts +201 -73
- package/src/sandbox-host/SandboxHost.tsx +3 -8
- package/src/unstable/useComposerInput.ts +9 -4
- package/src/unstable/useComposerInputHistory.ts +3 -2
- package/src/unstable/useLiveCompletionAdapter.ts +9 -14
- package/src/unstable/useMentionAdapter.ts +12 -3
- package/src/unstable/useMessageStallDetection.ts +4 -2
- package/src/unstable/useSlashCommandAdapter.ts +8 -4
- package/src/unstable/webmcp/useWebMcpProvider.ts +4 -0
- package/src/utils/getSelectionMessageId.test.ts +231 -0
- package/src/utils/getSelectionMessageId.ts +45 -7
- package/src/utils/hooks/useOnResizeContent.ts +2 -29
- package/src/utils/useIsomorphicLayoutEffect.test.tsx +21 -0
- package/src/utils/useIsomorphicLayoutEffect.ts +9 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSlashCommandAdapter.js","names":["
|
|
1
|
+
{"version":3,"file":"useSlashCommandAdapter.js","names":["useMemo","useRef","useIsomorphicLayoutEffect","Unstable_TriggerAdapter","Unstable_TriggerItem","Unstable_IconComponent","matchesTriggerItemQuery","Unstable_SlashCommand","id","label","description","icon","execute","Unstable_UseSlashCommandAdapterOptions","commands","removeOnExecute","iconMap","Record","fallbackIcon","Unstable_SlashCommandAction","onExecute","item","unstable_useSlashCommandAdapter","options","adapter","action","commandsRef","committedItemsRef","undefined","nextItems","map","toItem","items","areTriggerItemsEqual","current","categories","categoryItems","search","query","lower","toLowerCase","filter","find","c","cmd","type","metadata","previous","next","length","every","index","nextItem"],"sources":["../../src/unstable/useSlashCommandAdapter.ts"],"sourcesContent":["\"use client\";\n\nimport { useMemo, useRef } from \"react\";\nimport { useIsomorphicLayoutEffect } from \"../utils/useIsomorphicLayoutEffect\";\nimport type {\n Unstable_TriggerAdapter,\n Unstable_TriggerItem,\n} from \"@assistant-ui/core\";\nimport type { Unstable_IconComponent } from \"./useMentionAdapter\";\nimport { matchesTriggerItemQuery } from \"../primitives/composer/trigger/matchesTriggerItemQuery\";\n\n/** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_SlashCommand = {\n readonly id: string;\n readonly label?: string | undefined;\n readonly description?: string | undefined;\n readonly icon?: string | undefined;\n readonly execute: () => void;\n};\n\n/** @deprecated Experimental since 2026-04-06. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_UseSlashCommandAdapterOptions = {\n readonly commands: readonly Unstable_SlashCommand[];\n /** Strip the trigger text from the composer after executing. @default false */\n readonly removeOnExecute?: boolean | undefined;\n /** Maps `metadata.icon` / `category.id` string keys to React components. */\n readonly iconMap?: Record<string, Unstable_IconComponent>;\n /** Fallback icon when no entry in `iconMap` matches. */\n readonly fallbackIcon?: Unstable_IconComponent;\n};\n\n/** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_SlashCommandAction = {\n readonly onExecute: (item: Unstable_TriggerItem) => void;\n readonly removeOnExecute?: boolean | undefined;\n};\n\n/**\n * Bundles slash command definitions (with inline `execute` callbacks) into\n * `{adapter, action}` that plug directly into `ComposerTriggerPopover`.\n * `execute` stays in the hook closure and is never attached to the returned\n * `TriggerItem`, keeping items serializable.\n *\n * @deprecated Experimental since 2026-04-06. Not scheduled for removal; the API may change in any release.\n *\n * @example\n * ```tsx\n * const slash = unstable_useSlashCommandAdapter({\n * commands: [\n * { id: \"summarize\", execute: () => runSummarize(), icon: \"FileText\" },\n * { id: \"translate\", execute: () => runTranslate(), icon: \"Languages\" },\n * ],\n * });\n *\n * <ComposerTriggerPopover char=\"/\" {...slash} />\n * ```\n */\nexport function unstable_useSlashCommandAdapter(\n options: Unstable_UseSlashCommandAdapterOptions,\n): {\n adapter: Unstable_TriggerAdapter;\n action: Unstable_SlashCommandAction;\n iconMap?: Record<string, Unstable_IconComponent>;\n fallbackIcon?: Unstable_IconComponent;\n} {\n const { commands, removeOnExecute } = options;\n\n const commandsRef = useRef(commands);\n const committedItemsRef = useRef<readonly Unstable_TriggerItem[]>(undefined);\n const nextItems = commands.map(toItem);\n // A referential cache, never state: `items` is always structurally equal to\n // `nextItems`, so the render-time read cannot change what the adapter shows.\n const items = areTriggerItemsEqual(committedItemsRef.current, nextItems)\n ? committedItemsRef.current\n : nextItems;\n\n // The adapter's callbacks must only observe commands from committed renders.\n useIsomorphicLayoutEffect(() => {\n commandsRef.current = commands;\n committedItemsRef.current = items;\n }, [commands, items]);\n\n const adapter = useMemo<Unstable_TriggerAdapter>(\n () => ({\n categories: () => [],\n categoryItems: () => [],\n search: (query: string) => {\n const lower = query.toLowerCase();\n return items.filter((item) => matchesTriggerItemQuery(item, lower));\n },\n }),\n [items],\n );\n\n const action = useMemo<Unstable_SlashCommandAction>(\n () => ({\n onExecute: (item) => {\n commandsRef.current.find((c) => c.id === item.id)?.execute();\n },\n ...(removeOnExecute !== undefined ? { removeOnExecute } : {}),\n }),\n [removeOnExecute],\n );\n\n return useMemo(\n () => ({\n adapter,\n action,\n ...(options.iconMap ? { iconMap: options.iconMap } : {}),\n ...(options.fallbackIcon ? { fallbackIcon: options.fallbackIcon } : {}),\n }),\n [adapter, action, options.iconMap, options.fallbackIcon],\n );\n}\n\nfunction toItem(cmd: Unstable_SlashCommand): Unstable_TriggerItem {\n return {\n id: cmd.id,\n type: \"command\",\n label: cmd.label ?? `/${cmd.id}`,\n ...(cmd.description !== undefined ? { description: cmd.description } : {}),\n ...(cmd.icon !== undefined ? { metadata: { icon: cmd.icon } } : {}),\n };\n}\n\nfunction areTriggerItemsEqual(\n previous: readonly Unstable_TriggerItem[] | undefined,\n next: readonly Unstable_TriggerItem[],\n): previous is readonly Unstable_TriggerItem[] {\n if (!previous || previous.length !== next.length) return false;\n\n return previous.every((item, index) => {\n const nextItem = next[index];\n return (\n nextItem !== undefined &&\n item.id === nextItem.id &&\n item.label === nextItem.label &&\n item.description === nextItem.description &&\n item.metadata?.icon === nextItem.metadata?.icon\n );\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAyDA,SAAgBsB,gCACdC,SAMA;CACA,MAAM,EAAET,UAAUC,oBAAoBQ;CAEtC,MAAMG,cAAczB,OAAOa,QAAQ;CACnC,MAAMa,oBAAoB1B,OAAwC2B,KAAAA,CAAS;CAC3E,MAAMC,YAAYf,SAASgB,IAAIC,MAAM;CAGrC,MAAMC,QAAQC,qBAAqBN,kBAAkBO,SAASL,SAAS,IACnEF,kBAAkBO,UAClBL;CAGJ3B,gCAAgC;EAC9BwB,YAAYQ,UAAUpB;EACtBa,kBAAkBO,UAAUF;CAC9B,GAAG,CAAClB,UAAUkB,KAAK,CAAC;CAEpB,MAAMR,UAAUxB,eACP;EACLmC,kBAAkB,CAAA;EAClBC,qBAAqB,CAAA;EACrBC,SAASC,UAAkB;GACzB,MAAMC,QAAQD,MAAME,YAAY;GAChC,OAAOR,MAAMS,QAAQpB,SAASf,wBAAwBe,MAAMkB,KAAK,CAAC;EACpE;CACF,IACA,CAACP,KAAK,CACR;CAEA,MAAMP,SAASzB,eACN;EACLoB,YAAYC,SAAS;GACnBK,YAAYQ,QAAQQ,MAAMC,MAAMA,EAAEnC,OAAOa,KAAKb,EAAE,CAAC,EAAEI,QAAQ;EAC7D;EACA,GAAIG,oBAAoBa,KAAAA,IAAY,EAAEb,gBAAgB,IAAI,CAAC;CAC7D,IACA,CAACA,eAAe,CAClB;CAEA,OAAOf,eACE;EACLwB;EACAC;EACA,GAAIF,QAAQP,UAAU,EAAEA,SAASO,QAAQP,QAAQ,IAAI,CAAC;EACtD,GAAIO,QAAQL,eAAe,EAAEA,cAAcK,QAAQL,aAAa,IAAI,CAAC;CACvE,IACA;EAACM;EAASC;EAAQF,QAAQP;EAASO,QAAQL;CAAY,CACzD;AACF;AAEA,SAASa,OAAOa,KAAkD;CAChE,OAAO;EACLpC,IAAIoC,IAAIpC;EACRqC,MAAM;EACNpC,OAAOmC,IAAInC,SAAS,IAAImC,IAAIpC;EAC5B,GAAIoC,IAAIlC,gBAAgBkB,KAAAA,IAAY,EAAElB,aAAakC,IAAIlC,YAAY,IAAI,CAAC;EACxE,GAAIkC,IAAIjC,SAASiB,KAAAA,IAAY,EAAEkB,UAAU,EAAEnC,MAAMiC,IAAIjC,KAAK,EAAE,IAAI,CAAC;CACnE;AACF;AAEA,SAASsB,qBACPc,UACAC,MAC6C;CAC7C,IAAI,CAACD,YAAYA,SAASE,WAAWD,KAAKC,QAAQ,OAAO;CAEzD,OAAOF,SAASG,OAAO7B,MAAM8B,UAAU;EACrC,MAAMC,WAAWJ,KAAKG;EACtB,OACEC,aAAaxB,KAAAA,KACbP,KAAKb,OAAO4C,SAAS5C,MACrBa,KAAKZ,UAAU2C,SAAS3C,SACxBY,KAAKX,gBAAgB0C,SAAS1C,eAC9BW,KAAKyB,UAAUnC,SAASyC,SAASN,UAAUnC;CAE/C,CAAC;AACH"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { Tool } from "assistant-stream";
|
|
2
|
+
/** @deprecated Experimental since 2026-08-29. Not scheduled for removal; the API may change in any release. */
|
|
2
3
|
export type Unstable_WebMcpProviderOptions = {
|
|
3
4
|
filter?: (name: string, tool: Tool<any, any>) => boolean;
|
|
4
5
|
};
|
|
6
|
+
/** @deprecated Experimental since 2026-08-29. Not scheduled for removal; the API may change in any release. */
|
|
5
7
|
export type Unstable_WebMcpProviderResult = {
|
|
6
8
|
status: "unsupported" | "active";
|
|
7
9
|
registeredToolNames: readonly string[];
|
|
@@ -13,6 +15,8 @@ export type Unstable_WebMcpProviderResult = {
|
|
|
13
15
|
* Returns `status: "unsupported"` when the page exposes no
|
|
14
16
|
* `document.modelContext` (or `navigator.modelContext`), and the sorted names
|
|
15
17
|
* of the tools currently registered with the host.
|
|
18
|
+
*
|
|
19
|
+
* @deprecated Experimental since 2026-08-29. Not scheduled for removal; the API may change in any release.
|
|
16
20
|
*/
|
|
17
21
|
export declare const unstable_useWebMcpProvider: (options?: Unstable_WebMcpProviderOptions) => Unstable_WebMcpProviderResult;
|
|
18
22
|
//# sourceMappingURL=useWebMcpProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useWebMcpProvider.d.ts","sourceRoot":"","sources":["../../../src/unstable/webmcp/useWebMcpProvider.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAU7C,MAAM,MAAM,8BAA8B,GAAG;IAC3C,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC;CAC1D,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,MAAM,EAAE,aAAa,GAAG,QAAQ,CAAC;IACjC,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC,CAAC;AA8FF
|
|
1
|
+
{"version":3,"file":"useWebMcpProvider.d.ts","sourceRoot":"","sources":["../../../src/unstable/webmcp/useWebMcpProvider.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAU7C,+GAA+G;AAC/G,MAAM,MAAM,8BAA8B,GAAG;IAC3C,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC;CAC1D,CAAC;AAEF,+GAA+G;AAC/G,MAAM,MAAM,6BAA6B,GAAG;IAC1C,MAAM,EAAE,aAAa,GAAG,QAAQ,CAAC;IACjC,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC,CAAC;AA8FF;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B,GACrC,UAAS,8BAAmC,KAC3C,6BAyCF,CAAC"}
|
|
@@ -80,6 +80,8 @@ const useWebMcpRegistry = (t0) => {
|
|
|
80
80
|
* Returns `status: "unsupported"` when the page exposes no
|
|
81
81
|
* `document.modelContext` (or `navigator.modelContext`), and the sorted names
|
|
82
82
|
* of the tools currently registered with the host.
|
|
83
|
+
*
|
|
84
|
+
* @deprecated Experimental since 2026-08-29. Not scheduled for removal; the API may change in any release.
|
|
83
85
|
*/
|
|
84
86
|
const unstable_useWebMcpProvider = (options = {}) => {
|
|
85
87
|
const aui = useAui();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useWebMcpProvider.js","names":["c","_c","useCallback","useEffect","useInsertionEffect","useRef","useState","useSyncExternalStore","useResources","useTapRoot","withKey","useAui","Tool","getDefaultWebMcpHost","WebMcpHost","defaultWebMcpFilter","toWebMcpInputSchema","WebMcpRegistrationResource","shallowEqual","useModelContextSnapshot","ModelContextSnapshotSource","Unstable_WebMcpProviderOptions","filter","name","tool","Unstable_WebMcpProviderResult","status","registeredToolNames","EMPTY_NAMES","Object","freeze","EMPTY_TOOLS","Record","warned","WeakSet","signatures","WeakMap","description","signature","signatureOf","cached","get","JSON","stringify","set","NO_SUBSCRIPTION","modelContextToolSource","empty","read","aui","modelContext","getModelContext","tools","subscribe","onChange","useStableNames","names","cell","next","sort","previous","useWebMcpRegistry","t0","$","host","getCurrentTool","elements","entries","push","t1","error","has","add","console","warn","unstable_useWebMcpProvider","options","available","currentToolsRef","current","root","WebMcpProviderRoot","getValue","published","setPublished","_c"],"sources":["../../../src/unstable/webmcp/useWebMcpProvider.ts"],"sourcesContent":["\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useInsertionEffect,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport { useResources, useTapRoot, withKey } from \"@assistant-ui/tap\";\nimport { useAui } from \"@assistant-ui/store\";\nimport type { Tool } from \"assistant-stream\";\nimport { getDefaultWebMcpHost, type WebMcpHost } from \"./webmcp-host\";\nimport { defaultWebMcpFilter, toWebMcpInputSchema } from \"./convertTools\";\nimport { WebMcpRegistrationResource } from \"./WebMcpRegistrationResource\";\nimport { shallowEqual } from \"@assistant-ui/store/internal\";\nimport {\n useModelContextSnapshot,\n type ModelContextSnapshotSource,\n} from \"../useModelContextSnapshot\";\n\nexport type Unstable_WebMcpProviderOptions = {\n filter?: (name: string, tool: Tool<any, any>) => boolean;\n};\n\nexport type Unstable_WebMcpProviderResult = {\n status: \"unsupported\" | \"active\";\n registeredToolNames: readonly string[];\n};\n\nconst EMPTY_NAMES: readonly string[] = Object.freeze([]);\nconst EMPTY_TOOLS: Record<string, Tool<any, any>> = Object.freeze({});\n\n// The description is re-read on every sync so mutating it in place is\n// observed; the schema is converted only when the tool object itself changes.\n// Keyed on the tool so a schema or filter that throws warns once instead of\n// on every model-context notify.\nconst warned = new WeakSet<Tool<any, any>>();\n\nconst signatures = new WeakMap<\n Tool<any, any>,\n { description: string | undefined; signature: string }\n>();\n\nconst signatureOf = (tool: Tool<any, any>) => {\n const cached = signatures.get(tool);\n if (cached && cached.description === tool.description)\n return cached.signature;\n const signature = `${tool.description ?? \"\"}\\u0000${JSON.stringify(\n toWebMcpInputSchema(tool),\n )}`;\n signatures.set(tool, { description: tool.description, signature });\n return signature;\n};\n\nconst NO_SUBSCRIPTION = () => {};\n\n// No `isEqual`: a caller republishing an unchanged tool set is how it asks for\n// a re-sync, which is what re-attempts a tool whose filter has stopped throwing\n// and what picks up a description edited in place on a stable tool object.\nconst modelContextToolSource: ModelContextSnapshotSource<\n Record<string, Tool<any, any>>\n> = {\n empty: EMPTY_TOOLS,\n read: (aui) => aui.modelContext.getModelContext().tools ?? EMPTY_TOOLS,\n subscribe: (aui, onChange) =>\n aui.modelContext.subscribe?.(onChange) ?? NO_SUBSCRIPTION,\n};\n\nconst useStableNames = (names: readonly (string | null)[]) => {\n const [cell] = useState(() => ({ names: EMPTY_NAMES }));\n const next = names.filter((name): name is string => name !== null).sort();\n const previous = cell.names;\n if (shallowEqual(previous, next)) {\n return previous;\n }\n cell.names = next;\n return next;\n};\n\nconst useWebMcpRegistry = ({\n host,\n filter,\n tools,\n getCurrentTool,\n}: {\n host: WebMcpHost;\n filter: (name: string, tool: Tool<any, any>) => boolean;\n tools: Record<string, Tool<any, any>>;\n getCurrentTool: (name: string) => Tool<any, any> | undefined;\n}) => {\n const elements = [];\n for (const [name, tool] of Object.entries(tools)) {\n try {\n if (!filter(name, tool)) continue;\n const signature = signatureOf(tool);\n elements.push(\n withKey(\n name,\n WebMcpRegistrationResource({\n host,\n name,\n signature,\n tool,\n getCurrentTool,\n }),\n [host, name, signature, tool, getCurrentTool],\n ),\n );\n } catch (error) {\n if (warned.has(tool)) continue;\n warned.add(tool);\n console.warn(\n `[assistant-ui] Skipping WebMCP registration for tool \"${name}\": filter or schema conversion failed.`,\n error,\n );\n }\n }\n\n return useStableNames(useResources(elements));\n};\n\n/**\n * Publishes the frontend tools in the model context to a WebMCP-capable\n * browser, so the user's own browser agent can call them.\n *\n * Returns `status: \"unsupported\"` when the page exposes no\n * `document.modelContext` (or `navigator.modelContext`), and the sorted names\n * of the tools currently registered with the host.\n */\nexport const unstable_useWebMcpProvider = (\n options: Unstable_WebMcpProviderOptions = {},\n): Unstable_WebMcpProviderResult => {\n const aui = useAui();\n const [host] = useState(getDefaultWebMcpHost);\n const filter = options.filter ?? defaultWebMcpFilter;\n const tools = useModelContextSnapshot(\n aui,\n host.available,\n modelContextToolSource,\n );\n const currentToolsRef = useRef(tools);\n useInsertionEffect(() => {\n currentToolsRef.current = tools;\n }, [tools]);\n const getCurrentTool = useCallback(\n (name: string) => currentToolsRef.current[name],\n [],\n );\n\n const root = useTapRoot(function WebMcpProviderRoot() {\n return useWebMcpRegistry({\n host,\n filter,\n tools,\n getCurrentTool,\n });\n });\n const registeredToolNames = useSyncExternalStore(\n root.subscribe,\n root.getValue,\n () => EMPTY_NAMES,\n );\n\n const [published, setPublished] = useState(false);\n useEffect(() => {\n if (host.available) setPublished(true);\n }, [host]);\n\n return {\n status: published ? \"active\" : \"unsupported\",\n registeredToolNames,\n };\n};\n"],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"useWebMcpProvider.js","names":["c","_c","useCallback","useEffect","useInsertionEffect","useRef","useState","useSyncExternalStore","useResources","useTapRoot","withKey","useAui","Tool","getDefaultWebMcpHost","WebMcpHost","defaultWebMcpFilter","toWebMcpInputSchema","WebMcpRegistrationResource","shallowEqual","useModelContextSnapshot","ModelContextSnapshotSource","Unstable_WebMcpProviderOptions","filter","name","tool","Unstable_WebMcpProviderResult","status","registeredToolNames","EMPTY_NAMES","Object","freeze","EMPTY_TOOLS","Record","warned","WeakSet","signatures","WeakMap","description","signature","signatureOf","cached","get","JSON","stringify","set","NO_SUBSCRIPTION","modelContextToolSource","empty","read","aui","modelContext","getModelContext","tools","subscribe","onChange","useStableNames","names","cell","next","sort","previous","useWebMcpRegistry","t0","$","host","getCurrentTool","elements","entries","push","t1","error","has","add","console","warn","unstable_useWebMcpProvider","options","available","currentToolsRef","current","root","WebMcpProviderRoot","getValue","published","setPublished","_c"],"sources":["../../../src/unstable/webmcp/useWebMcpProvider.ts"],"sourcesContent":["\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useInsertionEffect,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport { useResources, useTapRoot, withKey } from \"@assistant-ui/tap\";\nimport { useAui } from \"@assistant-ui/store\";\nimport type { Tool } from \"assistant-stream\";\nimport { getDefaultWebMcpHost, type WebMcpHost } from \"./webmcp-host\";\nimport { defaultWebMcpFilter, toWebMcpInputSchema } from \"./convertTools\";\nimport { WebMcpRegistrationResource } from \"./WebMcpRegistrationResource\";\nimport { shallowEqual } from \"@assistant-ui/store/internal\";\nimport {\n useModelContextSnapshot,\n type ModelContextSnapshotSource,\n} from \"../useModelContextSnapshot\";\n\n/** @deprecated Experimental since 2026-08-29. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_WebMcpProviderOptions = {\n filter?: (name: string, tool: Tool<any, any>) => boolean;\n};\n\n/** @deprecated Experimental since 2026-08-29. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_WebMcpProviderResult = {\n status: \"unsupported\" | \"active\";\n registeredToolNames: readonly string[];\n};\n\nconst EMPTY_NAMES: readonly string[] = Object.freeze([]);\nconst EMPTY_TOOLS: Record<string, Tool<any, any>> = Object.freeze({});\n\n// The description is re-read on every sync so mutating it in place is\n// observed; the schema is converted only when the tool object itself changes.\n// Keyed on the tool so a schema or filter that throws warns once instead of\n// on every model-context notify.\nconst warned = new WeakSet<Tool<any, any>>();\n\nconst signatures = new WeakMap<\n Tool<any, any>,\n { description: string | undefined; signature: string }\n>();\n\nconst signatureOf = (tool: Tool<any, any>) => {\n const cached = signatures.get(tool);\n if (cached && cached.description === tool.description)\n return cached.signature;\n const signature = `${tool.description ?? \"\"}\\u0000${JSON.stringify(\n toWebMcpInputSchema(tool),\n )}`;\n signatures.set(tool, { description: tool.description, signature });\n return signature;\n};\n\nconst NO_SUBSCRIPTION = () => {};\n\n// No `isEqual`: a caller republishing an unchanged tool set is how it asks for\n// a re-sync, which is what re-attempts a tool whose filter has stopped throwing\n// and what picks up a description edited in place on a stable tool object.\nconst modelContextToolSource: ModelContextSnapshotSource<\n Record<string, Tool<any, any>>\n> = {\n empty: EMPTY_TOOLS,\n read: (aui) => aui.modelContext.getModelContext().tools ?? EMPTY_TOOLS,\n subscribe: (aui, onChange) =>\n aui.modelContext.subscribe?.(onChange) ?? NO_SUBSCRIPTION,\n};\n\nconst useStableNames = (names: readonly (string | null)[]) => {\n const [cell] = useState(() => ({ names: EMPTY_NAMES }));\n const next = names.filter((name): name is string => name !== null).sort();\n const previous = cell.names;\n if (shallowEqual(previous, next)) {\n return previous;\n }\n cell.names = next;\n return next;\n};\n\nconst useWebMcpRegistry = ({\n host,\n filter,\n tools,\n getCurrentTool,\n}: {\n host: WebMcpHost;\n filter: (name: string, tool: Tool<any, any>) => boolean;\n tools: Record<string, Tool<any, any>>;\n getCurrentTool: (name: string) => Tool<any, any> | undefined;\n}) => {\n const elements = [];\n for (const [name, tool] of Object.entries(tools)) {\n try {\n if (!filter(name, tool)) continue;\n const signature = signatureOf(tool);\n elements.push(\n withKey(\n name,\n WebMcpRegistrationResource({\n host,\n name,\n signature,\n tool,\n getCurrentTool,\n }),\n [host, name, signature, tool, getCurrentTool],\n ),\n );\n } catch (error) {\n if (warned.has(tool)) continue;\n warned.add(tool);\n console.warn(\n `[assistant-ui] Skipping WebMCP registration for tool \"${name}\": filter or schema conversion failed.`,\n error,\n );\n }\n }\n\n return useStableNames(useResources(elements));\n};\n\n/**\n * Publishes the frontend tools in the model context to a WebMCP-capable\n * browser, so the user's own browser agent can call them.\n *\n * Returns `status: \"unsupported\"` when the page exposes no\n * `document.modelContext` (or `navigator.modelContext`), and the sorted names\n * of the tools currently registered with the host.\n *\n * @deprecated Experimental since 2026-08-29. Not scheduled for removal; the API may change in any release.\n */\nexport const unstable_useWebMcpProvider = (\n options: Unstable_WebMcpProviderOptions = {},\n): Unstable_WebMcpProviderResult => {\n const aui = useAui();\n const [host] = useState(getDefaultWebMcpHost);\n const filter = options.filter ?? defaultWebMcpFilter;\n const tools = useModelContextSnapshot(\n aui,\n host.available,\n modelContextToolSource,\n );\n const currentToolsRef = useRef(tools);\n useInsertionEffect(() => {\n currentToolsRef.current = tools;\n }, [tools]);\n const getCurrentTool = useCallback(\n (name: string) => currentToolsRef.current[name],\n [],\n );\n\n const root = useTapRoot(function WebMcpProviderRoot() {\n return useWebMcpRegistry({\n host,\n filter,\n tools,\n getCurrentTool,\n });\n });\n const registeredToolNames = useSyncExternalStore(\n root.subscribe,\n root.getValue,\n () => EMPTY_NAMES,\n );\n\n const [published, setPublished] = useState(false);\n useEffect(() => {\n if (host.available) setPublished(true);\n }, [host]);\n\n return {\n status: published ? \"active\" : \"unsupported\",\n registeredToolNames,\n };\n};\n"],"mappings":";;;;;;;;;;;AAiCA,MAAM4B,cAAiCC,OAAOC,OAAO,CAAA,CAAE;AACvD,MAAMC,cAA8CF,OAAOC,OAAO,CAAC,CAAC;AAMpE,MAAMG,yBAAS,IAAIC,QAAwB;AAE3C,MAAMC,6BAAa,IAAIC,QAGrB;AAEF,MAAMG,eAAef,SAAyB;CAC5C,MAAMgB,SAASL,WAAWM,IAAIjB,IAAI;CAClC,IAAIgB,UAAUA,OAAOH,gBAAgBb,KAAKa,aACxC,OAAOG,OAAOF;CAChB,MAAMA,YAAY,GAAGd,KAAKa,eAAe,GAAE,QAASK,KAAKC,UACvD3B,oBAAoBQ,IAAI,CAC1B;CACAW,WAAWS,IAAIpB,MAAM;EAAEa,aAAab,KAAKa;EAAaC;CAAU,CAAC;CACjE,OAAOA;AACT;AAEA,MAAMO,wBAAwB,CAAC;AAK/B,MAAMC,yBAEF;CACFC,OAAOhB;CACPiB,OAAOC,QAAQA,IAAIC,aAAaC,gBAAgB,CAAC,CAACC,SAASrB;CAC3DsB,YAAYJ,KAAKK,aACfL,IAAIC,aAAaG,YAAYC,QAAQ,KAAKT;AAC9C;AAEA,MAAMU,kBAAkBC,UAAsC;CAC5D,MAAM,CAACC,QAAQnD,gBAAgB,EAAEkD,OAAO5B,YAAY,EAAE;CACtD,MAAM8B,OAAOF,MAAMlC,QAAQC,SAAyBA,SAAS,IAAI,CAAC,CAACoC,KAAK;CACxE,MAAMC,WAAWH,KAAKD;CACtB,IAAItC,aAAa0C,UAAUF,IAAI,GAC7B,OAAOE;CAETH,KAAKD,QAAQE;CACb,OAAOA;AACT;AAEA,MAAMG,qBAAoBC,OAAA;CAAA,MAAAC,IAAA9D,EAAA,CAAA;CAAC,MAAA,EAAA+D,MAAA1C,QAAA8B,OAAAa,mBAAAH;CAU1B,IAAAI;CAAA,IAAAH,EAAA,OAAAzC,UAAAyC,EAAA,OAAAE,kBAAAF,EAAA,OAAAC,QAAAD,EAAA,OAAAX,OAAA;EACCc,WAAiB,CAAA;EACjB,KAAK,MAAA,CAAA3C,MAAAC,SAAsBK,OAAMsC,QAASf,KAAK,GAC7C,IAAA;GACE,IAAI,CAAC9B,OAAOC,MAAMC,IAAI,GAAG;GACzB,MAAAc,YAAkBC,YAAYf,IAAI;GAClC0C,SAAQE,KACN1D,QACEa,MACAN,2BAA2B;IAAA+C;IAAAzC;IAAAe;IAAAd;IAAAyC;GAM3B,CAAC,GACD;IAACD;IAAMzC;IAAMe;IAAWd;IAAMyC;GAAc,CAC9C,CACF;EAAC,SAAAI,IAAA;GACMC,MAAAA,QAAAA;GACP,IAAIrC,OAAMsC,IAAK/C,IAAI,GAAG;GACtBS,OAAMuC,IAAKhD,IAAI;GACfiD,QAAOC,KACL,yDAAyDnD,KAAI,yCAC7D+C,KACF;EAAC;EAEJP,EAAA,KAAAzC;EAAAyC,EAAA,KAAAE;EAAAF,EAAA,KAAAC;EAAAD,EAAA,KAAAX;EAAAW,EAAA,KAAAG;CAAA,OAAAA,WAAAH,EAAA;CAAA,OAEMR,eAAe/C,aAAa0D,QAAQ,CAAC;AAAC;;;;;;;;;;;AAa/C,MAAaS,8BACXC,UAA0C,CAAC,MACT;CAClC,MAAM3B,MAAMtC,OAAO;CACnB,MAAM,CAACqD,QAAQ1D,SAASO,oBAAoB;CAC5C,MAAMS,SAASsD,QAAQtD,UAAUP;CACjC,MAAMqC,QAAQjC,wBACZ8B,KACAe,KAAKa,WACL/B,sBACF;CACA,MAAMgC,kBAAkBzE,OAAO+C,KAAK;CACpChD,yBAAyB;EACvB0E,gBAAgBC,UAAU3B;CAC5B,GAAG,CAACA,KAAK,CAAC;CACV,MAAMa,iBAAiB/D,aACpBqB,SAAiBuD,gBAAgBC,QAAQxD,OAC1C,CAAA,CACF;CAEA,MAAMyD,OAAOvE,WAAW,SAASwE,qBAAqB;EACpD,OAAOpB,kBAAkB;GACvBG;GACA1C;GACA8B;GACAa;EACF,CAAC;CACH,CAAC;CACD,MAAMtC,sBAAsBpB,qBAC1ByE,KAAK3B,WACL2B,KAAKE,gBACCtD,WACR;CAEA,MAAM,CAACuD,WAAWC,gBAAgB9E,SAAS,KAAK;CAChDH,gBAAgB;EACd,IAAI6D,KAAKa,WAAWO,aAAa,IAAI;CACvC,GAAG,CAACpB,IAAI,CAAC;CAET,OAAO;EACLtC,QAAQyD,YAAY,WAAW;EAC/BxD;CACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getSelectionMessageId.d.ts","sourceRoot":"","sources":["../../src/utils/getSelectionMessageId.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"getSelectionMessageId.d.ts","sourceRoot":"","sources":["../../src/utils/getSelectionMessageId.ts"],"names":[],"mappings":"AAmFA,eAAO,MAAM,qBAAqB,GAChC,WAAW,SAAS,EACpB,OAAO,OAAO,GAAG,IAAI,KACpB,MAAM,GAAG,IA8CX,CAAC"}
|
|
@@ -25,8 +25,25 @@ const findQuoteMarker = (node, messageElement) => {
|
|
|
25
25
|
if (!messageElement.contains(marker)) return null;
|
|
26
26
|
return marker;
|
|
27
27
|
};
|
|
28
|
-
const
|
|
29
|
-
|
|
28
|
+
const normalizeRangeEnd = (range) => {
|
|
29
|
+
if (range.endOffset !== 0 || range.collapsed) return range;
|
|
30
|
+
const walker = range.endContainer.ownerDocument?.createTreeWalker(range.commonAncestorContainer, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT);
|
|
31
|
+
if (!walker) return range;
|
|
32
|
+
walker.currentNode = range.endContainer;
|
|
33
|
+
while (walker.previousNode()) {
|
|
34
|
+
const node = walker.currentNode;
|
|
35
|
+
const offset = node instanceof Text ? node.length : node.childNodes.length;
|
|
36
|
+
if (range.comparePoint(node, offset) < 0) return range;
|
|
37
|
+
const marker = (node instanceof Element ? node : node.parentElement)?.closest(QUOTE_SELECTABLE_SELECTOR);
|
|
38
|
+
if (marker && isExcluded(marker) && !node.contains(range.endContainer)) return range;
|
|
39
|
+
if (!(node instanceof Text) || !node.data.trim()) continue;
|
|
40
|
+
const normalized = range.cloneRange();
|
|
41
|
+
normalized.setEnd(node, offset);
|
|
42
|
+
return normalized.collapsed ? range : normalized;
|
|
43
|
+
}
|
|
44
|
+
return range;
|
|
45
|
+
};
|
|
46
|
+
const intersectsExcluded = (scope, ranges) => {
|
|
30
47
|
for (const marker of scope.querySelectorAll(QUOTE_SELECTABLE_SELECTOR)) {
|
|
31
48
|
if (!isExcluded(marker)) continue;
|
|
32
49
|
if (ranges.some((range) => range.intersectsNode(marker))) return true;
|
|
@@ -34,8 +51,17 @@ const intersectsExcluded = (scope, selection) => {
|
|
|
34
51
|
return false;
|
|
35
52
|
};
|
|
36
53
|
const getSelectionMessageId = (selection, root) => {
|
|
37
|
-
|
|
54
|
+
let { anchorNode, focusNode } = selection;
|
|
38
55
|
if (!anchorNode || !focusNode) return null;
|
|
56
|
+
const ranges = Array.from({ length: selection.rangeCount }, (_, i) => selection.getRangeAt(i));
|
|
57
|
+
if (ranges.length === 1) {
|
|
58
|
+
const range = normalizeRangeEnd(ranges[0]);
|
|
59
|
+
if (range !== ranges[0]) {
|
|
60
|
+
ranges[0] = range;
|
|
61
|
+
anchorNode = range.startContainer;
|
|
62
|
+
focusNode = range.endContainer;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
39
65
|
const anchorMessageElement = findMessageElement(anchorNode);
|
|
40
66
|
const focusMessageElement = findMessageElement(focusNode);
|
|
41
67
|
if (!anchorMessageElement || anchorMessageElement !== focusMessageElement) return null;
|
|
@@ -50,11 +76,8 @@ const getSelectionMessageId = (selection, root) => {
|
|
|
50
76
|
if (!anchorMarker || anchorMarker !== focusMarker) return null;
|
|
51
77
|
}
|
|
52
78
|
const scope = anchorMarker ?? anchorMessageElement;
|
|
53
|
-
for (
|
|
54
|
-
|
|
55
|
-
if (!scope.contains(commonAncestorContainer)) return null;
|
|
56
|
-
}
|
|
57
|
-
return intersectsExcluded(scope, selection) ? null : messageId;
|
|
79
|
+
for (const { commonAncestorContainer } of ranges) if (!scope.contains(commonAncestorContainer)) return null;
|
|
80
|
+
return intersectsExcluded(scope, ranges) ? null : messageId;
|
|
58
81
|
};
|
|
59
82
|
//#endregion
|
|
60
83
|
export { getSelectionMessageId };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getSelectionMessageId.js","names":["QUOTE_SELECTABLE_SELECTOR","getElement","node","Node","HTMLElement","parentElement","findMessageElement","el","id","getAttribute","isExcluded","marker","Element","hasQuoteSelectableRegion","messageElement","matches","querySelectorAll","findQuoteMarker","closest","contains","
|
|
1
|
+
{"version":3,"file":"getSelectionMessageId.js","names":["QUOTE_SELECTABLE_SELECTOR","getElement","node","Node","HTMLElement","parentElement","findMessageElement","el","id","getAttribute","isExcluded","marker","Element","hasQuoteSelectableRegion","messageElement","matches","querySelectorAll","findQuoteMarker","closest","contains","normalizeRangeEnd","range","Range","endOffset","collapsed","walker","endContainer","ownerDocument","createTreeWalker","commonAncestorContainer","NodeFilter","SHOW_ELEMENT","SHOW_TEXT","currentNode","previousNode","offset","Text","length","childNodes","comparePoint","element","data","trim","normalized","cloneRange","setEnd","intersectsExcluded","scope","ranges","some","intersectsNode","getSelectionMessageId","selection","Selection","root","anchorNode","focusNode","Array","from","rangeCount","_","i","getRangeAt","startContainer","anchorMessageElement","focusMessageElement","messageId","anchorMarker","focusMarker"],"sources":["../../src/utils/getSelectionMessageId.ts"],"sourcesContent":["const QUOTE_SELECTABLE_SELECTOR = \"[data-aui-quote-selectable]\";\n\nconst getElement = (node: Node | null): HTMLElement | null => {\n return node instanceof HTMLElement ? node : (node?.parentElement ?? null);\n};\n\nconst findMessageElement = (node: Node | null): HTMLElement | null => {\n let el = node instanceof HTMLElement ? node : (node?.parentElement ?? null);\n while (el) {\n const id = el.getAttribute(\"data-message-id\");\n if (id) return el;\n el = el.parentElement;\n }\n return null;\n};\n\nconst isExcluded = (marker: Element): boolean => {\n return marker.getAttribute(\"data-aui-quote-selectable\") === \"false\";\n};\n\nconst hasQuoteSelectableRegion = (messageElement: HTMLElement) => {\n if (\n messageElement.matches(QUOTE_SELECTABLE_SELECTOR) &&\n !isExcluded(messageElement)\n ) {\n return true;\n }\n for (const marker of messageElement.querySelectorAll(\n QUOTE_SELECTABLE_SELECTOR,\n )) {\n if (!isExcluded(marker)) return true;\n }\n return false;\n};\n\nconst findQuoteMarker = (\n node: Node | null,\n messageElement: HTMLElement,\n): HTMLElement | null => {\n const marker = getElement(node)?.closest(QUOTE_SELECTABLE_SELECTOR);\n if (!(marker instanceof HTMLElement)) return null;\n if (!messageElement.contains(marker)) return null;\n return marker;\n};\n\nconst normalizeRangeEnd = (range: Range): Range => {\n if (range.endOffset !== 0 || range.collapsed) return range;\n\n const walker = range.endContainer.ownerDocument?.createTreeWalker(\n range.commonAncestorContainer,\n NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,\n );\n if (!walker) return range;\n walker.currentNode = range.endContainer;\n\n while (walker.previousNode()) {\n const node = walker.currentNode;\n const offset = node instanceof Text ? node.length : node.childNodes.length;\n if (range.comparePoint(node, offset) < 0) return range;\n const element = node instanceof Element ? node : node.parentElement;\n const marker = element?.closest(QUOTE_SELECTABLE_SELECTOR);\n if (marker && isExcluded(marker) && !node.contains(range.endContainer))\n return range;\n if (!(node instanceof Text) || !node.data.trim()) continue;\n\n const normalized = range.cloneRange();\n normalized.setEnd(node, offset);\n return normalized.collapsed ? range : normalized;\n }\n return range;\n};\n\nconst intersectsExcluded = (\n scope: Element,\n ranges: readonly Range[],\n): boolean => {\n for (const marker of scope.querySelectorAll(QUOTE_SELECTABLE_SELECTOR)) {\n if (!isExcluded(marker)) continue;\n if (ranges.some((range) => range.intersectsNode(marker))) return true;\n }\n return false;\n};\n\nexport const getSelectionMessageId = (\n selection: Selection,\n root?: Element | null,\n): string | null => {\n let { anchorNode, focusNode } = selection;\n if (!anchorNode || !focusNode) return null;\n\n const ranges = Array.from({ length: selection.rangeCount }, (_, i) =>\n selection.getRangeAt(i),\n );\n if (ranges.length === 1) {\n const range = normalizeRangeEnd(ranges[0]!);\n if (range !== ranges[0]) {\n ranges[0] = range;\n anchorNode = range.startContainer;\n focusNode = range.endContainer;\n }\n }\n\n const anchorMessageElement = findMessageElement(anchorNode);\n const focusMessageElement = findMessageElement(focusNode);\n\n if (!anchorMessageElement || anchorMessageElement !== focusMessageElement) {\n return null;\n }\n if (root && !root.contains(anchorMessageElement)) {\n return null;\n }\n\n const messageId = anchorMessageElement.getAttribute(\"data-message-id\");\n if (!messageId) return null;\n\n const anchorMarker = findQuoteMarker(anchorNode, anchorMessageElement);\n const focusMarker = findQuoteMarker(focusNode, anchorMessageElement);\n\n if (anchorMarker && isExcluded(anchorMarker)) return null;\n if (focusMarker && isExcluded(focusMarker)) return null;\n\n if (hasQuoteSelectableRegion(anchorMessageElement)) {\n if (!anchorMarker || anchorMarker !== focusMarker) return null;\n }\n\n const scope = anchorMarker ?? anchorMessageElement;\n\n for (const { commonAncestorContainer } of ranges) {\n if (!scope.contains(commonAncestorContainer)) return null;\n }\n\n return intersectsExcluded(scope, ranges) ? null : messageId;\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useOnResizeContent.d.ts","sourceRoot":"","sources":["../../../src/utils/hooks/useOnResizeContent.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useOnResizeContent.d.ts","sourceRoot":"","sources":["../../../src/utils/hooks/useOnResizeContent.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,kBAAkB,GAAI,UAAU,MAAM,IAAI,qCAStD,CAAC"}
|
|
@@ -2,39 +2,19 @@ import { useManagedRef } from "./useManagedRef.js";
|
|
|
2
2
|
import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
|
|
3
3
|
import "@assistant-ui/tap/react-shim";
|
|
4
4
|
import { useCallbackRef } from "radix-ui/internal";
|
|
5
|
+
import { observeContentResize } from "@assistant-ui/store/client";
|
|
5
6
|
//#region src/utils/hooks/useOnResizeContent.ts
|
|
6
7
|
const useOnResizeContent = (callback) => {
|
|
7
8
|
const $ = c(2);
|
|
8
9
|
const callbackRef = useCallbackRef(callback);
|
|
9
10
|
let t0;
|
|
10
11
|
if ($[0] !== callbackRef) {
|
|
11
|
-
t0 = (el) =>
|
|
12
|
-
const resizeObserver = new ResizeObserver(() => {
|
|
13
|
-
callbackRef();
|
|
14
|
-
});
|
|
15
|
-
const mutationObserver = new MutationObserver((mutations) => {
|
|
16
|
-
if (mutations.some(_temp)) callbackRef();
|
|
17
|
-
});
|
|
18
|
-
resizeObserver.observe(el);
|
|
19
|
-
mutationObserver.observe(el, {
|
|
20
|
-
childList: true,
|
|
21
|
-
subtree: true,
|
|
22
|
-
attributes: true,
|
|
23
|
-
characterData: true
|
|
24
|
-
});
|
|
25
|
-
return () => {
|
|
26
|
-
resizeObserver.disconnect();
|
|
27
|
-
mutationObserver.disconnect();
|
|
28
|
-
};
|
|
29
|
-
};
|
|
12
|
+
t0 = (el) => observeContentResize(el, callbackRef);
|
|
30
13
|
$[0] = callbackRef;
|
|
31
14
|
$[1] = t0;
|
|
32
15
|
} else t0 = $[1];
|
|
33
16
|
return useManagedRef(t0);
|
|
34
17
|
};
|
|
35
|
-
function _temp(m) {
|
|
36
|
-
return m.type !== "attributes" || m.attributeName !== "style";
|
|
37
|
-
}
|
|
38
18
|
//#endregion
|
|
39
19
|
export { useOnResizeContent };
|
|
40
20
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useOnResizeContent.js","names":["useCallbackRef","useCallback","useManagedRef","useOnResizeContent","callback","$","_c","callbackRef","t0","el","
|
|
1
|
+
{"version":3,"file":"useOnResizeContent.js","names":["observeContentResize","useCallbackRef","useCallback","useManagedRef","useOnResizeContent","callback","$","_c","callbackRef","t0","el","refCallback","_c","refCallback"],"sources":["../../../src/utils/hooks/useOnResizeContent.ts"],"sourcesContent":["import { observeContentResize } from \"@assistant-ui/store/client\";\nimport { useCallbackRef } from \"radix-ui/internal\";\nimport { useCallback } from \"react\";\nimport { useManagedRef } from \"./useManagedRef\";\n\nexport const useOnResizeContent = (callback: () => void) => {\n const callbackRef = useCallbackRef(callback);\n\n const refCallback = useCallback(\n (el: HTMLElement) => observeContentResize(el, callbackRef),\n [callbackRef],\n );\n\n return useManagedRef(refCallback);\n};\n"],"mappings":";;;;;;AAKA,MAAaI,sBAAqBC,aAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CAChC,MAAAC,cAAoBP,eAAeI,QAAQ;CAAE,IAAAI;CAAA,IAAAH,EAAA,OAAAE,aAAA;EAG3CC,MAAAC,OAAqBV,qBAAqBU,IAAIF,WAAW;EAACF,EAAA,KAAAE;EAAAF,EAAA,KAAAG;CAAA,OAAAA,KAAAH,EAAA;CAE1D,OAEKH,cAAcQ,EAAW;AAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useIsomorphicLayoutEffect.d.ts","sourceRoot":"","sources":["../../src/utils/useIsomorphicLayoutEffect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAmB,MAAM,OAAO,CAAC;AAMnD,eAAO,MAAM,yBAAyB,kBAEzB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { useEffect, useLayoutEffect } from "@assistant-ui/tap/react-shim";
|
|
2
|
+
const useIsomorphicLayoutEffect = typeof globalThis.window !== "undefined" ? useLayoutEffect : useEffect;
|
|
3
|
+
//#endregion
|
|
4
|
+
export { useIsomorphicLayoutEffect };
|
|
5
|
+
|
|
6
|
+
//# sourceMappingURL=useIsomorphicLayoutEffect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useIsomorphicLayoutEffect.js","names":["useEffect","useLayoutEffect","hasWindow","globalThis","window","useIsomorphicLayoutEffect"],"sources":["../../src/utils/useIsomorphicLayoutEffect.ts"],"sourcesContent":["import { useEffect, useLayoutEffect } from \"react\";\n\nconst hasWindow =\n typeof (globalThis as typeof globalThis & { window?: unknown }).window !==\n \"undefined\";\n\nexport const useIsomorphicLayoutEffect = hasWindow\n ? useLayoutEffect\n : useEffect;\n"],"mappings":";AAMA,MAAaK,4BAHX,OAAQF,WAAwDC,WAChE,cAGEH,kBACAD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.27",
|
|
4
4
|
"description": "Open-source TypeScript/React library for building production-grade AI chat experiences",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -55,12 +55,12 @@
|
|
|
55
55
|
],
|
|
56
56
|
"sideEffects": false,
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@assistant-ui/core": "^0.3.
|
|
59
|
-
"@assistant-ui/store": "^0.3.
|
|
60
|
-
"@assistant-ui/tap": "^0.9.
|
|
61
|
-
"assistant-cloud": "^0.2.
|
|
62
|
-
"assistant-stream": "^0.3.
|
|
63
|
-
"radix-ui": "^1.
|
|
58
|
+
"@assistant-ui/core": "^0.3.26",
|
|
59
|
+
"@assistant-ui/store": "^0.3.19",
|
|
60
|
+
"@assistant-ui/tap": "^0.9.22",
|
|
61
|
+
"assistant-cloud": "^0.2.7",
|
|
62
|
+
"assistant-stream": "^0.3.49",
|
|
63
|
+
"radix-ui": "^1.7.0",
|
|
64
64
|
"react-textarea-autosize": "^8.5.9",
|
|
65
65
|
"safe-content-frame": "^0.0.33",
|
|
66
66
|
"zod": "^4.6.5",
|
|
@@ -81,15 +81,15 @@
|
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
|
-
"@assistant-ui/vite": "0.0.
|
|
84
|
+
"@assistant-ui/vite": "0.0.21",
|
|
85
85
|
"@assistant-ui/x-buildutils": "0.0.30",
|
|
86
86
|
"@assistant-ui/x-react18": "0.0.0",
|
|
87
87
|
"@testing-library/react": "^16.3.3",
|
|
88
88
|
"@types/json-schema": "^7.0.15",
|
|
89
|
-
"@types/node": "^26.6.
|
|
89
|
+
"@types/node": "^26.6.4",
|
|
90
90
|
"@types/react": "^19.3.0",
|
|
91
91
|
"@types/react-dom": "^19.3.0",
|
|
92
|
-
"jsdom": "^30.
|
|
92
|
+
"jsdom": "^30.1.2",
|
|
93
93
|
"react": "^19.3.0",
|
|
94
94
|
"react-dom": "^19.3.0",
|
|
95
95
|
"vitest": "^5.0.3"
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
|
|
3
1
|
import { renderToString } from "react-dom/server";
|
|
4
2
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
3
|
|
|
@@ -27,9 +25,13 @@ describe("useToolCallElapsed", () => {
|
|
|
27
25
|
|
|
28
26
|
it("does not read the wall clock during server render", () => {
|
|
29
27
|
const nowSpy = vi.spyOn(Date, "now");
|
|
28
|
+
const errors = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
30
29
|
const Probe = () => <span>{useToolCallElapsed() ?? "none"}</span>;
|
|
31
30
|
|
|
32
31
|
expect(renderToString(<Probe />)).toBe("<span>none</span>");
|
|
33
32
|
expect(nowSpy).not.toHaveBeenCalled();
|
|
33
|
+
expect(errors.mock.calls.flat().join(" ")).not.toMatch(
|
|
34
|
+
/useLayoutEffect does nothing on the server/,
|
|
35
|
+
);
|
|
34
36
|
});
|
|
35
37
|
});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { useState } from "react";
|
|
4
4
|
import { useAuiState } from "@assistant-ui/store";
|
|
5
|
+
import { useIsomorphicLayoutEffect } from "../utils/useIsomorphicLayoutEffect";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Hook that returns the elapsed wall-clock time of the current tool call in
|
|
@@ -36,7 +37,7 @@ export const useToolCallElapsed = (): number | undefined => {
|
|
|
36
37
|
timing !== undefined && timing.completedAt === undefined && partRunning;
|
|
37
38
|
const [now, setNow] = useState<number | undefined>(undefined);
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
useIsomorphicLayoutEffect(() => {
|
|
40
41
|
if (!running) return undefined;
|
|
41
42
|
// The clock is read only after mount so a prerender never reads it; the
|
|
42
43
|
// layout effect still catches the elapsed value up before the first paint.
|
package/src/index.ts
CHANGED
|
@@ -164,11 +164,17 @@ export { useExternalStoreRuntime } from "./legacy-runtime/runtime-cores/external
|
|
|
164
164
|
export { useExternalStoreSharedOptions } from "@assistant-ui/core/react";
|
|
165
165
|
export {
|
|
166
166
|
useExternalMessageConverter,
|
|
167
|
+
/** @deprecated Experimental since 2025-01-26. Not scheduled for removal; the API may change in any release. */
|
|
167
168
|
convertExternalMessages as unstable_convertExternalMessages,
|
|
169
|
+
/** @deprecated Experimental since 2026-09-15. Not scheduled for removal; the API may change in any release. */
|
|
168
170
|
createExternalMessageConversionCache as unstable_createExternalMessageConversionCache,
|
|
171
|
+
/** @deprecated Experimental since 2026-09-15. Not scheduled for removal; the API may change in any release. */
|
|
169
172
|
type ExternalMessageConversionCache as Unstable_ExternalMessageConversionCache,
|
|
170
173
|
} from "./legacy-runtime/runtime-cores/external-store/external-message-converter";
|
|
171
|
-
export {
|
|
174
|
+
export {
|
|
175
|
+
/** @deprecated Experimental since 2025-02-02. Not scheduled for removal; the API may change in any release. */
|
|
176
|
+
createMessageConverter as unstable_createMessageConverter,
|
|
177
|
+
} from "./legacy-runtime/runtime-cores/external-store/createMessageConverter";
|
|
172
178
|
|
|
173
179
|
// --- local ---
|
|
174
180
|
export type {
|
|
@@ -496,15 +502,23 @@ export type { ToolExecutionStatus } from "./internal";
|
|
|
496
502
|
|
|
497
503
|
// Unstable - trigger popover (unified root for @ mentions, / slash commands, etc.)
|
|
498
504
|
export {
|
|
505
|
+
/** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */
|
|
499
506
|
useTriggerPopoverRootContext as unstable_useTriggerPopoverRootContext,
|
|
507
|
+
/** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */
|
|
500
508
|
useTriggerPopoverRootContextOptional as unstable_useTriggerPopoverRootContextOptional,
|
|
509
|
+
/** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */
|
|
501
510
|
useTriggerPopoverScopeContext as unstable_useTriggerPopoverScopeContext,
|
|
511
|
+
/** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */
|
|
502
512
|
useTriggerPopoverScopeContextOptional as unstable_useTriggerPopoverScopeContextOptional,
|
|
513
|
+
/** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */
|
|
503
514
|
useTriggerPopoverTriggers as unstable_useTriggerPopoverTriggers,
|
|
515
|
+
/** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */
|
|
504
516
|
useTriggerPopoverTriggersOptional as unstable_useTriggerPopoverTriggersOptional,
|
|
517
|
+
/** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */
|
|
505
518
|
type RegisteredTrigger as Unstable_RegisteredTrigger,
|
|
506
519
|
type TriggerMatch,
|
|
507
520
|
type TriggerMatcher,
|
|
521
|
+
/** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */
|
|
508
522
|
type TriggerBehavior as Unstable_TriggerBehavior,
|
|
509
523
|
} from "./primitives/composer/trigger";
|
|
510
524
|
import type {
|
|
@@ -572,6 +586,7 @@ export type {
|
|
|
572
586
|
McpAppsHost,
|
|
573
587
|
McpAppsRemoteHostOptions,
|
|
574
588
|
McpAppToolCallParams,
|
|
589
|
+
McpAppInitializeParams,
|
|
575
590
|
McpAppBridgeHandlers,
|
|
576
591
|
ToolCallMessagePartMcpMetadata,
|
|
577
592
|
} from "./mcp-apps";
|
|
@@ -584,7 +599,10 @@ export {
|
|
|
584
599
|
type Unstable_WebMcpProviderOptions,
|
|
585
600
|
type Unstable_WebMcpProviderResult,
|
|
586
601
|
} from "./unstable/webmcp/useWebMcpProvider";
|
|
587
|
-
export {
|
|
602
|
+
export {
|
|
603
|
+
/** @deprecated Experimental since 2026-08-29. Not scheduled for removal; the API may change in any release. */
|
|
604
|
+
defaultWebMcpFilter as unstable_defaultWebMcpFilter,
|
|
605
|
+
} from "./unstable/webmcp/convertTools";
|
|
588
606
|
|
|
589
607
|
// Shared surface carried by every distribution (scripts/check-distribution-barrels.mjs)
|
|
590
608
|
export type {
|
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
McpAppBridgeHandlers,
|
|
12
12
|
McpAppFrameProps,
|
|
13
13
|
McpAppHostContext,
|
|
14
|
+
McpAppInitializeParams,
|
|
14
15
|
McpAppsHost,
|
|
15
16
|
McpAppsRemoteHostOptions,
|
|
16
17
|
} from "./types";
|
|
@@ -229,8 +230,12 @@ describe("McpAppRenderer", () => {
|
|
|
229
230
|
await waitFor(() => expect(framePropsCalls().length).toBeGreaterThan(0));
|
|
230
231
|
const handlers = framePropsCalls().at(-1)?.handlers;
|
|
231
232
|
expect(handlers?.requestDisplayMode).toBeDefined();
|
|
232
|
-
|
|
233
|
+
const params: McpAppInitializeParams = {
|
|
234
|
+
appCapabilities: { availableDisplayModes: ["inline", "fullscreen"] },
|
|
235
|
+
};
|
|
236
|
+
handlers?.onInitialized?.(params);
|
|
233
237
|
expect(onInitialized).toHaveBeenCalledOnce();
|
|
238
|
+
expect(onInitialized).toHaveBeenCalledWith(params);
|
|
234
239
|
});
|
|
235
240
|
|
|
236
241
|
it("leaves mounted parts alone when renderer options are unchanged", async () => {
|
|
@@ -759,7 +764,7 @@ describe("McpAppRenderer", () => {
|
|
|
759
764
|
await handlers.updateModelContext?.({ text: "context" });
|
|
760
765
|
await handlers.openLink?.({ url: "https://example.com" });
|
|
761
766
|
await handlers.sendMessage?.({ text: "hello" });
|
|
762
|
-
handlers.onInitialized?.();
|
|
767
|
+
handlers.onInitialized?.({});
|
|
763
768
|
await handlers.callTool?.({ name: "search" });
|
|
764
769
|
await handlers.readResource?.({ uri: "ui://resource" });
|
|
765
770
|
await handlers.listResources?.();
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
useLayoutEffect,
|
|
6
|
-
useMemo,
|
|
7
|
-
useRef,
|
|
8
|
-
useState,
|
|
9
|
-
type ReactNode,
|
|
10
|
-
} from "react";
|
|
3
|
+
import { useEffect, useMemo, useRef, useState, type ReactNode } from "react";
|
|
4
|
+
import { useIsomorphicLayoutEffect } from "../utils/useIsomorphicLayoutEffect";
|
|
11
5
|
import type {
|
|
12
6
|
McpAppMetadata,
|
|
13
7
|
TextMessagePart,
|
|
@@ -204,7 +198,7 @@ function InlineRenderer({
|
|
|
204
198
|
const opts = resolvePartOptions(rendererOptions, part);
|
|
205
199
|
const app = getMcpAppFromToolPart(part);
|
|
206
200
|
const cachedAppRef = useRef<McpAppMetadata | undefined>(undefined);
|
|
207
|
-
|
|
201
|
+
useIsomorphicLayoutEffect(() => {
|
|
208
202
|
if (app != null) cachedAppRef.current = app;
|
|
209
203
|
}, [app]);
|
|
210
204
|
const appForRender = app ?? cachedAppRef.current;
|