@assistant-ui/react-native 0.1.30 → 0.1.31

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.
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadMessages.js","names":["ComponentType","FC","ReactNode","forwardRef","memo","useCallback","FlatList","FlatListProps","MessageState","ThreadMessage","RenderChildrenWithAccessor","useAuiState","MessageByIndexProvider","MessageComponents","Message","EditComposer","UserEditComposer","AssistantEditComposer","SystemEditComposer","UserMessage","AssistantMessage","SystemMessage","MessagesContent","components","children","value","message","ThreadMessagesProps","Omit","DEFAULT_SYSTEM_MESSAGE","getComponent","role","isEditing","_exhaustiveCheck","Error","ThreadMessageComponent","t0","$","_c","_temp","_temp2","t1","Component","t2","ThreadMessageByIndex","index","prev","next","displayName","ThreadMessageByChildren","aui","thread","getState","getItem","ThreadMessages","ref","flatListProps","messages","_temp3","renderItem","keyExtractor","_temp4","t3","s","s_0","composer","item","id"],"sources":["../../../src/primitives/thread/ThreadMessages.tsx"],"sourcesContent":["import {\n type ComponentType,\n type FC,\n type ReactNode,\n forwardRef,\n memo,\n useCallback,\n} from \"react\";\nimport { FlatList, type FlatListProps } from \"react-native\";\nimport type { MessageState, ThreadMessage } from \"@assistant-ui/core\";\nimport { RenderChildrenWithAccessor, useAuiState } from \"@assistant-ui/store\";\nimport { MessageByIndexProvider } from \"@assistant-ui/core/react\";\n\ntype MessageComponents =\n | {\n Message: ComponentType;\n EditComposer?: ComponentType | undefined;\n UserEditComposer?: ComponentType | undefined;\n AssistantEditComposer?: ComponentType | undefined;\n SystemEditComposer?: ComponentType | undefined;\n UserMessage?: ComponentType | undefined;\n AssistantMessage?: ComponentType | undefined;\n SystemMessage?: ComponentType | undefined;\n }\n | {\n Message?: ComponentType | undefined;\n EditComposer?: ComponentType | undefined;\n UserEditComposer?: ComponentType | undefined;\n AssistantEditComposer?: ComponentType | undefined;\n SystemEditComposer?: ComponentType | undefined;\n UserMessage: ComponentType;\n AssistantMessage: ComponentType;\n SystemMessage?: ComponentType | undefined;\n };\n\ntype MessagesContent =\n | {\n /** @deprecated Use the children render function instead. */\n components: MessageComponents;\n children?: never;\n }\n | {\n children: (value: { message: MessageState }) => ReactNode;\n components?: never;\n };\n\nexport type ThreadMessagesProps = Omit<\n FlatListProps<ThreadMessage>,\n \"data\" | \"renderItem\" | \"children\"\n> &\n MessagesContent;\n\nconst DEFAULT_SYSTEM_MESSAGE = () => null;\n\nconst getComponent = (\n components: MessageComponents,\n role: ThreadMessage[\"role\"],\n isEditing: boolean,\n) => {\n switch (role) {\n case \"user\":\n if (isEditing) {\n return (\n components.UserEditComposer ??\n components.EditComposer ??\n components.UserMessage ??\n (components.Message as ComponentType)\n );\n } else {\n return components.UserMessage ?? (components.Message as ComponentType);\n }\n case \"assistant\":\n if (isEditing) {\n return (\n components.AssistantEditComposer ??\n components.EditComposer ??\n components.AssistantMessage ??\n (components.Message as ComponentType)\n );\n } else {\n return (\n components.AssistantMessage ?? (components.Message as ComponentType)\n );\n }\n case \"system\":\n if (isEditing) {\n return (\n components.SystemEditComposer ??\n components.EditComposer ??\n components.SystemMessage ??\n (components.Message as ComponentType)\n );\n } else {\n return (\n components.SystemMessage ??\n (components.Message as ComponentType) ??\n DEFAULT_SYSTEM_MESSAGE\n );\n }\n default: {\n const _exhaustiveCheck: never = role;\n throw new Error(`Unknown message role: ${_exhaustiveCheck}`);\n }\n }\n};\n\nconst ThreadMessageComponent: FC<{ components: MessageComponents }> = ({\n components,\n}) => {\n const role = useAuiState((s) => s.message.role);\n const isEditing = useAuiState((s) => s.message.composer.isEditing);\n const Component = getComponent(components, role, isEditing);\n\n return <Component />;\n};\n\nconst ThreadMessageByIndex = memo(\n ({ index, components }: { index: number; components: MessageComponents }) => {\n return (\n <MessageByIndexProvider index={index}>\n <ThreadMessageComponent components={components} />\n </MessageByIndexProvider>\n );\n },\n (prev, next) =>\n prev.index === next.index && prev.components === next.components,\n);\nThreadMessageByIndex.displayName = \"ThreadPrimitive.MessageByIndex\";\n\nconst ThreadMessageByChildren = memo(\n ({\n index,\n children,\n }: {\n index: number;\n children: (value: { message: MessageState }) => ReactNode;\n }) => {\n return (\n <MessageByIndexProvider index={index}>\n <RenderChildrenWithAccessor\n getItemState={(aui) => aui.thread().message({ index }).getState()}\n >\n {(getItem) =>\n children({\n get message() {\n return getItem();\n },\n })\n }\n </RenderChildrenWithAccessor>\n </MessageByIndexProvider>\n );\n },\n (prev, next) => prev.index === next.index && prev.children === next.children,\n);\nThreadMessageByChildren.displayName = \"ThreadPrimitive.MessageByChildren\";\n\nexport const ThreadMessages = forwardRef<\n FlatList<ThreadMessage>,\n ThreadMessagesProps\n>(({ components, children, ...flatListProps }, ref) => {\n const messages = useAuiState((s) => s.thread.messages);\n\n const renderItem = useCallback(\n ({ index }: { item: ThreadMessage; index: number }) => {\n if (children) {\n return (\n <ThreadMessageByChildren index={index}>\n {children}\n </ThreadMessageByChildren>\n );\n }\n return <ThreadMessageByIndex index={index} components={components!} />;\n },\n [components, children],\n );\n\n const keyExtractor = useCallback((item: ThreadMessage) => item.id, []);\n\n return (\n <FlatList\n ref={ref}\n data={messages as unknown as ThreadMessage[]}\n renderItem={renderItem}\n keyExtractor={keyExtractor}\n {...flatListProps}\n />\n );\n});\nThreadMessages.displayName = \"ThreadPrimitive.Messages\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"ThreadMessages.js","names":["ComponentType","FC","ForwardedRef","ReactNode","RefObject","forwardRef","memo","useCallback","useEffect","useRef","FlatList","FlatListProps","LayoutChangeEvent","NativeScrollEvent","NativeSyntheticEvent","MessageState","ThreadMessage","RenderChildrenWithAccessor","useAuiEvent","useAuiState","MessageByIndexProvider","MessageComponents","Message","EditComposer","UserEditComposer","AssistantEditComposer","SystemEditComposer","UserMessage","AssistantMessage","SystemMessage","MessagesContent","components","children","value","message","ThreadMessagesFlatListProps","Omit","autoScroll","scrollToBottomOnRunStart","scrollToBottomOnInitialize","scrollToBottomOnThreadSwitch","ThreadMessagesProps","DEFAULT_SYSTEM_MESSAGE","AT_BOTTOM_THRESHOLD","getComponent","role","isEditing","_exhaustiveCheck","Error","ThreadMessageComponent","t0","$","_c","_temp","_temp2","t1","Component","t2","ThreadMessageByIndex","index","prev","next","displayName","ThreadMessageByChildren","aui","thread","getState","getItem","setForwardedRef","ref","T","current","useComposedFlatListRef","forwardedRef","flatListRef","node","setFlatListRef","const","useThreadMessagesFlatListAutoScroll","hasMessages","t3","t4","undefined","t5","Symbol","for","contentHeight","viewportHeight","scrollY","metricsRef","isAtBottomRef","lastScrollEventYRef","initializeScrollRequestedRef","pendingScrollToBottomRef","t6","updateIsAtBottom","t7","animated","contentHeight_0","viewportHeight_0","Math","max","scrollToEnd","scrollToBottom","t8","event","nativeEvent","layout","height","handleLayout","t9","event_0","contentOffset","contentSize","layoutMeasurement","previousEventY","wasPinnedToBottom","y","upwardMove","handleScroll","t10","_width","metrics","previousContentHeight","wasAtBottom","pendingScroll","handleContentSizeChange","t11","t12","t13","t14","t15","ThreadMessagesFlatList","flatListProps","onContentSizeChange","onLayout","onScroll","scrollEventThrottle","messages","_temp3","length","handleAutoScrollContentSizeChange","handleAutoScrollLayout","handleAutoScrollScroll","renderItem","keyExtractor","_temp4","scrollTracking","width","ThreadMessages","props","s","s_0","composer","item","id"],"sources":["../../../src/primitives/thread/ThreadMessages.tsx"],"sourcesContent":["import {\n type ComponentType,\n type FC,\n type ForwardedRef,\n type ReactNode,\n type RefObject,\n forwardRef,\n memo,\n useCallback,\n useEffect,\n useRef,\n} from \"react\";\nimport {\n FlatList,\n type FlatListProps,\n type LayoutChangeEvent,\n type NativeScrollEvent,\n type NativeSyntheticEvent,\n} from \"react-native\";\nimport type { MessageState, ThreadMessage } from \"@assistant-ui/core\";\nimport {\n RenderChildrenWithAccessor,\n useAuiEvent,\n useAuiState,\n} from \"@assistant-ui/store\";\nimport { MessageByIndexProvider } from \"@assistant-ui/core/react\";\n\ntype MessageComponents =\n | {\n Message: ComponentType;\n EditComposer?: ComponentType | undefined;\n UserEditComposer?: ComponentType | undefined;\n AssistantEditComposer?: ComponentType | undefined;\n SystemEditComposer?: ComponentType | undefined;\n UserMessage?: ComponentType | undefined;\n AssistantMessage?: ComponentType | undefined;\n SystemMessage?: ComponentType | undefined;\n }\n | {\n Message?: ComponentType | undefined;\n EditComposer?: ComponentType | undefined;\n UserEditComposer?: ComponentType | undefined;\n AssistantEditComposer?: ComponentType | undefined;\n SystemEditComposer?: ComponentType | undefined;\n UserMessage: ComponentType;\n AssistantMessage: ComponentType;\n SystemMessage?: ComponentType | undefined;\n };\n\ntype MessagesContent =\n | {\n /** @deprecated Use the children render function instead. */\n components: MessageComponents;\n children?: never;\n }\n | {\n children: (value: { message: MessageState }) => ReactNode;\n components?: never;\n };\n\nexport type ThreadMessagesFlatListProps = Omit<\n FlatListProps<ThreadMessage>,\n \"data\" | \"renderItem\" | \"children\"\n> &\n MessagesContent & {\n autoScroll?: boolean | undefined;\n scrollToBottomOnRunStart?: boolean | undefined;\n scrollToBottomOnInitialize?: boolean | undefined;\n scrollToBottomOnThreadSwitch?: boolean | undefined;\n };\n\n/** @deprecated Use ThreadMessagesFlatListProps instead. */\nexport type ThreadMessagesProps = ThreadMessagesFlatListProps;\n\nconst DEFAULT_SYSTEM_MESSAGE = () => null;\nconst AT_BOTTOM_THRESHOLD = 4;\n\nconst getComponent = (\n components: MessageComponents,\n role: ThreadMessage[\"role\"],\n isEditing: boolean,\n) => {\n switch (role) {\n case \"user\":\n if (isEditing) {\n return (\n components.UserEditComposer ??\n components.EditComposer ??\n components.UserMessage ??\n (components.Message as ComponentType)\n );\n } else {\n return components.UserMessage ?? (components.Message as ComponentType);\n }\n case \"assistant\":\n if (isEditing) {\n return (\n components.AssistantEditComposer ??\n components.EditComposer ??\n components.AssistantMessage ??\n (components.Message as ComponentType)\n );\n } else {\n return (\n components.AssistantMessage ?? (components.Message as ComponentType)\n );\n }\n case \"system\":\n if (isEditing) {\n return (\n components.SystemEditComposer ??\n components.EditComposer ??\n components.SystemMessage ??\n (components.Message as ComponentType)\n );\n } else {\n return (\n components.SystemMessage ??\n (components.Message as ComponentType) ??\n DEFAULT_SYSTEM_MESSAGE\n );\n }\n default: {\n const _exhaustiveCheck: never = role;\n throw new Error(`Unknown message role: ${_exhaustiveCheck}`);\n }\n }\n};\n\nconst ThreadMessageComponent: FC<{ components: MessageComponents }> = ({\n components,\n}) => {\n const role = useAuiState((s) => s.message.role);\n const isEditing = useAuiState((s) => s.message.composer.isEditing);\n const Component = getComponent(components, role, isEditing);\n\n return <Component />;\n};\n\nconst ThreadMessageByIndex = memo(\n ({ index, components }: { index: number; components: MessageComponents }) => {\n return (\n <MessageByIndexProvider index={index}>\n <ThreadMessageComponent components={components} />\n </MessageByIndexProvider>\n );\n },\n (prev, next) =>\n prev.index === next.index && prev.components === next.components,\n);\nThreadMessageByIndex.displayName = \"ThreadPrimitive.MessageByIndex\";\n\nconst ThreadMessageByChildren = memo(\n ({\n index,\n children,\n }: {\n index: number;\n children: (value: { message: MessageState }) => ReactNode;\n }) => {\n return (\n <MessageByIndexProvider index={index}>\n <RenderChildrenWithAccessor\n getItemState={(aui) => aui.thread().message({ index }).getState()}\n >\n {(getItem) =>\n children({\n get message() {\n return getItem();\n },\n })\n }\n </RenderChildrenWithAccessor>\n </MessageByIndexProvider>\n );\n },\n (prev, next) => prev.index === next.index && prev.children === next.children,\n);\nThreadMessageByChildren.displayName = \"ThreadPrimitive.MessageByChildren\";\n\nconst setForwardedRef = <T,>(ref: ForwardedRef<T>, value: T | null) => {\n if (typeof ref === \"function\") {\n ref(value);\n } else if (ref) {\n ref.current = value;\n }\n};\n\nconst useComposedFlatListRef = (\n forwardedRef: ForwardedRef<FlatList<ThreadMessage>>,\n) => {\n const flatListRef = useRef<FlatList<ThreadMessage> | null>(null);\n\n const setFlatListRef = useCallback(\n (node: FlatList<ThreadMessage> | null) => {\n flatListRef.current = node;\n setForwardedRef(forwardedRef, node);\n },\n [forwardedRef],\n );\n\n return [flatListRef, setFlatListRef] as const;\n};\n\nconst useThreadMessagesFlatListAutoScroll = ({\n flatListRef,\n hasMessages,\n autoScroll = true,\n scrollToBottomOnRunStart = true,\n scrollToBottomOnInitialize = true,\n scrollToBottomOnThreadSwitch = true,\n}: {\n flatListRef: RefObject<FlatList<ThreadMessage> | null>;\n hasMessages: boolean;\n autoScroll?: boolean | undefined;\n scrollToBottomOnRunStart?: boolean | undefined;\n scrollToBottomOnInitialize?: boolean | undefined;\n scrollToBottomOnThreadSwitch?: boolean | undefined;\n}) => {\n const metricsRef = useRef({\n contentHeight: 0,\n viewportHeight: 0,\n scrollY: 0,\n });\n const isAtBottomRef = useRef(true);\n const lastScrollEventYRef = useRef(0);\n const initializeScrollRequestedRef = useRef(false);\n const pendingScrollToBottomRef = useRef<false | { animated: boolean }>(false);\n\n const updateIsAtBottom = useCallback(() => {\n const { contentHeight, scrollY, viewportHeight } = metricsRef.current;\n isAtBottomRef.current =\n contentHeight <= viewportHeight ||\n contentHeight - scrollY - viewportHeight <= AT_BOTTOM_THRESHOLD;\n }, []);\n\n // Commanding a scroll records the intended position immediately; the\n // native scroll echo is bridged and throttled, so waiting for it lets a\n // fast stream observe stale metrics and drop out of following.\n const scrollToBottom = useCallback(\n (animated: boolean) => {\n const { contentHeight, viewportHeight } = metricsRef.current;\n metricsRef.current.scrollY = Math.max(0, contentHeight - viewportHeight);\n isAtBottomRef.current = true;\n flatListRef.current?.scrollToEnd({ animated });\n },\n [flatListRef],\n );\n\n const handleLayout = useCallback(\n (event: LayoutChangeEvent) => {\n metricsRef.current.viewportHeight = event.nativeEvent.layout.height;\n updateIsAtBottom();\n },\n [updateIsAtBottom],\n );\n\n const handleScroll = useCallback(\n (event: NativeSyntheticEvent<NativeScrollEvent>) => {\n const { contentOffset, contentSize, layoutMeasurement } =\n event.nativeEvent;\n const previousEventY = lastScrollEventYRef.current;\n const wasPinnedToBottom = isAtBottomRef.current;\n lastScrollEventYRef.current = contentOffset.y;\n metricsRef.current = {\n contentHeight: contentSize.height,\n viewportHeight: layoutMeasurement.height,\n scrollY: contentOffset.y,\n };\n updateIsAtBottom();\n const upwardMove = contentOffset.y < previousEventY;\n // Only a deliberate upward move unpins or cancels a pending scroll.\n // Gestures are detected echo-to-echo because a commanded scroll\n // optimistically moves the tracked position ahead of its ascending\n // animation echoes, and those echoes must not unpin mid-flight.\n if (wasPinnedToBottom && !upwardMove) {\n isAtBottomRef.current = true;\n }\n if (!isAtBottomRef.current && upwardMove) {\n pendingScrollToBottomRef.current = false;\n }\n },\n [updateIsAtBottom],\n );\n\n const handleContentSizeChange = useCallback(\n (_width: number, height: number) => {\n const metrics = metricsRef.current;\n const previousContentHeight = metrics.contentHeight;\n const wasAtBottom = isAtBottomRef.current;\n metrics.contentHeight = height;\n updateIsAtBottom();\n\n // FlatList.scrollToEnd is a no-op before the list has measured, so the\n // initialize and thread-switch scrolls land on the next content-size\n // event, once real metrics exist.\n const pendingScroll = pendingScrollToBottomRef.current;\n if (pendingScroll) {\n pendingScrollToBottomRef.current = false;\n scrollToBottom(pendingScroll.animated);\n return;\n }\n\n if (!autoScroll) return;\n if (!wasAtBottom) return;\n if (previousContentHeight === 0) return;\n if (height <= previousContentHeight) return;\n\n scrollToBottom(false);\n },\n [autoScroll, scrollToBottom, updateIsAtBottom],\n );\n\n useEffect(() => {\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 pendingScrollToBottomRef.current = { animated: false };\n scrollToBottom(false);\n }, [hasMessages, scrollToBottom, scrollToBottomOnInitialize]);\n\n useAuiEvent(\"thread.runStart\", () => {\n if (!scrollToBottomOnRunStart) return;\n pendingScrollToBottomRef.current = { animated: true };\n scrollToBottom(true);\n });\n\n useAuiEvent(\"threadListItem.switchedTo\", () => {\n if (!scrollToBottomOnThreadSwitch) return;\n initializeScrollRequestedRef.current = false;\n lastScrollEventYRef.current = 0;\n pendingScrollToBottomRef.current = { animated: false };\n scrollToBottom(false);\n });\n\n return {\n handleLayout,\n handleScroll,\n handleContentSizeChange,\n };\n};\n\nexport const ThreadMessagesFlatList = forwardRef<\n FlatList<ThreadMessage>,\n ThreadMessagesFlatListProps\n>(\n (\n {\n autoScroll,\n components,\n children,\n onContentSizeChange,\n onLayout,\n onScroll,\n scrollEventThrottle,\n scrollToBottomOnInitialize,\n scrollToBottomOnRunStart,\n scrollToBottomOnThreadSwitch,\n ...flatListProps\n },\n forwardedRef,\n ) => {\n const messages = useAuiState((s) => s.thread.messages);\n const [flatListRef, setFlatListRef] = useComposedFlatListRef(forwardedRef);\n const {\n handleContentSizeChange: handleAutoScrollContentSizeChange,\n handleLayout: handleAutoScrollLayout,\n handleScroll: handleAutoScrollScroll,\n } = useThreadMessagesFlatListAutoScroll({\n flatListRef,\n hasMessages: messages.length > 0,\n autoScroll,\n scrollToBottomOnInitialize,\n scrollToBottomOnRunStart,\n scrollToBottomOnThreadSwitch,\n });\n\n const renderItem = useCallback(\n ({ index }: { item: ThreadMessage; index: number }) => {\n if (children) {\n return (\n <ThreadMessageByChildren index={index}>\n {children}\n </ThreadMessageByChildren>\n );\n }\n return <ThreadMessageByIndex index={index} components={components!} />;\n },\n [components, children],\n );\n\n const keyExtractor = useCallback((item: ThreadMessage) => item.id, []);\n\n const scrollTracking =\n (autoScroll ?? true) ||\n (scrollToBottomOnInitialize ?? true) ||\n (scrollToBottomOnRunStart ?? true) ||\n (scrollToBottomOnThreadSwitch ?? true);\n\n const handleLayout = useCallback(\n (event: LayoutChangeEvent) => {\n handleAutoScrollLayout(event);\n onLayout?.(event);\n },\n [handleAutoScrollLayout, onLayout],\n );\n\n const handleScroll = useCallback(\n (event: NativeSyntheticEvent<NativeScrollEvent>) => {\n handleAutoScrollScroll(event);\n onScroll?.(event);\n },\n [handleAutoScrollScroll, onScroll],\n );\n\n const handleContentSizeChange = useCallback(\n (width: number, height: number) => {\n handleAutoScrollContentSizeChange(width, height);\n onContentSizeChange?.(width, height);\n },\n [handleAutoScrollContentSizeChange, onContentSizeChange],\n );\n\n return (\n <FlatList\n ref={setFlatListRef}\n data={messages as unknown as ThreadMessage[]}\n renderItem={renderItem}\n keyExtractor={keyExtractor}\n {...(scrollTracking\n ? {\n onContentSizeChange: handleContentSizeChange,\n onLayout: handleLayout,\n onScroll: handleScroll,\n scrollEventThrottle: scrollEventThrottle ?? 16,\n }\n : {\n ...(onContentSizeChange && { onContentSizeChange }),\n ...(onLayout && { onLayout }),\n ...(onScroll && { onScroll }),\n ...(scrollEventThrottle !== undefined && { scrollEventThrottle }),\n })}\n {...flatListProps}\n />\n );\n },\n);\nThreadMessagesFlatList.displayName = \"ThreadPrimitive.MessagesFlatList\";\n\n/** @deprecated Use ThreadPrimitive.MessagesFlatList instead. */\nexport const ThreadMessages = forwardRef<\n FlatList<ThreadMessage>,\n ThreadMessagesProps\n>(\n (\n {\n autoScroll = false,\n scrollToBottomOnInitialize = false,\n scrollToBottomOnRunStart = false,\n scrollToBottomOnThreadSwitch = false,\n ...props\n },\n ref,\n ) => (\n <ThreadMessagesFlatList\n ref={ref}\n autoScroll={autoScroll}\n scrollToBottomOnInitialize={scrollToBottomOnInitialize}\n scrollToBottomOnRunStart={scrollToBottomOnRunStart}\n scrollToBottomOnThreadSwitch={scrollToBottomOnThreadSwitch}\n {...props}\n />\n ),\n);\nThreadMessages.displayName = \"ThreadPrimitive.Messages\";\n"],"mappings":"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"}
@@ -1,12 +1,12 @@
1
1
  import { ThreadRoot, ThreadRootProps } from "./thread/ThreadRoot.js";
