@assistant-ui/react-native 0.1.31 → 0.1.32

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.
@@ -2,6 +2,7 @@ import { useAui } from "@assistant-ui/store";
2
2
  import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
3
3
  import { Pressable } from "react-native";
4
4
  import { jsx } from "react/jsx-runtime";
5
+ import "@assistant-ui/tap/react-shim";
5
6
  //#region src/primitives/attachment/AttachmentRemove.tsx
6
7
  const AttachmentRemove = (t0) => {
7
8
  const $ = c(9);
@@ -1 +1 @@
1
- {"version":3,"file":"AttachmentRemove.js","names":["ReactNode","useCallback","Pressable","PressableProps","useAui","AttachmentRemoveProps","Omit","children","AttachmentRemove","t0","$","_c","pressableProps","aui","t1","attachment","remove","handleRemove","t2"],"sources":["../../../src/primitives/attachment/AttachmentRemove.tsx"],"sourcesContent":["import { type ReactNode, useCallback } from \"react\";\nimport { Pressable, type PressableProps } from \"react-native\";\n\nimport { useAui } from \"@assistant-ui/store\";\n\nexport type AttachmentRemoveProps = Omit<PressableProps, \"onPress\"> & {\n children: ReactNode;\n};\n\nexport const AttachmentRemove = ({\n children,\n ...pressableProps\n}: AttachmentRemoveProps) => {\n const aui = useAui();\n\n const handleRemove = useCallback(() => {\n aui.attachment().remove();\n }, [aui]);\n\n return (\n <Pressable onPress={handleRemove} {...pressableProps}>\n {children}\n </Pressable>\n );\n};\n"],"mappings":";;;;;AASA,MAAaQ,oBAAmBC,OAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CAAA,IAAAJ;CAAA,IAAAK;CAAA,IAAAF,EAAA,OAAAD,IAAA;EAAC,CAAA,CAAAF,aAAAK,kBAAAH;EAGTC,EAAA,KAAAD;EAAAC,EAAA,KAAAH;EAAAG,EAAA,KAAAE;CAAA,OAAA;EAAAL,WAAAG,EAAA;EAAAE,iBAAAF,EAAA;CAAA;CACtB,MAAAG,MAAYT,OAAO;CAAE,IAAAU;CAAA,IAAAJ,EAAA,OAAAG,KAAA;EAEYC,WAAA;GAC/BD,IAAGE,WAAY,CAAC,CAAAC,OAAQ;EAAC;EAC1BN,EAAA,KAAAG;EAAAH,EAAA,KAAAI;CAAA,OAAAA,KAAAJ,EAAA;CAFD,MAAAO,eAAqBH;CAEX,IAAAI;CAAA,IAAAR,EAAA,OAAAH,YAAAG,EAAA,OAAAO,gBAAAP,EAAA,OAAAE,gBAAA;EAGRM,KAAA,oBAAC,WAAD;GAAoBD,SAAAA;GAAY,GAAML;GACnCL;EADO,CAAA;EAEEG,EAAA,KAAAH;EAAAG,EAAA,KAAAO;EAAAP,EAAA,KAAAE;EAAAF,EAAA,KAAAQ;CAAA,OAAAA,KAAAR,EAAA;CAAA,OAFZQ;AAEY"}
1
+ {"version":3,"file":"AttachmentRemove.js","names":["ReactNode","useCallback","Pressable","PressableProps","useAui","AttachmentRemoveProps","Omit","children","AttachmentRemove","t0","$","_c","pressableProps","aui","t1","attachment","remove","handleRemove","t2"],"sources":["../../../src/primitives/attachment/AttachmentRemove.tsx"],"sourcesContent":["import { type ReactNode, useCallback } from \"react\";\nimport { Pressable, type PressableProps } from \"react-native\";\n\nimport { useAui } from \"@assistant-ui/store\";\n\nexport type AttachmentRemoveProps = Omit<PressableProps, \"onPress\"> & {\n children: ReactNode;\n};\n\nexport const AttachmentRemove = ({\n children,\n ...pressableProps\n}: AttachmentRemoveProps) => {\n const aui = useAui();\n\n const handleRemove = useCallback(() => {\n aui.attachment().remove();\n }, [aui]);\n\n return (\n <Pressable onPress={handleRemove} {...pressableProps}>\n {children}\n </Pressable>\n );\n};\n"],"mappings":";;;;;;AASA,MAAaQ,oBAAmBC,OAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CAAA,IAAAJ;CAAA,IAAAK;CAAA,IAAAF,EAAA,OAAAD,IAAA;EAAC,CAAA,CAAAF,aAAAK,kBAAAH;EAGTC,EAAA,KAAAD;EAAAC,EAAA,KAAAH;EAAAG,EAAA,KAAAE;CAAA,OAAA;EAAAL,WAAAG,EAAA;EAAAE,iBAAAF,EAAA;CAAA;CACtB,MAAAG,MAAYT,OAAO;CAAE,IAAAU;CAAA,IAAAJ,EAAA,OAAAG,KAAA;EAEYC,WAAA;GAC/BD,IAAGE,WAAY,CAAC,CAAAC,OAAQ;EAAC;EAC1BN,EAAA,KAAAG;EAAAH,EAAA,KAAAI;CAAA,OAAAA,KAAAJ,EAAA;CAFD,MAAAO,eAAqBH;CAEX,IAAAI;CAAA,IAAAR,EAAA,OAAAH,YAAAG,EAAA,OAAAO,gBAAAP,EAAA,OAAAE,gBAAA;EAGRM,KAAA,oBAAC,WAAD;GAAoBD,SAAAA;GAAY,GAAML;GACnCL;EADO,CAAA;EAEEG,EAAA,KAAAH;EAAAG,EAAA,KAAAO;EAAAP,EAAA,KAAAE;EAAAF,EAAA,KAAAQ;CAAA,OAAAA,KAAAR,EAAA;CAAA,OAFZQ;AAEY"}
@@ -2,6 +2,7 @@ import { useAui, useAuiState } from "@assistant-ui/store";
2
2
  import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
3
3
  import { Pressable } from "react-native";
4
4
  import { jsx } from "react/jsx-runtime";
5
+ import "@assistant-ui/tap/react-shim";
5
6
  //#region src/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.tsx
6
7
  const ChainOfThoughtAccordionTrigger = (t0) => {
7
8
  const $ = c(10);
@@ -1 +1 @@
1
- {"version":3,"file":"ChainOfThoughtAccordionTrigger.js","names":["useCallback","ReactNode","Pressable","PressableProps","useAuiState","useAui","ChainOfThoughtAccordionTriggerProps","Omit","children","ChainOfThoughtAccordionTrigger","t0","$","_c","pressableProps","aui","collapsed","_temp","t1","chainOfThought","setCollapsed","onPress","t2","s"],"sources":["../../../src/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.tsx"],"sourcesContent":["import { useCallback, type ReactNode } from \"react\";\nimport { Pressable, type PressableProps } from \"react-native\";\nimport { useAuiState, useAui } from \"@assistant-ui/store\";\n\nexport type ChainOfThoughtAccordionTriggerProps = Omit<\n PressableProps,\n \"onPress\"\n> & {\n children: ReactNode;\n};\n\nexport const ChainOfThoughtAccordionTrigger = ({\n children,\n ...pressableProps\n}: ChainOfThoughtAccordionTriggerProps) => {\n const aui = useAui();\n const collapsed = useAuiState((s) => s.chainOfThought.collapsed);\n\n const onPress = useCallback(() => {\n aui.chainOfThought().setCollapsed(!collapsed);\n }, [aui, collapsed]);\n\n return (\n <Pressable onPress={onPress} {...pressableProps}>\n {children}\n </Pressable>\n );\n};\n"],"mappings":";;;;;AAWA,MAAaS,kCAAiCC,OAAA;CAAA,MAAAC,IAAAC,EAAA,EAAA;CAAA,IAAAJ;CAAA,IAAAK;CAAA,IAAAF,EAAA,OAAAD,IAAA;EAAC,CAAA,CAAAF,aAAAK,kBAAAH;EAGTC,EAAA,KAAAD;EAAAC,EAAA,KAAAH;EAAAG,EAAA,KAAAE;CAAA,OAAA;EAAAL,WAAAG,EAAA;EAAAE,iBAAAF,EAAA;CAAA;CACpC,MAAAG,MAAYT,OAAO;CACnB,MAAAU,YAAkBX,YAAYY,KAAiC;CAAE,IAAAC;CAAA,IAAAN,EAAA,OAAAG,OAAAH,EAAA,OAAAI,WAAA;EAErCE,WAAA;GAC1BH,IAAGI,eAAgB,CAAC,CAAAC,aAAc,CAACJ,SAAS;EAAC;EAC9CJ,EAAA,KAAAG;EAAAH,EAAA,KAAAI;EAAAJ,EAAA,KAAAM;CAAA,OAAAA,KAAAN,EAAA;CAFD,MAAAS,UAAgBH;CAEK,IAAAI;CAAA,IAAAV,EAAA,OAAAH,YAAAG,EAAA,OAAAS,WAAAT,EAAA,OAAAE,gBAAA;EAGnBQ,KAAA,oBAAC,WAAD;GAAoBD;GAAO,GAAMP;GAC9BL;EADO,CAAA;EAEEG,EAAA,KAAAH;EAAAG,EAAA,KAAAS;EAAAT,EAAA,KAAAE;EAAAF,EAAA,KAAAU;CAAA,OAAAA,KAAAV,EAAA;CAAA,OAFZU;AAEY;AAd8B,SAAAL,MAAAM,GAAA;CAAA,OAKPA,EAACJ,eAAeH;AAAU"}
1
+ {"version":3,"file":"ChainOfThoughtAccordionTrigger.js","names":["useCallback","ReactNode","Pressable","PressableProps","useAuiState","useAui","ChainOfThoughtAccordionTriggerProps","Omit","children","ChainOfThoughtAccordionTrigger","t0","$","_c","pressableProps","aui","collapsed","_temp","t1","chainOfThought","setCollapsed","onPress","t2","s"],"sources":["../../../src/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.tsx"],"sourcesContent":["import { useCallback, type ReactNode } from \"react\";\nimport { Pressable, type PressableProps } from \"react-native\";\nimport { useAuiState, useAui } from \"@assistant-ui/store\";\n\nexport type ChainOfThoughtAccordionTriggerProps = Omit<\n PressableProps,\n \"onPress\"\n> & {\n children: ReactNode;\n};\n\nexport const ChainOfThoughtAccordionTrigger = ({\n children,\n ...pressableProps\n}: ChainOfThoughtAccordionTriggerProps) => {\n const aui = useAui();\n const collapsed = useAuiState((s) => s.chainOfThought.collapsed);\n\n const onPress = useCallback(() => {\n aui.chainOfThought().setCollapsed(!collapsed);\n }, [aui, collapsed]);\n\n return (\n <Pressable onPress={onPress} {...pressableProps}>\n {children}\n </Pressable>\n );\n};\n"],"mappings":";;;;;;AAWA,MAAaS,kCAAiCC,OAAA;CAAA,MAAAC,IAAAC,EAAA,EAAA;CAAA,IAAAJ;CAAA,IAAAK;CAAA,IAAAF,EAAA,OAAAD,IAAA;EAAC,CAAA,CAAAF,aAAAK,kBAAAH;EAGTC,EAAA,KAAAD;EAAAC,EAAA,KAAAH;EAAAG,EAAA,KAAAE;CAAA,OAAA;EAAAL,WAAAG,EAAA;EAAAE,iBAAAF,EAAA;CAAA;CACpC,MAAAG,MAAYT,OAAO;CACnB,MAAAU,YAAkBX,YAAYY,KAAiC;CAAE,IAAAC;CAAA,IAAAN,EAAA,OAAAG,OAAAH,EAAA,OAAAI,WAAA;EAErCE,WAAA;GAC1BH,IAAGI,eAAgB,CAAC,CAAAC,aAAc,CAACJ,SAAS;EAAC;EAC9CJ,EAAA,KAAAG;EAAAH,EAAA,KAAAI;EAAAJ,EAAA,KAAAM;CAAA,OAAAA,KAAAN,EAAA;CAFD,MAAAS,UAAgBH;CAEK,IAAAI;CAAA,IAAAV,EAAA,OAAAH,YAAAG,EAAA,OAAAS,WAAAT,EAAA,OAAAE,gBAAA;EAGnBQ,KAAA,oBAAC,WAAD;GAAoBD;GAAO,GAAMP;GAC9BL;EADO,CAAA;EAEEG,EAAA,KAAAH;EAAAG,EAAA,KAAAS;EAAAT,EAAA,KAAAE;EAAAF,EAAA,KAAAU;CAAA,OAAAA,KAAAV,EAAA;CAAA,OAFZU;AAEY;AAd8B,SAAAL,MAAAM,GAAA;CAAA,OAKPA,EAACJ,eAAeH;AAAU"}
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadMessages.d.ts","names":[],"sources":["../../../src/primitives/thread/ThreadMessages.tsx"],"mappings":";;;;KA2BK;EAEC,SAAS;EACT,eAAe;EACf,mBAAmB;EACnB,wBAAwB;EACxB,qBAAqB;EACrB,cAAc;EACd,mBAAmB;EACnB,gBAAgB;;EAGhB,UAAU;EACV,eAAe;EACf,mBAAmB;EACnB,wBAAwB;EACxB,qBAAqB;EACrB,aAAa;EACb,kBAAkB;EAClB,gBAAgB;;KAGjB;;EAGC,YAAY;EACZ;;EAGA,WAAW;IAAS,SAAS;QAAmB;EAChD;;KAGM,8BAA8B,KACxC,cAAc,sDAGd;EACE;EACA;EACA;EACA;;;KAIQ,sBAAsB;cAmRrB,wCAAsB,0BAAA,8CAAA,cAAA,SAAA;;cA4GtB,gCAAc,0BAAA,8CAAA,cAAA,SAAA"}
1
+ {"version":3,"file":"ThreadMessages.d.ts","names":[],"sources":["../../../src/primitives/thread/ThreadMessages.tsx"],"mappings":";;;;KA2BK;EAEC,SAAS;EACT,eAAe;EACf,mBAAmB;EACnB,wBAAwB;EACxB,qBAAqB;EACrB,cAAc;EACd,mBAAmB;EACnB,gBAAgB;;EAGhB,UAAU;EACV,eAAe;EACf,mBAAmB;EACnB,wBAAwB;EACxB,qBAAqB;EACrB,aAAa;EACb,kBAAkB;EAClB,gBAAgB;;KAGjB;;EAGC,YAAY;EACZ;;EAGA,WAAW;IAAS,SAAS;QAAmB;EAChD;;KAGM,8BAA8B,KACxC,cAAc,sDAGd;EACE;EACA;EACA;EACA;;;KAIQ,sBAAsB;cAqSrB,wCAAsB,0BAAA,8CAAA,cAAA,SAAA;;cA4GtB,gCAAc,0BAAA,8CAAA,cAAA,SAAA"}
@@ -100,7 +100,7 @@ const useComposedFlatListRef = (forwardedRef) => {
100
100
  return t1;
101
101
  };
102
102
  const useThreadMessagesFlatListAutoScroll = (t0) => {
103
- const $ = c(22);
103
+ const $ = c(25);
104
104
  const { flatListRef, hasMessages, autoScroll: t1, scrollToBottomOnRunStart: t2, scrollToBottomOnInitialize: t3, scrollToBottomOnThreadSwitch: t4 } = t0;
105
105
  const autoScroll = t1 === void 0 ? true : t1;
106
106
  const scrollToBottomOnRunStart = t2 === void 0 ? true : t2;
@@ -142,16 +142,26 @@ const useThreadMessagesFlatListAutoScroll = (t0) => {
142
142
  } else t7 = $[3];
143
143
  const scrollToBottom = t7;
144
144
  let t8;
145
- if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
145
+ if ($[4] !== autoScroll || $[5] !== scrollToBottom) {
146
146
  t8 = (event) => {
147
- metricsRef.current.viewportHeight = event.nativeEvent.layout.height;
147
+ const wasAtBottom = isAtBottomRef.current;
148
+ const previousViewportHeight = metricsRef.current.viewportHeight;
149
+ const viewportHeight_1 = event.nativeEvent.layout.height;
150
+ metricsRef.current.viewportHeight = viewportHeight_1;
148
151
  updateIsAtBottom();
152
+ if (!wasAtBottom) return;
153
+ if (autoScroll && previousViewportHeight !== 0 && viewportHeight_1 !== previousViewportHeight) {
154
+ const pending = pendingScrollToBottomRef.current;
155
+ scrollToBottom(pending ? pending.animated : false);
156
+ } else isAtBottomRef.current = true;
149
157
  };
150
- $[4] = t8;
151
- } else t8 = $[4];
158
+ $[4] = autoScroll;
159
+ $[5] = scrollToBottom;
160
+ $[6] = t8;
161
+ } else t8 = $[6];
152
162
  const handleLayout = t8;
153
163
  let t9;
154
- if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
164
+ if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
155
165
  t9 = (event_0) => {
156
166
  const { contentOffset, contentSize, layoutMeasurement } = event_0.nativeEvent;
157
167
  const previousEventY = lastScrollEventYRef.current;
@@ -167,15 +177,15 @@ const useThreadMessagesFlatListAutoScroll = (t0) => {
167
177
  if (wasPinnedToBottom && !upwardMove) isAtBottomRef.current = true;
168
178
  if (!isAtBottomRef.current && upwardMove) pendingScrollToBottomRef.current = false;
169
179
  };
170
- $[5] = t9;
171
- } else t9 = $[5];
180
+ $[7] = t9;
181
+ } else t9 = $[7];
172
182
  const handleScroll = t9;
173
183
  let t10;
174
- if ($[6] !== autoScroll || $[7] !== scrollToBottom) {
184
+ if ($[8] !== autoScroll || $[9] !== scrollToBottom) {
175
185
  t10 = (_width, height) => {
176
186
  const metrics = metricsRef.current;
177
187
  const previousContentHeight = metrics.contentHeight;
178
- const wasAtBottom = isAtBottomRef.current;
188
+ const wasAtBottom_0 = isAtBottomRef.current;
179
189
  metrics.contentHeight = height;
180
190
  updateIsAtBottom();
181
191
  const pendingScroll = pendingScrollToBottomRef.current;
@@ -185,19 +195,19 @@ const useThreadMessagesFlatListAutoScroll = (t0) => {
185
195
  return;
186
196
  }
187
197
  if (!autoScroll) return;
188
- if (!wasAtBottom) return;
198
+ if (!wasAtBottom_0) return;
189
199
  if (previousContentHeight === 0) return;
190
200
  if (height <= previousContentHeight) return;
191
201
  scrollToBottom(false);
192
202
  };
193
- $[6] = autoScroll;
194
- $[7] = scrollToBottom;
195
- $[8] = t10;
196
- } else t10 = $[8];
203
+ $[8] = autoScroll;
204
+ $[9] = scrollToBottom;
205
+ $[10] = t10;
206
+ } else t10 = $[10];
197
207
  const handleContentSizeChange = t10;
198
208
  let t11;
199
209
  let t12;
200
- if ($[9] !== hasMessages || $[10] !== scrollToBottom || $[11] !== scrollToBottomOnInitialize) {
210
+ if ($[11] !== hasMessages || $[12] !== scrollToBottom || $[13] !== scrollToBottomOnInitialize) {
201
211
  t11 = () => {
202
212
  if (!scrollToBottomOnInitialize) return;
203
213
  if (!hasMessages) {
@@ -214,30 +224,30 @@ const useThreadMessagesFlatListAutoScroll = (t0) => {
214
224
  scrollToBottom,
215
225
  scrollToBottomOnInitialize
216
226
  ];
217
- $[9] = hasMessages;
218
- $[10] = scrollToBottom;
219
- $[11] = scrollToBottomOnInitialize;
220
- $[12] = t11;
221
- $[13] = t12;
227
+ $[11] = hasMessages;
228
+ $[12] = scrollToBottom;
229
+ $[13] = scrollToBottomOnInitialize;
230
+ $[14] = t11;
231
+ $[15] = t12;
222
232
  } else {
223
- t11 = $[12];
224
- t12 = $[13];
233
+ t11 = $[14];
234
+ t12 = $[15];
225
235
  }
226
236
  useEffect(t11, t12);
227
237
  let t13;
228
- if ($[14] !== scrollToBottom || $[15] !== scrollToBottomOnRunStart) {
238
+ if ($[16] !== scrollToBottom || $[17] !== scrollToBottomOnRunStart) {
229
239
  t13 = () => {
230
240
  if (!scrollToBottomOnRunStart) return;
231
241
  pendingScrollToBottomRef.current = { animated: true };
232
242
  scrollToBottom(true);
233
243
  };
234
- $[14] = scrollToBottom;
235
- $[15] = scrollToBottomOnRunStart;
236
- $[16] = t13;
237
- } else t13 = $[16];
244
+ $[16] = scrollToBottom;
245
+ $[17] = scrollToBottomOnRunStart;
246
+ $[18] = t13;
247
+ } else t13 = $[18];
238
248
  useAuiEvent("thread.runStart", t13);
239
249
  let t14;
240
- if ($[17] !== scrollToBottom || $[18] !== scrollToBottomOnThreadSwitch) {
250
+ if ($[19] !== scrollToBottom || $[20] !== scrollToBottomOnThreadSwitch) {
241
251
  t14 = () => {
242
252
  if (!scrollToBottomOnThreadSwitch) return;
243
253
  initializeScrollRequestedRef.current = false;
@@ -245,21 +255,22 @@ const useThreadMessagesFlatListAutoScroll = (t0) => {
245
255
  pendingScrollToBottomRef.current = { animated: false };
246
256
  scrollToBottom(false);
247
257
  };
248
- $[17] = scrollToBottom;
249
- $[18] = scrollToBottomOnThreadSwitch;
250
- $[19] = t14;
251
- } else t14 = $[19];
258
+ $[19] = scrollToBottom;
259
+ $[20] = scrollToBottomOnThreadSwitch;
260
+ $[21] = t14;
261
+ } else t14 = $[21];
252
262
  useAuiEvent("threadListItem.switchedTo", t14);
253
263
  let t15;
254
- if ($[20] !== handleContentSizeChange) {
264
+ if ($[22] !== handleContentSizeChange || $[23] !== handleLayout) {
255
265
  t15 = {
256
266
  handleLayout,
257
267
  handleScroll,
258
268
  handleContentSizeChange
259
269
  };
260
- $[20] = handleContentSizeChange;
261
- $[21] = t15;
262
- } else t15 = $[21];
270
+ $[22] = handleContentSizeChange;
271
+ $[23] = handleLayout;
272
+ $[24] = t15;
273
+ } else t15 = $[24];
263
274
  return t15;
264
275
  };
265
276
  const ThreadMessagesFlatList = forwardRef((t0, forwardedRef) => {
@@ -1 +1 @@
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
+ {"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","wasAtBottom","previousViewportHeight","viewportHeight_1","nativeEvent","layout","height","pending","handleLayout","t9","event_0","contentOffset","contentSize","layoutMeasurement","previousEventY","wasPinnedToBottom","y","upwardMove","handleScroll","t10","_width","metrics","previousContentHeight","wasAtBottom_0","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 const wasAtBottom = isAtBottomRef.current;\n const previousViewportHeight = metricsRef.current.viewportHeight;\n const viewportHeight = event.nativeEvent.layout.height;\n metricsRef.current.viewportHeight = viewportHeight;\n updateIsAtBottom();\n if (!wasAtBottom) return;\n // Layout changes are never user gestures, so they must not unpin. Past\n // the first measurement, a viewport change while pinned re-commands the\n // bottom position, since no content-size event follows a bare keyboard\n // open or close.\n if (\n autoScroll &&\n previousViewportHeight !== 0 &&\n viewportHeight !== previousViewportHeight\n ) {\n const pending = pendingScrollToBottomRef.current;\n scrollToBottom(pending ? pending.animated : false);\n } else {\n isAtBottomRef.current = true;\n }\n },\n [autoScroll, scrollToBottom, 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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"}
@@ -2,6 +2,7 @@ import { useAuiState } from "@assistant-ui/store";
2
2
  import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
3
3
  import { FlatList } from "react-native";
4
4
  import { jsx } from "react/jsx-runtime";
5
+ import "@assistant-ui/tap/react-shim";
5
6
  //#region src/primitives/threadList/ThreadListItems.tsx
6
7
  const ThreadListItems = (t0) => {
7
8
  const $ = c(9);
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadListItems.js","names":["ReactElement","useCallback","FlatList","FlatListProps","useAuiState","ThreadListItemsProps","Omit","renderItem","props","threadId","index","ThreadListItems","t0","$","_c","flatListProps","threadIds","_temp","t1","t2","item","renderFlatListItem","keyExtractor","_temp2","t3","s","threads","item_0"],"sources":["../../../src/primitives/threadList/ThreadListItems.tsx"],"sourcesContent":["import { type ReactElement, useCallback } from \"react\";\nimport { FlatList, type FlatListProps } from \"react-native\";\nimport { useAuiState } from \"@assistant-ui/store\";\n\nexport type ThreadListItemsProps = Omit<\n FlatListProps<string>,\n \"data\" | \"renderItem\"\n> & {\n renderItem: (props: { threadId: string; index: number }) => ReactElement;\n};\n\nexport const ThreadListItems = ({\n renderItem,\n ...flatListProps\n}: ThreadListItemsProps) => {\n const threadIds = useAuiState((s) => s.threads.threadIds);\n\n const renderFlatListItem = useCallback(\n ({ item, index }: { item: string; index: number }) => {\n return renderItem({ threadId: item, index });\n },\n [renderItem],\n );\n\n const keyExtractor = useCallback((item: string) => item, []);\n\n return (\n <FlatList\n data={threadIds as string[]}\n renderItem={renderFlatListItem}\n keyExtractor={keyExtractor}\n {...flatListProps}\n />\n );\n};\n"],"mappings":";;;;;AAWA,MAAaW,mBAAkBC,OAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CAAA,IAAAC;CAAA,IAAAR;CAAA,IAAAM,EAAA,OAAAD,IAAA;EAAC,CAAA,CAAAL,eAAAQ,iBAAAH;EAGTC,EAAA,KAAAD;EAAAC,EAAA,KAAAE;EAAAF,EAAA,KAAAN;CAAA,OAAA;EAAAQ,gBAAAF,EAAA;EAAAN,aAAAM,EAAA;CAAA;CACrB,MAAAG,YAAkBZ,YAAYa,KAA0B;CAAE,IAAAC;CAAA,IAAAL,EAAA,OAAAN,YAAA;EAGxDW,MAAAC,OAAA;GAAC,MAAA,EAAAC,MAAAV,UAAAS;GAAgD,OACxCZ,WAAW;IAAAE,UAAYW;IAAIV;GAAQ,CAAC;EAAC;EAC7CG,EAAA,KAAAN;EAAAM,EAAA,KAAAK;CAAA,OAAAA,KAAAL,EAAA;CAHH,MAAAQ,qBAA2BH;CAO3B,MAAAI,eAAqBC;CAIX,MAAAJ,KAAAH;CAAqB,IAAAQ;CAAA,IAAAX,EAAA,OAAAE,iBAAAF,EAAA,OAAAQ,sBAAAR,EAAA,OAAAM,IAAA;EAD7BK,KAAA,oBAAC,UAAD;GACQ,MAAAL;GACME,YAAAA;GACEC;GAAY,GACtBP;EAAa,CAAA;EACjBF,EAAA,KAAAE;EAAAF,EAAA,KAAAQ;EAAAR,EAAA,KAAAM;EAAAN,EAAA,KAAAW;CAAA,OAAAA,KAAAX,EAAA;CAAA,OALFW;AAKE;AArByB,SAAAP,MAAAQ,GAAA;CAAA,OAIQA,EAACC,QAAQV;AAAU;AAJ3B,SAAAO,OAAAI,QAAA;CAAA,OAasBP;AAAI"}
1
+ {"version":3,"file":"ThreadListItems.js","names":["ReactElement","useCallback","FlatList","FlatListProps","useAuiState","ThreadListItemsProps","Omit","renderItem","props","threadId","index","ThreadListItems","t0","$","_c","flatListProps","threadIds","_temp","t1","t2","item","renderFlatListItem","keyExtractor","_temp2","t3","s","threads","item_0"],"sources":["../../../src/primitives/threadList/ThreadListItems.tsx"],"sourcesContent":["import { type ReactElement, useCallback } from \"react\";\nimport { FlatList, type FlatListProps } from \"react-native\";\nimport { useAuiState } from \"@assistant-ui/store\";\n\nexport type ThreadListItemsProps = Omit<\n FlatListProps<string>,\n \"data\" | \"renderItem\"\n> & {\n renderItem: (props: { threadId: string; index: number }) => ReactElement;\n};\n\nexport const ThreadListItems = ({\n renderItem,\n ...flatListProps\n}: ThreadListItemsProps) => {\n const threadIds = useAuiState((s) => s.threads.threadIds);\n\n const renderFlatListItem = useCallback(\n ({ item, index }: { item: string; index: number }) => {\n return renderItem({ threadId: item, index });\n },\n [renderItem],\n );\n\n const keyExtractor = useCallback((item: string) => item, []);\n\n return (\n <FlatList\n data={threadIds as string[]}\n renderItem={renderFlatListItem}\n keyExtractor={keyExtractor}\n {...flatListProps}\n />\n );\n};\n"],"mappings":";;;;;;AAWA,MAAaW,mBAAkBC,OAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CAAA,IAAAC;CAAA,IAAAR;CAAA,IAAAM,EAAA,OAAAD,IAAA;EAAC,CAAA,CAAAL,eAAAQ,iBAAAH;EAGTC,EAAA,KAAAD;EAAAC,EAAA,KAAAE;EAAAF,EAAA,KAAAN;CAAA,OAAA;EAAAQ,gBAAAF,EAAA;EAAAN,aAAAM,EAAA;CAAA;CACrB,MAAAG,YAAkBZ,YAAYa,KAA0B;CAAE,IAAAC;CAAA,IAAAL,EAAA,OAAAN,YAAA;EAGxDW,MAAAC,OAAA;GAAC,MAAA,EAAAC,MAAAV,UAAAS;GAAgD,OACxCZ,WAAW;IAAAE,UAAYW;IAAIV;GAAQ,CAAC;EAAC;EAC7CG,EAAA,KAAAN;EAAAM,EAAA,KAAAK;CAAA,OAAAA,KAAAL,EAAA;CAHH,MAAAQ,qBAA2BH;CAO3B,MAAAI,eAAqBC;CAIX,MAAAJ,KAAAH;CAAqB,IAAAQ;CAAA,IAAAX,EAAA,OAAAE,iBAAAF,EAAA,OAAAQ,sBAAAR,EAAA,OAAAM,IAAA;EAD7BK,KAAA,oBAAC,UAAD;GACQ,MAAAL;GACME,YAAAA;GACEC;GAAY,GACtBP;EAAa,CAAA;EACjBF,EAAA,KAAAE;EAAAF,EAAA,KAAAQ;EAAAR,EAAA,KAAAM;EAAAN,EAAA,KAAAW;CAAA,OAAAA,KAAAX,EAAA;CAAA,OALFW;AAKE;AArByB,SAAAP,MAAAQ,GAAA;CAAA,OAIQA,EAACC,QAAQV;AAAU;AAJ3B,SAAAO,OAAAI,QAAA;CAAA,OAasBP;AAAI"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react-native",
3
- "version": "0.1.31",
3
+ "version": "0.1.32",
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.22",
35
- "@assistant-ui/store": "^0.2.21",
36
- "@assistant-ui/tap": "^0.9.5",
37
- "assistant-stream": "^0.3.27",
34
+ "@assistant-ui/core": "^0.2.23",
35
+ "@assistant-ui/store": "^0.2.22",
36
+ "@assistant-ui/tap": "^0.9.6",
37
+ "assistant-stream": "^0.3.28",
38
38
  "zustand": "^5.0.14"
39
39
  },
40
40
  "peerDependencies": {
@@ -56,7 +56,7 @@
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.19"
59
+ "@assistant-ui/x-buildutils": "0.0.20"
60
60
  },
61
61
  "publishConfig": {
62
62
  "access": "public",
@@ -623,4 +623,226 @@ describe("ThreadMessages", () => {
623
623
  expect(getFlatListProps().scrollEventThrottle).toBe(16);
624
624
  });
625
625
  });
626
+
627
+ describe("isAtBottom pin state", () => {
628
+ const mountPinned = async () => {
629
+ await mountFlatList({ components: messageComponents });
630
+ const props = getFlatListProps();
631
+ await act(async () => {
632
+ props.onLayout?.({
633
+ nativeEvent: { layout: { height: 100 } },
634
+ });
635
+ props.onScroll?.({
636
+ nativeEvent: {
637
+ contentOffset: { y: 200 },
638
+ contentSize: { height: 300, width: 0 },
639
+ layoutMeasurement: { height: 100, width: 0 },
640
+ },
641
+ });
642
+ props.onContentSizeChange?.(0, 300);
643
+ });
644
+ h.scrollToEnd.mockClear();
645
+ return props;
646
+ };
647
+
648
+ it("stays pinned when the viewport shrinks while at the bottom", async () => {
649
+ const props = await mountPinned();
650
+
651
+ await act(async () => {
652
+ props.onLayout?.({
653
+ nativeEvent: { layout: { height: 60 } },
654
+ });
655
+ });
656
+ h.scrollToEnd.mockClear();
657
+ await act(async () => {
658
+ props.onContentSizeChange?.(0, 340);
659
+ });
660
+
661
+ expect(h.scrollToEnd).toHaveBeenCalledWith({ animated: false });
662
+ });
663
+
664
+ it("commands a bottom scroll when the viewport shrinks while pinned", async () => {
665
+ const props = await mountPinned();
666
+
667
+ await act(async () => {
668
+ props.onLayout?.({
669
+ nativeEvent: { layout: { height: 60 } },
670
+ });
671
+ });
672
+
673
+ expect(h.scrollToEnd).toHaveBeenCalledWith({ animated: false });
674
+ });
675
+
676
+ it("preserves a pending animated scroll on a pinned viewport change", async () => {
677
+ const props = await mountPinned();
678
+
679
+ await emit("thread.runStart");
680
+ h.scrollToEnd.mockClear();
681
+ await act(async () => {
682
+ props.onLayout?.({
683
+ nativeEvent: { layout: { height: 60 } },
684
+ });
685
+ });
686
+
687
+ expect(h.scrollToEnd).toHaveBeenCalledTimes(1);
688
+ expect(h.scrollToEnd).toHaveBeenCalledWith({ animated: true });
689
+ });
690
+
691
+ it("ignores a layout event with an unchanged viewport height", async () => {
692
+ const props = await mountPinned();
693
+
694
+ await act(async () => {
695
+ props.onLayout?.({
696
+ nativeEvent: { layout: { height: 100 } },
697
+ });
698
+ });
699
+
700
+ expect(h.scrollToEnd).not.toHaveBeenCalled();
701
+ });
702
+
703
+ it("does not command a scroll on the first layout measurement", async () => {
704
+ await mountFlatList({ components: messageComponents });
705
+ getFlatListProps();
706
+ h.scrollToEnd.mockClear();
707
+
708
+ await act(async () => {
709
+ getFlatListProps().onLayout?.({
710
+ nativeEvent: { layout: { height: 100 } },
711
+ });
712
+ });
713
+
714
+ expect(h.scrollToEnd).not.toHaveBeenCalled();
715
+ });
716
+
717
+ it("does not command a scroll on viewport change when autoScroll is off", async () => {
718
+ await mountFlatList({ components: messageComponents, autoScroll: false });
719
+ const props = getFlatListProps();
720
+ await act(async () => {
721
+ props.onLayout?.({
722
+ nativeEvent: { layout: { height: 100 } },
723
+ });
724
+ props.onScroll?.({
725
+ nativeEvent: {
726
+ contentOffset: { y: 200 },
727
+ contentSize: { height: 300, width: 0 },
728
+ layoutMeasurement: { height: 100, width: 0 },
729
+ },
730
+ });
731
+ props.onContentSizeChange?.(0, 300);
732
+ });
733
+ h.scrollToEnd.mockClear();
734
+
735
+ await act(async () => {
736
+ props.onLayout?.({
737
+ nativeEvent: { layout: { height: 60 } },
738
+ });
739
+ });
740
+
741
+ expect(h.scrollToEnd).not.toHaveBeenCalled();
742
+ });
743
+
744
+ it("stays unpinned when the viewport shrinks after scrolling away", async () => {
745
+ const props = await mountPinned();
746
+
747
+ await act(async () => {
748
+ props.onScroll?.({
749
+ nativeEvent: {
750
+ contentOffset: { y: 50 },
751
+ contentSize: { height: 300, width: 0 },
752
+ layoutMeasurement: { height: 100, width: 0 },
753
+ },
754
+ });
755
+ });
756
+ await act(async () => {
757
+ props.onLayout?.({
758
+ nativeEvent: { layout: { height: 60 } },
759
+ });
760
+ });
761
+ await act(async () => {
762
+ props.onContentSizeChange?.(0, 340);
763
+ });
764
+
765
+ expect(h.scrollToEnd).not.toHaveBeenCalled();
766
+ });
767
+
768
+ it("keeps the pin through a downward scroll echo after a commanded scroll", async () => {
769
+ const props = await mountPinned();
770
+
771
+ await act(async () => {
772
+ props.onScroll?.({
773
+ nativeEvent: {
774
+ contentOffset: { y: 50 },
775
+ contentSize: { height: 300, width: 0 },
776
+ layoutMeasurement: { height: 100, width: 0 },
777
+ },
778
+ });
779
+ });
780
+ await emit("thread.runStart");
781
+ await act(async () => {
782
+ props.onContentSizeChange?.(0, 320);
783
+ });
784
+ h.scrollToEnd.mockClear();
785
+ await act(async () => {
786
+ props.onScroll?.({
787
+ nativeEvent: {
788
+ contentOffset: { y: 150 },
789
+ contentSize: { height: 320, width: 0 },
790
+ layoutMeasurement: { height: 100, width: 0 },
791
+ },
792
+ });
793
+ });
794
+ await act(async () => {
795
+ props.onContentSizeChange?.(0, 360);
796
+ });
797
+
798
+ expect(h.scrollToEnd).toHaveBeenCalledWith({ animated: false });
799
+ });
800
+
801
+ it("unpins and cancels a pending scroll on an upward gesture echo", async () => {
802
+ const props = await mountPinned();
803
+
804
+ await emit("thread.runStart");
805
+ h.scrollToEnd.mockClear();
806
+ await act(async () => {
807
+ props.onScroll?.({
808
+ nativeEvent: {
809
+ contentOffset: { y: 50 },
810
+ contentSize: { height: 300, width: 0 },
811
+ layoutMeasurement: { height: 100, width: 0 },
812
+ },
813
+ });
814
+ });
815
+ await act(async () => {
816
+ props.onContentSizeChange?.(0, 360);
817
+ });
818
+
819
+ expect(h.scrollToEnd).not.toHaveBeenCalled();
820
+ });
821
+
822
+ it("scrolls on content growth while pinned and stays put while unpinned", async () => {
823
+ const props = await mountPinned();
824
+ h.scrollToEnd.mockClear();
825
+
826
+ await act(async () => {
827
+ props.onContentSizeChange?.(0, 340);
828
+ });
829
+ expect(h.scrollToEnd).toHaveBeenCalledWith({ animated: false });
830
+
831
+ await act(async () => {
832
+ props.onScroll?.({
833
+ nativeEvent: {
834
+ contentOffset: { y: 50 },
835
+ contentSize: { height: 340, width: 0 },
836
+ layoutMeasurement: { height: 100, width: 0 },
837
+ },
838
+ });
839
+ });
840
+ h.scrollToEnd.mockClear();
841
+ await act(async () => {
842
+ props.onContentSizeChange?.(0, 380);
843
+ });
844
+
845
+ expect(h.scrollToEnd).not.toHaveBeenCalled();
846
+ });
847
+ });
626
848
  });
@@ -249,10 +249,28 @@ const useThreadMessagesFlatListAutoScroll = ({
249
249
 
250
250
  const handleLayout = useCallback(
251
251
  (event: LayoutChangeEvent) => {
252
- metricsRef.current.viewportHeight = event.nativeEvent.layout.height;
252
+ const wasAtBottom = isAtBottomRef.current;
253
+ const previousViewportHeight = metricsRef.current.viewportHeight;
254
+ const viewportHeight = event.nativeEvent.layout.height;
255
+ metricsRef.current.viewportHeight = viewportHeight;
253
256
  updateIsAtBottom();
257
+ if (!wasAtBottom) return;
258
+ // Layout changes are never user gestures, so they must not unpin. Past
259
+ // the first measurement, a viewport change while pinned re-commands the
260
+ // bottom position, since no content-size event follows a bare keyboard
261
+ // open or close.
262
+ if (
263
+ autoScroll &&
264
+ previousViewportHeight !== 0 &&
265
+ viewportHeight !== previousViewportHeight
266
+ ) {
267
+ const pending = pendingScrollToBottomRef.current;
268
+ scrollToBottom(pending ? pending.animated : false);
269
+ } else {
270
+ isAtBottomRef.current = true;
271
+ }
254
272
  },
255
- [updateIsAtBottom],
273
+ [autoScroll, scrollToBottom, updateIsAtBottom],
256
274
  );
257
275
 
258
276
  const handleScroll = useCallback(