@assistant-ui/react-native 0.1.40 → 0.1.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/primitives/composer/ComposerAddAttachment.d.ts +3 -5
- package/dist/primitives/composer/ComposerAddAttachment.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerAddAttachment.js +2 -4
- package/dist/primitives/composer/ComposerAddAttachment.js.map +1 -1
- package/dist/primitives/message/MessageContent.d.ts.map +1 -1
- package/dist/primitives/message/MessageContent.js +1 -1
- package/dist/primitives/message/MessageContent.js.map +1 -1
- package/dist/primitives/message/MessageParts.js +2 -14
- package/dist/primitives/message/MessageParts.js.map +1 -1
- package/dist/primitives/thread/ThreadMessages.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadMessages.js +186 -152
- package/dist/primitives/thread/ThreadMessages.js.map +1 -1
- package/package.json +9 -8
- package/src/index.ts +29 -0
- package/src/primitives/composer/ComposerAddAttachment.tsx +3 -8
- package/src/primitives/message/MessageContent.runtime.test.tsx +81 -0
- package/src/primitives/message/MessageContent.test.tsx +6 -1
- package/src/primitives/message/MessageContent.tsx +13 -7
- package/src/primitives/message/MessageParts.test.tsx +116 -0
- package/src/primitives/message/MessageParts.tsx +1 -24
- package/src/primitives/thread/ThreadMessages.test.tsx +256 -53
- package/src/primitives/thread/ThreadMessages.tsx +67 -26
|
@@ -206,6 +206,7 @@ const useComposedFlatListRef = (
|
|
|
206
206
|
const useThreadMessagesFlatListAutoScroll = ({
|
|
207
207
|
flatListRef,
|
|
208
208
|
hasMessages,
|
|
209
|
+
horizontal = false,
|
|
209
210
|
autoScroll = true,
|
|
210
211
|
scrollToBottomOnRunStart = true,
|
|
211
212
|
scrollToBottomOnInitialize = true,
|
|
@@ -213,6 +214,7 @@ const useThreadMessagesFlatListAutoScroll = ({
|
|
|
213
214
|
}: {
|
|
214
215
|
flatListRef: RefObject<FlatList<ThreadMessage> | null>;
|
|
215
216
|
hasMessages: boolean;
|
|
217
|
+
horizontal?: boolean | null | undefined;
|
|
216
218
|
autoScroll?: boolean | undefined;
|
|
217
219
|
scrollToBottomOnRunStart?: boolean | undefined;
|
|
218
220
|
scrollToBottomOnInitialize?: boolean | undefined;
|
|
@@ -224,9 +226,16 @@ const useThreadMessagesFlatListAutoScroll = ({
|
|
|
224
226
|
scrollY: 0,
|
|
225
227
|
});
|
|
226
228
|
const isAtBottomRef = useRef(true);
|
|
227
|
-
const
|
|
229
|
+
const lastScrollEventOffsetRef = useRef(0);
|
|
228
230
|
const initializeScrollRequestedRef = useRef(false);
|
|
229
|
-
const
|
|
231
|
+
const contentSizeVersionRef = useRef(0);
|
|
232
|
+
const pendingScrollToBottomRef = useRef<
|
|
233
|
+
| false
|
|
234
|
+
| {
|
|
235
|
+
animated: boolean;
|
|
236
|
+
minimumContentSizeVersion: number;
|
|
237
|
+
}
|
|
238
|
+
>(false);
|
|
230
239
|
|
|
231
240
|
const updateIsAtBottom = useCallback(() => {
|
|
232
241
|
const { contentHeight, scrollY, viewportHeight } = metricsRef.current;
|
|
@@ -241,9 +250,10 @@ const useThreadMessagesFlatListAutoScroll = ({
|
|
|
241
250
|
const scrollToBottom = useCallback(
|
|
242
251
|
(animated: boolean) => {
|
|
243
252
|
const { contentHeight, viewportHeight } = metricsRef.current;
|
|
244
|
-
|
|
253
|
+
const offset = Math.max(0, contentHeight - viewportHeight);
|
|
254
|
+
metricsRef.current.scrollY = offset;
|
|
245
255
|
isAtBottomRef.current = true;
|
|
246
|
-
flatListRef.current?.
|
|
256
|
+
flatListRef.current?.scrollToOffset({ offset, animated });
|
|
247
257
|
},
|
|
248
258
|
[flatListRef],
|
|
249
259
|
);
|
|
@@ -252,9 +262,21 @@ const useThreadMessagesFlatListAutoScroll = ({
|
|
|
252
262
|
(event: LayoutChangeEvent) => {
|
|
253
263
|
const wasAtBottom = isAtBottomRef.current;
|
|
254
264
|
const previousViewportHeight = metricsRef.current.viewportHeight;
|
|
255
|
-
const viewportHeight =
|
|
265
|
+
const viewportHeight = horizontal
|
|
266
|
+
? event.nativeEvent.layout.width
|
|
267
|
+
: event.nativeEvent.layout.height;
|
|
256
268
|
metricsRef.current.viewportHeight = viewportHeight;
|
|
257
269
|
updateIsAtBottom();
|
|
270
|
+
const pending = pendingScrollToBottomRef.current;
|
|
271
|
+
if (
|
|
272
|
+
pending &&
|
|
273
|
+
contentSizeVersionRef.current >= pending.minimumContentSizeVersion &&
|
|
274
|
+
viewportHeight > 0
|
|
275
|
+
) {
|
|
276
|
+
pendingScrollToBottomRef.current = false;
|
|
277
|
+
scrollToBottom(pending.animated);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
258
280
|
if (!wasAtBottom) return;
|
|
259
281
|
// Layout changes are never user gestures, so they must not unpin. Past
|
|
260
282
|
// the first measurement, a viewport change while pinned re-commands the
|
|
@@ -265,29 +287,31 @@ const useThreadMessagesFlatListAutoScroll = ({
|
|
|
265
287
|
previousViewportHeight !== 0 &&
|
|
266
288
|
viewportHeight !== previousViewportHeight
|
|
267
289
|
) {
|
|
268
|
-
const pending = pendingScrollToBottomRef.current;
|
|
269
290
|
scrollToBottom(pending ? pending.animated : false);
|
|
270
291
|
} else {
|
|
271
292
|
isAtBottomRef.current = true;
|
|
272
293
|
}
|
|
273
294
|
},
|
|
274
|
-
[autoScroll, scrollToBottom, updateIsAtBottom],
|
|
295
|
+
[autoScroll, horizontal, scrollToBottom, updateIsAtBottom],
|
|
275
296
|
);
|
|
276
297
|
|
|
277
298
|
const handleScroll = useCallback(
|
|
278
299
|
(event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
279
300
|
const { contentOffset, contentSize, layoutMeasurement } =
|
|
280
301
|
event.nativeEvent;
|
|
281
|
-
const
|
|
302
|
+
const scrollOffset = horizontal ? contentOffset.x : contentOffset.y;
|
|
303
|
+
const previousEventOffset = lastScrollEventOffsetRef.current;
|
|
282
304
|
const wasPinnedToBottom = isAtBottomRef.current;
|
|
283
|
-
|
|
305
|
+
lastScrollEventOffsetRef.current = scrollOffset;
|
|
284
306
|
metricsRef.current = {
|
|
285
|
-
contentHeight: contentSize.height,
|
|
286
|
-
viewportHeight:
|
|
287
|
-
|
|
307
|
+
contentHeight: horizontal ? contentSize.width : contentSize.height,
|
|
308
|
+
viewportHeight: horizontal
|
|
309
|
+
? layoutMeasurement.width
|
|
310
|
+
: layoutMeasurement.height,
|
|
311
|
+
scrollY: scrollOffset,
|
|
288
312
|
};
|
|
289
313
|
updateIsAtBottom();
|
|
290
|
-
const upwardMove =
|
|
314
|
+
const upwardMove = scrollOffset < previousEventOffset;
|
|
291
315
|
// Only a deliberate upward move unpins or cancels a pending scroll.
|
|
292
316
|
// Gestures are detected echo-to-echo because a commanded scroll
|
|
293
317
|
// optimistically moves the tracked position ahead of its ascending
|
|
@@ -299,22 +323,28 @@ const useThreadMessagesFlatListAutoScroll = ({
|
|
|
299
323
|
pendingScrollToBottomRef.current = false;
|
|
300
324
|
}
|
|
301
325
|
},
|
|
302
|
-
[updateIsAtBottom],
|
|
326
|
+
[horizontal, updateIsAtBottom],
|
|
303
327
|
);
|
|
304
328
|
|
|
305
329
|
const handleContentSizeChange = useCallback(
|
|
306
|
-
(
|
|
330
|
+
(width: number, height: number) => {
|
|
307
331
|
const metrics = metricsRef.current;
|
|
332
|
+
const contentHeight = horizontal ? width : height;
|
|
308
333
|
const previousContentHeight = metrics.contentHeight;
|
|
309
334
|
const wasAtBottom = isAtBottomRef.current;
|
|
310
|
-
|
|
335
|
+
if (contentHeight > 0) contentSizeVersionRef.current += 1;
|
|
336
|
+
metrics.contentHeight = contentHeight;
|
|
311
337
|
updateIsAtBottom();
|
|
312
338
|
|
|
313
|
-
//
|
|
314
|
-
//
|
|
315
|
-
// event, once real metrics exist.
|
|
339
|
+
// Initialize and thread-switch requests are repeated after the list has
|
|
340
|
+
// measured so the explicit bottom offset uses real content metrics.
|
|
316
341
|
const pendingScroll = pendingScrollToBottomRef.current;
|
|
317
|
-
if (
|
|
342
|
+
if (
|
|
343
|
+
pendingScroll &&
|
|
344
|
+
contentSizeVersionRef.current >=
|
|
345
|
+
pendingScroll.minimumContentSizeVersion &&
|
|
346
|
+
metrics.viewportHeight > 0
|
|
347
|
+
) {
|
|
318
348
|
pendingScrollToBottomRef.current = false;
|
|
319
349
|
scrollToBottom(pendingScroll.animated);
|
|
320
350
|
return;
|
|
@@ -323,11 +353,11 @@ const useThreadMessagesFlatListAutoScroll = ({
|
|
|
323
353
|
if (!autoScroll) return;
|
|
324
354
|
if (!wasAtBottom) return;
|
|
325
355
|
if (previousContentHeight === 0) return;
|
|
326
|
-
if (
|
|
356
|
+
if (contentHeight <= previousContentHeight) return;
|
|
327
357
|
|
|
328
358
|
scrollToBottom(false);
|
|
329
359
|
},
|
|
330
|
-
[autoScroll, scrollToBottom, updateIsAtBottom],
|
|
360
|
+
[autoScroll, horizontal, scrollToBottom, updateIsAtBottom],
|
|
331
361
|
);
|
|
332
362
|
|
|
333
363
|
useEffect(() => {
|
|
@@ -339,21 +369,30 @@ const useThreadMessagesFlatListAutoScroll = ({
|
|
|
339
369
|
if (initializeScrollRequestedRef.current) return;
|
|
340
370
|
|
|
341
371
|
initializeScrollRequestedRef.current = true;
|
|
342
|
-
pendingScrollToBottomRef.current = {
|
|
372
|
+
pendingScrollToBottomRef.current = {
|
|
373
|
+
animated: false,
|
|
374
|
+
minimumContentSizeVersion: Math.max(1, contentSizeVersionRef.current),
|
|
375
|
+
};
|
|
343
376
|
scrollToBottom(false);
|
|
344
377
|
}, [hasMessages, scrollToBottom, scrollToBottomOnInitialize]);
|
|
345
378
|
|
|
346
379
|
useAuiEvent("thread.runStart", () => {
|
|
347
380
|
if (!scrollToBottomOnRunStart) return;
|
|
348
|
-
pendingScrollToBottomRef.current = {
|
|
381
|
+
pendingScrollToBottomRef.current = {
|
|
382
|
+
animated: true,
|
|
383
|
+
minimumContentSizeVersion: contentSizeVersionRef.current + 1,
|
|
384
|
+
};
|
|
349
385
|
scrollToBottom(true);
|
|
350
386
|
});
|
|
351
387
|
|
|
352
388
|
useAuiEvent("threads.selectionChanged", () => {
|
|
353
389
|
if (!scrollToBottomOnThreadSwitch) return;
|
|
354
390
|
initializeScrollRequestedRef.current = false;
|
|
355
|
-
|
|
356
|
-
pendingScrollToBottomRef.current = {
|
|
391
|
+
lastScrollEventOffsetRef.current = 0;
|
|
392
|
+
pendingScrollToBottomRef.current = {
|
|
393
|
+
animated: false,
|
|
394
|
+
minimumContentSizeVersion: contentSizeVersionRef.current + 1,
|
|
395
|
+
};
|
|
357
396
|
scrollToBottom(false);
|
|
358
397
|
});
|
|
359
398
|
|
|
@@ -393,6 +432,7 @@ export const ThreadMessagesFlatList = forwardRef<
|
|
|
393
432
|
} = useThreadMessagesFlatListAutoScroll({
|
|
394
433
|
flatListRef,
|
|
395
434
|
hasMessages: messages.length > 0,
|
|
435
|
+
horizontal: flatListProps.horizontal,
|
|
396
436
|
autoScroll,
|
|
397
437
|
scrollToBottomOnInitialize,
|
|
398
438
|
scrollToBottomOnRunStart,
|
|
@@ -451,6 +491,7 @@ export const ThreadMessagesFlatList = forwardRef<
|
|
|
451
491
|
data={messages as unknown as ThreadMessage[]}
|
|
452
492
|
renderItem={renderItem}
|
|
453
493
|
keyExtractor={keyExtractor}
|
|
494
|
+
maintainVisibleContentPosition={{ minIndexForVisible: 0 }}
|
|
454
495
|
{...(scrollTracking
|
|
455
496
|
? {
|
|
456
497
|
onContentSizeChange: handleContentSizeChange,
|