@assistant-ui/react 0.14.9 → 0.14.12
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/augmentations.d.ts.map +1 -1
- package/dist/context/react/utils/createContextHook.d.ts.map +1 -1
- package/dist/context/react/utils/createContextStoreHook.d.ts.map +1 -1
- package/dist/context/react/utils/createStateHookForRuntime.d.ts.map +1 -1
- package/dist/context/react/utils/createStateHookForRuntime.js.map +1 -1
- package/dist/context/react/utils/useRuntimeState.d.ts.map +1 -1
- package/dist/devtools/DevToolsHooks.d.ts.map +1 -1
- package/dist/devtools/DevToolsHooks.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/internal.d.ts +3 -4
- package/dist/internal.js +1 -3
- package/dist/internal.js.map +1 -1
- package/dist/legacy-runtime/cloud/useCloudThreadListRuntime.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/types.d.ts +3 -2
- package/dist/legacy-runtime/runtime-cores/assistant-transport/types.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +4 -10
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.d.ts +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.d.ts.map +1 -1
- 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.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalRoot.js +5 -1
- package/dist/primitives/assistantModal/AssistantModalRoot.js.map +1 -1
- package/dist/primitives/composer/trigger/triggerKeyboardResource.js.map +1 -1
- package/dist/primitives/messagePart/MessagePartImage.js.map +1 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.d.ts.map +1 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.js +12 -2
- package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
- package/dist/unstable/useMentionAdapter.js.map +1 -1
- package/dist/unstable/useSlashCommandAdapter.js.map +1 -1
- package/dist/utils/Primitive.d.ts.map +1 -1
- package/dist/utils/useToolArgsFieldStatus.d.ts +2 -2
- package/dist/utils/useToolArgsFieldStatus.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/augmentations.ts +0 -2
- package/src/context/react/utils/createStateHookForRuntime.ts +0 -1
- package/src/devtools/DevToolsHooks.ts +0 -4
- package/src/index.ts +6 -1
- package/src/internal.ts +1 -4
- package/src/legacy-runtime/runtime/ThreadListRuntime.ts +1 -4
- package/src/legacy-runtime/runtime-cores/assistant-transport/types.ts +3 -1
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts +7 -27
- package/src/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.ts +1 -1
- package/src/mcp-apps/McpAppRenderer.tsx +1 -1
- package/src/mcp-apps/app-frame.tsx +2 -2
- package/src/primitives/assistantModal/AssistantModalRoot.tsx +1 -1
- package/src/primitives/composer/ComposerAttachmentDropzone.test.tsx +1 -1
- package/src/primitives/composer/ComposerInput.test.tsx +1 -1
- package/src/primitives/composer/trigger/TriggerPopoverRootContext.test.tsx +1 -1
- package/src/primitives/composer/trigger/triggerKeyboardResource.ts +0 -2
- package/src/primitives/messagePart/MessagePartImage.tsx +0 -1
- package/src/primitives/thread/useThreadViewportAutoScroll.ts +26 -2
- package/src/primitives/threadListItem/ThreadListItemTitle.test.tsx +1 -1
- package/src/tests/BaseComposerRuntimeCore.test.ts +2 -2
- package/src/tests/ExternalStoreThreadRuntimeCore.test.ts +62 -0
- package/src/tests/MessageParts.loading.test.tsx +1 -1
- package/src/tests/RemoteThreadListRuntime.adapterProvider.test.tsx +2 -4
- package/src/tests/auiV0Encode.test.ts +1 -1
- package/src/unstable/useMentionAdapter.ts +0 -3
- package/src/unstable/useSlashCommandAdapter.ts +0 -2
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.d.ts +0 -2
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.js +0 -2
- package/src/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.test.ts +0 -892
- package/src/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.ts +0 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-frame.js","names":[],"sources":["../../src/mcp-apps/app-frame.tsx"],"sourcesContent":["\"use client\";\n\nimport { type MutableRefObject, useEffect, useRef, useState } from \"react\";\nimport { type RenderedFrame, SafeContentFrame } from \"safe-content-frame\";\nimport { type McpAppBridge, createMcpAppBridge } from \"./bridge\";\nimport type {\n McpAppBridgeHandlers,\n McpAppFrameProps,\n McpAppHostContext,\n} from \"./types\";\n\nconst DEFAULT_PRODUCT = \"assistant-ui-mcp-app\";\nconst INIT_TIMEOUT_MS = 5000;\nconst DEFAULT_MAX_HEIGHT = 800;\n\nfunction useBridgeNotify<T>(\n value: T | undefined,\n bridgeRef: MutableRefObject<McpAppBridge | null>,\n widgetReadyRef: MutableRefObject<boolean>,\n pendingRef: MutableRefObject<T | undefined>,\n lastSentRef: MutableRefObject<T | undefined>,\n notify: (bridge: McpAppBridge, v: T) => void,\n) {\n // biome-ignore lint/correctness/useExhaustiveDependencies: refs and notify are stable; we re-run only when value changes.\n useEffect(() => {\n if (!bridgeRef.current) return;\n if (value === undefined) return;\n if (lastSentRef.current === value) return;\n if (!widgetReadyRef.current) {\n pendingRef.current = value;\n return;\n }\n notify(bridgeRef.current, value);\n lastSentRef.current = value;\n }, [value]);\n}\n\ntype LiveSnapshot = {\n handlers: McpAppBridgeHandlers | undefined;\n hostInfo: McpAppFrameProps[\"hostInfo\"];\n hostContext: McpAppFrameProps[\"hostContext\"];\n input: unknown;\n output: unknown;\n};\n\n// Proxy each per-call handler through liveRef so the bridge always dispatches\n// to the latest handler reference (e.g. inline callbacks closing over state).\n// Capability presence is snapshot at mount: a handler added later requires a\n// remount (keyed on resource URI) to expose the capability to the widget.\nfunction buildLiveHandlers(\n initial: McpAppBridgeHandlers | undefined,\n liveRef: { readonly current: LiveSnapshot },\n): McpAppBridgeHandlers {\n const live = () => liveRef.current.handlers;\n const has = <K extends keyof McpAppBridgeHandlers>(key: K) =>\n initial?.[key] !== undefined;\n const out: McpAppBridgeHandlers = {};\n if (has(\"allowedTools\")) {\n Object.defineProperty(out, \"allowedTools\", {\n get: () => live()?.allowedTools,\n enumerable: true,\n configurable: true,\n });\n }\n const liveCall = <K extends keyof McpAppBridgeHandlers>(\n key: K,\n ): NonNullable<McpAppBridgeHandlers[K]> =>\n ((p: unknown) => {\n const fn = live()?.[key] as ((p: unknown) => unknown) | undefined;\n if (!fn) {\n throw new Error(`${key} handler is no longer available`);\n }\n return fn(p);\n }) as NonNullable<McpAppBridgeHandlers[K]>;\n if (has(\"callTool\")) out.callTool = liveCall(\"callTool\");\n if (has(\"readResource\")) out.readResource = liveCall(\"readResource\");\n if (has(\"listResources\")) out.listResources = liveCall(\"listResources\");\n if (has(\"openLink\")) out.openLink = liveCall(\"openLink\");\n if (has(\"sendMessage\")) out.sendMessage = liveCall(\"sendMessage\");\n if (has(\"updateModelContext\"))\n out.updateModelContext = liveCall(\"updateModelContext\");\n if (has(\"requestDisplayMode\"))\n out.requestDisplayMode = liveCall(\"requestDisplayMode\");\n out.onSizeChange = (p) => live()?.onSizeChange?.(p);\n out.onInitialized = () => live()?.onInitialized?.();\n out.onRequestTeardown = (p) => live()?.onRequestTeardown?.(p);\n out.onLog = (p) => live()?.onLog?.(p);\n out.onError = (e) => live()?.onError?.(e);\n return out;\n}\n\nexport function McpAppFrame({\n app,\n resource,\n input,\n output,\n sandbox,\n handlers,\n hostInfo,\n hostContext,\n maxHeight = DEFAULT_MAX_HEIGHT,\n}: McpAppFrameProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [contentHeight, setContentHeight] = useState<number | undefined>(\n undefined,\n );\n const bridgeRef = useRef<McpAppBridge | null>(null);\n const lastSentInputRef = useRef<unknown>(undefined);\n const lastSentOutputRef = useRef<unknown>(undefined);\n const lastSentHostContextRef = useRef<McpAppHostContext | undefined>(\n undefined,\n );\n // Per MCP Apps spec, the host should defer notifications until the widget\n // signals readiness via `notifications/initialized`. Until then, we record\n // pending values and flush them on init.\n const widgetReadyRef = useRef(false);\n const pendingInputRef = useRef<unknown>(undefined);\n const pendingOutputRef = useRef<unknown>(undefined);\n const pendingHostContextRef = useRef<McpAppHostContext | undefined>(\n undefined,\n );\n\n const liveRef = useRef<LiveSnapshot>(null!);\n liveRef.current = {\n handlers,\n hostInfo,\n hostContext,\n input,\n output,\n };\n\n const resourceUri = resource.uri;\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: re-mounts only on resource URI; live values flow through liveRef\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n let cancelled = false;\n let initTimeoutId: ReturnType<typeof setTimeout> | null = null;\n let frame: RenderedFrame | null = null;\n const sb = sandbox;\n const html = resource.html;\n\n const scf = new SafeContentFrame(sb?.product ?? DEFAULT_PRODUCT, {\n ...(sb?.sandbox !== undefined && { sandbox: sb.sandbox }),\n ...(sb?.useShadowDom !== undefined && { useShadowDom: sb.useShadowDom }),\n ...(sb?.enableBrowserCaching !== undefined && {\n enableBrowserCaching: sb.enableBrowserCaching,\n }),\n ...(sb?.salt !== undefined && { salt: sb.salt }),\n });\n\n const renderOpts =\n sb?.unsafeDocumentWrite !== undefined\n ? { unsafeDocumentWrite: sb.unsafeDocumentWrite }\n : undefined;\n\n scf\n .renderHtml(html, container, renderOpts)\n .then((rendered) => {\n if (cancelled) {\n rendered.dispose();\n return;\n }\n frame = rendered;\n const current = liveRef.current;\n const liveHandlers = buildLiveHandlers(current.handlers, liveRef);\n const liveOnInitialized = liveHandlers.onInitialized;\n const flushPending = () => {\n if (widgetReadyRef.current) return;\n widgetReadyRef.current = true;\n const b = bridgeRef.current;\n if (!b) return;\n if (pendingInputRef.current !== undefined) {\n b.notifyToolInput(pendingInputRef.current);\n lastSentInputRef.current = pendingInputRef.current;\n pendingInputRef.current = undefined;\n }\n if (pendingOutputRef.current !== undefined) {\n b.notifyToolResult(pendingOutputRef.current);\n lastSentOutputRef.current = pendingOutputRef.current;\n pendingOutputRef.current = undefined;\n }\n if (pendingHostContextRef.current !== undefined) {\n b.notifyHostContextChanged(pendingHostContextRef.current);\n lastSentHostContextRef.current = pendingHostContextRef.current;\n pendingHostContextRef.current = undefined;\n }\n };\n const wrappedHandlers: McpAppBridgeHandlers = {\n ...liveHandlers,\n onInitialized: () => {\n if (initTimeoutId !== null) {\n clearTimeout(initTimeoutId);\n initTimeoutId = null;\n }\n flushPending();\n liveOnInitialized?.();\n },\n onSizeChange: (p) => {\n if (\n typeof p.height === \"number\" &&\n Number.isFinite(p.height) &&\n p.height > 0\n ) {\n setContentHeight(p.height);\n }\n liveHandlers.onSizeChange?.(p);\n },\n };\n // Safety net: if the widget never sends notifications/initialized\n // (broken or non-spec-compliant), flush the queue anyway so the host\n // doesn't appear hung.\n initTimeoutId = setTimeout(() => {\n initTimeoutId = null;\n flushPending();\n }, INIT_TIMEOUT_MS);\n bridgeRef.current = createMcpAppBridge({\n frame: rendered,\n handlers: wrappedHandlers,\n hostInfo: current.hostInfo,\n hostContext: current.hostContext,\n });\n\n if (current.input !== undefined)\n pendingInputRef.current = current.input;\n if (current.output !== undefined)\n pendingOutputRef.current = current.output;\n // hostContext is delivered inside the ui/initialize response; subsequent\n // changes flow through useBridgeNotify's pending path.\n })\n .catch((err) => {\n liveRef.current.handlers?.onError?.(\n err instanceof Error ? err : new Error(String(err)),\n );\n });\n\n return () => {\n cancelled = true;\n if (initTimeoutId !== null) {\n clearTimeout(initTimeoutId);\n initTimeoutId = null;\n }\n bridgeRef.current?.dispose();\n bridgeRef.current = null;\n frame?.dispose();\n frame = null;\n lastSentInputRef.current = undefined;\n lastSentOutputRef.current = undefined;\n lastSentHostContextRef.current = undefined;\n widgetReadyRef.current = false;\n pendingInputRef.current = undefined;\n pendingOutputRef.current = undefined;\n pendingHostContextRef.current = undefined;\n setContentHeight(undefined);\n };\n }, [resourceUri]);\n\n useBridgeNotify(\n input,\n bridgeRef,\n widgetReadyRef,\n pendingInputRef,\n lastSentInputRef,\n (b, v) => b.notifyToolInput(v),\n );\n useBridgeNotify(\n output,\n bridgeRef,\n widgetReadyRef,\n pendingOutputRef,\n lastSentOutputRef,\n (b, v) => b.notifyToolResult(v),\n );\n useBridgeNotify(\n hostContext,\n bridgeRef,\n widgetReadyRef,\n pendingHostContextRef,\n lastSentHostContextRef,\n (b, v) => b.notifyHostContextChanged(v),\n );\n\n const resolvedHeight =\n contentHeight != null ? Math.min(contentHeight, maxHeight) : undefined;\n const mergedStyle =\n resolvedHeight != null\n ? { ...sandbox?.style, height: resolvedHeight }\n : sandbox?.style;\n\n return (\n <div\n ref={containerRef}\n className={sandbox?.className}\n style={mergedStyle}\n data-mcp-app-resource={app.resourceUri}\n data-mcp-app-prefers-border={\n resource.meta?.prefersBorder ? \"\" : undefined\n }\n />\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"app-frame.js","names":[],"sources":["../../src/mcp-apps/app-frame.tsx"],"sourcesContent":["\"use client\";\n\nimport { type MutableRefObject, useEffect, useRef, useState } from \"react\";\nimport { type RenderedFrame, SafeContentFrame } from \"safe-content-frame\";\nimport { type McpAppBridge, createMcpAppBridge } from \"./bridge\";\nimport type {\n McpAppBridgeHandlers,\n McpAppFrameProps,\n McpAppHostContext,\n} from \"./types\";\n\nconst DEFAULT_PRODUCT = \"assistant-ui-mcp-app\";\nconst INIT_TIMEOUT_MS = 5000;\nconst DEFAULT_MAX_HEIGHT = 800;\n\nfunction useBridgeNotify<T>(\n value: T | undefined,\n bridgeRef: MutableRefObject<McpAppBridge | null>,\n widgetReadyRef: MutableRefObject<boolean>,\n pendingRef: MutableRefObject<T | undefined>,\n lastSentRef: MutableRefObject<T | undefined>,\n notify: (bridge: McpAppBridge, v: T) => void,\n) {\n useEffect(() => {\n if (!bridgeRef.current) return;\n if (value === undefined) return;\n if (lastSentRef.current === value) return;\n if (!widgetReadyRef.current) {\n pendingRef.current = value;\n return;\n }\n notify(bridgeRef.current, value);\n lastSentRef.current = value;\n // oxlint-disable-next-line tap-hooks/exhaustive-deps -- refs are stable; notify is assumed stable; re-run only when value changes\n }, [value]);\n}\n\ntype LiveSnapshot = {\n handlers: McpAppBridgeHandlers | undefined;\n hostInfo: McpAppFrameProps[\"hostInfo\"];\n hostContext: McpAppFrameProps[\"hostContext\"];\n input: unknown;\n output: unknown;\n};\n\n// Proxy each per-call handler through liveRef so the bridge always dispatches\n// to the latest handler reference (e.g. inline callbacks closing over state).\n// Capability presence is snapshot at mount: a handler added later requires a\n// remount (keyed on resource URI) to expose the capability to the widget.\nfunction buildLiveHandlers(\n initial: McpAppBridgeHandlers | undefined,\n liveRef: { readonly current: LiveSnapshot },\n): McpAppBridgeHandlers {\n const live = () => liveRef.current.handlers;\n const has = <K extends keyof McpAppBridgeHandlers>(key: K) =>\n initial?.[key] !== undefined;\n const out: McpAppBridgeHandlers = {};\n if (has(\"allowedTools\")) {\n Object.defineProperty(out, \"allowedTools\", {\n get: () => live()?.allowedTools,\n enumerable: true,\n configurable: true,\n });\n }\n const liveCall = <K extends keyof McpAppBridgeHandlers>(\n key: K,\n ): NonNullable<McpAppBridgeHandlers[K]> =>\n ((p: unknown) => {\n const fn = live()?.[key] as ((p: unknown) => unknown) | undefined;\n if (!fn) {\n throw new Error(`${key} handler is no longer available`);\n }\n return fn(p);\n }) as NonNullable<McpAppBridgeHandlers[K]>;\n if (has(\"callTool\")) out.callTool = liveCall(\"callTool\");\n if (has(\"readResource\")) out.readResource = liveCall(\"readResource\");\n if (has(\"listResources\")) out.listResources = liveCall(\"listResources\");\n if (has(\"openLink\")) out.openLink = liveCall(\"openLink\");\n if (has(\"sendMessage\")) out.sendMessage = liveCall(\"sendMessage\");\n if (has(\"updateModelContext\"))\n out.updateModelContext = liveCall(\"updateModelContext\");\n if (has(\"requestDisplayMode\"))\n out.requestDisplayMode = liveCall(\"requestDisplayMode\");\n out.onSizeChange = (p) => live()?.onSizeChange?.(p);\n out.onInitialized = () => live()?.onInitialized?.();\n out.onRequestTeardown = (p) => live()?.onRequestTeardown?.(p);\n out.onLog = (p) => live()?.onLog?.(p);\n out.onError = (e) => live()?.onError?.(e);\n return out;\n}\n\nexport function McpAppFrame({\n app,\n resource,\n input,\n output,\n sandbox,\n handlers,\n hostInfo,\n hostContext,\n maxHeight = DEFAULT_MAX_HEIGHT,\n}: McpAppFrameProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [contentHeight, setContentHeight] = useState<number | undefined>(\n undefined,\n );\n const bridgeRef = useRef<McpAppBridge | null>(null);\n const lastSentInputRef = useRef<unknown>(undefined);\n const lastSentOutputRef = useRef<unknown>(undefined);\n const lastSentHostContextRef = useRef<McpAppHostContext | undefined>(\n undefined,\n );\n // Per MCP Apps spec, the host should defer notifications until the widget\n // signals readiness via `notifications/initialized`. Until then, we record\n // pending values and flush them on init.\n const widgetReadyRef = useRef(false);\n const pendingInputRef = useRef<unknown>(undefined);\n const pendingOutputRef = useRef<unknown>(undefined);\n const pendingHostContextRef = useRef<McpAppHostContext | undefined>(\n undefined,\n );\n\n const liveRef = useRef<LiveSnapshot>(null!);\n liveRef.current = {\n handlers,\n hostInfo,\n hostContext,\n input,\n output,\n };\n\n const resourceUri = resource.uri;\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n let cancelled = false;\n let initTimeoutId: ReturnType<typeof setTimeout> | null = null;\n let frame: RenderedFrame | null = null;\n const sb = sandbox;\n const html = resource.html;\n\n const scf = new SafeContentFrame(sb?.product ?? DEFAULT_PRODUCT, {\n ...(sb?.sandbox !== undefined && { sandbox: sb.sandbox }),\n ...(sb?.useShadowDom !== undefined && { useShadowDom: sb.useShadowDom }),\n ...(sb?.enableBrowserCaching !== undefined && {\n enableBrowserCaching: sb.enableBrowserCaching,\n }),\n ...(sb?.salt !== undefined && { salt: sb.salt }),\n });\n\n const renderOpts =\n sb?.unsafeDocumentWrite !== undefined\n ? { unsafeDocumentWrite: sb.unsafeDocumentWrite }\n : undefined;\n\n scf\n .renderHtml(html, container, renderOpts)\n .then((rendered) => {\n if (cancelled) {\n rendered.dispose();\n return;\n }\n frame = rendered;\n const current = liveRef.current;\n const liveHandlers = buildLiveHandlers(current.handlers, liveRef);\n const liveOnInitialized = liveHandlers.onInitialized;\n const flushPending = () => {\n if (widgetReadyRef.current) return;\n widgetReadyRef.current = true;\n const b = bridgeRef.current;\n if (!b) return;\n if (pendingInputRef.current !== undefined) {\n b.notifyToolInput(pendingInputRef.current);\n lastSentInputRef.current = pendingInputRef.current;\n pendingInputRef.current = undefined;\n }\n if (pendingOutputRef.current !== undefined) {\n b.notifyToolResult(pendingOutputRef.current);\n lastSentOutputRef.current = pendingOutputRef.current;\n pendingOutputRef.current = undefined;\n }\n if (pendingHostContextRef.current !== undefined) {\n b.notifyHostContextChanged(pendingHostContextRef.current);\n lastSentHostContextRef.current = pendingHostContextRef.current;\n pendingHostContextRef.current = undefined;\n }\n };\n const wrappedHandlers: McpAppBridgeHandlers = {\n ...liveHandlers,\n onInitialized: () => {\n if (initTimeoutId !== null) {\n clearTimeout(initTimeoutId);\n initTimeoutId = null;\n }\n flushPending();\n liveOnInitialized?.();\n },\n onSizeChange: (p) => {\n if (\n typeof p.height === \"number\" &&\n Number.isFinite(p.height) &&\n p.height > 0\n ) {\n setContentHeight(p.height);\n }\n liveHandlers.onSizeChange?.(p);\n },\n };\n // Safety net: if the widget never sends notifications/initialized\n // (broken or non-spec-compliant), flush the queue anyway so the host\n // doesn't appear hung.\n initTimeoutId = setTimeout(() => {\n initTimeoutId = null;\n flushPending();\n }, INIT_TIMEOUT_MS);\n bridgeRef.current = createMcpAppBridge({\n frame: rendered,\n handlers: wrappedHandlers,\n hostInfo: current.hostInfo,\n hostContext: current.hostContext,\n });\n\n if (current.input !== undefined)\n pendingInputRef.current = current.input;\n if (current.output !== undefined)\n pendingOutputRef.current = current.output;\n // hostContext is delivered inside the ui/initialize response; subsequent\n // changes flow through useBridgeNotify's pending path.\n })\n .catch((err) => {\n liveRef.current.handlers?.onError?.(\n err instanceof Error ? err : new Error(String(err)),\n );\n });\n\n return () => {\n cancelled = true;\n if (initTimeoutId !== null) {\n clearTimeout(initTimeoutId);\n initTimeoutId = null;\n }\n bridgeRef.current?.dispose();\n bridgeRef.current = null;\n frame?.dispose();\n frame = null;\n lastSentInputRef.current = undefined;\n lastSentOutputRef.current = undefined;\n lastSentHostContextRef.current = undefined;\n widgetReadyRef.current = false;\n pendingInputRef.current = undefined;\n pendingOutputRef.current = undefined;\n pendingHostContextRef.current = undefined;\n setContentHeight(undefined);\n };\n // oxlint-disable-next-line tap-hooks/exhaustive-deps -- re-mount only on resource URI change; live values flow through liveRef\n }, [resourceUri]);\n\n useBridgeNotify(\n input,\n bridgeRef,\n widgetReadyRef,\n pendingInputRef,\n lastSentInputRef,\n (b, v) => b.notifyToolInput(v),\n );\n useBridgeNotify(\n output,\n bridgeRef,\n widgetReadyRef,\n pendingOutputRef,\n lastSentOutputRef,\n (b, v) => b.notifyToolResult(v),\n );\n useBridgeNotify(\n hostContext,\n bridgeRef,\n widgetReadyRef,\n pendingHostContextRef,\n lastSentHostContextRef,\n (b, v) => b.notifyHostContextChanged(v),\n );\n\n const resolvedHeight =\n contentHeight != null ? Math.min(contentHeight, maxHeight) : undefined;\n const mergedStyle =\n resolvedHeight != null\n ? { ...sandbox?.style, height: resolvedHeight }\n : sandbox?.style;\n\n return (\n <div\n ref={containerRef}\n className={sandbox?.className}\n style={mergedStyle}\n data-mcp-app-resource={app.resourceUri}\n data-mcp-app-prefers-border={\n resource.meta?.prefersBorder ? \"\" : undefined\n }\n />\n );\n}\n"],"mappings":"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"}
|
|
@@ -14,7 +14,11 @@ const useAssistantModalOpenState = ({ defaultOpen = false, unstable_openOnRunSta
|
|
|
14
14
|
return aui.on("thread.runStart", () => {
|
|
15
15
|
setOpen(true);
|
|
16
16
|
});
|
|
17
|
-
}, [
|
|
17
|
+
}, [
|
|
18
|
+
unstable_openOnRunStart,
|
|
19
|
+
aui,
|
|
20
|
+
setOpen
|
|
21
|
+
]);
|
|
18
22
|
return state;
|
|
19
23
|
};
|
|
20
24
|
const AssistantModalPrimitiveRoot = ({ __scopeAssistantModal, defaultOpen, unstable_openOnRunStart, open, onOpenChange, ...rest }) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantModalRoot.js","names":["PopoverPrimitive"],"sources":["../../../src/primitives/assistantModal/AssistantModalRoot.tsx"],"sourcesContent":["\"use client\";\n\nimport { type FC, useEffect, useState } from \"react\";\nimport { Popover as PopoverPrimitive } from \"radix-ui\";\nimport { type ScopedProps, usePopoverScope } from \"./scope\";\nimport { useAui } from \"@assistant-ui/store\";\n\nexport namespace AssistantModalPrimitiveRoot {\n export type Props = PopoverPrimitive.PopoverProps & {\n unstable_openOnRunStart?: boolean | undefined;\n };\n}\n\nconst useAssistantModalOpenState = ({\n defaultOpen = false,\n unstable_openOnRunStart = true,\n}: {\n defaultOpen?: boolean | undefined;\n unstable_openOnRunStart?: boolean | undefined;\n}) => {\n const state = useState(defaultOpen);\n\n const [, setOpen] = state;\n const aui = useAui();\n useEffect(() => {\n if (!unstable_openOnRunStart) return undefined;\n\n return aui.on(\"thread.runStart\", () => {\n setOpen(true);\n });\n }, [unstable_openOnRunStart, aui]);\n\n return state;\n};\n\nexport const AssistantModalPrimitiveRoot: FC<\n AssistantModalPrimitiveRoot.Props\n> = ({\n __scopeAssistantModal,\n defaultOpen,\n unstable_openOnRunStart,\n open,\n onOpenChange,\n ...rest\n}: ScopedProps<AssistantModalPrimitiveRoot.Props>) => {\n const scope = usePopoverScope(__scopeAssistantModal);\n\n const [modalOpen, setOpen] = useAssistantModalOpenState({\n defaultOpen,\n unstable_openOnRunStart,\n });\n\n const openChangeHandler = (open: boolean) => {\n onOpenChange?.(open);\n setOpen(open);\n };\n\n return (\n <PopoverPrimitive.Root\n {...scope}\n open={open === undefined ? modalOpen : open}\n onOpenChange={openChangeHandler}\n {...rest}\n />\n );\n};\n\nAssistantModalPrimitiveRoot.displayName = \"AssistantModalPrimitive.Root\";\n"],"mappings":";;;;;;;AAaA,MAAM,8BAA8B,EAClC,cAAc,OACd,0BAA0B,WAItB;CACJ,MAAM,QAAQ,SAAS,WAAW;CAElC,MAAM,GAAG,WAAW;CACpB,MAAM,MAAM,OAAO;CACnB,gBAAgB;EACd,IAAI,CAAC,yBAAyB,OAAO,KAAA;EAErC,OAAO,IAAI,GAAG,yBAAyB;GACrC,QAAQ,IAAI;EACd,CAAC;CACH,GAAG,CAAC
|
|
1
|
+
{"version":3,"file":"AssistantModalRoot.js","names":["PopoverPrimitive"],"sources":["../../../src/primitives/assistantModal/AssistantModalRoot.tsx"],"sourcesContent":["\"use client\";\n\nimport { type FC, useEffect, useState } from \"react\";\nimport { Popover as PopoverPrimitive } from \"radix-ui\";\nimport { type ScopedProps, usePopoverScope } from \"./scope\";\nimport { useAui } from \"@assistant-ui/store\";\n\nexport namespace AssistantModalPrimitiveRoot {\n export type Props = PopoverPrimitive.PopoverProps & {\n unstable_openOnRunStart?: boolean | undefined;\n };\n}\n\nconst useAssistantModalOpenState = ({\n defaultOpen = false,\n unstable_openOnRunStart = true,\n}: {\n defaultOpen?: boolean | undefined;\n unstable_openOnRunStart?: boolean | undefined;\n}) => {\n const state = useState(defaultOpen);\n\n const [, setOpen] = state;\n const aui = useAui();\n useEffect(() => {\n if (!unstable_openOnRunStart) return undefined;\n\n return aui.on(\"thread.runStart\", () => {\n setOpen(true);\n });\n }, [unstable_openOnRunStart, aui, setOpen]);\n\n return state;\n};\n\nexport const AssistantModalPrimitiveRoot: FC<\n AssistantModalPrimitiveRoot.Props\n> = ({\n __scopeAssistantModal,\n defaultOpen,\n unstable_openOnRunStart,\n open,\n onOpenChange,\n ...rest\n}: ScopedProps<AssistantModalPrimitiveRoot.Props>) => {\n const scope = usePopoverScope(__scopeAssistantModal);\n\n const [modalOpen, setOpen] = useAssistantModalOpenState({\n defaultOpen,\n unstable_openOnRunStart,\n });\n\n const openChangeHandler = (open: boolean) => {\n onOpenChange?.(open);\n setOpen(open);\n };\n\n return (\n <PopoverPrimitive.Root\n {...scope}\n open={open === undefined ? modalOpen : open}\n onOpenChange={openChangeHandler}\n {...rest}\n />\n );\n};\n\nAssistantModalPrimitiveRoot.displayName = \"AssistantModalPrimitive.Root\";\n"],"mappings":";;;;;;;AAaA,MAAM,8BAA8B,EAClC,cAAc,OACd,0BAA0B,WAItB;CACJ,MAAM,QAAQ,SAAS,WAAW;CAElC,MAAM,GAAG,WAAW;CACpB,MAAM,MAAM,OAAO;CACnB,gBAAgB;EACd,IAAI,CAAC,yBAAyB,OAAO,KAAA;EAErC,OAAO,IAAI,GAAG,yBAAyB;GACrC,QAAQ,IAAI;EACd,CAAC;CACH,GAAG;EAAC;EAAyB;EAAK;CAAO,CAAC;CAE1C,OAAO;AACT;AAEA,MAAa,+BAER,EACH,uBACA,aACA,yBACA,MACA,cACA,GAAG,WACiD;CACpD,MAAM,QAAQ,gBAAgB,qBAAqB;CAEnD,MAAM,CAAC,WAAW,WAAW,2BAA2B;EACtD;EACA;CACF,CAAC;CAED,MAAM,qBAAqB,SAAkB;EAC3C,eAAe,IAAI;EACnB,QAAQ,IAAI;CACd;CAEA,OACE,oBAACA,QAAiB,MAAlB;EACE,GAAI;EACJ,MAAM,SAAS,KAAA,IAAY,YAAY;EACvC,cAAc;EACd,GAAI;CACL,CAAA;AAEL;AAEA,4BAA4B,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"triggerKeyboardResource.js","names":[],"sources":["../../../../src/primitives/composer/trigger/triggerKeyboardResource.ts"],"sourcesContent":["import {\n resource,\n tapEffect,\n tapEffectEvent,\n tapState,\n} from \"@assistant-ui/tap\";\nimport type {\n Unstable_TriggerCategory,\n Unstable_TriggerItem,\n} from \"@assistant-ui/core\";\n\n/** Relies on `Unstable_TriggerCategory` never carrying a `type` field. */\nfunction isTriggerItem(\n x: Unstable_TriggerItem | Unstable_TriggerCategory,\n): x is Unstable_TriggerItem {\n return \"type\" in x;\n}\n\n/** Key event shape accepted by the keyboard handler. */\nexport type TriggerPopoverKeyEvent = {\n readonly key: string;\n readonly shiftKey: boolean;\n preventDefault(): void;\n};\n\nexport type TriggerKeyboardResourceOutput = {\n /** Index of the currently highlighted entry within the navigable list. */\n readonly highlightedIndex: number;\n /** ID of the currently highlighted item (for `aria-activedescendant`). */\n readonly highlightedItemId: string | undefined;\n /** Move the highlight to an entry index (e.g. from pointer hover). Out-of-range values are ignored. */\n highlightIndex(index: number): void;\n /** Handle a key event; returns `true` if it was consumed. */\n handleKeyDown(e: TriggerPopoverKeyEvent): boolean;\n};\n\n/**\n * Owns keyboard-driven highlight state for the popover. Delegates selection,\n * category drill-in, back, and close to the callbacks supplied by the parent.\n */\nexport const TriggerKeyboardResource = resource(\n ({\n navigableList,\n isSearchMode,\n activeCategoryId,\n query,\n popoverId,\n open,\n selectItem,\n selectCategory,\n goBack,\n close,\n }: {\n navigableList: readonly (Unstable_TriggerCategory | Unstable_TriggerItem)[];\n isSearchMode: boolean;\n activeCategoryId: string | null;\n query: string;\n popoverId: string;\n open: boolean;\n selectItem: (item: Unstable_TriggerItem) => void;\n selectCategory: (categoryId: string) => void;\n goBack: () => void;\n close: () => void;\n }): TriggerKeyboardResourceOutput => {\n const [highlightedIndex, setHighlightedIndex] = tapState(0);\n\n
|
|
1
|
+
{"version":3,"file":"triggerKeyboardResource.js","names":[],"sources":["../../../../src/primitives/composer/trigger/triggerKeyboardResource.ts"],"sourcesContent":["import {\n resource,\n tapEffect,\n tapEffectEvent,\n tapState,\n} from \"@assistant-ui/tap\";\nimport type {\n Unstable_TriggerCategory,\n Unstable_TriggerItem,\n} from \"@assistant-ui/core\";\n\n/** Relies on `Unstable_TriggerCategory` never carrying a `type` field. */\nfunction isTriggerItem(\n x: Unstable_TriggerItem | Unstable_TriggerCategory,\n): x is Unstable_TriggerItem {\n return \"type\" in x;\n}\n\n/** Key event shape accepted by the keyboard handler. */\nexport type TriggerPopoverKeyEvent = {\n readonly key: string;\n readonly shiftKey: boolean;\n preventDefault(): void;\n};\n\nexport type TriggerKeyboardResourceOutput = {\n /** Index of the currently highlighted entry within the navigable list. */\n readonly highlightedIndex: number;\n /** ID of the currently highlighted item (for `aria-activedescendant`). */\n readonly highlightedItemId: string | undefined;\n /** Move the highlight to an entry index (e.g. from pointer hover). Out-of-range values are ignored. */\n highlightIndex(index: number): void;\n /** Handle a key event; returns `true` if it was consumed. */\n handleKeyDown(e: TriggerPopoverKeyEvent): boolean;\n};\n\n/**\n * Owns keyboard-driven highlight state for the popover. Delegates selection,\n * category drill-in, back, and close to the callbacks supplied by the parent.\n */\nexport const TriggerKeyboardResource = resource(\n ({\n navigableList,\n isSearchMode,\n activeCategoryId,\n query,\n popoverId,\n open,\n selectItem,\n selectCategory,\n goBack,\n close,\n }: {\n navigableList: readonly (Unstable_TriggerCategory | Unstable_TriggerItem)[];\n isSearchMode: boolean;\n activeCategoryId: string | null;\n query: string;\n popoverId: string;\n open: boolean;\n selectItem: (item: Unstable_TriggerItem) => void;\n selectCategory: (categoryId: string) => void;\n goBack: () => void;\n close: () => void;\n }): TriggerKeyboardResourceOutput => {\n const [highlightedIndex, setHighlightedIndex] = tapState(0);\n\n tapEffect(() => {\n setHighlightedIndex(0);\n }, [navigableList]);\n\n tapEffect(() => {\n setHighlightedIndex(0);\n }, [isSearchMode, activeCategoryId]);\n\n const highlightIndex = tapEffectEvent((index: number) => {\n if (index < 0 || index >= navigableList.length) return;\n if (index === highlightedIndex) return;\n setHighlightedIndex(index);\n });\n\n const handleKeyDown = tapEffectEvent(\n (e: TriggerPopoverKeyEvent): boolean => {\n if (!open) return false;\n\n switch (e.key) {\n case \"ArrowDown\": {\n e.preventDefault();\n setHighlightedIndex((prev) => {\n const len = navigableList.length;\n if (len === 0) return 0;\n return prev < len - 1 ? prev + 1 : 0;\n });\n return true;\n }\n case \"ArrowUp\": {\n e.preventDefault();\n setHighlightedIndex((prev) => {\n const len = navigableList.length;\n if (len === 0) return 0;\n return prev > 0 ? prev - 1 : len - 1;\n });\n return true;\n }\n case \"Enter\":\n case \"Tab\": {\n if (e.shiftKey) return false;\n e.preventDefault();\n const item = navigableList[highlightedIndex];\n if (!item) return true;\n\n if (isTriggerItem(item)) {\n selectItem(item);\n } else {\n selectCategory(item.id);\n }\n return true;\n }\n case \"Escape\": {\n e.preventDefault();\n close();\n return true;\n }\n case \"Backspace\": {\n if (activeCategoryId && query === \"\") {\n e.preventDefault();\n goBack();\n return true;\n }\n return false;\n }\n default:\n return false;\n }\n },\n );\n\n const highlightedEntry = navigableList[highlightedIndex];\n const highlightedItemId =\n open && highlightedEntry\n ? `${popoverId}-option-${highlightedEntry.id}`\n : undefined;\n\n return {\n highlightedIndex,\n highlightedItemId,\n highlightIndex,\n handleKeyDown,\n };\n },\n);\n"],"mappings":";;;AAYA,SAAS,cACP,GAC2B;CAC3B,OAAO,UAAU;AACnB;;;;;AAwBA,MAAa,0BAA0B,UACpC,EACC,eACA,cACA,kBACA,OACA,WACA,MACA,YACA,gBACA,QACA,YAYmC;CACnC,MAAM,CAAC,kBAAkB,uBAAuB,SAAS,CAAC;CAE1D,gBAAgB;EACd,oBAAoB,CAAC;CACvB,GAAG,CAAC,aAAa,CAAC;CAElB,gBAAgB;EACd,oBAAoB,CAAC;CACvB,GAAG,CAAC,cAAc,gBAAgB,CAAC;CAEnC,MAAM,iBAAiB,gBAAgB,UAAkB;EACvD,IAAI,QAAQ,KAAK,SAAS,cAAc,QAAQ;EAChD,IAAI,UAAU,kBAAkB;EAChC,oBAAoB,KAAK;CAC3B,CAAC;CAED,MAAM,gBAAgB,gBACnB,MAAuC;EACtC,IAAI,CAAC,MAAM,OAAO;EAElB,QAAQ,EAAE,KAAV;GACE,KAAK;IACH,EAAE,eAAe;IACjB,qBAAqB,SAAS;KAC5B,MAAM,MAAM,cAAc;KAC1B,IAAI,QAAQ,GAAG,OAAO;KACtB,OAAO,OAAO,MAAM,IAAI,OAAO,IAAI;IACrC,CAAC;IACD,OAAO;GAET,KAAK;IACH,EAAE,eAAe;IACjB,qBAAqB,SAAS;KAC5B,MAAM,MAAM,cAAc;KAC1B,IAAI,QAAQ,GAAG,OAAO;KACtB,OAAO,OAAO,IAAI,OAAO,IAAI,MAAM;IACrC,CAAC;IACD,OAAO;GAET,KAAK;GACL,KAAK,OAAO;IACV,IAAI,EAAE,UAAU,OAAO;IACvB,EAAE,eAAe;IACjB,MAAM,OAAO,cAAc;IAC3B,IAAI,CAAC,MAAM,OAAO;IAElB,IAAI,cAAc,IAAI,GACpB,WAAW,IAAI;SAEf,eAAe,KAAK,EAAE;IAExB,OAAO;GACT;GACA,KAAK;IACH,EAAE,eAAe;IACjB,MAAM;IACN,OAAO;GAET,KAAK;IACH,IAAI,oBAAoB,UAAU,IAAI;KACpC,EAAE,eAAe;KACjB,OAAO;KACP,OAAO;IACT;IACA,OAAO;GAET,SACE,OAAO;EACX;CACF,CACF;CAEA,MAAM,mBAAmB,cAAc;CAMvC,OAAO;EACL;EACA,mBANA,QAAQ,mBACJ,GAAG,UAAU,UAAU,iBAAiB,OACxC,KAAA;EAKJ;EACA;CACF;AACF,CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessagePartImage.js","names":[],"sources":["../../../src/primitives/messagePart/MessagePartImage.tsx"],"sourcesContent":["\"use client\";\n\nimport { Primitive } from \"../../utils/Primitive\";\nimport {\n type ComponentRef,\n forwardRef,\n type ComponentPropsWithoutRef,\n} from \"react\";\nimport { useMessagePartImage } from \"./useMessagePartImage\";\n\nexport namespace MessagePartPrimitiveImage {\n export type Element = ComponentRef<typeof Primitive.img>;\n /**\n * Props for the MessagePartPrimitive.Image component.\n * Accepts all standard img element props.\n */\n export type Props = ComponentPropsWithoutRef<typeof Primitive.img>;\n}\n\n/**\n * Renders an image from the current message part context.\n *\n * This component displays image content from the current message part,\n * automatically setting the src attribute from the message part's image data.\n *\n * @example\n * ```tsx\n * <MessagePartPrimitive.Image\n * alt=\"Generated image\"\n * className=\"message-image\"\n * style={{ maxWidth: '100%' }}\n * />\n * ```\n */\nexport const MessagePartPrimitiveImage = forwardRef<\n MessagePartPrimitiveImage.Element,\n MessagePartPrimitiveImage.Props\n>((props, forwardedRef) => {\n const { image } = useMessagePartImage();\n
|
|
1
|
+
{"version":3,"file":"MessagePartImage.js","names":[],"sources":["../../../src/primitives/messagePart/MessagePartImage.tsx"],"sourcesContent":["\"use client\";\n\nimport { Primitive } from \"../../utils/Primitive\";\nimport {\n type ComponentRef,\n forwardRef,\n type ComponentPropsWithoutRef,\n} from \"react\";\nimport { useMessagePartImage } from \"./useMessagePartImage\";\n\nexport namespace MessagePartPrimitiveImage {\n export type Element = ComponentRef<typeof Primitive.img>;\n /**\n * Props for the MessagePartPrimitive.Image component.\n * Accepts all standard img element props.\n */\n export type Props = ComponentPropsWithoutRef<typeof Primitive.img>;\n}\n\n/**\n * Renders an image from the current message part context.\n *\n * This component displays image content from the current message part,\n * automatically setting the src attribute from the message part's image data.\n *\n * @example\n * ```tsx\n * <MessagePartPrimitive.Image\n * alt=\"Generated image\"\n * className=\"message-image\"\n * style={{ maxWidth: '100%' }}\n * />\n * ```\n */\nexport const MessagePartPrimitiveImage = forwardRef<\n MessagePartPrimitiveImage.Element,\n MessagePartPrimitiveImage.Props\n>((props, forwardedRef) => {\n const { image } = useMessagePartImage();\n return <Primitive.img src={image} {...props} ref={forwardedRef} />;\n});\n\nMessagePartPrimitiveImage.displayName = \"MessagePartPrimitive.Image\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAkCA,MAAa,4BAA4B,YAGtC,OAAO,iBAAiB;CACzB,MAAM,EAAE,UAAU,oBAAoB;CACtC,OAAO,oBAAC,UAAU,KAAX;EAAe,KAAK;EAAO,GAAI;EAAO,KAAK;CAAe,CAAA;AACnE,CAAC;AAED,0BAA0B,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useThreadViewportAutoScroll.d.ts","names":[],"sources":["../../../src/primitives/thread/useThreadViewportAutoScroll.ts"],"mappings":";;;kBAWiB,2BAAA;EAAA,KACH,OAAA;IADG;;;;;;IAQb,UAAA;IAcA;;;AAO4B;AAIhC;IAlBI,wBAAA;
|
|
1
|
+
{"version":3,"file":"useThreadViewportAutoScroll.d.ts","names":[],"sources":["../../../src/primitives/thread/useThreadViewportAutoScroll.ts"],"mappings":";;;kBAWiB,2BAAA;EAAA,KACH,OAAA;IADG;;;;;;IAQb,UAAA;IAcA;;;AAO4B;AAIhC;IAlBI,wBAAA;IAkMH;;;;;IA3LG,0BAAA;IAgBD;;;;;IATC,4BAAA;EAAA;AAAA;AAAA,cAIS,2BAAA,oBAAgD,WAAA;EAAa,UAAA;EAAA,wBAAA;EAAA,0BAAA;EAAA;AAAA,GAKvE,2BAAA,CAA4B,OAAA,KAAU,WAAA,CAAY,QAAA"}
|
|
@@ -16,6 +16,9 @@ const useThreadViewportAutoScroll = ({ autoScroll, scrollToBottomOnRunStart = tr
|
|
|
16
16
|
const threadViewportStore = useThreadViewportStore();
|
|
17
17
|
if (autoScroll === void 0) autoScroll = threadViewportStore.getState().turnAnchor !== "top";
|
|
18
18
|
const lastScrollTop = useRef(0);
|
|
19
|
+
const lastScrollHeight = useRef(0);
|
|
20
|
+
const lastObservedScrollHeight = useRef(0);
|
|
21
|
+
const lastObservedClientHeight = useRef(0);
|
|
19
22
|
const scrollingToBottomBehaviorRef = useRef(null);
|
|
20
23
|
const scrollToBottom = useCallback((behavior) => {
|
|
21
24
|
const div = divRef.current;
|
|
@@ -45,16 +48,23 @@ const useThreadViewportAutoScroll = ({ autoScroll, scrollToBottomOnRunStart = tr
|
|
|
45
48
|
const div = divRef.current;
|
|
46
49
|
if (!div) return;
|
|
47
50
|
const isAtBottom = threadViewportStore.getState().isAtBottom;
|
|
48
|
-
const newIsAtBottom = Math.abs(div.scrollHeight - div.scrollTop - div.clientHeight)
|
|
51
|
+
const newIsAtBottom = Math.abs(div.scrollHeight - div.scrollTop - div.clientHeight) <= 1 || div.scrollHeight <= div.clientHeight;
|
|
49
52
|
if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {} else {
|
|
50
53
|
if (newIsAtBottom) {
|
|
51
54
|
if (div.scrollHeight > div.clientHeight + 1) scrollingToBottomBehaviorRef.current = null;
|
|
52
|
-
}
|
|
55
|
+
} else if (lastScrollTop.current > div.scrollTop && lastScrollHeight.current === div.scrollHeight) scrollingToBottomBehaviorRef.current = null;
|
|
53
56
|
if ((newIsAtBottom || scrollingToBottomBehaviorRef.current === null) && newIsAtBottom !== isAtBottom) writableStore(threadViewportStore).setState({ isAtBottom: newIsAtBottom });
|
|
54
57
|
}
|
|
55
58
|
lastScrollTop.current = div.scrollTop;
|
|
59
|
+
lastScrollHeight.current = div.scrollHeight;
|
|
56
60
|
};
|
|
57
61
|
const resizeRef = useOnResizeContent(() => {
|
|
62
|
+
const div = divRef.current;
|
|
63
|
+
if (!div) return;
|
|
64
|
+
const { scrollHeight, clientHeight } = div;
|
|
65
|
+
if (scrollHeight === lastObservedScrollHeight.current && clientHeight === lastObservedClientHeight.current) return;
|
|
66
|
+
lastObservedScrollHeight.current = scrollHeight;
|
|
67
|
+
lastObservedClientHeight.current = clientHeight;
|
|
58
68
|
const scrollBehavior = scrollingToBottomBehaviorRef.current;
|
|
59
69
|
if (scrollBehavior && hasActiveTopAnchor()) scrollingToBottomBehaviorRef.current = null;
|
|
60
70
|
else if (scrollBehavior) scrollToBottom(scrollBehavior);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useThreadViewportAutoScroll.js","names":[],"sources":["../../../src/primitives/thread/useThreadViewportAutoScroll.ts"],"sourcesContent":["\"use client\";\n\nimport { useComposedRefs } from \"@radix-ui/react-compose-refs\";\nimport { useCallback, useLayoutEffect, useRef, type RefCallback } from \"react\";\nimport { useAuiEvent, useAuiState } from \"@assistant-ui/store\";\nimport { useOnResizeContent } from \"../../utils/hooks/useOnResizeContent\";\nimport { useOnScrollToBottom } from \"../../utils/hooks/useOnScrollToBottom\";\nimport { useManagedRef } from \"../../utils/hooks/useManagedRef\";\nimport { writableStore } from \"../../context/ReadonlyStore\";\nimport { useThreadViewportStore } from \"../../context/react/ThreadViewportContext\";\n\nexport namespace useThreadViewportAutoScroll {\n export type Options = {\n /**\n * Whether to automatically scroll to the bottom when new messages are added.\n * When enabled, the viewport will automatically scroll to show the latest content.\n *\n * Default false if `turnAnchor` is \"top\", otherwise defaults to true.\n */\n autoScroll?: boolean | undefined;\n\n /**\n * Whether to scroll to bottom when a new run starts.\n *\n * Defaults to true.\n */\n scrollToBottomOnRunStart?: boolean | undefined;\n\n /**\n * Whether to scroll to bottom when messages first appear in the thread.\n *\n * Defaults to true.\n */\n scrollToBottomOnInitialize?: boolean | undefined;\n\n /**\n * Whether to scroll to bottom when switching to a different thread.\n *\n * Defaults to true.\n */\n scrollToBottomOnThreadSwitch?: boolean | undefined;\n };\n}\n\nexport const useThreadViewportAutoScroll = <TElement extends HTMLElement>({\n autoScroll,\n scrollToBottomOnRunStart = true,\n scrollToBottomOnInitialize = true,\n scrollToBottomOnThreadSwitch = true,\n}: useThreadViewportAutoScroll.Options): RefCallback<TElement> => {\n const divRef = useRef<TElement>(null);\n const hasMessages = useAuiState((s) => s.thread.messages.length > 0);\n const initializeScrollRequestedRef = useRef(false);\n const scheduledFrameRef = useRef<number | null>(null);\n\n const threadViewportStore = useThreadViewportStore();\n if (autoScroll === undefined) {\n autoScroll = threadViewportStore.getState().turnAnchor !== \"top\";\n }\n\n const lastScrollTop = useRef<number>(0);\n\n // Pending bottom-scroll intent. Planted by initialize/run-start/switch/button\n // triggers, cleared only when handleScroll confirms we reached bottom.\n const scrollingToBottomBehaviorRef = useRef<ScrollBehavior | null>(null);\n\n const scrollToBottom = useCallback((behavior: ScrollBehavior) => {\n const div = divRef.current;\n if (!div) return;\n\n scrollingToBottomBehaviorRef.current = behavior;\n div.scrollTo({ top: div.scrollHeight, behavior });\n }, []);\n\n const scheduleScrollToBottom = useCallback(\n (behavior: ScrollBehavior) => {\n scrollingToBottomBehaviorRef.current = behavior;\n if (scheduledFrameRef.current !== null) {\n cancelAnimationFrame(scheduledFrameRef.current);\n }\n scheduledFrameRef.current = requestAnimationFrame(() => {\n scheduledFrameRef.current = null;\n scrollToBottom(behavior);\n });\n },\n [scrollToBottom],\n );\n\n useLayoutEffect(\n () => () => {\n if (scheduledFrameRef.current !== null) {\n cancelAnimationFrame(scheduledFrameRef.current);\n }\n },\n [],\n );\n\n const hasActiveTopAnchor = useCallback(() => {\n const state = threadViewportStore.getState();\n return (\n state.turnAnchor === \"top\" &&\n state.element.viewport === divRef.current &&\n state.element.anchor !== null\n );\n }, [threadViewportStore]);\n\n const handleScroll = () => {\n const div = divRef.current;\n if (!div) return;\n\n const isAtBottom = threadViewportStore.getState().isAtBottom;\n const newIsAtBottom =\n Math.abs(div.scrollHeight - div.scrollTop - div.clientHeight) < 1 ||\n div.scrollHeight <= div.clientHeight;\n\n const isInFlightDownwardScroll =\n !newIsAtBottom && lastScrollTop.current < div.scrollTop;\n if (isInFlightDownwardScroll) {\n // no-op: a smooth scroll-to-bottom fires many midpoint scroll events\n // before landing, don't flicker isAtBottom or clear intent mid-animation\n } else {\n if (newIsAtBottom) {\n // newIsAtBottom is ambiguous when the viewport doesn't overflow —\n // keep intent alive until content can actually scroll\n const viewportOverflows = div.scrollHeight > div.clientHeight + 1;\n if (viewportOverflows) {\n scrollingToBottomBehaviorRef.current = null;\n }\n }\n\n const shouldUpdate =\n newIsAtBottom || scrollingToBottomBehaviorRef.current === null;\n\n if (shouldUpdate && newIsAtBottom !== isAtBottom) {\n writableStore(threadViewportStore).setState({\n isAtBottom: newIsAtBottom,\n });\n }\n }\n\n lastScrollTop.current = div.scrollTop;\n };\n\n const resizeRef = useOnResizeContent(() => {\n const scrollBehavior = scrollingToBottomBehaviorRef.current;\n if (scrollBehavior && hasActiveTopAnchor()) {\n // Let the top-anchor reserve own scrolling while a run starts to avoid a bottom-scroll race.\n scrollingToBottomBehaviorRef.current = null;\n } else if (scrollBehavior) {\n scrollToBottom(scrollBehavior);\n } else if (autoScroll && threadViewportStore.getState().isAtBottom) {\n scrollToBottom(\"instant\");\n }\n\n handleScroll();\n });\n\n const scrollRef = useManagedRef<HTMLElement>((el) => {\n el.addEventListener(\"scroll\", handleScroll);\n return () => {\n el.removeEventListener(\"scroll\", handleScroll);\n };\n });\n\n useLayoutEffect(() => {\n if (!scrollToBottomOnInitialize) return;\n if (!hasMessages) {\n initializeScrollRequestedRef.current = false;\n return;\n }\n if (initializeScrollRequestedRef.current) return;\n\n initializeScrollRequestedRef.current = true;\n // defer to an in-flight run (e.g. first message on a new thread) that\n // already planted intent — otherwise we'd downgrade its \"auto\" to \"instant\"\n if (scrollingToBottomBehaviorRef.current !== null) return;\n scheduleScrollToBottom(\"instant\");\n }, [hasMessages, scheduleScrollToBottom, scrollToBottomOnInitialize]);\n\n useOnScrollToBottom(({ behavior }) => {\n scrollToBottom(behavior);\n });\n\n useAuiEvent(\"thread.runStart\", () => {\n if (!scrollToBottomOnRunStart) return;\n if (threadViewportStore.getState().turnAnchor === \"top\") return;\n scheduleScrollToBottom(\"auto\");\n });\n\n useAuiEvent(\"threadListItem.switchedTo\", () => {\n if (!scrollToBottomOnThreadSwitch) return;\n scheduleScrollToBottom(\"instant\");\n });\n\n const autoScrollRef = useComposedRefs<TElement>(resizeRef, scrollRef, divRef);\n return autoScrollRef as RefCallback<TElement>;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useThreadViewportAutoScroll.js","names":[],"sources":["../../../src/primitives/thread/useThreadViewportAutoScroll.ts"],"sourcesContent":["\"use client\";\n\nimport { useComposedRefs } from \"@radix-ui/react-compose-refs\";\nimport { useCallback, useLayoutEffect, useRef, type RefCallback } from \"react\";\nimport { useAuiEvent, useAuiState } from \"@assistant-ui/store\";\nimport { useOnResizeContent } from \"../../utils/hooks/useOnResizeContent\";\nimport { useOnScrollToBottom } from \"../../utils/hooks/useOnScrollToBottom\";\nimport { useManagedRef } from \"../../utils/hooks/useManagedRef\";\nimport { writableStore } from \"../../context/ReadonlyStore\";\nimport { useThreadViewportStore } from \"../../context/react/ThreadViewportContext\";\n\nexport namespace useThreadViewportAutoScroll {\n export type Options = {\n /**\n * Whether to automatically scroll to the bottom when new messages are added.\n * When enabled, the viewport will automatically scroll to show the latest content.\n *\n * Default false if `turnAnchor` is \"top\", otherwise defaults to true.\n */\n autoScroll?: boolean | undefined;\n\n /**\n * Whether to scroll to bottom when a new run starts.\n *\n * Defaults to true.\n */\n scrollToBottomOnRunStart?: boolean | undefined;\n\n /**\n * Whether to scroll to bottom when messages first appear in the thread.\n *\n * Defaults to true.\n */\n scrollToBottomOnInitialize?: boolean | undefined;\n\n /**\n * Whether to scroll to bottom when switching to a different thread.\n *\n * Defaults to true.\n */\n scrollToBottomOnThreadSwitch?: boolean | undefined;\n };\n}\n\nexport const useThreadViewportAutoScroll = <TElement extends HTMLElement>({\n autoScroll,\n scrollToBottomOnRunStart = true,\n scrollToBottomOnInitialize = true,\n scrollToBottomOnThreadSwitch = true,\n}: useThreadViewportAutoScroll.Options): RefCallback<TElement> => {\n const divRef = useRef<TElement>(null);\n const hasMessages = useAuiState((s) => s.thread.messages.length > 0);\n const initializeScrollRequestedRef = useRef(false);\n const scheduledFrameRef = useRef<number | null>(null);\n\n const threadViewportStore = useThreadViewportStore();\n if (autoScroll === undefined) {\n autoScroll = threadViewportStore.getState().turnAnchor !== \"top\";\n }\n\n const lastScrollTop = useRef<number>(0);\n const lastScrollHeight = useRef<number>(0);\n const lastObservedScrollHeight = useRef<number>(0);\n const lastObservedClientHeight = useRef<number>(0);\n\n // Pending bottom-scroll intent. Planted by initialize/run-start/switch/button\n // triggers, cleared when handleScroll confirms we reached bottom, or when the\n // user actively scrolls up while content size is stable.\n const scrollingToBottomBehaviorRef = useRef<ScrollBehavior | null>(null);\n\n const scrollToBottom = useCallback((behavior: ScrollBehavior) => {\n const div = divRef.current;\n if (!div) return;\n\n scrollingToBottomBehaviorRef.current = behavior;\n div.scrollTo({ top: div.scrollHeight, behavior });\n }, []);\n\n const scheduleScrollToBottom = useCallback(\n (behavior: ScrollBehavior) => {\n scrollingToBottomBehaviorRef.current = behavior;\n if (scheduledFrameRef.current !== null) {\n cancelAnimationFrame(scheduledFrameRef.current);\n }\n scheduledFrameRef.current = requestAnimationFrame(() => {\n scheduledFrameRef.current = null;\n scrollToBottom(behavior);\n });\n },\n [scrollToBottom],\n );\n\n useLayoutEffect(\n () => () => {\n if (scheduledFrameRef.current !== null) {\n cancelAnimationFrame(scheduledFrameRef.current);\n }\n },\n [],\n );\n\n const hasActiveTopAnchor = useCallback(() => {\n const state = threadViewportStore.getState();\n return (\n state.turnAnchor === \"top\" &&\n state.element.viewport === divRef.current &&\n state.element.anchor !== null\n );\n }, [threadViewportStore]);\n\n const handleScroll = () => {\n const div = divRef.current;\n if (!div) return;\n\n const isAtBottom = threadViewportStore.getState().isAtBottom;\n const newIsAtBottom =\n Math.abs(div.scrollHeight - div.scrollTop - div.clientHeight) <= 1 ||\n div.scrollHeight <= div.clientHeight;\n\n const isInFlightDownwardScroll =\n !newIsAtBottom && lastScrollTop.current < div.scrollTop;\n if (isInFlightDownwardScroll) {\n // no-op: a smooth scroll-to-bottom fires many midpoint scroll events\n // before landing, don't flicker isAtBottom or clear intent mid-animation\n } else {\n if (newIsAtBottom) {\n // newIsAtBottom is ambiguous when the viewport doesn't overflow —\n // keep intent alive until content can actually scroll\n const viewportOverflows = div.scrollHeight > div.clientHeight + 1;\n if (viewportOverflows) {\n scrollingToBottomBehaviorRef.current = null;\n }\n } else if (\n lastScrollTop.current > div.scrollTop &&\n lastScrollHeight.current === div.scrollHeight\n ) {\n // scrollHeight equality rules out content-driven shifts being misread as user scroll-up\n scrollingToBottomBehaviorRef.current = null;\n }\n\n const shouldUpdate =\n newIsAtBottom || scrollingToBottomBehaviorRef.current === null;\n\n if (shouldUpdate && newIsAtBottom !== isAtBottom) {\n writableStore(threadViewportStore).setState({\n isAtBottom: newIsAtBottom,\n });\n }\n }\n\n lastScrollTop.current = div.scrollTop;\n lastScrollHeight.current = div.scrollHeight;\n };\n\n const resizeRef = useOnResizeContent(() => {\n const div = divRef.current;\n if (!div) return;\n\n const { scrollHeight, clientHeight } = div;\n if (\n scrollHeight === lastObservedScrollHeight.current &&\n clientHeight === lastObservedClientHeight.current\n ) {\n return;\n }\n lastObservedScrollHeight.current = scrollHeight;\n lastObservedClientHeight.current = clientHeight;\n\n const scrollBehavior = scrollingToBottomBehaviorRef.current;\n if (scrollBehavior && hasActiveTopAnchor()) {\n // Let the top-anchor reserve own scrolling while a run starts to avoid a bottom-scroll race.\n scrollingToBottomBehaviorRef.current = null;\n } else if (scrollBehavior) {\n scrollToBottom(scrollBehavior);\n } else if (autoScroll && threadViewportStore.getState().isAtBottom) {\n scrollToBottom(\"instant\");\n }\n\n handleScroll();\n });\n\n const scrollRef = useManagedRef<HTMLElement>((el) => {\n el.addEventListener(\"scroll\", handleScroll);\n return () => {\n el.removeEventListener(\"scroll\", handleScroll);\n };\n });\n\n useLayoutEffect(() => {\n if (!scrollToBottomOnInitialize) return;\n if (!hasMessages) {\n initializeScrollRequestedRef.current = false;\n return;\n }\n if (initializeScrollRequestedRef.current) return;\n\n initializeScrollRequestedRef.current = true;\n // defer to an in-flight run (e.g. first message on a new thread) that\n // already planted intent — otherwise we'd downgrade its \"auto\" to \"instant\"\n if (scrollingToBottomBehaviorRef.current !== null) return;\n scheduleScrollToBottom(\"instant\");\n }, [hasMessages, scheduleScrollToBottom, scrollToBottomOnInitialize]);\n\n useOnScrollToBottom(({ behavior }) => {\n scrollToBottom(behavior);\n });\n\n useAuiEvent(\"thread.runStart\", () => {\n if (!scrollToBottomOnRunStart) return;\n if (threadViewportStore.getState().turnAnchor === \"top\") return;\n scheduleScrollToBottom(\"auto\");\n });\n\n useAuiEvent(\"threadListItem.switchedTo\", () => {\n if (!scrollToBottomOnThreadSwitch) return;\n scheduleScrollToBottom(\"instant\");\n });\n\n const autoScrollRef = useComposedRefs<TElement>(resizeRef, scrollRef, divRef);\n return autoScrollRef as RefCallback<TElement>;\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMentionAdapter.js","names":[],"sources":["../../src/unstable/useMentionAdapter.ts"],"sourcesContent":["\"use client\";\n\nimport { useMemo, type FC } from \"react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport type {\n Unstable_DirectiveFormatter,\n Unstable_TriggerAdapter,\n Unstable_TriggerCategory,\n Unstable_TriggerItem,\n} from \"@assistant-ui/core\";\nimport { unstable_defaultDirectiveFormatter } from \"@assistant-ui/core\";\nimport type { ReadonlyJSONObject } from \"assistant-stream/utils\";\n\n/** Icon component shape consumed by `ComposerTriggerPopover`'s `iconMap`. */\nexport type Unstable_IconComponent = FC<{ className?: string }>;\n\nexport type Unstable_Mention = {\n readonly id: string;\n readonly type: string;\n readonly label: string;\n readonly description?: string | undefined;\n /** Shortcut for `metadata.icon`; merged with `metadata` if both are given. */\n readonly icon?: string | undefined;\n readonly metadata?: ReadonlyJSONObject | undefined;\n};\n\nexport type Unstable_MentionCategory = {\n readonly id: string;\n readonly label: string;\n readonly items: readonly Unstable_Mention[];\n};\n\nexport type Unstable_ModelContextToolsOptions = {\n /** Wrap tools in a dedicated category (drill-down mode). */\n readonly category?: { readonly id: string; readonly label: string };\n /** Format tool name for display. */\n readonly formatLabel?: (toolName: string) => string;\n /** Default icon key for each tool. */\n readonly icon?: string;\n};\n\nexport type Unstable_UseMentionAdapterOptions = {\n /** Flat mention list. Ignored when `categories` is set. */\n readonly items?: readonly Unstable_Mention[];\n /** Categorized mentions for drill-down navigation. */\n readonly categories?: readonly Unstable_MentionCategory[];\n /**\n * How tools registered via `useAssistantTool` integrate.\n * - `false`: exclude.\n * - `true`: include (default when no `items`/`categories`; as a category\n * if `categories` is set, flat otherwise).\n * - object: explicit config.\n *\n * Omitted → defaults to `true` iff neither `items` nor `categories`.\n */\n readonly includeModelContextTools?:\n | boolean\n | Unstable_ModelContextToolsOptions;\n /** Directive formatter. @default unstable_defaultDirectiveFormatter */\n readonly formatter?: Unstable_DirectiveFormatter;\n /** Fires after an item is inserted into the composer. */\n readonly onInserted?: (item: Unstable_TriggerItem) => void;\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\nexport type Unstable_MentionDirective = {\n readonly formatter: Unstable_DirectiveFormatter;\n readonly onInserted?: ((item: Unstable_TriggerItem) => void) | undefined;\n};\n\n/**\n * @deprecated Under active development and might change without notice.\n *\n * Creates a spreadable `{ adapter, directive }` bundle for `@` mentions.\n * Supports tools registered via `useAssistantTool`, explicit items, or both —\n * flat or categorized.\n *\n * @example\n * ```tsx\n * const mention = unstable_useMentionAdapter();\n * <ComposerTriggerPopover char=\"@\" {...mention} />\n * ```\n */\nexport function unstable_useMentionAdapter(\n options?: Unstable_UseMentionAdapterOptions,\n): {\n adapter: Unstable_TriggerAdapter;\n directive: Unstable_MentionDirective;\n iconMap?: Record<string, Unstable_IconComponent>;\n fallbackIcon?: Unstable_IconComponent;\n} {\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const aui = useAui();\n\n const items = options?.items;\n const categories = options?.categories;\n const includeTools =\n options?.includeModelContextTools ?? (!items && !categories);\n const toolsConfig =\n typeof includeTools === \"object\" ? includeTools : undefined;\n const wantsTools = includeTools !== false;\n const formatter = options?.formatter;\n const onInserted = options?.onInserted;\n\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const adapter = useMemo<Unstable_TriggerAdapter>(() => {\n const getModelContextTools = (): Unstable_TriggerItem[] => {\n if (!wantsTools) return [];\n const ctx = aui.thread().getModelContext();\n const tools = ctx.tools;\n if (!tools) return [];\n const formatLabel = toolsConfig?.formatLabel;\n const defaultIcon = toolsConfig?.icon;\n return Object.entries(tools).map(([name, tool]) =>\n toTriggerItem({\n id: name,\n type: \"tool\",\n label: formatLabel ? formatLabel(name) : name,\n description: tool.description ?? undefined,\n icon: defaultIcon,\n }),\n );\n };\n\n // Categorized: drill-down mode\n if (categories && categories.length > 0) {\n const groups = categories.map((cat) => ({\n id: cat.id,\n label: cat.label,\n items: cat.items.map(toTriggerItem),\n }));\n\n let toolCategory: {\n id: string;\n label: string;\n items: Unstable_TriggerItem[];\n } | null = null;\n if (wantsTools) {\n const toolItems = getModelContextTools();\n if (toolItems.length > 0) {\n toolCategory = {\n id: toolsConfig?.category?.id ?? \"tools\",\n label: toolsConfig?.category?.label ?? \"Tools\",\n items: toolItems,\n };\n }\n }\n const allGroups = toolCategory ? [...groups, toolCategory] : groups;\n\n return {\n categories: () => allGroups.map(({ id, label }) => ({ id, label })),\n categoryItems: (id) => allGroups.find((g) => g.id === id)?.items ?? [],\n search: (query) => {\n const lower = query.toLowerCase();\n return allGroups\n .flatMap((g) => g.items)\n .filter((item) => matchesQuery(item, lower));\n },\n };\n }\n\n // Flat: items + (optionally) tools, all in one search pool\n const flatItems = (items ?? []).map(toTriggerItem);\n const getFlatPool = (): Unstable_TriggerItem[] => {\n if (!wantsTools) return flatItems;\n const toolItems = getModelContextTools();\n // Dedupe by id — explicit items win.\n const seen = new Set(flatItems.map((i) => i.id));\n return [...flatItems, ...toolItems.filter((t) => !seen.has(t.id))];\n };\n\n return {\n categories: (): readonly Unstable_TriggerCategory[] => [],\n categoryItems: () => [],\n search: (query) => {\n const lower = query.toLowerCase();\n return getFlatPool().filter((item) => matchesQuery(item, lower));\n },\n };\n }, [aui, items, categories, wantsTools, toolsConfig]);\n\n // biome-ignore lint/correctness/useHookAtTopLevel: `unstable_` prefix not recognized as hook\n const directive = useMemo<Unstable_MentionDirective>(\n () => ({\n formatter: formatter ?? unstable_defaultDirectiveFormatter,\n ...(onInserted ? { onInserted } : {}),\n }),\n [formatter, onInserted],\n );\n\n return {\n adapter,\n directive,\n ...(options?.iconMap ? { iconMap: options.iconMap } : {}),\n ...(options?.fallbackIcon ? { fallbackIcon: options.fallbackIcon } : {}),\n };\n}\n\nfunction toTriggerItem(m: Unstable_Mention): Unstable_TriggerItem {\n const metadata =\n m.icon !== undefined ? { ...(m.metadata ?? {}), icon: m.icon } : m.metadata;\n return {\n id: m.id,\n type: m.type,\n label: m.label,\n ...(m.description !== undefined ? { description: m.description } : {}),\n ...(metadata !== undefined ? { metadata } : {}),\n };\n}\n\nfunction matchesQuery(item: Unstable_TriggerItem, lower: string): boolean {\n if (!lower) return true;\n if (item.id.toLowerCase().includes(lower)) return true;\n if (item.label.toLowerCase().includes(lower)) return true;\n if (item.description?.toLowerCase().includes(lower)) return true;\n return false;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useMentionAdapter.js","names":[],"sources":["../../src/unstable/useMentionAdapter.ts"],"sourcesContent":["\"use client\";\n\nimport { useMemo, type FC } from \"react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport type {\n Unstable_DirectiveFormatter,\n Unstable_TriggerAdapter,\n Unstable_TriggerCategory,\n Unstable_TriggerItem,\n} from \"@assistant-ui/core\";\nimport { unstable_defaultDirectiveFormatter } from \"@assistant-ui/core\";\nimport type { ReadonlyJSONObject } from \"assistant-stream/utils\";\n\n/** Icon component shape consumed by `ComposerTriggerPopover`'s `iconMap`. */\nexport type Unstable_IconComponent = FC<{ className?: string }>;\n\nexport type Unstable_Mention = {\n readonly id: string;\n readonly type: string;\n readonly label: string;\n readonly description?: string | undefined;\n /** Shortcut for `metadata.icon`; merged with `metadata` if both are given. */\n readonly icon?: string | undefined;\n readonly metadata?: ReadonlyJSONObject | undefined;\n};\n\nexport type Unstable_MentionCategory = {\n readonly id: string;\n readonly label: string;\n readonly items: readonly Unstable_Mention[];\n};\n\nexport type Unstable_ModelContextToolsOptions = {\n /** Wrap tools in a dedicated category (drill-down mode). */\n readonly category?: { readonly id: string; readonly label: string };\n /** Format tool name for display. */\n readonly formatLabel?: (toolName: string) => string;\n /** Default icon key for each tool. */\n readonly icon?: string;\n};\n\nexport type Unstable_UseMentionAdapterOptions = {\n /** Flat mention list. Ignored when `categories` is set. */\n readonly items?: readonly Unstable_Mention[];\n /** Categorized mentions for drill-down navigation. */\n readonly categories?: readonly Unstable_MentionCategory[];\n /**\n * How tools registered via `useAssistantTool` integrate.\n * - `false`: exclude.\n * - `true`: include (default when no `items`/`categories`; as a category\n * if `categories` is set, flat otherwise).\n * - object: explicit config.\n *\n * Omitted → defaults to `true` iff neither `items` nor `categories`.\n */\n readonly includeModelContextTools?:\n | boolean\n | Unstable_ModelContextToolsOptions;\n /** Directive formatter. @default unstable_defaultDirectiveFormatter */\n readonly formatter?: Unstable_DirectiveFormatter;\n /** Fires after an item is inserted into the composer. */\n readonly onInserted?: (item: Unstable_TriggerItem) => void;\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\nexport type Unstable_MentionDirective = {\n readonly formatter: Unstable_DirectiveFormatter;\n readonly onInserted?: ((item: Unstable_TriggerItem) => void) | undefined;\n};\n\n/**\n * @deprecated Under active development and might change without notice.\n *\n * Creates a spreadable `{ adapter, directive }` bundle for `@` mentions.\n * Supports tools registered via `useAssistantTool`, explicit items, or both —\n * flat or categorized.\n *\n * @example\n * ```tsx\n * const mention = unstable_useMentionAdapter();\n * <ComposerTriggerPopover char=\"@\" {...mention} />\n * ```\n */\nexport function unstable_useMentionAdapter(\n options?: Unstable_UseMentionAdapterOptions,\n): {\n adapter: Unstable_TriggerAdapter;\n directive: Unstable_MentionDirective;\n iconMap?: Record<string, Unstable_IconComponent>;\n fallbackIcon?: Unstable_IconComponent;\n} {\n const aui = useAui();\n\n const items = options?.items;\n const categories = options?.categories;\n const includeTools =\n options?.includeModelContextTools ?? (!items && !categories);\n const toolsConfig =\n typeof includeTools === \"object\" ? includeTools : undefined;\n const wantsTools = includeTools !== false;\n const formatter = options?.formatter;\n const onInserted = options?.onInserted;\n\n const adapter = useMemo<Unstable_TriggerAdapter>(() => {\n const getModelContextTools = (): Unstable_TriggerItem[] => {\n if (!wantsTools) return [];\n const ctx = aui.thread().getModelContext();\n const tools = ctx.tools;\n if (!tools) return [];\n const formatLabel = toolsConfig?.formatLabel;\n const defaultIcon = toolsConfig?.icon;\n return Object.entries(tools).map(([name, tool]) =>\n toTriggerItem({\n id: name,\n type: \"tool\",\n label: formatLabel ? formatLabel(name) : name,\n description: tool.description ?? undefined,\n icon: defaultIcon,\n }),\n );\n };\n\n // Categorized: drill-down mode\n if (categories && categories.length > 0) {\n const groups = categories.map((cat) => ({\n id: cat.id,\n label: cat.label,\n items: cat.items.map(toTriggerItem),\n }));\n\n let toolCategory: {\n id: string;\n label: string;\n items: Unstable_TriggerItem[];\n } | null = null;\n if (wantsTools) {\n const toolItems = getModelContextTools();\n if (toolItems.length > 0) {\n toolCategory = {\n id: toolsConfig?.category?.id ?? \"tools\",\n label: toolsConfig?.category?.label ?? \"Tools\",\n items: toolItems,\n };\n }\n }\n const allGroups = toolCategory ? [...groups, toolCategory] : groups;\n\n return {\n categories: () => allGroups.map(({ id, label }) => ({ id, label })),\n categoryItems: (id) => allGroups.find((g) => g.id === id)?.items ?? [],\n search: (query) => {\n const lower = query.toLowerCase();\n return allGroups\n .flatMap((g) => g.items)\n .filter((item) => matchesQuery(item, lower));\n },\n };\n }\n\n // Flat: items + (optionally) tools, all in one search pool\n const flatItems = (items ?? []).map(toTriggerItem);\n const getFlatPool = (): Unstable_TriggerItem[] => {\n if (!wantsTools) return flatItems;\n const toolItems = getModelContextTools();\n // Dedupe by id — explicit items win.\n const seen = new Set(flatItems.map((i) => i.id));\n return [...flatItems, ...toolItems.filter((t) => !seen.has(t.id))];\n };\n\n return {\n categories: (): readonly Unstable_TriggerCategory[] => [],\n categoryItems: () => [],\n search: (query) => {\n const lower = query.toLowerCase();\n return getFlatPool().filter((item) => matchesQuery(item, lower));\n },\n };\n }, [aui, items, categories, wantsTools, toolsConfig]);\n\n const directive = useMemo<Unstable_MentionDirective>(\n () => ({\n formatter: formatter ?? unstable_defaultDirectiveFormatter,\n ...(onInserted ? { onInserted } : {}),\n }),\n [formatter, onInserted],\n );\n\n return {\n adapter,\n directive,\n ...(options?.iconMap ? { iconMap: options.iconMap } : {}),\n ...(options?.fallbackIcon ? { fallbackIcon: options.fallbackIcon } : {}),\n };\n}\n\nfunction toTriggerItem(m: Unstable_Mention): Unstable_TriggerItem {\n const metadata =\n m.icon !== undefined ? { ...(m.metadata ?? {}), icon: m.icon } : m.metadata;\n return {\n id: m.id,\n type: m.type,\n label: m.label,\n ...(m.description !== undefined ? { description: m.description } : {}),\n ...(metadata !== undefined ? { metadata } : {}),\n };\n}\n\nfunction matchesQuery(item: Unstable_TriggerItem, lower: string): boolean {\n if (!lower) return true;\n if (item.id.toLowerCase().includes(lower)) return true;\n if (item.label.toLowerCase().includes(lower)) return true;\n if (item.description?.toLowerCase().includes(lower)) return true;\n return false;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSlashCommandAdapter.js","names":[],"sources":["../../src/unstable/useSlashCommandAdapter.ts"],"sourcesContent":["\"use client\";\n\nimport { useMemo, useRef } from \"react\";\nimport type {\n Unstable_TriggerAdapter,\n Unstable_TriggerItem,\n} from \"@assistant-ui/core\";\nimport type { Unstable_IconComponent } from \"./useMentionAdapter\";\n\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\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\nexport type Unstable_SlashCommandAction = {\n readonly onExecute: (item: Unstable_TriggerItem) => void;\n readonly removeOnExecute?: boolean | undefined;\n};\n\n/**\n * @deprecated Under active development and may change without notice.\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 * @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
|
|
1
|
+
{"version":3,"file":"useSlashCommandAdapter.js","names":[],"sources":["../../src/unstable/useSlashCommandAdapter.ts"],"sourcesContent":["\"use client\";\n\nimport { useMemo, useRef } from \"react\";\nimport type {\n Unstable_TriggerAdapter,\n Unstable_TriggerItem,\n} from \"@assistant-ui/core\";\nimport type { Unstable_IconComponent } from \"./useMentionAdapter\";\n\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\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\nexport type Unstable_SlashCommandAction = {\n readonly onExecute: (item: Unstable_TriggerItem) => void;\n readonly removeOnExecute?: boolean | undefined;\n};\n\n/**\n * @deprecated Under active development and may change without notice.\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 * @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 commandsRef.current = commands;\n\n return useMemo(() => {\n const adapter: Unstable_TriggerAdapter = {\n categories: () => [],\n categoryItems: () => [],\n search: (query: string) => {\n const lower = query.toLowerCase();\n return commandsRef.current\n .filter((c) => matchesQuery(c, lower))\n .map(toItem);\n },\n };\n\n const action: Unstable_SlashCommandAction = {\n onExecute: (item) => {\n commandsRef.current.find((c) => c.id === item.id)?.execute();\n },\n ...(removeOnExecute !== undefined ? { removeOnExecute } : {}),\n };\n\n return {\n adapter,\n action,\n ...(options.iconMap ? { iconMap: options.iconMap } : {}),\n ...(options.fallbackIcon ? { fallbackIcon: options.fallbackIcon } : {}),\n };\n }, [removeOnExecute, options.iconMap, options.fallbackIcon]);\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 matchesQuery(cmd: Unstable_SlashCommand, lower: string): boolean {\n if (!lower) return true;\n if (cmd.id.toLowerCase().includes(lower)) return true;\n if (cmd.label?.toLowerCase().includes(lower)) return true;\n if (cmd.description?.toLowerCase().includes(lower)) return true;\n return false;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAoDA,SAAgB,gCACd,SAMA;CACA,MAAM,EAAE,UAAU,oBAAoB;CAEtC,MAAM,cAAc,OAAO,QAAQ;CACnC,YAAY,UAAU;CAEtB,OAAO,cAAc;EAmBnB,OAAO;GACL,SAAA;IAlBA,kBAAkB,CAAC;IACnB,qBAAqB,CAAC;IACtB,SAAS,UAAkB;KACzB,MAAM,QAAQ,MAAM,YAAY;KAChC,OAAO,YAAY,QAChB,QAAQ,MAAM,aAAa,GAAG,KAAK,CAAC,EACpC,IAAI,MAAM;IACf;GAWM;GACN,QAAA;IARA,YAAY,SAAS;KACnB,YAAY,QAAQ,MAAM,MAAM,EAAE,OAAO,KAAK,EAAE,GAAG,QAAQ;IAC7D;IACA,GAAI,oBAAoB,KAAA,IAAY,EAAE,gBAAgB,IAAI,CAAC;GAKtD;GACL,GAAI,QAAQ,UAAU,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;GACtD,GAAI,QAAQ,eAAe,EAAE,cAAc,QAAQ,aAAa,IAAI,CAAC;EACvE;CACF,GAAG;EAAC;EAAiB,QAAQ;EAAS,QAAQ;CAAY,CAAC;AAC7D;AAEA,SAAS,OAAO,KAAkD;CAChE,OAAO;EACL,IAAI,IAAI;EACR,MAAM;EACN,OAAO,IAAI,SAAS,IAAI,IAAI;EAC5B,GAAI,IAAI,gBAAgB,KAAA,IAAY,EAAE,aAAa,IAAI,YAAY,IAAI,CAAC;EACxE,GAAI,IAAI,SAAS,KAAA,IAAY,EAAE,UAAU,EAAE,MAAM,IAAI,KAAK,EAAE,IAAI,CAAC;CACnE;AACF;AAEA,SAAS,aAAa,KAA4B,OAAwB;CACxE,IAAI,CAAC,OAAO,OAAO;CACnB,IAAI,IAAI,GAAG,YAAY,EAAE,SAAS,KAAK,GAAG,OAAO;CACjD,IAAI,IAAI,OAAO,YAAY,EAAE,SAAS,KAAK,GAAG,OAAO;CACrD,IAAI,IAAI,aAAa,YAAY,EAAE,SAAS,KAAK,GAAG,OAAO;CAC3D,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Primitive.d.ts","names":[],"sources":["../../src/utils/Primitive.tsx"],"mappings":";;;;;;AAawE;;;;AA+B9D;AAAA;;cAlBJ,KAAA;AAAA,KAmBD,aAAA,WAAwB,KAAK;AAAA,KAE7B,mBAAA,WAA8B,WAAA,IACjC,wBAAA,CAAyB,CAAA;EACvB,MAAA,GAAS,YAAA;AAAA;AAAA,KAGR,cAAA,WAAyB,aAAA,IAAiB,mBAAA,SACrC,WAAA,EAAgB,CAAA;AAAA,iBAajB,cAAA,WAAyB,WAAA,
|
|
1
|
+
{"version":3,"file":"Primitive.d.ts","names":[],"sources":["../../src/utils/Primitive.tsx"],"mappings":";;;;;;AAawE;;;;AA+B9D;AAAA;;cAlBJ,KAAA;AAAA,KAmBD,aAAA,WAAwB,KAAK;AAAA,KAE7B,mBAAA,WAA8B,WAAA,IACjC,wBAAA,CAAyB,CAAA;EACvB,MAAA,GAAS,YAAA;AAAA;AAAA,KAGR,cAAA,WAAyB,aAAA,IAAiB,mBAAA,SACrC,WAAA,EAAgB,CAAA;AAAA,iBAajB,cAAA,WAAyB,WAAA,EAAa,SAAA,EAAW,CAAA,GAwCtC,yBAAA,CAChB,mBAAA,CAAoB,CAAA,IAAK,aAAA,CAAc,YAAA,CAAa,CAAA;AAAA,cAclD,SAAA;;;;aAxEO,YAAA;EAAA;;;;aAAA,YAAA;EAAA;;;;aAAA,YAAA;EAAA;;;;aAAA,YAAA;EAAA;;;;aAAA,YAAA;EAAA;;;;aAAA,YAAA;EAAA;;;;aAAA,YAAA;EAAA;;;;aAAA,YAAA;EAAA;;;;aAAA,YAAA;EAAA;;;;aAAA,YAAA;EAAA;;;;aAAA,YAAA;EAAA;;;;aAAA,YAAA;EAAA;;;;aAAA,YAAA;EAAA;;;;aAAA,YAAA;EAAA;;;;aAAA,YAAA;EAAA;;;;aAAA,YAAA;EAAA;;;;aAAA,YAAA;EAAA"}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
//#region src/utils/useToolArgsFieldStatus.d.ts
|
|
2
2
|
declare const useToolArgsFieldStatus: (fieldPath: (string | number)[]) => {
|
|
3
|
-
type: string;
|
|
4
|
-
} | {
|
|
5
3
|
readonly type: "running";
|
|
6
4
|
} | {
|
|
7
5
|
readonly type: "complete";
|
|
@@ -9,6 +7,8 @@ declare const useToolArgsFieldStatus: (fieldPath: (string | number)[]) => {
|
|
|
9
7
|
readonly type: "incomplete";
|
|
10
8
|
readonly reason: "cancelled" | "length" | "content-filter" | "other" | "error";
|
|
11
9
|
readonly error?: unknown;
|
|
10
|
+
} | {
|
|
11
|
+
type: string;
|
|
12
12
|
};
|
|
13
13
|
//#endregion
|
|
14
14
|
export { useToolArgsFieldStatus };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useToolArgsFieldStatus.d.ts","names":[],"sources":["../../src/utils/useToolArgsFieldStatus.ts"],"mappings":";cAKa,sBAAA,GAA0B,SAAA"}
|
|
1
|
+
{"version":3,"file":"useToolArgsFieldStatus.d.ts","names":[],"sources":["../../src/utils/useToolArgsFieldStatus.ts"],"mappings":";cAKa,sBAAA,GAA0B,SAAA;EAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.12",
|
|
4
4
|
"description": "Open-source TypeScript/React library for building production-grade AI chat experiences",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"radix-ui",
|
|
@@ -48,23 +48,23 @@
|
|
|
48
48
|
],
|
|
49
49
|
"sideEffects": false,
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@assistant-ui/core": "^0.2.
|
|
52
|
-
"@assistant-ui/store": "^0.2.
|
|
53
|
-
"@assistant-ui/tap": "^0.5.
|
|
51
|
+
"@assistant-ui/core": "^0.2.8",
|
|
52
|
+
"@assistant-ui/store": "^0.2.13",
|
|
53
|
+
"@assistant-ui/tap": "^0.5.14",
|
|
54
54
|
"@radix-ui/primitive": "^1.1.3",
|
|
55
55
|
"@radix-ui/react-compose-refs": "^1.1.2",
|
|
56
56
|
"@radix-ui/react-context": "^1.1.3",
|
|
57
57
|
"@radix-ui/react-primitive": "^2.1.4",
|
|
58
58
|
"@radix-ui/react-use-callback-ref": "^1.1.1",
|
|
59
59
|
"@radix-ui/react-use-escape-keydown": "^1.1.1",
|
|
60
|
-
"assistant-cloud": "^0.1.
|
|
61
|
-
"assistant-stream": "^0.3.
|
|
60
|
+
"assistant-cloud": "^0.1.30",
|
|
61
|
+
"assistant-stream": "^0.3.18",
|
|
62
62
|
"nanoid": "^5.1.11",
|
|
63
63
|
"radix-ui": "^1.4.3",
|
|
64
64
|
"react-textarea-autosize": "^8.5.9",
|
|
65
65
|
"safe-content-frame": "^0.0.20",
|
|
66
66
|
"zod": "^4.4.3",
|
|
67
|
-
"zustand": "^5.0.
|
|
67
|
+
"zustand": "^5.0.14"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
70
|
"@types/react": "*",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"react": "^19.2.6",
|
|
91
91
|
"react-dom": "^19.2.6",
|
|
92
92
|
"vitest": "^4.1.7",
|
|
93
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
93
|
+
"@assistant-ui/x-buildutils": "0.0.10"
|
|
94
94
|
},
|
|
95
95
|
"publishConfig": {
|
|
96
96
|
"access": "public",
|
package/src/augmentations.ts
CHANGED
|
@@ -22,10 +22,8 @@
|
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
24
|
export namespace Assistant {
|
|
25
|
-
// biome-ignore lint/suspicious/noEmptyInterface: declaration merging
|
|
26
25
|
export interface Commands {}
|
|
27
26
|
|
|
28
|
-
// biome-ignore lint/suspicious/noEmptyInterface: declaration merging
|
|
29
27
|
export interface ExternalState {}
|
|
30
28
|
}
|
|
31
29
|
|
|
@@ -75,7 +75,6 @@ export function createStateHookForRuntime<TState>(
|
|
|
75
75
|
if (!store) return null;
|
|
76
76
|
|
|
77
77
|
// it is ok to call useRuntimeStateInternal conditionally because it will never become null if its available
|
|
78
|
-
// biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
|
|
79
78
|
return useRuntimeStateInternal(store, selector);
|
|
80
79
|
}
|
|
81
80
|
|
|
@@ -54,7 +54,6 @@ const getHook = (): DevToolsHook => {
|
|
|
54
54
|
return newHook;
|
|
55
55
|
};
|
|
56
56
|
|
|
57
|
-
// biome-ignore lint/complexity/noStaticOnlyClass: intentional namespace for DevTools API
|
|
58
57
|
export class DevToolsHooks {
|
|
59
58
|
static subscribe(listener: () => void): Unsubscribe {
|
|
60
59
|
const hook = getHook();
|
|
@@ -79,12 +78,10 @@ export class DevToolsHooks {
|
|
|
79
78
|
|
|
80
79
|
private static notifyListeners(apiId: number): void {
|
|
81
80
|
const hook = getHook();
|
|
82
|
-
// biome-ignore lint/suspicious/useIterableCallbackReturn: forEach callback intentionally has no return
|
|
83
81
|
hook.listeners.forEach((listener) => listener(apiId));
|
|
84
82
|
}
|
|
85
83
|
}
|
|
86
84
|
|
|
87
|
-
// biome-ignore lint/complexity/noStaticOnlyClass: intentional namespace for DevTools API
|
|
88
85
|
export class DevToolsProviderApi {
|
|
89
86
|
private static readonly MAX_EVENT_LOGS_PER_API = 200;
|
|
90
87
|
|
|
@@ -145,7 +142,6 @@ export class DevToolsProviderApi {
|
|
|
145
142
|
|
|
146
143
|
private static notifyListeners(apiId: number): void {
|
|
147
144
|
const hook = getHook();
|
|
148
|
-
// biome-ignore lint/suspicious/useIterableCallbackReturn: forEach callback intentionally has no return
|
|
149
145
|
hook.listeners.forEach((listener) => listener(apiId));
|
|
150
146
|
}
|
|
151
147
|
}
|
package/src/index.ts
CHANGED
|
@@ -127,14 +127,17 @@ export type { ThreadMessageLike } from "@assistant-ui/core";
|
|
|
127
127
|
export {
|
|
128
128
|
getExternalStoreMessages,
|
|
129
129
|
bindExternalStoreMessage,
|
|
130
|
+
pickExternalStoreSharedOptions,
|
|
130
131
|
} from "@assistant-ui/core";
|
|
131
132
|
export type {
|
|
132
133
|
ExternalStoreAdapter,
|
|
133
134
|
ExternalStoreMessageConverter,
|
|
135
|
+
ExternalStoreSharedOptions,
|
|
134
136
|
ExternalStoreThreadListAdapter,
|
|
135
137
|
ExternalStoreThreadData,
|
|
136
138
|
} from "@assistant-ui/core";
|
|
137
139
|
export { useExternalStoreRuntime } from "./legacy-runtime/runtime-cores/external-store/useExternalStoreRuntime";
|
|
140
|
+
export { useExternalStoreSharedOptions } from "@assistant-ui/core/react";
|
|
138
141
|
export {
|
|
139
142
|
useExternalMessageConverter,
|
|
140
143
|
convertExternalMessages as unstable_convertExternalMessages,
|
|
@@ -184,6 +187,8 @@ export {
|
|
|
184
187
|
useInlineRender,
|
|
185
188
|
type Toolkit,
|
|
186
189
|
type ToolDefinition,
|
|
190
|
+
type ToolkitDeclaration,
|
|
191
|
+
type ToolkitDeclarationDefinition,
|
|
187
192
|
Tools,
|
|
188
193
|
DataRenderers,
|
|
189
194
|
Interactables,
|
|
@@ -254,7 +259,7 @@ export * as ThreadListItemPrimitive from "./primitives/threadListItem";
|
|
|
254
259
|
export * as ThreadListItemMorePrimitive from "./primitives/threadListItemMore";
|
|
255
260
|
export * as SelectionToolbarPrimitive from "./primitives/selectionToolbar";
|
|
256
261
|
|
|
257
|
-
export { groupPartByType } from "@assistant-ui/core/react";
|
|
262
|
+
export { groupPartByType, type GroupByContext } from "@assistant-ui/core/react";
|
|
258
263
|
export { useMessagePartText } from "./primitives/messagePart/useMessagePartText";
|
|
259
264
|
export { useMessagePartReasoning } from "./primitives/messagePart/useMessagePartReasoning";
|
|
260
265
|
export { useMessagePartSource } from "./primitives/messagePart/useMessagePartSource";
|
package/src/internal.ts
CHANGED
|
@@ -23,10 +23,7 @@ export type {
|
|
|
23
23
|
|
|
24
24
|
// React-specific (stay in react)
|
|
25
25
|
export { splitLocalRuntimeOptions } from "./legacy-runtime/runtime-cores/local/LocalRuntimeOptions";
|
|
26
|
-
export {
|
|
27
|
-
useToolInvocations,
|
|
28
|
-
type ToolExecutionStatus,
|
|
29
|
-
} from "@assistant-ui/core/react";
|
|
26
|
+
export type { ToolExecutionStatus } from "@assistant-ui/core";
|
|
30
27
|
|
|
31
28
|
export { useSmooth } from "./utils/smooth/useSmooth";
|
|
32
29
|
export {
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
export type {
|
|
2
|
-
ThreadListState,
|
|
3
|
-
ThreadListRuntime,
|
|
4
|
-
} from "@assistant-ui/core";
|
|
1
|
+
export type { ThreadListState, ThreadListRuntime } from "@assistant-ui/core";
|
|
5
2
|
|
|
6
3
|
export type { ThreadListRuntimeCoreBinding } from "@assistant-ui/core/internal";
|
|
7
4
|
export { ThreadListRuntimeImpl } from "@assistant-ui/core/internal";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ToolModelContentPart } from "assistant-stream";
|
|
1
2
|
import type { ThreadMessage } from "@assistant-ui/core";
|
|
2
3
|
import type { ReadonlyJSONValue } from "assistant-stream/utils";
|
|
3
4
|
import type {
|
|
@@ -7,7 +8,7 @@ import type {
|
|
|
7
8
|
LanguageModelConfig,
|
|
8
9
|
} from "@assistant-ui/core";
|
|
9
10
|
import type { UserCommands } from "../../../augmentations";
|
|
10
|
-
import type { ToolExecutionStatus } from "
|
|
11
|
+
import type { ToolExecutionStatus } from "@assistant-ui/core";
|
|
11
12
|
|
|
12
13
|
// Message part types
|
|
13
14
|
export type TextPart = {
|
|
@@ -47,6 +48,7 @@ export type AddToolResultCommand = {
|
|
|
47
48
|
readonly result: ReadonlyJSONValue;
|
|
48
49
|
readonly isError: boolean;
|
|
49
50
|
readonly artifact?: ReadonlyJSONValue;
|
|
51
|
+
readonly modelContent?: readonly ToolModelContentPart[];
|
|
50
52
|
};
|
|
51
53
|
|
|
52
54
|
export type AssistantTransportCommand =
|