2
- import { ThreadMessages, ThreadMessagesProps } from "./thread/ThreadMessages.js";
2
+ import { ThreadMessages, ThreadMessagesFlatList, ThreadMessagesFlatListProps, ThreadMessagesProps } from "./thread/ThreadMessages.js";
3
3
  import { ThreadEmpty, ThreadEmptyProps } from "./thread/ThreadEmpty.js";
4
4
  import { ThreadIf, ThreadIfProps } from "./thread/ThreadIf.js";
5
5
  import { ThreadSuggestion, ThreadSuggestionProps } from "./thread/ThreadSuggestion.js";
6
6
  import { ThreadPrimitiveMessageByIndex as MessageByIndex, ThreadPrimitiveSuggestionByIndex as SuggestionByIndex, ThreadPrimitiveSuggestions as Suggestions, ThreadPrimitiveUnstable_MessageById as Unstable_MessageById } from "@assistant-ui/core/react";
7
7
  declare namespace thread_d_exports {
8
- export { ThreadEmpty as Empty, ThreadEmptyProps as EmptyProps, ThreadIf as If, ThreadIfProps as IfProps, MessageByIndex, ThreadMessages as Messages, ThreadMessagesProps as MessagesProps, ThreadRoot as Root, ThreadRootProps as RootProps, ThreadSuggestion as Suggestion, SuggestionByIndex, ThreadSuggestionProps as SuggestionProps, Suggestions, Unstable_MessageById };
8
+ export { ThreadEmpty as Empty, ThreadEmptyProps as EmptyProps, ThreadIf as If, ThreadIfProps as IfProps, MessageByIndex, ThreadMessages as Messages, ThreadMessagesFlatList as MessagesFlatList, ThreadMessagesFlatListProps as MessagesFlatListProps, ThreadMessagesProps as MessagesProps, ThreadRoot as Root, ThreadRootProps as RootProps, ThreadSuggestion as Suggestion, SuggestionByIndex, ThreadSuggestionProps as SuggestionProps, Suggestions, Unstable_MessageById };
9
9
  }
