@assistant-ui/react 0.15.24 → 0.15.26
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 +3 -1
- package/dist/hooks/useToolCallElapsed.d.ts.map +1 -1
- package/dist/hooks/useToolCallElapsed.js +8 -8
- package/dist/hooks/useToolCallElapsed.js.map +1 -1
- package/dist/index.d.ts +39 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- 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 +56 -46
- package/dist/mcp-apps/app-frame.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/types.d.ts +1 -0
- package/dist/mcp-apps/types.d.ts.map +1 -1
- package/dist/mcp-apps/types.js.map +1 -1
- package/dist/model-context/makeAssistantVisible.d.ts +1 -0
- package/dist/model-context/makeAssistantVisible.d.ts.map +1 -1
- package/dist/model-context/makeAssistantVisible.js +31 -12
- package/dist/model-context/makeAssistantVisible.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/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 +15 -1
- package/dist/primitives/composer/ComposerInput.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerInput.js +5 -4
- 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/trigger/TriggerPopover.d.ts +11 -11
- package/dist/primitives/composer/trigger/TriggerPopover.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopover.js +62 -60
- package/dist/primitives/composer/trigger/TriggerPopover.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverAction.d.ts +8 -8
- package/dist/primitives/composer/trigger/TriggerPopoverAction.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverAction.js +6 -6
- package/dist/primitives/composer/trigger/TriggerPopoverAction.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverCategories.d.ts +3 -3
- package/dist/primitives/composer/trigger/TriggerPopoverCategories.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverCategories.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverDirective.d.ts +9 -9
- package/dist/primitives/composer/trigger/TriggerPopoverDirective.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverDirective.js +7 -7
- package/dist/primitives/composer/trigger/TriggerPopoverDirective.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverItems.d.ts +5 -5
- package/dist/primitives/composer/trigger/TriggerPopoverItems.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverItems.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverRootContext.d.ts +8 -8
- package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js +8 -8
- package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js.map +1 -1
- package/dist/primitives/composer/trigger/index.d.ts +1 -1
- 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 +55 -15
- package/dist/primitives/composer.d.ts.map +1 -1
- package/dist/primitives/composer.js +8 -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/ThreadLoadEarlier.d.ts +19 -0
- package/dist/primitives/thread/ThreadLoadEarlier.d.ts.map +1 -0
- package/dist/primitives/thread/ThreadLoadEarlier.js +36 -0
- package/dist/primitives/thread/ThreadLoadEarlier.js.map +1 -0
- package/dist/primitives/thread/ThreadMessages.d.ts +1 -1
- package/dist/primitives/thread/ThreadMessages.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadMessages.js +2 -2
- 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 +195 -17
- package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
- package/dist/primitives/thread.d.ts +2 -0
- package/dist/primitives/thread.d.ts.map +1 -1
- package/dist/primitives/thread.js +5 -2
- package/dist/primitives/thread.js.map +1 -1
- package/dist/sandbox-host/SandboxHost.d.ts.map +1 -1
- package/dist/sandbox-host/SandboxHost.js +9 -3
- package/dist/sandbox-host/SandboxHost.js.map +1 -1
- package/dist/unstable/useComposerInput.d.ts +7 -4
- package/dist/unstable/useComposerInput.d.ts.map +1 -1
- package/dist/unstable/useComposerInput.js +4 -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 +12 -8
- 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/smooth/useSmooth.js +6 -6
- package/dist/utils/smooth/useSmooth.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 +12 -11
- package/src/hooks/useToolCallElapsed.test.tsx +37 -0
- package/src/hooks/useToolCallElapsed.ts +10 -7
- package/src/index.ts +47 -5
- package/src/mcp-apps/McpAppRenderer.tsx +3 -9
- package/src/mcp-apps/app-frame.test.tsx +38 -0
- package/src/mcp-apps/app-frame.tsx +5 -8
- package/src/mcp-apps/csp.test.ts +112 -0
- package/src/mcp-apps/csp.ts +124 -0
- package/src/mcp-apps/types.ts +1 -0
- package/src/model-context/frame/AssistantFrame.test.ts +45 -0
- package/src/model-context/frame/SPEC_AssistantFrame.md +1 -0
- package/src/model-context/makeAssistantVisible.test.tsx +315 -5
- package/src/model-context/makeAssistantVisible.tsx +43 -7
- package/src/primitives/assistantModal/AssistantModalRoot.tsx +1 -0
- package/src/primitives/attachment.ts +4 -1
- package/src/primitives/composer/ComposerCancel.focus.test.tsx +307 -0
- package/src/primitives/composer/ComposerCancel.ts +8 -1
- package/src/primitives/composer/ComposerInput.tsx +20 -2
- package/src/primitives/composer/ComposerRoot.tsx +6 -5
- package/src/primitives/composer/composerInputSelector.ts +4 -0
- package/src/primitives/composer/trigger/TriggerPopover.tsx +11 -11
- package/src/primitives/composer/trigger/TriggerPopoverAction.tsx +10 -13
- package/src/primitives/composer/trigger/TriggerPopoverCategories.tsx +2 -2
- package/src/primitives/composer/trigger/TriggerPopoverDirective.tsx +11 -14
- package/src/primitives/composer/trigger/TriggerPopoverExports.test.ts +61 -0
- package/src/primitives/composer/trigger/TriggerPopoverItems.tsx +3 -3
- package/src/primitives/composer/trigger/TriggerPopoverRootContext.tsx +8 -8
- package/src/primitives/composer/useComposerCancelWithFocus.ts +57 -0
- package/src/primitives/composer.ts +70 -15
- package/src/primitives/message/MessagePartsGrouped.test.tsx +106 -2
- package/src/primitives/message/MessagePartsGrouped.tsx +36 -134
- package/src/primitives/message/MessageRoot.test.tsx +125 -2
- package/src/primitives/selectionToolbar/SelectionToolbarRoot.test.tsx +20 -12
- package/src/primitives/selectionToolbar/SelectionToolbarRoot.tsx +6 -2
- package/src/primitives/thread/ThreadLoadEarlier.tsx +35 -0
- package/src/primitives/thread/ThreadMessages.tsx +1 -0
- package/src/primitives/thread/ThreadViewport.tsx +3 -3
- package/src/primitives/thread/topAnchor/useTopAnchorReserve.ts +2 -2
- package/src/primitives/thread/useThreadViewportAutoScroll.test.tsx +922 -68
- package/src/primitives/thread/useThreadViewportAutoScroll.ts +330 -56
- package/src/primitives/thread.ts +2 -0
- package/src/sandbox-host/SandboxHost.test.tsx +34 -0
- package/src/sandbox-host/SandboxHost.tsx +11 -12
- package/src/tests/toolCallTiming.test.tsx +19 -2
- package/src/unstable/useComposerInput.ts +7 -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.test.tsx +40 -0
- package/src/unstable/useMessageStallDetection.ts +14 -8
- 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/smooth/useSmooth.test.tsx +16 -5
- package/src/utils/smooth/useSmooth.ts +7 -7
- package/src/utils/useIsomorphicLayoutEffect.test.tsx +21 -0
- package/src/utils/useIsomorphicLayoutEffect.ts +9 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useThreadViewportAutoScroll.js","names":["useComposedRefs","useCallback","useLayoutEffect","useRef","RefCallback","useAuiEvent","useAuiState","isUserScrollUp","isViewportAtBottom","viewportOverflows","useOnResizeContent","useOnScrollToBottom","useManagedRef","writableStore","useThreadViewportStore","ACTIVATION_KEYS","Set","TEXT_ENTRY_SELECTOR","join","useThreadViewportAutoScroll","Options","autoScroll","scrollToBottomOnRunStart","scrollToBottomOnInitialize","scrollToBottomOnThreadSwitch","HTMLElement","TElement","divRef","hasMessages","s","thread","messages","length","isRunning","initializeScrollRequestedRef","scheduledFrameRef","threadViewportStore","undefined","getState","turnAnchor","lastScrollTop","lastScrollHeight","lastObservedScrollHeight","lastObservedClientHeight","scrollingToBottomBehaviorRef","ScrollBehavior","followBottomRef","previousAutoScrollRef","previousAutoScroll","current","div","scrollToBottom","behavior","scrollTo","top","scrollHeight","cancelScheduledFrame","cancelAnimationFrame","scheduleScrollToBottom","requestAnimationFrame","hasActiveTopAnchor","state","element","viewport","anchor","handleScroll","isAtBottom","newIsAtBottom","isInFlightDownwardScroll","scrollTop","userScrolledUp","shouldUpdate","setState","resizeRef","clientHeight","scrollBehavior","scrollRef","el","cancelPendingScrollToBottom","cancelOnKeyDown","event","KeyboardEvent","has","key","target","Element","closest","addEventListener","removeEventListener","autoScrollRef"],"sources":["../../../src/primitives/thread/useThreadViewportAutoScroll.ts"],"sourcesContent":["\"use client\";\n\nimport { useComposedRefs } from \"radix-ui/internal\";\nimport { useCallback, useLayoutEffect, useRef, type RefCallback } from \"react\";\nimport { useAuiEvent, useAuiState } from \"@assistant-ui/store\";\nimport {\n isUserScrollUp,\n isViewportAtBottom,\n viewportOverflows,\n} from \"@assistant-ui/store/client\";\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\n// Enter and Space activate a focused control, which is how a collapsible tool\n// call expands without a pointer event ever firing. No other key changes thread\n// content: the keys that scroll the viewport reach handleScroll, which already\n// clears the intent when the user scrolls up.\nconst ACTIVATION_KEYS = new Set([\"Enter\", \" \"]);\n\n// A control that consumes the activation key itself instead of acting on thread\n// content. `contenteditable=\"false\"` marks a non-editable island inside an\n// editable tree, so it is excluded the way ComposerRoot already excludes it;\n// the input types left out are the ones a key activates rather than fills.\nconst TEXT_ENTRY_SELECTOR = [\n \"textarea\",\n \"select\",\n \"[contenteditable]:not([contenteditable='false'])\",\n \"input:not([type='checkbox']):not([type='radio']):not([type='button'])\" +\n \":not([type='submit']):not([type='reset']):not([type='image'])\" +\n \":not([type='range']):not([type='file']):not([type='color'])\",\n].join(\", \");\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 isRunning = useAuiState((s) => s.thread.isRunning);\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 const followBottomRef = useRef(autoScroll);\n const previousAutoScrollRef = useRef(autoScroll);\n\n useLayoutEffect(() => {\n const previousAutoScroll = previousAutoScrollRef.current;\n previousAutoScrollRef.current = autoScroll;\n if (previousAutoScroll || !autoScroll) return;\n\n const div = divRef.current;\n followBottomRef.current = div !== null && isViewportAtBottom(div);\n }, [autoScroll]);\n\n const scrollToBottom = useCallback((behavior: ScrollBehavior) => {\n const div = divRef.current;\n if (!div) return;\n\n followBottomRef.current = true;\n scrollingToBottomBehaviorRef.current = behavior;\n div.scrollTo({ top: div.scrollHeight, behavior });\n }, []);\n\n const cancelScheduledFrame = useCallback(() => {\n if (scheduledFrameRef.current === null) return;\n cancelAnimationFrame(scheduledFrameRef.current);\n scheduledFrameRef.current = null;\n }, []);\n\n const scheduleScrollToBottom = useCallback(\n (behavior: ScrollBehavior) => {\n scrollingToBottomBehaviorRef.current = behavior;\n cancelScheduledFrame();\n scheduledFrameRef.current = requestAnimationFrame(() => {\n scheduledFrameRef.current = null;\n scrollToBottom(behavior);\n });\n },\n [cancelScheduledFrame, scrollToBottom],\n );\n\n useLayoutEffect(() => () => cancelScheduledFrame(), [cancelScheduledFrame]);\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 = isViewportAtBottom(div);\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 const userScrolledUp = isUserScrollUp(\n {\n scrollTop: lastScrollTop.current,\n scrollHeight: lastScrollHeight.current,\n },\n div,\n );\n\n if (newIsAtBottom) {\n // newIsAtBottom is ambiguous when the viewport doesn't overflow —\n // keep intent alive until content can actually scroll\n if (viewportOverflows(div)) {\n scrollingToBottomBehaviorRef.current = null;\n }\n if (autoScroll) followBottomRef.current = true;\n } else if (userScrolledUp) {\n cancelScheduledFrame();\n scrollingToBottomBehaviorRef.current = null;\n followBottomRef.current = false;\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 (\n autoScroll &&\n !(isRunning && hasActiveTopAnchor()) &&\n followBottomRef.current\n ) {\n scrollToBottom(\"instant\");\n }\n\n handleScroll();\n });\n\n const scrollRef = useManagedRef<HTMLElement>((el) => {\n // A user gesture invalidates pending bottom-scroll intent; otherwise an\n // intent kept alive by a non-overflowing thread (see handleScroll) hijacks\n // the next content growth, e.g. expanding a collapsible tool call. Keyboard\n // activation reaches that same content without ever emitting a pointer\n // event, so it has to cancel the intent too.\n const cancelPendingScrollToBottom = () => {\n // A scheduled frame re-plants the intent when it runs, so clearing the\n // ref alone leaves the gesture undone.\n cancelScheduledFrame();\n scrollingToBottomBehaviorRef.current = null;\n };\n // The composer renders inside the viewport, so its keystrokes bubble here;\n // only an activation key aimed at something other than a text field is a\n // gesture on thread content.\n const cancelOnKeyDown = (event: KeyboardEvent) => {\n if (!ACTIVATION_KEYS.has(event.key)) return;\n const target = event.target as Element | null;\n if (target?.closest?.(TEXT_ENTRY_SELECTOR)) return;\n cancelPendingScrollToBottom();\n };\n el.addEventListener(\"scroll\", handleScroll);\n el.addEventListener(\"pointerdown\", cancelPendingScrollToBottom);\n el.addEventListener(\"keydown\", cancelOnKeyDown);\n return () => {\n el.removeEventListener(\"scroll\", handleScroll);\n el.removeEventListener(\"pointerdown\", cancelPendingScrollToBottom);\n el.removeEventListener(\"keydown\", cancelOnKeyDown);\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(\"threads.selectionChanged\", () => {\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":["useComposedRefs","useCallback","useRef","RefCallback","useAui","useAuiEvent","useAuiState","isUserScrollUp","isViewportAtBottom","viewportOverflows","useOnResizeContent","useOnScrollToBottom","useManagedRef","writableStore","useThreadViewportStore","useIsomorphicLayoutEffect","SCROLL_KEYS","Set","INTENT_CANCEL_KEYS","TEXT_ENTRY_SELECTOR","join","MESSAGE_SELECTOR","DISCLOSURE_SELECTOR","isOpeningDisclosure","target","Element","control","closest","tagName","parentElement","hasAttribute","getAttribute","MessageOffset","id","offset","offsetInViewport","element","viewport","getBoundingClientRect","top","measureFirstMessage","HTMLElement","querySelector","dataset","measureReaderMessage","bottom","spanning","querySelectorAll","rect","findMessage","keepMessageAt","anchor","heldScrollTop","current","delta","scrollTop","Math","abs","scrollTo","behavior","useThreadViewportAutoScroll","Options","autoScroll","scrollToBottomOnRunStart","scrollToBottomOnInitialize","scrollToBottomOnThreadSwitch","TElement","aui","divRef","hasMessages","s","thread","messages","length","firstMessageId","threadId","threadListItem","isRunning","initializeScrollRequestedRef","scheduledFrameRef","threadViewportStore","undefined","getState","turnAnchor","lastScrollTop","lastScrollHeight","lastObservedScrollHeight","lastObservedClientHeight","firstMessageRef","prependAnchorRef","heldScrollTopRef","scrollingToBottomBehaviorRef","ScrollBehavior","followBottomRef","followPausedRef","scrolledSincePauseRef","previousAutoScrollRef","previousAutoScroll","div","scrollToBottom","scrollHeight","cancelScheduledFrame","cancelAnimationFrame","scheduleScrollToBottom","requestAnimationFrame","hasActiveTopAnchor","state","onScroll","isAtBottom","newIsAtBottom","isInFlightDownwardScroll","userScrolledUp","shouldUpdate","setState","onScrollRef","handleScroll","onResize","clientHeight","scrollBehavior","onResizeRef","resizeRef","scrollRef","el","cancelPendingScrollToBottom","cancelOnKeyDown","event","KeyboardEvent","has","key","pauseFollowOnExpand","MouseEvent","noteScrollGesture","Event","type","releasePrependAnchor","gestures","const","resumeGestures","addEventListener","passive","capture","gesture","removeEventListener","previousRef","previous","previousFirstMessageId","prepended","some","message","index","autoScrollRef"],"sources":["../../../src/primitives/thread/useThreadViewportAutoScroll.ts"],"sourcesContent":["\"use client\";\n\nimport { useComposedRefs } from \"radix-ui/internal\";\nimport { useCallback, useRef, type RefCallback } from \"react\";\nimport { useAui, useAuiEvent, useAuiState } from \"@assistant-ui/store\";\nimport {\n isUserScrollUp,\n isViewportAtBottom,\n viewportOverflows,\n} from \"@assistant-ui/store/client\";\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\";\nimport { useIsomorphicLayoutEffect } from \"../../utils/useIsomorphicLayoutEffect\";\n\nconst SCROLL_KEYS = new Set([\n \" \",\n \"ArrowUp\",\n \"ArrowDown\",\n \"PageUp\",\n \"PageDown\",\n \"Home\",\n \"End\",\n]);\n\n// Enter and Space activate focused controls, while navigation keys can\n// interrupt scrolling without producing a scroll event.\nconst INTENT_CANCEL_KEYS = new Set([\"Enter\", ...SCROLL_KEYS]);\n\n// A control that consumes the activation key itself instead of acting on thread\n// content. `contenteditable=\"false\"` marks a non-editable island inside an\n// editable tree, so it is excluded the way ComposerRoot already excludes it;\n// the input types left out are the ones a key activates rather than fills.\nconst TEXT_ENTRY_SELECTOR = [\n \"textarea\",\n \"select\",\n \"[contenteditable]:not([contenteditable='false'])\",\n \"input:not([type='checkbox']):not([type='radio']):not([type='button'])\" +\n \":not([type='submit']):not([type='reset']):not([type='image'])\" +\n \":not([type='range']):not([type='file']):not([type='color'])\",\n].join(\", \");\n\nconst MESSAGE_SELECTOR = \"[data-message-id]\";\n\nconst DISCLOSURE_SELECTOR = \"[aria-expanded], summary\";\n\n// A popup trigger (menu, dialog, listbox) also carries aria-expanded, but what\n// it opens is portaled away from the thread, so only disclosures count.\nconst isOpeningDisclosure = (target: Element): boolean => {\n const control = target.closest(DISCLOSURE_SELECTOR);\n if (!control) return false;\n if (control.tagName === \"SUMMARY\")\n return control.parentElement?.hasAttribute(\"open\") === false;\n return (\n control.getAttribute(\"aria-expanded\") === \"false\" &&\n (control.getAttribute(\"aria-haspopup\") ?? \"false\") === \"false\" &&\n control.getAttribute(\"role\") !== \"combobox\"\n );\n};\n\ntype MessageOffset = { readonly id: string; readonly offset: number };\n\nconst offsetInViewport = (element: Element, viewport: Element) =>\n element.getBoundingClientRect().top - viewport.getBoundingClientRect().top;\n\nconst measureFirstMessage = (viewport: HTMLElement): MessageOffset | null => {\n const element = viewport.querySelector<HTMLElement>(MESSAGE_SELECTOR);\n const id = element?.dataset[\"messageId\"];\n return element && id\n ? { id, offset: offsetInViewport(element, viewport) }\n : null;\n};\n\n/** The first message that starts in view, else the one spanning its top edge. */\nconst measureReaderMessage = (viewport: HTMLElement): MessageOffset | null => {\n const { top, bottom } = viewport.getBoundingClientRect();\n let spanning: MessageOffset | null = null;\n for (const element of viewport.querySelectorAll<HTMLElement>(\n MESSAGE_SELECTOR,\n )) {\n const rect = element.getBoundingClientRect();\n if (rect.bottom <= top) continue;\n if (rect.top >= bottom) break;\n const id = element.dataset[\"messageId\"];\n if (!id) continue;\n if (rect.top >= top) return { id, offset: rect.top - top };\n spanning ??= { id, offset: rect.top - top };\n }\n return spanning;\n};\n\nconst findMessage = (viewport: HTMLElement, id: string) => {\n for (const element of viewport.querySelectorAll<HTMLElement>(\n MESSAGE_SELECTOR,\n )) {\n if (element.dataset[\"messageId\"] === id) return element;\n }\n return null;\n};\n\n/**\n * Scrolls so the message sits at `anchor.offset` again, recording the\n * position it scrolls to in `heldScrollTop` before the scroll event can\n * report it; false when the message is gone.\n */\nconst keepMessageAt = (\n viewport: HTMLElement,\n anchor: MessageOffset,\n heldScrollTop: { current: number },\n) => {\n const element = findMessage(viewport, anchor.id);\n if (!element) return false;\n const delta = offsetInViewport(element, viewport) - anchor.offset;\n heldScrollTop.current = viewport.scrollTop + delta;\n if (Math.abs(delta) >= 1) {\n viewport.scrollTo({ top: heldScrollTop.current, behavior: \"instant\" });\n }\n heldScrollTop.current = viewport.scrollTop;\n return true;\n};\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 aui = useAui();\n const divRef = useRef<TElement>(null);\n const hasMessages = useAuiState((s) => s.thread.messages.length > 0);\n const firstMessageId = useAuiState((s) => s.thread.messages[0]?.id);\n const threadId = useAuiState((s) => s.threadListItem.id);\n const isRunning = useAuiState((s) => s.thread.isRunning);\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 const firstMessageRef = useRef<MessageOffset | null>(null);\n // The row the reader sees when earlier messages land, held until a gesture\n // or a scroll this hook did not make, so content above it that settles\n // afterwards (lazy layout, late media) does not move it on engines without\n // native scroll anchoring.\n const prependAnchorRef = useRef<MessageOffset | null>(null);\n const heldScrollTopRef = useRef(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 const followBottomRef = useRef(autoScroll);\n // Set by expanding a disclosure; suspends follow without clearing its intent\n // until a scroll gesture reaches an overflowing bottom (content that fits\n // reads as at the bottom), a run starts, the thread changes, or the reader\n // asks for the bottom again.\n const followPausedRef = useRef(false);\n const scrolledSincePauseRef = useRef(false);\n const previousAutoScrollRef = useRef(autoScroll);\n\n useIsomorphicLayoutEffect(() => {\n const previousAutoScroll = previousAutoScrollRef.current;\n previousAutoScrollRef.current = autoScroll;\n if (previousAutoScroll || !autoScroll) return;\n\n const div = divRef.current;\n followBottomRef.current = div !== null && isViewportAtBottom(div);\n }, [autoScroll]);\n\n const scrollToBottom = useCallback((behavior: ScrollBehavior) => {\n const div = divRef.current;\n if (!div) return;\n\n followBottomRef.current = true;\n followPausedRef.current = false;\n scrollingToBottomBehaviorRef.current = behavior;\n prependAnchorRef.current = null;\n div.scrollTo({ top: div.scrollHeight, behavior });\n lastScrollTop.current = div.scrollTop;\n lastScrollHeight.current = div.scrollHeight;\n }, []);\n\n const cancelScheduledFrame = useCallback(() => {\n if (scheduledFrameRef.current === null) return;\n cancelAnimationFrame(scheduledFrameRef.current);\n scheduledFrameRef.current = null;\n }, []);\n\n const scheduleScrollToBottom = useCallback(\n (behavior: ScrollBehavior) => {\n scrollingToBottomBehaviorRef.current = behavior;\n cancelScheduledFrame();\n scheduledFrameRef.current = requestAnimationFrame(() => {\n scheduledFrameRef.current = null;\n scrollToBottom(behavior);\n });\n },\n [cancelScheduledFrame, scrollToBottom],\n );\n\n useIsomorphicLayoutEffect(\n () => () => cancelScheduledFrame(),\n [cancelScheduledFrame],\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 onScroll = () => {\n const div = divRef.current;\n if (!div) return;\n\n if (\n prependAnchorRef.current &&\n Math.abs(div.scrollTop - heldScrollTopRef.current) >= 1\n ) {\n prependAnchorRef.current = null;\n }\n\n const isAtBottom = threadViewportStore.getState().isAtBottom;\n const newIsAtBottom = isViewportAtBottom(div);\n\n const isInFlightDownwardScroll =\n scrollingToBottomBehaviorRef.current !== null &&\n !newIsAtBottom &&\n 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 const userScrolledUp = isUserScrollUp(\n {\n scrollTop: lastScrollTop.current,\n scrollHeight: lastScrollHeight.current,\n },\n div,\n );\n\n if (newIsAtBottom) {\n // newIsAtBottom is ambiguous when the viewport doesn't overflow —\n // keep intent alive until content can actually scroll\n if (viewportOverflows(div)) {\n scrollingToBottomBehaviorRef.current = null;\n if (scrolledSincePauseRef.current) followPausedRef.current = false;\n }\n if (autoScroll) followBottomRef.current = true;\n } else if (userScrolledUp) {\n cancelScheduledFrame();\n scrollingToBottomBehaviorRef.current = null;\n followBottomRef.current = false;\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 firstMessageRef.current = measureFirstMessage(div);\n };\n const onScrollRef = useRef(onScroll);\n useIsomorphicLayoutEffect(() => {\n onScrollRef.current = onScroll;\n });\n const handleScroll = useCallback(() => onScrollRef.current(), []);\n\n const onResize = () => {\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 // A scheduled frame owns the pending behavior; resize callbacks must not apply it early.\n if (scheduledFrameRef.current === null) {\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 (\n autoScroll &&\n !(isRunning && hasActiveTopAnchor()) &&\n followBottomRef.current &&\n !followPausedRef.current\n ) {\n scrollToBottom(\"instant\");\n } else if (\n prependAnchorRef.current &&\n !keepMessageAt(div, prependAnchorRef.current, heldScrollTopRef)\n ) {\n prependAnchorRef.current = null;\n }\n }\n\n handleScroll();\n };\n const onResizeRef = useRef(onResize);\n useIsomorphicLayoutEffect(() => {\n onResizeRef.current = onResize;\n });\n const resizeRef = useOnResizeContent(() => onResizeRef.current());\n\n const scrollRef = useManagedRef<HTMLElement>(\n useCallback(\n (el) => {\n // A user gesture invalidates pending bottom-scroll intent; otherwise an\n // intent kept alive by a non-overflowing thread (see handleScroll) hijacks\n // the next content growth, e.g. expanding a collapsible tool call. Keyboard\n // activation reaches that same content without ever emitting a pointer\n // event, so it has to cancel the intent too.\n const cancelPendingScrollToBottom = () => {\n // With nothing pending, the last scroll event already published isAtBottom.\n if (\n scrollingToBottomBehaviorRef.current === null &&\n scheduledFrameRef.current === null\n )\n return;\n // A scheduled frame re-plants the intent when it runs, so clearing the\n // ref alone leaves the gesture undone.\n cancelScheduledFrame();\n scrollingToBottomBehaviorRef.current = null;\n handleScroll();\n };\n // The composer renders inside the viewport, so its keystrokes bubble here;\n // cancellation keys only represent a gesture on thread content when they\n // originate outside text entry.\n const cancelOnKeyDown = (event: KeyboardEvent) => {\n if (!INTENT_CANCEL_KEYS.has(event.key)) return;\n const target = event.target as Element | null;\n if (target?.closest?.(TEXT_ENTRY_SELECTOR)) return;\n cancelPendingScrollToBottom();\n };\n // Expanding a disclosure inside a message, such as a reasoning block or a\n // tool call card, means the reader is inspecting it, so bottom follow\n // pauses until the reader scrolls or asks for the bottom again. Every\n // other interaction, including copying or selecting text, keeps\n // following. Activation is read from click, which keyboard activation also\n // dispatches, in the capture phase so the state is read before any handler\n // on the disclosure flips it or stops the event.\n const pauseFollowOnExpand = (event: MouseEvent) => {\n const target = event.target as Element | null;\n if (\n !target?.closest?.(MESSAGE_SELECTOR) ||\n target.closest(TEXT_ENTRY_SELECTOR) ||\n !isOpeningDisclosure(target)\n )\n return;\n followPausedRef.current = true;\n scrolledSincePauseRef.current = false;\n cancelPendingScrollToBottom();\n };\n const noteScrollGesture = (event: Event) => {\n if (event instanceof KeyboardEvent) {\n if (!SCROLL_KEYS.has(event.key)) return;\n if (\n (event.target as Element | null)?.closest?.(TEXT_ENTRY_SELECTOR)\n )\n return;\n } else if (event.type === \"pointerdown\" && event.target !== el) {\n return;\n }\n scrolledSincePauseRef.current = true;\n };\n const releasePrependAnchor = () => {\n prependAnchorRef.current = null;\n };\n const gestures = [\n \"pointerdown\",\n \"wheel\",\n \"touchstart\",\n \"keydown\",\n ] as const;\n const resumeGestures = [\n \"pointerdown\",\n \"wheel\",\n \"touchmove\",\n \"keydown\",\n ] as const;\n el.addEventListener(\"scroll\", handleScroll);\n el.addEventListener(\"pointerdown\", cancelPendingScrollToBottom);\n el.addEventListener(\"wheel\", cancelPendingScrollToBottom, {\n passive: true,\n });\n el.addEventListener(\"touchstart\", cancelPendingScrollToBottom, {\n passive: true,\n });\n el.addEventListener(\"keydown\", cancelOnKeyDown);\n el.addEventListener(\"click\", pauseFollowOnExpand, { capture: true });\n for (const gesture of resumeGestures) {\n el.addEventListener(gesture, noteScrollGesture, { passive: true });\n }\n for (const gesture of gestures) {\n el.addEventListener(gesture, releasePrependAnchor, { passive: true });\n }\n return () => {\n el.removeEventListener(\"scroll\", handleScroll);\n el.removeEventListener(\"pointerdown\", cancelPendingScrollToBottom);\n el.removeEventListener(\"wheel\", cancelPendingScrollToBottom);\n el.removeEventListener(\"touchstart\", cancelPendingScrollToBottom);\n el.removeEventListener(\"keydown\", cancelOnKeyDown);\n el.removeEventListener(\"click\", pauseFollowOnExpand, {\n capture: true,\n });\n for (const gesture of resumeGestures) {\n el.removeEventListener(gesture, noteScrollGesture);\n }\n for (const gesture of gestures) {\n el.removeEventListener(gesture, releasePrependAnchor);\n }\n };\n },\n [cancelScheduledFrame, handleScroll],\n ),\n );\n\n // Earlier messages prepended above the reader put the old first message back\n // where the reader saw it, so growth below it in the same commit (a\n // streaming reply) is not mistaken for the page; where the browser already\n // anchored the scroll, this is a no-op.\n const previousRef = useRef({ threadId, firstMessageId });\n useIsomorphicLayoutEffect(() => {\n const previous = previousRef.current;\n previousRef.current = { threadId, firstMessageId };\n if (previous.threadId !== threadId) {\n prependAnchorRef.current = null;\n return;\n }\n const div = divRef.current;\n const previousFirstMessageId = previous.firstMessageId;\n if (\n !div ||\n previousFirstMessageId === undefined ||\n previousFirstMessageId === firstMessageId ||\n (autoScroll && followBottomRef.current && !followPausedRef.current) ||\n scrollingToBottomBehaviorRef.current !== null\n )\n return;\n const prepended = aui.thread\n .getState()\n .messages.some(\n (message, index) => index > 0 && message.id === previousFirstMessageId,\n );\n if (!prepended) return;\n\n const anchor = firstMessageRef.current;\n if (\n anchor?.id === previousFirstMessageId &&\n keepMessageAt(div, anchor, heldScrollTopRef)\n ) {\n prependAnchorRef.current = measureReaderMessage(div) ?? anchor;\n } else {\n // Message roots that render no `data-message-id` leave only the total\n // growth to go on, which counts any growth below the reader too.\n const top =\n div.scrollHeight - (lastScrollHeight.current - lastScrollTop.current);\n if (Math.abs(div.scrollTop - top) >= 1) {\n div.scrollTo({ top, behavior: \"instant\" });\n }\n }\n lastScrollTop.current = div.scrollTop;\n lastScrollHeight.current = div.scrollHeight;\n }, [aui, autoScroll, firstMessageId, threadId]);\n\n useIsomorphicLayoutEffect(() => {\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 prependAnchorRef.current = null;\n followPausedRef.current = false;\n if (!scrollToBottomOnRunStart) return;\n if (threadViewportStore.getState().turnAnchor === \"top\") return;\n scheduleScrollToBottom(\"auto\");\n });\n\n useAuiEvent(\"threads.selectionChanged\", () => {\n followPausedRef.current = false;\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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"}
|
|
@@ -7,7 +7,9 @@ export { ThreadPrimitiveViewportFooter as ViewportFooter } from "./thread/Thread
|
|
|
7
7
|
export { ThreadPrimitiveMessages as Messages } from "./thread/ThreadMessages.js";
|
|
8
8
|
export { ThreadPrimitiveMessageByIndex as MessageByIndex } from "./thread/ThreadMessages.js";
|
|
9
9
|
export { ThreadPrimitiveUnstable_MessageById as Unstable_MessageById } from "./thread/ThreadMessages.js";
|
|
10
|
+
export { ThreadPrimitiveRow as Row } from "./thread/ThreadMessages.js";
|
|
10
11
|
export { ThreadPrimitiveScrollToBottom as ScrollToBottom } from "./thread/ThreadScrollToBottom.js";
|
|
12
|
+
export { ThreadPrimitiveLoadEarlier as LoadEarlier } from "./thread/ThreadLoadEarlier.js";
|
|
11
13
|
export { ThreadPrimitiveSuggestion as Suggestion } from "./thread/ThreadSuggestion.js";
|
|
12
14
|
export { ThreadPrimitiveSuggestions as Suggestions, ThreadPrimitiveSuggestionByIndex as SuggestionByIndex, } from "./thread/ThreadSuggestions.js";
|
|
13
15
|
//# sourceMappingURL=thread.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thread.d.ts","sourceRoot":"","sources":["../../src/primitives/thread.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,IAAI,IAAI,EAAE,+BAA4B;AAClE,OAAO,EAAE,oBAAoB,IAAI,KAAK,EAAE,gCAA6B;AACrE,OAAO,EAAE,iBAAiB,IAAI,EAAE,EAAE,6BAA0B;AAC5D,OAAO,EAAE,uBAAuB,IAAI,QAAQ,EAAE,mCAAgC;AAC9E,OAAO,EAAE,+BAA+B,IAAI,gBAAgB,EAAE,uDAAoD;AAClH,OAAO,EAAE,6BAA6B,IAAI,cAAc,EAAE,yCAAsC;AAChG,OAAO,EAAE,uBAAuB,IAAI,QAAQ,EAAE,mCAAgC;AAC9E,OAAO,EAAE,6BAA6B,IAAI,cAAc,EAAE,mCAAgC;AAC1F,OAAO,EAAE,mCAAmC,IAAI,oBAAoB,EAAE,mCAAgC;AACtG,OAAO,EAAE,6BAA6B,IAAI,cAAc,EAAE,yCAAsC;AAChG,OAAO,EAAE,yBAAyB,IAAI,UAAU,EAAE,qCAAkC;AACpF,OAAO,EACL,0BAA0B,IAAI,WAAW,EACzC,gCAAgC,IAAI,iBAAiB,GACtD,sCAAmC"}
|
|
1
|
+
{"version":3,"file":"thread.d.ts","sourceRoot":"","sources":["../../src/primitives/thread.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,IAAI,IAAI,EAAE,+BAA4B;AAClE,OAAO,EAAE,oBAAoB,IAAI,KAAK,EAAE,gCAA6B;AACrE,OAAO,EAAE,iBAAiB,IAAI,EAAE,EAAE,6BAA0B;AAC5D,OAAO,EAAE,uBAAuB,IAAI,QAAQ,EAAE,mCAAgC;AAC9E,OAAO,EAAE,+BAA+B,IAAI,gBAAgB,EAAE,uDAAoD;AAClH,OAAO,EAAE,6BAA6B,IAAI,cAAc,EAAE,yCAAsC;AAChG,OAAO,EAAE,uBAAuB,IAAI,QAAQ,EAAE,mCAAgC;AAC9E,OAAO,EAAE,6BAA6B,IAAI,cAAc,EAAE,mCAAgC;AAC1F,OAAO,EAAE,mCAAmC,IAAI,oBAAoB,EAAE,mCAAgC;AACtG,OAAO,EAAE,kBAAkB,IAAI,GAAG,EAAE,mCAAgC;AACpE,OAAO,EAAE,6BAA6B,IAAI,cAAc,EAAE,yCAAsC;AAChG,OAAO,EAAE,0BAA0B,IAAI,WAAW,EAAE,sCAAmC;AACvF,OAAO,EAAE,yBAAyB,IAAI,UAAU,EAAE,qCAAkC;AACpF,OAAO,EACL,0BAA0B,IAAI,WAAW,EACzC,gCAAgC,IAAI,iBAAiB,GACtD,sCAAmC"}
|
|
@@ -5,17 +5,20 @@ import { ThreadPrimitiveEmpty } from "./thread/ThreadEmpty.js";
|
|
|
5
5
|
import { ThreadPrimitiveIf } from "./thread/ThreadIf.js";
|
|
6
6
|
import { ThreadPrimitiveViewport } from "./thread/ThreadViewport.js";
|
|
7
7
|
import { ThreadPrimitiveViewportFooter } from "./thread/ThreadViewportFooter.js";
|
|
8
|
-
import { ThreadPrimitiveMessageByIndex, ThreadPrimitiveMessages, ThreadPrimitiveUnstable_MessageById } from "./thread/ThreadMessages.js";
|
|
8
|
+
import { ThreadPrimitiveMessageByIndex, ThreadPrimitiveMessages, ThreadPrimitiveRow, ThreadPrimitiveUnstable_MessageById } from "./thread/ThreadMessages.js";
|
|
9
9
|
import { ThreadPrimitiveScrollToBottom } from "./thread/ThreadScrollToBottom.js";
|
|
10
|
+
import { ThreadPrimitiveLoadEarlier } from "./thread/ThreadLoadEarlier.js";
|
|
10
11
|
import { ThreadPrimitiveSuggestion } from "./thread/ThreadSuggestion.js";
|
|
11
12
|
import { ThreadPrimitiveSuggestionByIndex, ThreadPrimitiveSuggestions } from "./thread/ThreadSuggestions.js";
|
|
12
13
|
//#region src/primitives/thread.ts
|
|
13
14
|
var thread_exports = /* @__PURE__ */ __exportAll({
|
|
14
15
|
Empty: () => ThreadPrimitiveEmpty,
|
|
15
16
|
If: () => ThreadPrimitiveIf,
|
|
17
|
+
LoadEarlier: () => ThreadPrimitiveLoadEarlier,
|
|
16
18
|
MessageByIndex: () => ThreadPrimitiveMessageByIndex,
|
|
17
19
|
Messages: () => ThreadPrimitiveMessages,
|
|
18
20
|
Root: () => ThreadPrimitiveRoot,
|
|
21
|
+
Row: () => ThreadPrimitiveRow,
|
|
19
22
|
ScrollToBottom: () => ThreadPrimitiveScrollToBottom,
|
|
20
23
|
Suggestion: () => ThreadPrimitiveSuggestion,
|
|
21
24
|
SuggestionByIndex: () => ThreadPrimitiveSuggestionByIndex,
|
|
@@ -26,6 +29,6 @@ var thread_exports = /* @__PURE__ */ __exportAll({
|
|
|
26
29
|
ViewportProvider: () => ThreadPrimitiveViewportProvider
|
|
27
30
|
});
|
|
28
31
|
//#endregion
|
|
29
|
-
export { ThreadPrimitiveEmpty as Empty, ThreadPrimitiveIf as If, ThreadPrimitiveMessageByIndex as MessageByIndex, ThreadPrimitiveMessages as Messages, ThreadPrimitiveRoot as Root, ThreadPrimitiveScrollToBottom as ScrollToBottom, ThreadPrimitiveSuggestion as Suggestion, ThreadPrimitiveSuggestionByIndex as SuggestionByIndex, ThreadPrimitiveSuggestions as Suggestions, ThreadPrimitiveUnstable_MessageById as Unstable_MessageById, ThreadPrimitiveViewport as Viewport, ThreadPrimitiveViewportFooter as ViewportFooter, ThreadPrimitiveViewportProvider as ViewportProvider, thread_exports };
|
|
32
|
+
export { ThreadPrimitiveEmpty as Empty, ThreadPrimitiveIf as If, ThreadPrimitiveLoadEarlier as LoadEarlier, ThreadPrimitiveMessageByIndex as MessageByIndex, ThreadPrimitiveMessages as Messages, ThreadPrimitiveRoot as Root, ThreadPrimitiveRow as Row, ThreadPrimitiveScrollToBottom as ScrollToBottom, ThreadPrimitiveSuggestion as Suggestion, ThreadPrimitiveSuggestionByIndex as SuggestionByIndex, ThreadPrimitiveSuggestions as Suggestions, ThreadPrimitiveUnstable_MessageById as Unstable_MessageById, ThreadPrimitiveViewport as Viewport, ThreadPrimitiveViewportFooter as ViewportFooter, ThreadPrimitiveViewportProvider as ViewportProvider, thread_exports };
|
|
30
33
|
|
|
31
34
|
//# sourceMappingURL=thread.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thread.js","names":["ThreadPrimitiveRoot","Root","ThreadPrimitiveEmpty","Empty","ThreadPrimitiveIf","If","ThreadPrimitiveViewport","Viewport","ThreadPrimitiveViewportProvider","ViewportProvider","ThreadPrimitiveViewportFooter","ViewportFooter","ThreadPrimitiveMessages","Messages","ThreadPrimitiveMessageByIndex","MessageByIndex","ThreadPrimitiveUnstable_MessageById","Unstable_MessageById","ThreadPrimitiveScrollToBottom","ScrollToBottom","ThreadPrimitiveSuggestion","Suggestion","ThreadPrimitiveSuggestions","Suggestions","ThreadPrimitiveSuggestionByIndex","SuggestionByIndex"],"sources":["../../src/primitives/thread.ts"],"sourcesContent":["export { ThreadPrimitiveRoot as Root } from \"./thread/ThreadRoot\";\nexport { ThreadPrimitiveEmpty as Empty } from \"./thread/ThreadEmpty\";\nexport { ThreadPrimitiveIf as If } from \"./thread/ThreadIf\";\nexport { ThreadPrimitiveViewport as Viewport } from \"./thread/ThreadViewport\";\nexport { ThreadPrimitiveViewportProvider as ViewportProvider } from \"../context/providers/ThreadViewportProvider\";\nexport { ThreadPrimitiveViewportFooter as ViewportFooter } from \"./thread/ThreadViewportFooter\";\nexport { ThreadPrimitiveMessages as Messages } from \"./thread/ThreadMessages\";\nexport { ThreadPrimitiveMessageByIndex as MessageByIndex } from \"./thread/ThreadMessages\";\nexport { ThreadPrimitiveUnstable_MessageById as Unstable_MessageById } from \"./thread/ThreadMessages\";\nexport { ThreadPrimitiveScrollToBottom as ScrollToBottom } from \"./thread/ThreadScrollToBottom\";\nexport { ThreadPrimitiveSuggestion as Suggestion } from \"./thread/ThreadSuggestion\";\nexport {\n ThreadPrimitiveSuggestions as Suggestions,\n ThreadPrimitiveSuggestionByIndex as SuggestionByIndex,\n} from \"./thread/ThreadSuggestions\";\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"thread.js","names":["ThreadPrimitiveRoot","Root","ThreadPrimitiveEmpty","Empty","ThreadPrimitiveIf","If","ThreadPrimitiveViewport","Viewport","ThreadPrimitiveViewportProvider","ViewportProvider","ThreadPrimitiveViewportFooter","ViewportFooter","ThreadPrimitiveMessages","Messages","ThreadPrimitiveMessageByIndex","MessageByIndex","ThreadPrimitiveUnstable_MessageById","Unstable_MessageById","ThreadPrimitiveRow","Row","ThreadPrimitiveScrollToBottom","ScrollToBottom","ThreadPrimitiveLoadEarlier","LoadEarlier","ThreadPrimitiveSuggestion","Suggestion","ThreadPrimitiveSuggestions","Suggestions","ThreadPrimitiveSuggestionByIndex","SuggestionByIndex"],"sources":["../../src/primitives/thread.ts"],"sourcesContent":["export { ThreadPrimitiveRoot as Root } from \"./thread/ThreadRoot\";\nexport { ThreadPrimitiveEmpty as Empty } from \"./thread/ThreadEmpty\";\nexport { ThreadPrimitiveIf as If } from \"./thread/ThreadIf\";\nexport { ThreadPrimitiveViewport as Viewport } from \"./thread/ThreadViewport\";\nexport { ThreadPrimitiveViewportProvider as ViewportProvider } from \"../context/providers/ThreadViewportProvider\";\nexport { ThreadPrimitiveViewportFooter as ViewportFooter } from \"./thread/ThreadViewportFooter\";\nexport { ThreadPrimitiveMessages as Messages } from \"./thread/ThreadMessages\";\nexport { ThreadPrimitiveMessageByIndex as MessageByIndex } from \"./thread/ThreadMessages\";\nexport { ThreadPrimitiveUnstable_MessageById as Unstable_MessageById } from \"./thread/ThreadMessages\";\nexport { ThreadPrimitiveRow as Row } from \"./thread/ThreadMessages\";\nexport { ThreadPrimitiveScrollToBottom as ScrollToBottom } from \"./thread/ThreadScrollToBottom\";\nexport { ThreadPrimitiveLoadEarlier as LoadEarlier } from \"./thread/ThreadLoadEarlier\";\nexport { ThreadPrimitiveSuggestion as Suggestion } from \"./thread/ThreadSuggestion\";\nexport {\n ThreadPrimitiveSuggestions as Suggestions,\n ThreadPrimitiveSuggestionByIndex as SuggestionByIndex,\n} from \"./thread/ThreadSuggestions\";\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SandboxHost.d.ts","sourceRoot":"","sources":["../../src/sandbox-host/SandboxHost.tsx"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"SandboxHost.d.ts","sourceRoot":"","sources":["../../src/sandbox-host/SandboxHost.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,aAAa,EAA+B,MAAM,OAAO,CAAC;AAExE,OAAO,EAEL,KAAK,aAAa,EAElB,KAAK,aAAa,EACnB,MAAM,oBAAoB,CAAC;AAO5B,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,IAAI,CACjC,aAAa,EACb,QAAQ,GAAG,QAAQ,GAAG,aAAa,CACpC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IACzC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,cAAc,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,YAAY,EAAE,CACZ,KAAK,EAAE,gBAAgB,EACvB,IAAI,EAAE,cAAc,KACjB,aAAa,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC/C,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;CACjE,CAAC;AAEF,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE;IAAE,MAAM,EAAE,iBAAiB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACnD,OAAO,CAIT;AASD,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,UAAU,EACV,OAAO,EACP,SAA8B,EAC9B,YAAY,EACZ,OAAO,EACP,cAAc,GACf,EAAE,gBAAgB,+BAkKlB"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { useIsomorphicLayoutEffect } from "../utils/useIsomorphicLayoutEffect.js";
|
|
2
3
|
import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
|
|
3
|
-
import { useEffect,
|
|
4
|
+
import { useEffect, useRef, useState } from "@assistant-ui/tap/react-shim";
|
|
4
5
|
import { invokeUserCallback } from "@assistant-ui/core/internal";
|
|
5
6
|
import { jsx } from "react/jsx-runtime";
|
|
6
7
|
import { SafeContentFrame, isShimLoadError } from "safe-content-frame";
|
|
@@ -59,7 +60,7 @@ function SandboxHost(t0) {
|
|
|
59
60
|
t3 = $[9];
|
|
60
61
|
t4 = $[10];
|
|
61
62
|
}
|
|
62
|
-
|
|
63
|
+
useIsomorphicLayoutEffect(t3, t4);
|
|
63
64
|
let t5;
|
|
64
65
|
if ($[11] === Symbol.for("react.memo_cache_sentinel")) {
|
|
65
66
|
t5 = () => {
|
|
@@ -69,6 +70,7 @@ function SandboxHost(t0) {
|
|
|
69
70
|
let frame = null;
|
|
70
71
|
let bridge = null;
|
|
71
72
|
let onMessage = null;
|
|
73
|
+
const renderController = new AbortController();
|
|
72
74
|
const { content: liveContent, sandbox: sb } = liveRef.current;
|
|
73
75
|
const reportError = (err) => {
|
|
74
76
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -80,7 +82,10 @@ function SandboxHost(t0) {
|
|
|
80
82
|
...sb?.enableBrowserCaching !== void 0 && { enableBrowserCaching: sb.enableBrowserCaching },
|
|
81
83
|
...sb?.salt !== void 0 && { salt: sb.salt }
|
|
82
84
|
});
|
|
83
|
-
const renderOpts =
|
|
85
|
+
const renderOpts = {
|
|
86
|
+
signal: renderController.signal,
|
|
87
|
+
...sb?.unsafeDocumentWrite !== void 0 && { unsafeDocumentWrite: sb.unsafeDocumentWrite }
|
|
88
|
+
};
|
|
84
89
|
scf.renderHtml(liveContent.html, container, renderOpts).then((rendered) => {
|
|
85
90
|
if (cancelled) {
|
|
86
91
|
rendered.dispose();
|
|
@@ -134,6 +139,7 @@ function SandboxHost(t0) {
|
|
|
134
139
|
const bridgeToDispose = bridge;
|
|
135
140
|
bridge = null;
|
|
136
141
|
if (bridgeToDispose) runCleanup(() => bridgeToDispose.dispose());
|
|
142
|
+
runCleanup(() => renderController.abort());
|
|
137
143
|
const frameToDispose = frame;
|
|
138
144
|
frame = null;
|
|
139
145
|
if (frameToDispose) runCleanup(() => frameToDispose.dispose());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SandboxHost.js","names":["c","_c","CSSProperties","useEffect","useLayoutEffect","useRef","useState","isShimLoadError","RenderedFrame","SafeContentFrame","SandboxOption","invokeUserCallback","DEFAULT_PRODUCT","DEFAULT_MAX_HEIGHT","LOAD_TIMEOUT_MS","SandboxHostConfig","sandbox","useShadowDom","enableBrowserCaching","salt","product","className","style","unsafeDocumentWrite","SandboxHostFrame","Pick","SandboxHostApi","setHeight","height","SandboxBridge","onMessage","event","MessageEvent","dispose","SandboxContent","html","SandboxHostProps","content","contentKey","maxHeight","createBridge","frame","host","onError","error","Error","containerProps","Record","isSandboxFrameMessage","iframe","HTMLIFrameElement","origin","source","contentWindow","LiveSnapshot","SandboxHost","t0","$","t1","undefined","containerRef","contentHeight","setContentHeight","t2","liveRef","t3","t4","current","t5","Symbol","for","container","cancelled","bridge","liveContent","sb","reportError","err","String","bind","scf","renderOpts","renderHtml","then","rendered","hostApi","Number","isFinite","sendMessage","window","addEventListener","fullyLoadedPromiseWithTimeout","catch","err_0","code","err_1","cleanupFailed","cleanupError","runCleanup","cleanup","t6","console","listener","removeEventListener","bridgeToDispose","frameToDispose","resolvedHeight","Math","min","t7","mergedStyle","t8","t9","_c"],"sources":["../../src/sandbox-host/SandboxHost.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type CSSProperties,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\nimport {\n isShimLoadError,\n type RenderedFrame,\n SafeContentFrame,\n type SandboxOption,\n} from \"safe-content-frame\";\nimport { invokeUserCallback } from \"@assistant-ui/core/internal\";\n\nconst DEFAULT_PRODUCT = \"assistant-ui-sandbox\";\nconst DEFAULT_MAX_HEIGHT = 800;\nconst LOAD_TIMEOUT_MS = 10_000;\n\nexport type SandboxHostConfig = {\n sandbox?: SandboxOption[];\n useShadowDom?: boolean;\n enableBrowserCaching?: boolean;\n salt?: string;\n product?: string;\n className?: string;\n style?: CSSProperties;\n unsafeDocumentWrite?: boolean;\n};\n\nexport type SandboxHostFrame = Pick<\n RenderedFrame,\n \"iframe\" | \"origin\" | \"sendMessage\"\n>;\n\nexport type SandboxHostApi = {\n setHeight: (height: number) => void;\n};\n\nexport type SandboxBridge = {\n onMessage: (event: MessageEvent) => void;\n dispose: () => void;\n};\n\nexport type SandboxContent = { html: string };\n\nexport type SandboxHostProps = {\n content: SandboxContent;\n contentKey: string;\n sandbox?: SandboxHostConfig | undefined;\n maxHeight?: number | undefined;\n createBridge: (\n frame: SandboxHostFrame,\n host: SandboxHostApi,\n ) => SandboxBridge;\n onError?: ((error: Error) => void) | undefined;\n containerProps?: Record<string, string | undefined> | undefined;\n};\n\nexport function isSandboxFrameMessage(\n event: MessageEvent,\n frame: { iframe: HTMLIFrameElement; origin: string },\n): boolean {\n return (\n event.source === frame.iframe.contentWindow && event.origin === frame.origin\n );\n}\n\ntype LiveSnapshot = {\n content: SandboxContent;\n sandbox: SandboxHostConfig | undefined;\n createBridge: SandboxHostProps[\"createBridge\"];\n onError: SandboxHostProps[\"onError\"];\n};\n\nexport function SandboxHost({\n content,\n contentKey,\n sandbox,\n maxHeight = DEFAULT_MAX_HEIGHT,\n createBridge,\n onError,\n containerProps,\n}: SandboxHostProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [contentHeight, setContentHeight] = useState<number | undefined>(\n undefined,\n );\n\n const liveRef = useRef<LiveSnapshot>({\n content,\n sandbox,\n createBridge,\n onError,\n });\n useLayoutEffect(() => {\n liveRef.current = { content, sandbox, createBridge, onError };\n }, [content, sandbox, createBridge, onError]);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n let cancelled = false;\n let frame: RenderedFrame | null = null;\n let bridge: SandboxBridge | null = null;\n let onMessage: ((event: MessageEvent) => void) | null = null;\n\n const { content: liveContent, sandbox: sb } = liveRef.current;\n\n const reportError = (err: unknown) => {\n const error = err instanceof Error ? err : new Error(String(err));\n invokeUserCallback(\n \"assistant-ui\",\n \"SandboxHost onError\",\n liveRef.current.onError?.bind(liveRef.current),\n error,\n );\n };\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(liveContent.html, container, renderOpts)\n .then((rendered) => {\n if (cancelled) {\n rendered.dispose();\n return;\n }\n frame = rendered;\n\n const hostApi: SandboxHostApi = {\n setHeight: (height) => {\n if (\n typeof height === \"number\" &&\n Number.isFinite(height) &&\n height > 0\n ) {\n setContentHeight(height);\n }\n },\n };\n\n bridge = liveRef.current.createBridge(\n {\n iframe: rendered.iframe,\n origin: rendered.origin,\n sendMessage: rendered.sendMessage,\n },\n hostApi,\n );\n\n // Single owner of the window listener; the cross-origin guard runs\n // here so the bridge only sees frame-validated messages.\n onMessage = (event) => {\n if (!isSandboxFrameMessage(event, rendered)) return;\n bridge?.onMessage(event);\n };\n window.addEventListener(\"message\", onMessage);\n\n // renderHtml resolves at iframe load, which a shim that was never\n // served also reaches, so a completed render is only known once the\n // frame says so. render-timeout is excluded because the shim is\n // running and the guest may still paint, and reporting it would leave\n // a host that renders an error state showing one over a frame that\n // recovers.\n rendered.fullyLoadedPromiseWithTimeout(LOAD_TIMEOUT_MS).catch((err) => {\n if (cancelled) return;\n if (isShimLoadError(err) && err.code === \"render-timeout\") return;\n reportError(err);\n });\n })\n .catch((err) => {\n if (cancelled) return;\n frame?.dispose();\n frame = null;\n reportError(err);\n });\n\n return () => {\n cancelled = true;\n let cleanupFailed = false;\n let cleanupError: unknown;\n const runCleanup = (cleanup: () => void) => {\n try {\n cleanup();\n } catch (error) {\n if (cleanupFailed) {\n console.error(error);\n } else {\n cleanupFailed = true;\n cleanupError = error;\n }\n }\n };\n\n if (onMessage) {\n const listener = onMessage;\n onMessage = null;\n runCleanup(() => window.removeEventListener(\"message\", listener));\n }\n const bridgeToDispose = bridge;\n bridge = null;\n if (bridgeToDispose) runCleanup(() => bridgeToDispose.dispose());\n const frameToDispose = frame;\n frame = null;\n if (frameToDispose) runCleanup(() => frameToDispose.dispose());\n runCleanup(() => setContentHeight(undefined));\n\n if (cleanupFailed) throw cleanupError;\n };\n // oxlint-disable-next-line react/exhaustive-deps -- re-init only on contentKey change; live values flow through liveRef\n }, [contentKey]);\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 {...containerProps}\n ref={containerRef}\n className={sandbox?.className}\n style={mergedStyle}\n />\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SandboxHost.js","names":["c","_c","CSSProperties","useEffect","useRef","useState","useIsomorphicLayoutEffect","isShimLoadError","RenderedFrame","SafeContentFrame","SandboxOption","invokeUserCallback","DEFAULT_PRODUCT","DEFAULT_MAX_HEIGHT","LOAD_TIMEOUT_MS","SandboxHostConfig","sandbox","useShadowDom","enableBrowserCaching","salt","product","className","style","unsafeDocumentWrite","SandboxHostFrame","Pick","SandboxHostApi","setHeight","height","SandboxBridge","onMessage","event","MessageEvent","dispose","SandboxContent","html","SandboxHostProps","content","contentKey","maxHeight","createBridge","frame","host","onError","error","Error","containerProps","Record","isSandboxFrameMessage","iframe","HTMLIFrameElement","origin","source","contentWindow","LiveSnapshot","SandboxHost","t0","$","t1","undefined","containerRef","contentHeight","setContentHeight","t2","liveRef","t3","t4","current","t5","Symbol","for","container","cancelled","bridge","renderController","AbortController","liveContent","sb","reportError","err","String","bind","scf","renderOpts","signal","renderHtml","then","rendered","hostApi","Number","isFinite","sendMessage","window","addEventListener","fullyLoadedPromiseWithTimeout","catch","err_0","code","err_1","cleanupFailed","cleanupError","runCleanup","cleanup","t6","console","listener","removeEventListener","bridgeToDispose","abort","frameToDispose","resolvedHeight","Math","min","t7","mergedStyle","t8","t9","_c"],"sources":["../../src/sandbox-host/SandboxHost.tsx"],"sourcesContent":["\"use client\";\n\nimport { type CSSProperties, useEffect, useRef, useState } from \"react\";\nimport { useIsomorphicLayoutEffect } from \"../utils/useIsomorphicLayoutEffect\";\nimport {\n isShimLoadError,\n type RenderedFrame,\n SafeContentFrame,\n type SandboxOption,\n} from \"safe-content-frame\";\nimport { invokeUserCallback } from \"@assistant-ui/core/internal\";\n\nconst DEFAULT_PRODUCT = \"assistant-ui-sandbox\";\nconst DEFAULT_MAX_HEIGHT = 800;\nconst LOAD_TIMEOUT_MS = 10_000;\n\nexport type SandboxHostConfig = {\n sandbox?: SandboxOption[];\n useShadowDom?: boolean;\n enableBrowserCaching?: boolean;\n salt?: string;\n product?: string;\n className?: string;\n style?: CSSProperties;\n unsafeDocumentWrite?: boolean;\n};\n\nexport type SandboxHostFrame = Pick<\n RenderedFrame,\n \"iframe\" | \"origin\" | \"sendMessage\"\n>;\n\nexport type SandboxHostApi = {\n setHeight: (height: number) => void;\n};\n\nexport type SandboxBridge = {\n onMessage: (event: MessageEvent) => void;\n dispose: () => void;\n};\n\nexport type SandboxContent = { html: string };\n\nexport type SandboxHostProps = {\n content: SandboxContent;\n contentKey: string;\n sandbox?: SandboxHostConfig | undefined;\n maxHeight?: number | undefined;\n createBridge: (\n frame: SandboxHostFrame,\n host: SandboxHostApi,\n ) => SandboxBridge;\n onError?: ((error: Error) => void) | undefined;\n containerProps?: Record<string, string | undefined> | undefined;\n};\n\nexport function isSandboxFrameMessage(\n event: MessageEvent,\n frame: { iframe: HTMLIFrameElement; origin: string },\n): boolean {\n return (\n event.source === frame.iframe.contentWindow && event.origin === frame.origin\n );\n}\n\ntype LiveSnapshot = {\n content: SandboxContent;\n sandbox: SandboxHostConfig | undefined;\n createBridge: SandboxHostProps[\"createBridge\"];\n onError: SandboxHostProps[\"onError\"];\n};\n\nexport function SandboxHost({\n content,\n contentKey,\n sandbox,\n maxHeight = DEFAULT_MAX_HEIGHT,\n createBridge,\n onError,\n containerProps,\n}: SandboxHostProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [contentHeight, setContentHeight] = useState<number | undefined>(\n undefined,\n );\n\n const liveRef = useRef<LiveSnapshot>({\n content,\n sandbox,\n createBridge,\n onError,\n });\n useIsomorphicLayoutEffect(() => {\n liveRef.current = { content, sandbox, createBridge, onError };\n }, [content, sandbox, createBridge, onError]);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n let cancelled = false;\n let frame: RenderedFrame | null = null;\n let bridge: SandboxBridge | null = null;\n let onMessage: ((event: MessageEvent) => void) | null = null;\n const renderController = new AbortController();\n\n const { content: liveContent, sandbox: sb } = liveRef.current;\n\n const reportError = (err: unknown) => {\n const error = err instanceof Error ? err : new Error(String(err));\n invokeUserCallback(\n \"assistant-ui\",\n \"SandboxHost onError\",\n liveRef.current.onError?.bind(liveRef.current),\n error,\n );\n };\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 signal: renderController.signal,\n ...(sb?.unsafeDocumentWrite !== undefined && {\n unsafeDocumentWrite: sb.unsafeDocumentWrite,\n }),\n };\n\n scf\n .renderHtml(liveContent.html, container, renderOpts)\n .then((rendered) => {\n if (cancelled) {\n rendered.dispose();\n return;\n }\n frame = rendered;\n\n const hostApi: SandboxHostApi = {\n setHeight: (height) => {\n if (\n typeof height === \"number\" &&\n Number.isFinite(height) &&\n height > 0\n ) {\n setContentHeight(height);\n }\n },\n };\n\n bridge = liveRef.current.createBridge(\n {\n iframe: rendered.iframe,\n origin: rendered.origin,\n sendMessage: rendered.sendMessage,\n },\n hostApi,\n );\n\n // Single owner of the window listener; the cross-origin guard runs\n // here so the bridge only sees frame-validated messages.\n onMessage = (event) => {\n if (!isSandboxFrameMessage(event, rendered)) return;\n bridge?.onMessage(event);\n };\n window.addEventListener(\"message\", onMessage);\n\n // renderHtml resolves at iframe load, which a shim that was never\n // served also reaches, so a completed render is only known once the\n // frame says so. render-timeout is excluded because the shim is\n // running and the guest may still paint, and reporting it would leave\n // a host that renders an error state showing one over a frame that\n // recovers.\n rendered.fullyLoadedPromiseWithTimeout(LOAD_TIMEOUT_MS).catch((err) => {\n if (cancelled) return;\n if (isShimLoadError(err) && err.code === \"render-timeout\") return;\n reportError(err);\n });\n })\n .catch((err) => {\n if (cancelled) return;\n frame?.dispose();\n frame = null;\n reportError(err);\n });\n\n return () => {\n cancelled = true;\n let cleanupFailed = false;\n let cleanupError: unknown;\n const runCleanup = (cleanup: () => void) => {\n try {\n cleanup();\n } catch (error) {\n if (cleanupFailed) {\n console.error(error);\n } else {\n cleanupFailed = true;\n cleanupError = error;\n }\n }\n };\n\n if (onMessage) {\n const listener = onMessage;\n onMessage = null;\n runCleanup(() => window.removeEventListener(\"message\", listener));\n }\n const bridgeToDispose = bridge;\n bridge = null;\n if (bridgeToDispose) runCleanup(() => bridgeToDispose.dispose());\n runCleanup(() => renderController.abort());\n const frameToDispose = frame;\n frame = null;\n if (frameToDispose) runCleanup(() => frameToDispose.dispose());\n runCleanup(() => setContentHeight(undefined));\n\n if (cleanupFailed) throw cleanupError;\n };\n // oxlint-disable-next-line react/exhaustive-deps -- re-init only on contentKey change; live values flow through liveRef\n }, [contentKey]);\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 {...containerProps}\n ref={containerRef}\n className={sandbox?.className}\n style={mergedStyle}\n />\n );\n}\n"],"mappings":";;;;;;;;AAYA,MAAMY,kBAAkB;AACxB,MAAMC,qBAAqB;AAC3B,MAAMC,kBAAkB;AA0CxB,SAAgBkC,sBACdjB,OACAU,OACS;CACT,OACEV,MAAMqB,WAAWX,MAAMQ,OAAOI,iBAAiBtB,MAAMoB,WAAWV,MAAMU;AAE1E;AASA,SAAOI,YAAAC,IAAA;CAAA,MAAAC,IAAAxD,EAAA,EAAA;CAAqB,MAAA,EAAAoC,SAAAC,YAAAtB,SAAAuB,WAAAmB,IAAAlB,cAAAG,SAAAG,mBAAAU;CAI1B,MAAAjB,YAAAmB,OAAAC,KAAAA,IAAA9C,qBAAA6C;CAKA,MAAAE,eAAqBxD,OAAuB,IAAI;CAChD,MAAA,CAAAyD,eAAAC,oBAA0CzD,SACxCsD,KAAAA,CACF;CAAE,IAAAI;CAAA,IAAAN,EAAA,OAAApB,WAAAoB,EAAA,OAAAjB,gBAAAiB,EAAA,OAAAd,WAAAc,EAAA,OAAAzC,SAAA;EAEmC+C,KAAA;GAAA1B;GAAArB;GAAAwB;GAAAG;EAKrC;EAACc,EAAA,KAAApB;EAAAoB,EAAA,KAAAjB;EAAAiB,EAAA,KAAAd;EAAAc,EAAA,KAAAzC;EAAAyC,EAAA,KAAAM;CAAA,OAAAA,KAAAN,EAAA;CALD,MAAAO,UAAgB5D,OAAqB2D,EAKpC;CAAE,IAAAE;CAAA,IAAAC;CAAA,IAAAT,EAAA,OAAApB,WAAAoB,EAAA,OAAAjB,gBAAAiB,EAAA,OAAAd,WAAAc,EAAA,OAAAzC,SAAA;EACuBiD,WAAA;GACxBD,QAAOG,UAAW;IAAA9B;IAAArB;IAAAwB;IAAAG;GAA0C;EAA7C;EACduB,KAAA;GAAC7B;GAASrB;GAASwB;GAAcG;EAAO;EAACc,EAAA,KAAApB;EAAAoB,EAAA,KAAAjB;EAAAiB,EAAA,KAAAd;EAAAc,EAAA,KAAAzC;EAAAyC,EAAA,KAAAQ;EAAAR,EAAA,MAAAS;CAAA,OAAA;EAAAD,KAAAR,EAAA;EAAAS,KAAAT,EAAA;CAAA;CAF5CnD,0BAA0B2D,IAEvBC,EAAyC;CAAC,IAAAE;CAAA,IAAAX,EAAA,QAAAY,OAAAC,IAAA,2BAAA,GAAA;EAEnCF,WAAA;GACR,MAAAG,YAAkBX,aAAYO;GAC9B,IAAI,CAACI,WAAS;GAEd,IAAAC,YAAgB;GAChB,IAAA/B,QAAkC;GAClC,IAAAgC,SAAmC;GACnC,IAAA3C,YAAwD;GACxD,MAAA4C,mBAAyB,IAAIC,gBAAgB;GAE7C,MAAA,EAAAtC,SAAAuC,aAAA5D,SAAA6D,OAA8Cb,QAAOG;GAErD,MAAAW,eAAoBC,QAAA;IAClB,MAAAnC,QAAcmC,eAAelC,QAAfkC,MAAA,IAAiClC,MAAMmC,OAAOD,GAAG,CAAC;IAChEpE,mBACE,gBACA,uBACAqD,QAAOG,QAAQxB,SAAcsC,KAACjB,QAAOG,OAAQ,GAC7CvB,KACF;GAAC;GAGH,MAAAsC,MAAY,IAAIzE,iBAAiBoE,IAAEzD,WAAFR,iBAAgC;IAAA,GAC3DiE,IAAE7D,YAAc2C,KAAAA,KAAhB,EAAA3C,SAAwC6D,GAAE7D,QAAS;IAAC,GACpD6D,IAAE5D,iBAAmB0C,KAAAA,KAArB,EAAA1C,cAAkD4D,GAAE5D,aAAc;IAAC,GACnE4D,IAAE3D,yBAA2ByC,KAAAA,KAA7B,EAAAzC,sBACoB2D,GAAE3D,qBAC1B;IAAC,GACG2D,IAAE1D,SAAWwC,KAAAA,KAAb,EAAAxC,MAAkC0D,GAAE1D,KAAM;GAChD,CAAC;GAED,MAAAgE,aAAmB;IAAAC,QACTV,iBAAgBU;IAAO,GAC3BP,IAAEtD,wBAA0BoC,KAAAA,KAA5B,EAAApC,qBACmBsD,GAAEtD,oBACzB;GACF;GAEA2D,IAAGG,WACWT,YAAWzC,MAAOoC,WAAWY,UAAU,CAAC,CAAAG,MAC9CC,aAAA;IACJ,IAAIf,WAAS;KACXe,SAAQtD,QAAS;KAAC;IAAA;IAGpBQ,QAAQ8C;IAcRd,SAAST,QAAOG,QAAQ3B,aACtB;KAAAS,QACUsC,SAAQtC;KAAOE,QACfoC,SAAQpC;KAAOwC,aACVJ,SAAQI;IACvB,GACAH,EAlB8B7D,YACnBC,WAAA;KACT,IACE,OAAOA,WAAW,YAClB6D,OAAMC,SAAU9D,MAAM,KACtBA,SAAS,GAETkC,iBAAiBlC,MAAM;IACxB,EAUH4D,CACF;IAIA1D,aAAYA,UAAAA;KACVA,IAAIA,CAACkB,sBAAsBjB,OAAOwD,QAAQ,GAAC;KAC3Cd,QAAM3C,UAAYC,KAAK;IAAC;IAE1B6D,OAAMC,iBAAkB,WAAW/D,SAAS;IAQ5CyD,SAAQO,8BAA+BhF,eAAe,CAAC,CAAAiF,OAAOC,UAAA;KAC5D,IAAIxB,WAAS;KACb,IAAIjE,gBAAgBwE,KAAoC,KAA5BA,MAAGkB,SAAU,kBAAgB;KACzDnB,YAAYC,KAAG;IAAC,CACjB;GAAC,CACH,CAAC,CAAAgB,OACKG,UAAA;IACL,IAAI1B,WAAS;IACb/B,OAAKR,QAAU;IACfQ,QAAQA;IACRqC,YAAYC,KAAG;GAAC,CACjB;GAAC,aAEG;IACLP,YAAYA;IACZ,IAAA2B,gBAAoB;IAChBC,IAAAA;IACJ,MAAAC,cAAmBC,YAAA;KACjB,IAAA;MACEA,QAAQ;KAAC,SAAAC,IAAA;MACF3D,MAAAA,UAAAA;MACP,IAAIuD,eACFK,QAAO5D,MAAOA,OAAK;WAAC;OAEpBuD,gBAAgBA;OAChBC,eAAexD;MAAH;KACb;IACF;IAGH,IAAId,WAAS;KACX,MAAA2E,WAAiB3E;KACjBA,YAAYA;KACZuE,iBAAiBT,OAAMc,oBAAqB,WAAWD,QAAQ,CAAC;IAAC;IAEnE,MAAAE,kBAAwBlC;IACxBA,SAASA;IACT,IAAIkC,iBAAiBN,iBAAiBM,gBAAe1E,QAAS,CAAC;IAC/DoE,iBAAiB3B,iBAAgBkC,MAAO,CAAC;IACzC,MAAAC,iBAAuBpE;IACvBA,QAAQA;IACR,IAAIoE,gBAAgBR,iBAAiBQ,eAAc5E,QAAS,CAAC;IAC7DoE,iBAAiBvC,iBAAiBH,KAAAA,CAAS,CAAC;IAE5C,IAAIwC,eAAe,MAAMC;GAAa;EACvC;EAEF3C,EAAA,MAAAW;CAAA,OAAAA,KAAAX,EAAA;CAAA,IAAA8C;CAAA,IAAA9C,EAAA,QAAAnB,YAAA;EAAEiE,KAAA,CAACjE,UAAU;EAACmB,EAAA,MAAAnB;EAAAmB,EAAA,MAAA8C;CAAA,OAAAA,KAAA9C,EAAA;CAjIftD,UAAUiE,IAiIPmC,EAAY;CAEf,MAAAO,iBACEjD,iBAAiB,OAAOkD,KAAIC,IAAKnD,eAAetB,SAAqB,IAArEoB,KAAAA;CAAuE,IAAAsD;CAAA,IAAAxD,EAAA,QAAAqD,kBAAArD,EAAA,QAAAzC,SAAAM,OAAA;EAEvE2F,KAAAH,kBAAkB,OAAlB;GAAA,GACS9F,SAAOM;GAAOM,QAAUkF;EAChB,IAAb9F,SAAOM;EAAOmC,EAAA,MAAAqD;EAAArD,EAAA,MAAAzC,SAAAM;EAAAmC,EAAA,MAAAwD;CAAA,OAAAA,KAAAxD,EAAA;CAHpB,MAAAyD,cACED;CAQa,MAAAE,KAAAnG,SAAOK;CAAW,IAAA+F;CAAA,IAAA3D,EAAA,QAAAX,kBAAAW,EAAA,QAAAyD,eAAAzD,EAAA,QAAA0D,IAAA;EAH/BC,KAAA,oBAAA,OAAA;GAKE,GAJItE;GACCc,KAAAA;GACM,WAAAuD;GACJD,OAAAA;EAAW,CAAA;EAClBzD,EAAA,MAAAX;EAAAW,EAAA,MAAAyD;EAAAzD,EAAA,MAAA0D;EAAA1D,EAAA,MAAA2D;CAAA,OAAAA,KAAA3D,EAAA;CAAA,OALF2D;AAKE"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ComposerSendOptions } from "@assistant-ui/core/store";
|
|
2
2
|
import { type TriggerPopoverAriaProps } from "../primitives/composer/useComposerInputState.js";
|
|
3
|
+
/** @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release. */
|
|
3
4
|
export type Unstable_UseComposerInputOptions = {
|
|
4
5
|
/**
|
|
5
6
|
* Disables the input in addition to the composer's own disabled sources
|
|
@@ -8,6 +9,7 @@ export type Unstable_UseComposerInputOptions = {
|
|
|
8
9
|
*/
|
|
9
10
|
disabled?: boolean | undefined;
|
|
10
11
|
};
|
|
12
|
+
/** @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release. */
|
|
11
13
|
export type Unstable_ComposerInput = {
|
|
12
14
|
/** Current composer text, or `""` when the composer is not editing. */
|
|
13
15
|
value: string;
|
|
@@ -35,8 +37,6 @@ export type Unstable_ComposerInput = {
|
|
|
35
37
|
canSend: boolean;
|
|
36
38
|
};
|
|
37
39
|
/**
|
|
38
|
-
* @deprecated Under active development and might change without notice.
|
|
39
|
-
*
|
|
40
40
|
* Headless bridge to the composer's text value and send action, for building a
|
|
41
41
|
* custom composer input without `ComposerPrimitive.Input`. It is a thin bridge,
|
|
42
42
|
* not a second input: it does not own keyboard behavior, autosize, IME or
|
|
@@ -44,6 +44,8 @@ export type Unstable_ComposerInput = {
|
|
|
44
44
|
* state. Spread `unstable_useTriggerPopoverAriaProps()` onto your element for
|
|
45
45
|
* trigger-popover combobox semantics.
|
|
46
46
|
*
|
|
47
|
+
* @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release.
|
|
48
|
+
*
|
|
47
49
|
* @example
|
|
48
50
|
* ```tsx
|
|
49
51
|
* const { value, setText, send, isDisabled, canSend } = unstable_useComposerInput();
|
|
@@ -61,16 +63,17 @@ export type Unstable_ComposerInput = {
|
|
|
61
63
|
* ```
|
|
62
64
|
*/
|
|
63
65
|
export declare function unstable_useComposerInput(options?: Unstable_UseComposerInputOptions): Unstable_ComposerInput;
|
|
66
|
+
/** @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release. */
|
|
64
67
|
export type Unstable_TriggerPopoverAriaProps = TriggerPopoverAriaProps;
|
|
65
68
|
/**
|
|
66
|
-
* @deprecated Under active development and might change without notice.
|
|
67
|
-
*
|
|
68
69
|
* ARIA combobox attributes for the focused element (typically the composer
|
|
69
70
|
* input) describing the open trigger popover, per the WAI-ARIA editable
|
|
70
71
|
* combobox pattern. Returns an empty object outside a `TriggerPopoverRoot` or
|
|
71
72
|
* when no popover is open. Spread these last so they take precedence over any
|
|
72
73
|
* matching ARIA props you set yourself, mirroring `ComposerPrimitive.Input`.
|
|
73
74
|
*
|
|
75
|
+
* @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release.
|
|
76
|
+
*
|
|
74
77
|
* @example
|
|
75
78
|
* ```tsx
|
|
76
79
|
* const aria = unstable_useTriggerPopoverAriaProps();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useComposerInput.d.ts","sourceRoot":"","sources":["../../src/unstable/useComposerInput.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EACL,KAAK,uBAAuB,EAI7B,wDAAqD;AAEtD,MAAM,MAAM,gCAAgC,GAAG;IAC7C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;OAGG;IACH,IAAI,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC1C;;;OAGG;IACH,UAAU,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,CAAC,EAAE,gCAAgC,GACzC,sBAAsB,CA0BxB;AAED,MAAM,MAAM,gCAAgC,GAAG,uBAAuB,CAAC;AAEvE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mCAAmC,IAAI,gCAAgC,CAEtF"}
|
|
1
|
+
{"version":3,"file":"useComposerInput.d.ts","sourceRoot":"","sources":["../../src/unstable/useComposerInput.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EACL,KAAK,uBAAuB,EAI7B,wDAAqD;AAEtD,+GAA+G;AAC/G,MAAM,MAAM,gCAAgC,GAAG;IAC7C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAChC,CAAC;AAEF,+GAA+G;AAC/G,MAAM,MAAM,sBAAsB,GAAG;IACnC,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;OAGG;IACH,IAAI,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC1C;;;OAGG;IACH,UAAU,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,CAAC,EAAE,gCAAgC,GACzC,sBAAsB,CA0BxB;AAED,+GAA+G;AAC/G,MAAM,MAAM,gCAAgC,GAAG,uBAAuB,CAAC;AAEvE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mCAAmC,IAAI,gCAAgC,CAEtF"}
|
|
@@ -6,8 +6,6 @@ import { useAui } from "@assistant-ui/store";
|
|
|
6
6
|
import { flushTapSync } from "@assistant-ui/tap";
|
|
7
7
|
//#region src/unstable/useComposerInput.ts
|
|
8
8
|
/**
|
|
9
|
-
* @deprecated Under active development and might change without notice.
|
|
10
|
-
*
|
|
11
9
|
* Headless bridge to the composer's text value and send action, for building a
|
|
12
10
|
* custom composer input without `ComposerPrimitive.Input`. It is a thin bridge,
|
|
13
11
|
* not a second input: it does not own keyboard behavior, autosize, IME or
|
|
@@ -15,6 +13,8 @@ import { flushTapSync } from "@assistant-ui/tap";
|
|
|
15
13
|
* state. Spread `unstable_useTriggerPopoverAriaProps()` onto your element for
|
|
16
14
|
* trigger-popover combobox semantics.
|
|
17
15
|
*
|
|
16
|
+
* @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release.
|
|
17
|
+
*
|
|
18
18
|
* @example
|
|
19
19
|
* ```tsx
|
|
20
20
|
* const { value, setText, send, isDisabled, canSend } = unstable_useComposerInput();
|
|
@@ -55,14 +55,14 @@ function unstable_useComposerInput(options) {
|
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
|
-
* @deprecated Under active development and might change without notice.
|
|
59
|
-
*
|
|
60
58
|
* ARIA combobox attributes for the focused element (typically the composer
|
|
61
59
|
* input) describing the open trigger popover, per the WAI-ARIA editable
|
|
62
60
|
* combobox pattern. Returns an empty object outside a `TriggerPopoverRoot` or
|
|
63
61
|
* when no popover is open. Spread these last so they take precedence over any
|
|
64
62
|
* matching ARIA props you set yourself, mirroring `ComposerPrimitive.Input`.
|
|
65
63
|
*
|
|
64
|
+
* @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release.
|
|
65
|
+
*
|
|
66
66
|
* @example
|
|
67
67
|
* ```tsx
|
|
68
68
|
* const aria = unstable_useTriggerPopoverAriaProps();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useComposerInput.js","names":["useCallback","useAui","flushTapSync","useComposerSend","ComposerSendOptions","TriggerPopoverAriaProps","useComposerInputDisabled","useComposerInputValue","useTriggerPopoverAriaProps","Unstable_UseComposerInputOptions","disabled","Unstable_ComposerInput","value","setText","text","send","options","isDisabled","canSend","unstable_useComposerInput","aui","composer","getState","isEditing","rawSend","sendDisabled","sendOptions","Unstable_TriggerPopoverAriaProps","unstable_useTriggerPopoverAriaProps"],"sources":["../../src/unstable/useComposerInput.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { flushTapSync } from \"@assistant-ui/tap\";\nimport { useComposerSend } from \"@assistant-ui/core/react\";\nimport type { ComposerSendOptions } from \"@assistant-ui/core/store\";\nimport {\n type TriggerPopoverAriaProps,\n useComposerInputDisabled,\n useComposerInputValue,\n useTriggerPopoverAriaProps,\n} from \"../primitives/composer/useComposerInputState\";\n\nexport type Unstable_UseComposerInputOptions = {\n /**\n * Disables the input in addition to the composer's own disabled sources\n * (thread disabled, active dictation). When disabled, `isDisabled` is `true`\n * and `canSend` is `false`.\n */\n disabled?: boolean | undefined;\n};\n\nexport type Unstable_ComposerInput = {\n /** Current composer text, or `\"\"` when the composer is not editing. */\n value: string;\n /**\n * Writes `text` into the composer, mirroring `ComposerPrimitive.Input`:\n * a no-op unless the composer is editing; the controlled value stays in\n * sync within the same tick.\n */\n setText(text: string): void;\n /**\n * Sends the current message when `canSend` is `true`; otherwise a no-op.\n * Accepts the same options as the composer send action.\n */\n send(options?: ComposerSendOptions): void;\n /**\n * Whether the input is disabled, combining the `disabled` option with the\n * composer's own disabled sources (thread disabled, active dictation).\n */\n isDisabled: boolean;\n /**\n * Whether a send is currently available. Matches `ComposerPrimitive.Send`\n * gating (non-empty editing composer, not running without queue support) and\n * is additionally `false` while `isDisabled`.\n */\n canSend: boolean;\n};\n\n/**\n *
|
|
1
|
+
{"version":3,"file":"useComposerInput.js","names":["useCallback","useAui","flushTapSync","useComposerSend","ComposerSendOptions","TriggerPopoverAriaProps","useComposerInputDisabled","useComposerInputValue","useTriggerPopoverAriaProps","Unstable_UseComposerInputOptions","disabled","Unstable_ComposerInput","value","setText","text","send","options","isDisabled","canSend","unstable_useComposerInput","aui","composer","getState","isEditing","rawSend","sendDisabled","sendOptions","Unstable_TriggerPopoverAriaProps","unstable_useTriggerPopoverAriaProps"],"sources":["../../src/unstable/useComposerInput.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { flushTapSync } from \"@assistant-ui/tap\";\nimport { useComposerSend } from \"@assistant-ui/core/react\";\nimport type { ComposerSendOptions } from \"@assistant-ui/core/store\";\nimport {\n type TriggerPopoverAriaProps,\n useComposerInputDisabled,\n useComposerInputValue,\n useTriggerPopoverAriaProps,\n} from \"../primitives/composer/useComposerInputState\";\n\n/** @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_UseComposerInputOptions = {\n /**\n * Disables the input in addition to the composer's own disabled sources\n * (thread disabled, active dictation). When disabled, `isDisabled` is `true`\n * and `canSend` is `false`.\n */\n disabled?: boolean | undefined;\n};\n\n/** @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_ComposerInput = {\n /** Current composer text, or `\"\"` when the composer is not editing. */\n value: string;\n /**\n * Writes `text` into the composer, mirroring `ComposerPrimitive.Input`:\n * a no-op unless the composer is editing; the controlled value stays in\n * sync within the same tick.\n */\n setText(text: string): void;\n /**\n * Sends the current message when `canSend` is `true`; otherwise a no-op.\n * Accepts the same options as the composer send action.\n */\n send(options?: ComposerSendOptions): void;\n /**\n * Whether the input is disabled, combining the `disabled` option with the\n * composer's own disabled sources (thread disabled, active dictation).\n */\n isDisabled: boolean;\n /**\n * Whether a send is currently available. Matches `ComposerPrimitive.Send`\n * gating (non-empty editing composer, not running without queue support) and\n * is additionally `false` while `isDisabled`.\n */\n canSend: boolean;\n};\n\n/**\n * Headless bridge to the composer's text value and send action, for building a\n * custom composer input without `ComposerPrimitive.Input`. It is a thin bridge,\n * not a second input: it does not own keyboard behavior, autosize, IME or\n * contentEditable sync, paste/drop attachments, focus management, or rich-text\n * state. Spread `unstable_useTriggerPopoverAriaProps()` onto your element for\n * trigger-popover combobox semantics.\n *\n * @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release.\n *\n * @example\n * ```tsx\n * const { value, setText, send, isDisabled, canSend } = unstable_useComposerInput();\n * <textarea\n * value={value}\n * disabled={isDisabled}\n * onChange={(e) => setText(e.target.value)}\n * onKeyDown={(e) => {\n * if (e.key === \"Enter\" && !e.shiftKey && canSend) {\n * e.preventDefault();\n * send();\n * }\n * }}\n * />\n * ```\n */\nexport function unstable_useComposerInput(\n options?: Unstable_UseComposerInputOptions,\n): Unstable_ComposerInput {\n const aui = useAui();\n const value = useComposerInputValue();\n const isDisabled = useComposerInputDisabled(options?.disabled);\n\n const setText = useCallback(\n (text: string) => {\n if (!aui.composer.getState().isEditing) return;\n flushTapSync(() => {\n aui.composer.setText(text);\n });\n },\n [aui],\n );\n\n const { send: rawSend, disabled: sendDisabled } = useComposerSend();\n const canSend = !sendDisabled && !isDisabled;\n const send = useCallback(\n (sendOptions?: ComposerSendOptions) => {\n if (!canSend) return;\n rawSend(sendOptions);\n },\n [canSend, rawSend],\n );\n\n return { value, setText, send, isDisabled, canSend };\n}\n\n/** @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_TriggerPopoverAriaProps = TriggerPopoverAriaProps;\n\n/**\n * ARIA combobox attributes for the focused element (typically the composer\n * input) describing the open trigger popover, per the WAI-ARIA editable\n * combobox pattern. Returns an empty object outside a `TriggerPopoverRoot` or\n * when no popover is open. Spread these last so they take precedence over any\n * matching ARIA props you set yourself, mirroring `ComposerPrimitive.Input`.\n *\n * @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release.\n *\n * @example\n * ```tsx\n * const aria = unstable_useTriggerPopoverAriaProps();\n * <textarea {...aria} />\n * ```\n */\nexport function unstable_useTriggerPopoverAriaProps(): Unstable_TriggerPopoverAriaProps {\n return useTriggerPopoverAriaProps();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8EA,SAAgBmB,0BACdH,SACwB;CACxB,MAAMI,MAAMnB,OAAO;CACnB,MAAMW,QAAQL,sBAAsB;CACpC,MAAMU,aAAaX,yBAAyBU,SAASN,QAAQ;CAE7D,MAAMG,UAAUb,aACbc,SAAiB;EAChB,IAAI,CAACM,IAAIC,SAASC,SAAS,CAAC,CAACC,WAAW;EACxCrB,mBAAmB;GACjBkB,IAAIC,SAASR,QAAQC,IAAI;EAC3B,CAAC;CACH,GACA,CAACM,GAAG,CACN;CAEA,MAAM,EAAEL,MAAMS,SAASd,UAAUe,iBAAiBtB,gBAAgB;CAClE,MAAMe,UAAU,CAACO,gBAAgB,CAACR;CASlC,OAAO;EAAEL;EAAOC;EAASE,MARZf,aACV0B,gBAAsC;GACrC,IAAI,CAACR,SAAS;GACdM,QAAQE,WAAW;EACrB,GACA,CAACR,SAASM,OAAO,CAGMT;EAAME;EAAYC;CAAQ;AACrD;;;;;;;;;;;;;;;;AAoBA,SAAgBU,sCAAwE;CACtF,OAAOpB,2BAA2B;AACpC"}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { type KeyboardEventHandler } from "react";
|
|
2
|
+
/** @deprecated Experimental since 2026-06-11. Not scheduled for removal; the API may change in any release. */
|
|
2
3
|
export type Unstable_ComposerInputHistory = {
|
|
3
4
|
/** Keydown handler to spread onto `ComposerPrimitive.Input`. */
|
|
4
5
|
onKeyDown: KeyboardEventHandler<HTMLTextAreaElement>;
|
|
5
6
|
};
|
|
6
7
|
/**
|
|
7
|
-
* @deprecated Under active development and might change without notice.
|
|
8
|
-
*
|
|
9
8
|
* Terminal-style input history for the thread composer: ArrowUp on an
|
|
10
9
|
* empty draft recalls previously sent user messages (newest first),
|
|
11
10
|
* ArrowDown steps back toward the newest and finally restores the draft
|
|
@@ -17,6 +16,8 @@ export type Unstable_ComposerInputHistory = {
|
|
|
17
16
|
* to modifier keys, and to consumer handlers that already called
|
|
18
17
|
* `preventDefault`. It is inert on edit composers.
|
|
19
18
|
*
|
|
19
|
+
* @deprecated Experimental since 2026-06-11. Not scheduled for removal; the API may change in any release.
|
|
20
|
+
*
|
|
20
21
|
* @example
|
|
21
22
|
* ```tsx
|
|
22
23
|
* const history = unstable_useComposerInputHistory();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useComposerInputHistory.d.ts","sourceRoot":"","sources":["../../src/unstable/useComposerInputHistory.ts"],"names":[],"mappings":"AAEA,OAAO,EAML,KAAK,oBAAoB,EAC1B,MAAM,OAAO,CAAC;AAOf,MAAM,MAAM,6BAA6B,GAAG;IAC1C,gEAAgE;IAChE,SAAS,EAAE,oBAAoB,CAAC,mBAAmB,CAAC,CAAC;CACtD,CAAC;AA2BF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gCAAgC,IAAI,6BAA6B,CA6FhF"}
|
|
1
|
+
{"version":3,"file":"useComposerInputHistory.d.ts","sourceRoot":"","sources":["../../src/unstable/useComposerInputHistory.ts"],"names":[],"mappings":"AAEA,OAAO,EAML,KAAK,oBAAoB,EAC1B,MAAM,OAAO,CAAC;AAOf,+GAA+G;AAC/G,MAAM,MAAM,6BAA6B,GAAG;IAC1C,gEAAgE;IAChE,SAAS,EAAE,oBAAoB,CAAC,mBAAmB,CAAC,CAAC;CACtD,CAAC;AA2BF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gCAAgC,IAAI,6BAA6B,CA6FhF"}
|
|
@@ -20,8 +20,6 @@ const deriveHistory = (messages) => {
|
|
|
20
20
|
const isOnFirstLine = (value, caret) => !value.slice(0, caret).includes("\n");
|
|
21
21
|
const isOnLastLine = (value, caret) => !value.slice(caret).includes("\n");
|
|
22
22
|
/**
|
|
23
|
-
* @deprecated Under active development and might change without notice.
|
|
24
|
-
*
|
|
25
23
|
* Terminal-style input history for the thread composer: ArrowUp on an
|
|
26
24
|
* empty draft recalls previously sent user messages (newest first),
|
|
27
25
|
* ArrowDown steps back toward the newest and finally restores the draft
|
|
@@ -33,6 +31,8 @@ const isOnLastLine = (value, caret) => !value.slice(caret).includes("\n");
|
|
|
33
31
|
* to modifier keys, and to consumer handlers that already called
|
|
34
32
|
* `preventDefault`. It is inert on edit composers.
|
|
35
33
|
*
|
|
34
|
+
* @deprecated Experimental since 2026-06-11. Not scheduled for removal; the API may change in any release.
|
|
35
|
+
*
|
|
36
36
|
* @example
|
|
37
37
|
* ```tsx
|
|
38
38
|
* const history = unstable_useComposerInputHistory();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useComposerInputHistory.js","names":["useCallback","useEffect","useMemo","useRef","KeyboardEvent","KeyboardEventHandler","useAui","flushTapSync","ThreadMessage","getThreadMessageText","useTriggerPopoverRootContextOptional","Unstable_ComposerInputHistory","onKeyDown","HTMLTextAreaElement","BrowseState","cursor","draftSnapshot","lastRecalledText","deriveHistory","messages","entries","i","length","message","role","text","trim","push","isOnFirstLine","value","caret","slice","includes","isOnLastLine","unstable_useComposerInputHistory","aui","popoverCtx","browseRef","composer","getState","type","undefined","on","current","e","defaultPrevented","key","nativeEvent","isComposing","shiftKey","ctrlKey","metaKey","altKey","getActiveAria","textarea","currentTarget","selectionStart","selectionEnd","browse","commitText","setText","requestAnimationFrame","setSelectionRange","preventDefault","recall","history","entry","thread","next"],"sources":["../../src/unstable/useComposerInputHistory.ts"],"sourcesContent":["\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n type KeyboardEvent,\n type KeyboardEventHandler,\n} from \"react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { flushTapSync } from \"@assistant-ui/tap\";\nimport type { ThreadMessage } from \"@assistant-ui/core\";\nimport { getThreadMessageText } from \"@assistant-ui/core/internal\";\nimport { useTriggerPopoverRootContextOptional } from \"../primitives/composer/trigger/TriggerPopoverRootContext\";\n\nexport type Unstable_ComposerInputHistory = {\n /** Keydown handler to spread onto `ComposerPrimitive.Input`. */\n onKeyDown: KeyboardEventHandler<HTMLTextAreaElement>;\n};\n\ntype BrowseState = {\n cursor: number;\n draftSnapshot: string;\n lastRecalledText: string;\n};\n\nconst deriveHistory = (messages: readonly ThreadMessage[]): string[] => {\n const entries: string[] = [];\n for (let i = messages.length - 1; i >= 0; i--) {\n const message = messages[i]!;\n if (message.role !== \"user\") continue;\n const text = getThreadMessageText(message).trim();\n if (!text) continue;\n if (entries[entries.length - 1] === text) continue;\n entries.push(text);\n }\n return entries;\n};\n\nconst isOnFirstLine = (value: string, caret: number): boolean =>\n !value.slice(0, caret).includes(\"\\n\");\n\nconst isOnLastLine = (value: string, caret: number): boolean =>\n !value.slice(caret).includes(\"\\n\");\n\n/**\n *
|
|
1
|
+
{"version":3,"file":"useComposerInputHistory.js","names":["useCallback","useEffect","useMemo","useRef","KeyboardEvent","KeyboardEventHandler","useAui","flushTapSync","ThreadMessage","getThreadMessageText","useTriggerPopoverRootContextOptional","Unstable_ComposerInputHistory","onKeyDown","HTMLTextAreaElement","BrowseState","cursor","draftSnapshot","lastRecalledText","deriveHistory","messages","entries","i","length","message","role","text","trim","push","isOnFirstLine","value","caret","slice","includes","isOnLastLine","unstable_useComposerInputHistory","aui","popoverCtx","browseRef","composer","getState","type","undefined","on","current","e","defaultPrevented","key","nativeEvent","isComposing","shiftKey","ctrlKey","metaKey","altKey","getActiveAria","textarea","currentTarget","selectionStart","selectionEnd","browse","commitText","setText","requestAnimationFrame","setSelectionRange","preventDefault","recall","history","entry","thread","next"],"sources":["../../src/unstable/useComposerInputHistory.ts"],"sourcesContent":["\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n type KeyboardEvent,\n type KeyboardEventHandler,\n} from \"react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { flushTapSync } from \"@assistant-ui/tap\";\nimport type { ThreadMessage } from \"@assistant-ui/core\";\nimport { getThreadMessageText } from \"@assistant-ui/core/internal\";\nimport { useTriggerPopoverRootContextOptional } from \"../primitives/composer/trigger/TriggerPopoverRootContext\";\n\n/** @deprecated Experimental since 2026-06-11. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_ComposerInputHistory = {\n /** Keydown handler to spread onto `ComposerPrimitive.Input`. */\n onKeyDown: KeyboardEventHandler<HTMLTextAreaElement>;\n};\n\ntype BrowseState = {\n cursor: number;\n draftSnapshot: string;\n lastRecalledText: string;\n};\n\nconst deriveHistory = (messages: readonly ThreadMessage[]): string[] => {\n const entries: string[] = [];\n for (let i = messages.length - 1; i >= 0; i--) {\n const message = messages[i]!;\n if (message.role !== \"user\") continue;\n const text = getThreadMessageText(message).trim();\n if (!text) continue;\n if (entries[entries.length - 1] === text) continue;\n entries.push(text);\n }\n return entries;\n};\n\nconst isOnFirstLine = (value: string, caret: number): boolean =>\n !value.slice(0, caret).includes(\"\\n\");\n\nconst isOnLastLine = (value: string, caret: number): boolean =>\n !value.slice(caret).includes(\"\\n\");\n\n/**\n * Terminal-style input history for the thread composer: ArrowUp on an\n * empty draft recalls previously sent user messages (newest first),\n * ArrowDown steps back toward the newest and finally restores the draft\n * that was being typed when browsing started.\n *\n * Recall only triggers when the caret is on the first/last line with no\n * selection, so multi-line editing keeps native arrow behavior. The\n * handler yields to an open mention/slash popover, to IME composition,\n * to modifier keys, and to consumer handlers that already called\n * `preventDefault`. It is inert on edit composers.\n *\n * @deprecated Experimental since 2026-06-11. Not scheduled for removal; the API may change in any release.\n *\n * @example\n * ```tsx\n * const history = unstable_useComposerInputHistory();\n * <ComposerPrimitive.Input {...history} />\n * ```\n */\nexport function unstable_useComposerInputHistory(): Unstable_ComposerInputHistory {\n const aui = useAui();\n const popoverCtx = useTriggerPopoverRootContextOptional();\n const browseRef = useRef<BrowseState | null>(null);\n\n useEffect(() => {\n if (aui.composer.getState().type !== \"thread\") return undefined;\n\n return aui.on(\"threads.selectionChanged\", () => {\n browseRef.current = null;\n });\n }, [aui]);\n\n const onKeyDown = useCallback(\n (e: KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.defaultPrevented) return;\n if (e.key !== \"ArrowUp\" && e.key !== \"ArrowDown\") return;\n if (e.nativeEvent.isComposing) return;\n if (e.shiftKey || e.ctrlKey || e.metaKey || e.altKey) return;\n if (popoverCtx && popoverCtx.getActiveAria() !== null) return;\n if (aui.composer.getState().type !== \"thread\") return;\n\n const textarea = e.currentTarget;\n const { selectionStart, selectionEnd, value } = textarea;\n if (selectionStart !== selectionEnd) return;\n\n if (browseRef.current && value !== browseRef.current.lastRecalledText) {\n browseRef.current = null;\n }\n const browse = browseRef.current;\n\n const commitText = (text: string): void => {\n flushTapSync(() => aui.composer.setText(text));\n // React's controlled-value commit restores the pre-recall caret;\n // reposition after the commit, before paint.\n requestAnimationFrame(() => {\n textarea.setSelectionRange(text.length, text.length);\n });\n e.preventDefault();\n };\n\n const recall = (\n history: readonly string[],\n cursor: number,\n draftSnapshot: string,\n ): void => {\n const entry = history[cursor];\n if (entry === undefined) {\n e.preventDefault();\n return;\n }\n browseRef.current = { cursor, draftSnapshot, lastRecalledText: entry };\n commitText(entry);\n };\n\n if (e.key === \"ArrowUp\") {\n if (!isOnFirstLine(value, selectionStart)) return;\n\n if (!browse) {\n if (value.trim() !== \"\") return;\n const history = deriveHistory(aui.thread.getState().messages);\n if (history.length === 0) return;\n recall(history, 0, value);\n return;\n }\n\n const history = deriveHistory(aui.thread.getState().messages);\n const next = browse.cursor + 1;\n if (next >= history.length) {\n e.preventDefault();\n return;\n }\n recall(history, next, browse.draftSnapshot);\n return;\n }\n\n if (!browse) return;\n if (!isOnLastLine(value, selectionEnd)) return;\n\n const next = browse.cursor - 1;\n if (next < 0) {\n browseRef.current = null;\n commitText(browse.draftSnapshot);\n return;\n }\n\n const history = deriveHistory(aui.thread.getState().messages);\n recall(history, next, browse.draftSnapshot);\n },\n [aui, popoverCtx],\n );\n\n return useMemo(() => ({ onKeyDown }), [onKeyDown]);\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Unstable_TriggerAdapter, Unstable_TriggerItem } from "@assistant-ui/core";
|
|
2
|
+
/** @deprecated Experimental since 2026-06-15. Not scheduled for removal; the API may change in any release. */
|
|
2
3
|
export type Unstable_UseLiveCompletionAdapterOptions = {
|
|
3
4
|
/**
|
|
4
5
|
* Fetches the items for a query from an async source. Called debounced; the
|
|
@@ -17,8 +18,6 @@ export type Unstable_UseLiveCompletionAdapterOptions = {
|
|
|
17
18
|
readonly enabled?: boolean | undefined;
|
|
18
19
|
};
|
|
19
20
|
/**
|
|
20
|
-
* @deprecated Under active development and may change without notice.
|
|
21
|
-
*
|
|
22
21
|
* Bridges an async completion source (a server search, a gateway RPC) into the
|
|
23
22
|
* synchronous `Unstable_TriggerAdapter` that `ComposerTriggerPopover` consumes.
|
|
24
23
|
* `search(query)` returns the last fetched items synchronously and schedules a
|
|
@@ -29,6 +28,8 @@ export type Unstable_UseLiveCompletionAdapterOptions = {
|
|
|
29
28
|
* `isLoading` is `true` while a fetch is in flight. Pass it to the popover's
|
|
30
29
|
* `isLoading` prop to render a loading state.
|
|
31
30
|
*
|
|
31
|
+
* @deprecated Experimental since 2026-06-15. Not scheduled for removal; the API may change in any release.
|
|
32
|
+
*
|
|
32
33
|
* @example
|
|
33
34
|
* ```tsx
|
|
34
35
|
* const mentions = unstable_useLiveCompletionAdapter({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLiveCompletionAdapter.d.ts","sourceRoot":"","sources":["../../src/unstable/useLiveCompletionAdapter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useLiveCompletionAdapter.d.ts","sourceRoot":"","sources":["../../src/unstable/useLiveCompletionAdapter.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,uBAAuB,EACvB,oBAAoB,EACrB,MAAM,oBAAoB,CAAC;AAE5B,+GAA+G;AAC/G,MAAM,MAAM,wCAAwC,GAAG;IACrD;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,oBAAoB,EAAE,CAAC,CAAC;IAC9E;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAChD,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,qFAAqF;IACrF,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACxC,CAAC;AAKF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,wCAAwC,GAChD;IAAE,OAAO,EAAE,uBAAuB,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAiK1D"}
|