10
10
  //#endregion
11
- export { ThreadEmpty as Empty, type ThreadEmptyProps as EmptyProps, ThreadIf as If, type ThreadIfProps as IfProps, MessageByIndex, ThreadMessages as Messages, type ThreadMessagesProps as MessagesProps, ThreadRoot as Root, type ThreadRootProps as RootProps, ThreadSuggestion as Suggestion, SuggestionByIndex, type ThreadSuggestionProps as SuggestionProps, Suggestions, Unstable_MessageById, thread_d_exports };
11
+ export { ThreadEmpty as Empty, type ThreadEmptyProps as EmptyProps, ThreadIf as If, type ThreadIfProps as IfProps, MessageByIndex, ThreadMessages as Messages, ThreadMessagesFlatList as MessagesFlatList, type ThreadMessagesFlatListProps as MessagesFlatListProps, type ThreadMessagesProps as MessagesProps, ThreadRoot as Root, type ThreadRootProps as RootProps, ThreadSuggestion as Suggestion, SuggestionByIndex, type ThreadSuggestionProps as SuggestionProps, Suggestions, Unstable_MessageById, thread_d_exports };
12
12
  //# sourceMappingURL=thread.d.ts.map
@@ -1,6 +1,6 @@
1
1
  import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
2
  import { ThreadRoot } from "./thread/ThreadRoot.js";
3
- import { ThreadMessages } from "./thread/ThreadMessages.js";
3
+ import { ThreadMessages, ThreadMessagesFlatList } from "./thread/ThreadMessages.js";
4
4
  import { ThreadEmpty } from "./thread/ThreadEmpty.js";
5
5
  import { ThreadIf } from "./thread/ThreadIf.js";
6
6
  import { ThreadSuggestion } from "./thread/ThreadSuggestion.js";
@@ -11,6 +11,7 @@ var thread_exports = /* @__PURE__ */ __exportAll({
11
11
  If: () => ThreadIf,
12
12
  MessageByIndex: () => MessageByIndex,
13
13
  Messages: () => ThreadMessages,
14
+ MessagesFlatList: () => ThreadMessagesFlatList,
14
15
  Root: () => ThreadRoot,
15
16
  Suggestion: () => ThreadSuggestion,
16
17
  SuggestionByIndex: () => SuggestionByIndex,
@@ -18,6 +19,6 @@ var thread_exports = /* @__PURE__ */ __exportAll({
18
19
  Unstable_MessageById: () => Unstable_MessageById
19
20
  });
20
21
  //#endregion
21
- export { ThreadEmpty as Empty, ThreadIf as If, MessageByIndex, ThreadMessages as Messages, ThreadRoot as Root, ThreadSuggestion as Suggestion, SuggestionByIndex, Suggestions, Unstable_MessageById, thread_exports };
22
+ export { ThreadEmpty as Empty, ThreadIf as If, MessageByIndex, ThreadMessages as Messages, ThreadMessagesFlatList as MessagesFlatList, ThreadRoot as Root, ThreadSuggestion as Suggestion, SuggestionByIndex, Suggestions, Unstable_MessageById, thread_exports };
22
23
 
23
24
  //# sourceMappingURL=thread.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"thread.js","names":["ThreadRoot","Root","ThreadRootProps","RootProps","ThreadMessages","Messages","ThreadMessagesProps","MessagesProps","ThreadPrimitiveMessageByIndex","MessageByIndex","ThreadPrimitiveUnstable_MessageById","Unstable_MessageById","ThreadEmpty","Empty","ThreadEmptyProps","EmptyProps","ThreadIf","If","ThreadIfProps","IfProps","ThreadSuggestion","Suggestion","ThreadSuggestionProps","SuggestionProps","ThreadPrimitiveSuggestions","Suggestions","ThreadPrimitiveSuggestionByIndex","SuggestionByIndex"],"sources":["../../src/primitives/thread.ts"],"sourcesContent":["export {\n ThreadRoot as Root,\n type ThreadRootProps as RootProps,\n} from \"./thread/ThreadRoot\";\nexport {\n ThreadMessages as Messages,\n type ThreadMessagesProps as MessagesProps,\n} from \"./thread/ThreadMessages\";\nexport { ThreadPrimitiveMessageByIndex as MessageByIndex } from \"@assistant-ui/core/react\";\nexport { ThreadPrimitiveUnstable_MessageById as Unstable_MessageById } from \"@assistant-ui/core/react\";\nexport {\n ThreadEmpty as Empty,\n type ThreadEmptyProps as EmptyProps,\n} from \"./thread/ThreadEmpty\";\nexport {\n ThreadIf as If,\n type ThreadIfProps as IfProps,\n} from \"./thread/ThreadIf\";\nexport {\n ThreadSuggestion as Suggestion,\n type ThreadSuggestionProps as SuggestionProps,\n} from \"./thread/ThreadSuggestion\";\nexport {\n ThreadPrimitiveSuggestions as Suggestions,\n ThreadPrimitiveSuggestionByIndex as SuggestionByIndex,\n} from \"@assistant-ui/core/react\";\n"],"mappings":""}
1
+ {"version":3,"file":"thread.js","names":["ThreadRoot","Root","ThreadRootProps","RootProps","ThreadMessagesFlatList","MessagesFlatList","ThreadMessagesFlatListProps","MessagesFlatListProps","ThreadMessages","Messages","ThreadMessagesProps","MessagesProps","ThreadPrimitiveMessageByIndex","MessageByIndex","ThreadPrimitiveUnstable_MessageById","Unstable_MessageById","ThreadEmpty","Empty","ThreadEmptyProps","EmptyProps","ThreadIf","If","ThreadIfProps","IfProps","ThreadSuggestion","Suggestion","ThreadSuggestionProps","SuggestionProps","ThreadPrimitiveSuggestions","Suggestions","ThreadPrimitiveSuggestionByIndex","SuggestionByIndex"],"sources":["../../src/primitives/thread.ts"],"sourcesContent":["export {\n ThreadRoot as Root,\n type ThreadRootProps as RootProps,\n} from \"./thread/ThreadRoot\";\nexport {\n ThreadMessagesFlatList as MessagesFlatList,\n type ThreadMessagesFlatListProps as MessagesFlatListProps,\n ThreadMessages as Messages,\n type ThreadMessagesProps as MessagesProps,\n} from \"./thread/ThreadMessages\";\nexport { ThreadPrimitiveMessageByIndex as MessageByIndex } from \"@assistant-ui/core/react\";\nexport { ThreadPrimitiveUnstable_MessageById as Unstable_MessageById } from \"@assistant-ui/core/react\";\nexport {\n ThreadEmpty as Empty,\n type ThreadEmptyProps as EmptyProps,\n} from \"./thread/ThreadEmpty\";\nexport {\n ThreadIf as If,\n type ThreadIfProps as IfProps,\n} from \"./thread/ThreadIf\";\nexport {\n ThreadSuggestion as Suggestion,\n type ThreadSuggestionProps as SuggestionProps,\n} from \"./thread/ThreadSuggestion\";\nexport {\n ThreadPrimitiveSuggestions as Suggestions,\n ThreadPrimitiveSuggestionByIndex as SuggestionByIndex,\n} from \"@assistant-ui/core/react\";\n"],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react-native",
3
- "version": "0.1.30",
3
+ "version": "0.1.31",
4
4
  "description": "React Native bindings for assistant-ui",
5
5
  "keywords": [
6
6
  "assistant",
@@ -31,10 +31,10 @@
31
31
  ],
32
32
  "sideEffects": false,
33
33
  "dependencies": {
34
- "@assistant-ui/core": "^0.2.21",
35
- "@assistant-ui/store": "^0.2.20",
36
- "@assistant-ui/tap": "^0.9.4",
37
- "assistant-stream": "^0.3.26",
34
+ "@assistant-ui/core": "^0.2.22",
35
+ "@assistant-ui/store": "^0.2.21",
36
+ "@assistant-ui/tap": "^0.9.5",
37
+ "assistant-stream": "^0.3.27",
38
38
  "zustand": "^5.0.14"
39
39
  },
40
40
  "peerDependencies": {
@@ -51,12 +51,12 @@
51
51
  "@types/react": "^19.2.17",
52
52
  "@types/react-dom": "^19.2.3",
53
53
  "jsdom": "^29.1.1",
54
- "react": "^19.2.7",
55
- "react-dom": "^19.2.7",
54
+ "react": "^19.2.8",
55
+ "react-dom": "^19.2.8",
56
56
  "react-native": "^0.86.0",
57
57
  "react-native-web": "^0.21.2",
58
58
  "vitest": "^4.1.10",
59
- "@assistant-ui/x-buildutils": "0.0.18"
59
+ "@assistant-ui/x-buildutils": "0.0.19"
60
60
  },
61
61
  "publishConfig": {
62
62
  "access": "public",
@@ -3,7 +3,7 @@ import { createRoot, type Root } from "react-dom/client";
3
3
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
4
  import type { FlatList } from "react-native";
5
5
  import type { ThreadMessage } from "@assistant-ui/core";
6
- import { ThreadMessages } from "./ThreadMessages";
6
+ import { ThreadMessages, ThreadMessagesFlatList } from "./ThreadMessages";
7
7
 
8
8
  type Msg = { id: string; role: string };
9
9
 
@@ -13,16 +13,96 @@ const h = vi.hoisted(() => ({
13
13
  message: { role: "user" as string, composer: { isEditing: false } },
14
14
  },
15
15
  itemState: { role: "user" } as { role: string },
16
+ events: {} as Record<string, Set<() => void>>,
17
+ flatListProps: null as Record<string, unknown> | null,
18
+ scrollToEnd: vi.fn(),
16
19
  }));
17
20
 
18
- vi.mock("@assistant-ui/store", () => ({
19
- useAuiState: <T,>(selector: (s: typeof h.state) => T) => selector(h.state),
20
- RenderChildrenWithAccessor: ({
21
- children,
22
- }: {
23
- children: (getItem: () => unknown) => unknown;
24
- }) => children(() => h.itemState),
25
- }));
21
+ vi.mock("react-native", async (importOriginal) => {
22
+ const actual = await importOriginal<typeof import("react-native")>();
23
+ const React = await import("react");
24
+
25
+ const FlatListMock = React.forwardRef(function FlatListMock(
26
+ props: Record<string, unknown>,
27
+ ref,
28
+ ) {
29
+ h.flatListProps = props;
30
+
31
+ React.useImperativeHandle(ref, () => ({
32
+ scrollToEnd: h.scrollToEnd,
33
+ }));
34
+
35
+ const data = (props.data as unknown[]) ?? [];
36
+ const renderItem = props.renderItem as
37
+ | ((value: {
38
+ item: unknown;
39
+ index: number;
40
+ separators: {
41
+ highlight: () => void;
42
+ unhighlight: () => void;
43
+ updateProps: () => void;
44
+ };
45
+ }) => React.ReactNode)
46
+ | undefined;
47
+ const keyExtractor = props.keyExtractor as
48
+ | ((item: unknown, index: number) => string)
49
+ | undefined;
50
+
51
+ return React.createElement(
52
+ "div",
53
+ { "data-testid": "flatlist" },
54
+ data.map((item, index) =>
55
+ React.createElement(
56
+ "div",
57
+ { key: keyExtractor?.(item, index) ?? index },
58
+ renderItem?.({
59
+ item,
60
+ index,
61
+ separators: {
62
+ highlight: vi.fn(),
63
+ unhighlight: vi.fn(),
64
+ updateProps: vi.fn(),
65
+ },
66
+ }),
67
+ ),
68
+ ),
69
+ );
70
+ });
71
+
72
+ return {
73
+ ...actual,
74
+ FlatList: FlatListMock,
75
+ };
76
+ });
77
+
78
+ vi.mock("@assistant-ui/store", async () => {
79
+ const React = await import("react");
80
+
81
+ return {
82
+ useAuiState: <T,>(selector: (s: typeof h.state) => T) => selector(h.state),
83
+ useAuiEvent: (
84
+ selector: string | { scope: string; event: string },
85
+ callback: () => void,
86
+ ) => {
87
+ React.useEffect(() => {
88
+ const event =
89
+ typeof selector === "string"
90
+ ? selector
91
+ : `${selector.scope}.${selector.event}`;
92
+ const callbacks = (h.events[event] ??= new Set());
93
+ callbacks.add(callback);
94
+ return () => {
95
+ callbacks.delete(callback);
96
+ };
97
+ }, [selector, callback]);
98
+ },
99
+ RenderChildrenWithAccessor: ({
100
+ children,
101
+ }: {
102
+ children: (getItem: () => unknown) => unknown;
103
+ }) => children(() => h.itemState),
104
+ };
105
+ });
26
106
 
27
107
  vi.mock("@assistant-ui/core/react", () => ({
28
108
  MessageByIndexProvider: ({ children }: { children: unknown }) => children,
@@ -39,6 +119,9 @@ describe("ThreadMessages", () => {
39
119
  h.state.message.role = "user";
40
120
  h.state.message.composer.isEditing = false;
41
121
  h.itemState = { role: "user" };
122
+ h.events = {};
123
+ h.flatListProps = null;
124
+ h.scrollToEnd.mockReset();
42
125
 
43
126
  container = document.createElement("div");
44
127
  document.body.appendChild(container);
@@ -58,6 +141,37 @@ describe("ThreadMessages", () => {
58
141
  });
59
142
  };
60
143
 
144
+ const mountFlatList = async (
145
+ props: Parameters<typeof ThreadMessagesFlatList>[0],
146
+ ) => {
147
+ await act(async () => {
148
+ root.render(<ThreadMessagesFlatList {...props} />);
149
+ });
150
+ };
151
+
152
+ const getFlatListProps = () => {
153
+ const props = h.flatListProps as {
154
+ onContentSizeChange?: (width: number, height: number) => void;
155
+ onLayout?: (event: unknown) => void;
156
+ onScroll?: (event: unknown) => void;
157
+ scrollEventThrottle?: number;
158
+ } | null;
159
+ if (!props) throw new Error("FlatList was not rendered");
160
+ return props;
161
+ };
162
+
163
+ const emit = async (event: string) => {
164
+ await act(async () => {
165
+ h.events[event]?.forEach((callback) => {
166
+ callback();
167
+ });
168
+ });
169
+ };
170
+
171
+ const messageComponents = {
172
+ Message: () => <span data-testid="c-message">message</span>,
173
+ };
174
+
61
175
  describe("components mode dispatch", () => {
62
176
  const makeComponents = () => ({
63
177
  Message: vi.fn(() => <span data-testid="c-message">message</span>),
@@ -209,4 +323,304 @@ describe("ThreadMessages", () => {
209
323
 
210
324
  expect(ref.current).not.toBeNull();
211
325
  });
326
+
327
+ it("keeps deprecated Messages off the scroll-tracking path", async () => {
328
+ h.state.thread.messages = [{ id: "1", role: "user" }];
329
+ await mount({ components: messageComponents });
330
+ const props = getFlatListProps();
331
+
332
+ expect(props.onScroll).toBeUndefined();
333
+ expect(props.onLayout).toBeUndefined();
334
+ expect(props.onContentSizeChange).toBeUndefined();
335
+ expect(props.scrollEventThrottle).toBeUndefined();
336
+ });
337
+
338
+ it("keeps deprecated Messages from auto-scrolling by default", async () => {
339
+ h.state.thread.messages = [{ id: "1", role: "user" }];
340
+ await mount({ components: messageComponents });
341
+ const props = getFlatListProps();
342
+
343
+ await act(async () => {
344
+ props.onLayout?.({
345
+ nativeEvent: { layout: { height: 100 } },
346
+ });
347
+ props.onScroll?.({
348
+ nativeEvent: {
349
+ contentOffset: { y: 0 },
350
+ contentSize: { height: 100, width: 0 },
351
+ layoutMeasurement: { height: 100, width: 0 },
352
+ },
353
+ });
354
+ props.onContentSizeChange?.(0, 140);
355
+ });
356
+ await emit("thread.runStart");
357
+ await emit("threadListItem.switchedTo");
358
+
359
+ expect(h.scrollToEnd).not.toHaveBeenCalled();
360
+ });
361
+
362
+ describe("MessagesFlatList auto-scroll", () => {
363
+ it("scrolls to the bottom when messages first appear", async () => {
364
+ h.state.thread.messages = [{ id: "1", role: "user" }];
365
+
366
+ await mountFlatList({ components: messageComponents });
367
+
368
+ expect(h.scrollToEnd).toHaveBeenCalledWith({ animated: false });
369
+ });
370
+
371
+ it("scrolls to the bottom when a run starts", async () => {
372
+ h.state.thread.messages = [{ id: "1", role: "user" }];
373
+ await mountFlatList({ components: messageComponents });
374
+ h.scrollToEnd.mockClear();
375
+
376
+ await emit("thread.runStart");
377
+
378
+ expect(h.scrollToEnd).toHaveBeenCalledWith({ animated: true });
379
+ });
380
+
381
+ it("scrolls when content grows while already at the bottom", async () => {
382
+ h.state.thread.messages = [{ id: "1", role: "user" }];
383
+ await mountFlatList({ components: messageComponents });
384
+ const props = getFlatListProps();
385
+ h.scrollToEnd.mockClear();
386
+
387
+ await act(async () => {
388
+ props.onLayout?.({
389
+ nativeEvent: { layout: { height: 100 } },
390
+ });
391
+ props.onScroll?.({
392
+ nativeEvent: {
393
+ contentOffset: { y: 0 },
394
+ contentSize: { height: 100, width: 0 },
395
+ layoutMeasurement: { height: 100, width: 0 },
396
+ },
397
+ });
398
+ props.onContentSizeChange?.(0, 140);
399
+ });
400
+
401
+ expect(h.scrollToEnd).toHaveBeenCalledWith({ animated: false });
402
+ });
403
+
404
+ it("does not treat the first content-size event as automatic content growth", async () => {
405
+ h.state.thread.messages = [{ id: "1", role: "user" }];
406
+ await mountFlatList({
407
+ components: messageComponents,
408
+ scrollToBottomOnInitialize: false,
409
+ });
410
+ const props = getFlatListProps();
411
+
412
+ await act(async () => {
413
+ props.onContentSizeChange?.(0, 140);
414
+ });
415
+
416
+ expect(h.scrollToEnd).not.toHaveBeenCalled();
417
+ });
418
+
419
+ it("lands the initialize scroll on the first content-size event", async () => {
420
+ h.state.thread.messages = [{ id: "1", role: "user" }];
421
+ await mountFlatList({ components: messageComponents });
422
+ const props = getFlatListProps();
423
+
424
+ expect(h.scrollToEnd).toHaveBeenCalledTimes(1);
425
+
426
+ await act(async () => {
427
+ props.onContentSizeChange?.(0, 140);
428
+ });
429
+
430
+ expect(h.scrollToEnd).toHaveBeenCalledTimes(2);
431
+
432
+ await act(async () => {
433
+ props.onContentSizeChange?.(0, 140);
434
+ });
435
+
436
+ expect(h.scrollToEnd).toHaveBeenCalledTimes(2);
437
+ });
438
+
439
+ it("lands the thread-switch scroll on the next content-size event", async () => {
440
+ h.state.thread.messages = [{ id: "1", role: "user" }];
441
+ await mountFlatList({ components: messageComponents });
442
+ const props = getFlatListProps();
443
+ h.scrollToEnd.mockClear();
444
+
445
+ await emit("threadListItem.switchedTo");
446
+ expect(h.scrollToEnd).toHaveBeenCalledTimes(1);
447
+
448
+ await act(async () => {
449
+ props.onContentSizeChange?.(0, 80);
450
+ });
451
+
452
+ expect(h.scrollToEnd).toHaveBeenCalledTimes(2);
453
+ });
454
+
455
+ it("keeps following through consecutive growth events without scroll echoes", async () => {
456
+ h.state.thread.messages = [{ id: "1", role: "user" }];
457
+ await mountFlatList({ components: messageComponents });
458
+ const props = getFlatListProps();
459
+
460
+ await act(async () => {
461
+ props.onLayout?.({
462
+ nativeEvent: { layout: { height: 100 } },
463
+ });
464
+ props.onScroll?.({
465
+ nativeEvent: {
466
+ contentOffset: { y: 0 },
467
+ contentSize: { height: 100, width: 0 },
468
+ layoutMeasurement: { height: 100, width: 0 },
469
+ },
470
+ });
471
+ });
472
+ h.scrollToEnd.mockClear();
473
+
474
+ await act(async () => {
475
+ props.onContentSizeChange?.(0, 140);
476
+ });
477
+ await act(async () => {
478
+ props.onContentSizeChange?.(0, 180);
479
+ });
480
+
481
+ expect(h.scrollToEnd).toHaveBeenCalledTimes(2);
482
+ });
483
+
484
+ it("does not scroll when content grows after the user scrolled away", async () => {
485
+ h.state.thread.messages = [{ id: "1", role: "user" }];
486
+ await mountFlatList({ components: messageComponents });
487
+ const props = getFlatListProps();
488
+ h.scrollToEnd.mockClear();
489
+
490
+ await act(async () => {
491
+ props.onLayout?.({
492
+ nativeEvent: { layout: { height: 100 } },
493
+ });
494
+ props.onScroll?.({
495
+ nativeEvent: {
496
+ contentOffset: { y: 200 },
497
+ contentSize: { height: 300, width: 0 },
498
+ layoutMeasurement: { height: 100, width: 0 },
499
+ },
500
+ });
501
+ props.onScroll?.({
502
+ nativeEvent: {
503
+ contentOffset: { y: 50 },
504
+ contentSize: { height: 300, width: 0 },
505
+ layoutMeasurement: { height: 100, width: 0 },
506
+ },
507
+ });
508
+ props.onContentSizeChange?.(0, 340);
509
+ });
510
+
511
+ expect(h.scrollToEnd).not.toHaveBeenCalled();
512
+ });
513
+
514
+ it("lands the run-start scroll once the appended message resizes content", async () => {
515
+ h.state.thread.messages = [{ id: "1", role: "user" }];
516
+ await mountFlatList({ components: messageComponents });
517
+ const props = getFlatListProps();
518
+
519
+ await act(async () => {
520
+ props.onLayout?.({
521
+ nativeEvent: { layout: { height: 100 } },
522
+ });
523
+ props.onScroll?.({
524
+ nativeEvent: {
525
+ contentOffset: { y: 200 },
526
+ contentSize: { height: 300, width: 0 },
527
+ layoutMeasurement: { height: 100, width: 0 },
528
+ },
529
+ });
530
+ props.onScroll?.({
531
+ nativeEvent: {
532
+ contentOffset: { y: 50 },
533
+ contentSize: { height: 300, width: 0 },
534
+ layoutMeasurement: { height: 100, width: 0 },
535
+ },
536
+ });
537
+ });
538
+ h.scrollToEnd.mockClear();
539
+
540
+ await emit("thread.runStart");
541
+ expect(h.scrollToEnd).toHaveBeenCalledTimes(1);
542
+
543
+ await act(async () => {
544
+ props.onScroll?.({
545
+ nativeEvent: {
546
+ contentOffset: { y: 120 },
547
+ contentSize: { height: 300, width: 0 },
548
+ layoutMeasurement: { height: 100, width: 0 },
549
+ },
550
+ });
551
+ props.onContentSizeChange?.(0, 360);
552
+ });
553
+
554
+ expect(h.scrollToEnd).toHaveBeenCalledTimes(2);
555
+ expect(h.scrollToEnd).toHaveBeenLastCalledWith({ animated: true });
556
+ });
557
+
558
+ it("scrolls to the bottom when switching threads", async () => {
559
+ h.state.thread.messages = [{ id: "1", role: "user" }];
560
+ await mountFlatList({ components: messageComponents });
561
+ h.scrollToEnd.mockClear();
562
+
563
+ await emit("threadListItem.switchedTo");
564
+
565
+ expect(h.scrollToEnd).toHaveBeenCalledWith({ animated: false });
566
+ });
567
+
568
+ it("does not rearm initialize scroll when thread-switch scroll is disabled", async () => {
569
+ h.state.thread.messages = [{ id: "1", role: "user" }];
570
+ await mountFlatList({
571
+ components: messageComponents,
572
+ scrollToBottomOnThreadSwitch: false,
573
+ });
574
+ h.scrollToEnd.mockClear();
575
+
576
+ await emit("threadListItem.switchedTo");
577
+
578
+ h.state.thread.messages = [
579
+ { id: "2", role: "user" },
580
+ { id: "3", role: "assistant" },
581
+ ];
582
+ await mountFlatList({
583
+ components: messageComponents,
584
+ scrollToBottomOnThreadSwitch: false,
585
+ });
586
+
587
+ expect(h.scrollToEnd).not.toHaveBeenCalled();
588
+ });
589
+
590
+ it("honors opt-outs for automatic content growth and run-start scrolls", async () => {
591
+ h.state.thread.messages = [{ id: "1", role: "user" }];
592
+ await mountFlatList({
593
+ autoScroll: false,
594
+ components: messageComponents,
595
+ scrollToBottomOnInitialize: false,
596
+ scrollToBottomOnRunStart: false,
597
+ });
598
+ const props = getFlatListProps();
599
+ h.scrollToEnd.mockClear();
600
+
601
+ await act(async () => {
602
+ props.onLayout?.({
603
+ nativeEvent: { layout: { height: 100 } },
604
+ });
605
+ props.onScroll?.({
606
+ nativeEvent: {
607
+ contentOffset: { y: 0 },
608
+ contentSize: { height: 100, width: 0 },
609
+ layoutMeasurement: { height: 100, width: 0 },
610
+ },
611
+ });
612
+ props.onContentSizeChange?.(0, 140);
613
+ });
614
+ await emit("thread.runStart");
615
+
616
+ expect(h.scrollToEnd).not.toHaveBeenCalled();
617
+ });
618
+
619
+ it("sets a useful default scroll throttle", async () => {
620
+ h.state.thread.messages = [{ id: "1", role: "user" }];
621
+ await mountFlatList({ components: messageComponents });
622
+
623
+ expect(getFlatListProps().scrollEventThrottle).toBe(16);
624
+ });
625
+ });
212
626
  });