@xinghunm/ai-chat 1.1.0 → 1.1.1
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.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +235 -66
- package/dist/index.mjs +235 -66
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -68,6 +68,7 @@ var DEFAULT_AI_CHAT_LABELS = {
|
|
|
68
68
|
sendButton: "Send",
|
|
69
69
|
stopButton: "Stop",
|
|
70
70
|
retryButton: "Retry",
|
|
71
|
+
scrollToLatest: "Jump to latest",
|
|
71
72
|
placeholder: "Ask something...",
|
|
72
73
|
modeLabelAsk: "Ask",
|
|
73
74
|
modeLabelPlan: "Plan",
|
|
@@ -3360,6 +3361,8 @@ var HeroSubtitle = import_styled8.default.p`
|
|
|
3360
3361
|
|
|
3361
3362
|
// src/components/chat-thread/index.tsx
|
|
3362
3363
|
var import_jsx_runtime10 = require("@emotion/react/jsx-runtime");
|
|
3364
|
+
var CHAT_THREAD_PINNED_THRESHOLD_PX = 32;
|
|
3365
|
+
var isThreadPinnedToBottom = (container) => container.scrollHeight - container.clientHeight - container.scrollTop <= CHAT_THREAD_PINNED_THRESHOLD_PX;
|
|
3363
3366
|
var renderChatMessage = ({
|
|
3364
3367
|
message,
|
|
3365
3368
|
mode,
|
|
@@ -3433,6 +3436,7 @@ var ChatThreadView = ({
|
|
|
3433
3436
|
streamingMessage,
|
|
3434
3437
|
error,
|
|
3435
3438
|
retryButtonLabel,
|
|
3439
|
+
scrollToLatestLabel,
|
|
3436
3440
|
onRetry,
|
|
3437
3441
|
onConfirmationSubmit,
|
|
3438
3442
|
onQuestionnaireSubmit,
|
|
@@ -3446,9 +3450,15 @@ var ChatThreadView = ({
|
|
|
3446
3450
|
const latestTurn = conversationTurns[conversationTurns.length - 1];
|
|
3447
3451
|
const previousTurns = conversationTurns.slice(0, -1);
|
|
3448
3452
|
const latestUserMessageId = latestTurn?.userMessage?.id;
|
|
3449
|
-
const
|
|
3453
|
+
const latestHistoryMessage = historyMessages[historyMessages.length - 1];
|
|
3454
|
+
const latestTurnRef = (0, import_react11.useRef)(null);
|
|
3450
3455
|
const reservedSpaceFrameRef = (0, import_react11.useRef)(null);
|
|
3456
|
+
const isPinnedRef = (0, import_react11.useRef)(true);
|
|
3457
|
+
const lastHistoryMessageIdRef = (0, import_react11.useRef)(latestHistoryMessage?.id);
|
|
3458
|
+
const lastStreamingMessageIdRef = (0, import_react11.useRef)(streamingMessage?.id);
|
|
3451
3459
|
const [latestTurnMinHeight, setLatestTurnMinHeight] = (0, import_react11.useState)(0);
|
|
3460
|
+
const [isDetached, setIsDetached] = (0, import_react11.useState)(false);
|
|
3461
|
+
const [pendingNewMessageCount, setPendingNewMessageCount] = (0, import_react11.useState)(0);
|
|
3452
3462
|
const measureLatestTurnMinHeight = (0, import_react11.useCallback)(() => {
|
|
3453
3463
|
const container = containerRef.current;
|
|
3454
3464
|
if (!container)
|
|
@@ -3459,26 +3469,86 @@ var ChatThreadView = ({
|
|
|
3459
3469
|
const nextMinHeight = Math.max(0, container.clientHeight - paddingTop - paddingBottom);
|
|
3460
3470
|
setLatestTurnMinHeight((current) => current === nextMinHeight ? current : nextMinHeight);
|
|
3461
3471
|
}, []);
|
|
3462
|
-
const
|
|
3472
|
+
const scrollToBottom = (0, import_react11.useCallback)((force = false) => {
|
|
3463
3473
|
const container = containerRef.current;
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
const
|
|
3469
|
-
|
|
3470
|
-
0,
|
|
3471
|
-
container.scrollTop + (targetRect.top - containerRect.top) - CHAT_THREAD_SCROLL_TOP_GAP
|
|
3472
|
-
);
|
|
3474
|
+
if (!container)
|
|
3475
|
+
return false;
|
|
3476
|
+
if (!force && !isPinnedRef.current)
|
|
3477
|
+
return false;
|
|
3478
|
+
const nextScrollTop = Math.max(0, container.scrollHeight - container.clientHeight);
|
|
3479
|
+
container.scrollTop = nextScrollTop;
|
|
3473
3480
|
if (typeof container.scrollTo === "function") {
|
|
3474
3481
|
container.scrollTo({
|
|
3475
3482
|
top: nextScrollTop,
|
|
3476
3483
|
behavior: "auto"
|
|
3477
3484
|
});
|
|
3485
|
+
}
|
|
3486
|
+
return true;
|
|
3487
|
+
}, []);
|
|
3488
|
+
const markThreadPinned = (0, import_react11.useCallback)(() => {
|
|
3489
|
+
isPinnedRef.current = true;
|
|
3490
|
+
setIsDetached(false);
|
|
3491
|
+
setPendingNewMessageCount(0);
|
|
3492
|
+
}, []);
|
|
3493
|
+
const scrollToBottomAndPin = (0, import_react11.useCallback)(
|
|
3494
|
+
(force = false) => {
|
|
3495
|
+
const didScroll = scrollToBottom(force);
|
|
3496
|
+
if (!didScroll)
|
|
3497
|
+
return;
|
|
3498
|
+
markThreadPinned();
|
|
3499
|
+
},
|
|
3500
|
+
[markThreadPinned, scrollToBottom]
|
|
3501
|
+
);
|
|
3502
|
+
const handleContainerScroll = (0, import_react11.useCallback)(() => {
|
|
3503
|
+
const container = containerRef.current;
|
|
3504
|
+
if (!container)
|
|
3478
3505
|
return;
|
|
3506
|
+
const nextPinned = isThreadPinnedToBottom(container);
|
|
3507
|
+
isPinnedRef.current = nextPinned;
|
|
3508
|
+
setIsDetached(!nextPinned);
|
|
3509
|
+
if (nextPinned) {
|
|
3510
|
+
setPendingNewMessageCount(0);
|
|
3479
3511
|
}
|
|
3480
|
-
container.scrollTop = nextScrollTop;
|
|
3481
3512
|
}, []);
|
|
3513
|
+
(0, import_react11.useLayoutEffect)(() => {
|
|
3514
|
+
const nextHistoryMessageId = latestHistoryMessage?.id;
|
|
3515
|
+
if (lastHistoryMessageIdRef.current === nextHistoryMessageId) {
|
|
3516
|
+
return;
|
|
3517
|
+
}
|
|
3518
|
+
lastHistoryMessageIdRef.current = nextHistoryMessageId;
|
|
3519
|
+
if (!latestHistoryMessage) {
|
|
3520
|
+
return;
|
|
3521
|
+
}
|
|
3522
|
+
if (latestHistoryMessage.role === "user") {
|
|
3523
|
+
window.requestAnimationFrame(() => {
|
|
3524
|
+
if (!scrollToBottom(true)) {
|
|
3525
|
+
return;
|
|
3526
|
+
}
|
|
3527
|
+
markThreadPinned();
|
|
3528
|
+
});
|
|
3529
|
+
return;
|
|
3530
|
+
}
|
|
3531
|
+
if (!isPinnedRef.current && latestHistoryMessage.role === "assistant" && latestHistoryMessage.id !== lastStreamingMessageIdRef.current) {
|
|
3532
|
+
window.requestAnimationFrame(() => {
|
|
3533
|
+
setPendingNewMessageCount((current) => current + 1);
|
|
3534
|
+
});
|
|
3535
|
+
}
|
|
3536
|
+
}, [latestHistoryMessage, markThreadPinned, scrollToBottom]);
|
|
3537
|
+
(0, import_react11.useLayoutEffect)(() => {
|
|
3538
|
+
const nextStreamingMessageId = streamingMessage?.id;
|
|
3539
|
+
if (lastStreamingMessageIdRef.current === nextStreamingMessageId) {
|
|
3540
|
+
return;
|
|
3541
|
+
}
|
|
3542
|
+
lastStreamingMessageIdRef.current = nextStreamingMessageId;
|
|
3543
|
+
if (!streamingMessage || streamingMessage.role !== "assistant") {
|
|
3544
|
+
return;
|
|
3545
|
+
}
|
|
3546
|
+
if (!isPinnedRef.current) {
|
|
3547
|
+
window.requestAnimationFrame(() => {
|
|
3548
|
+
setPendingNewMessageCount((current) => current + 1);
|
|
3549
|
+
});
|
|
3550
|
+
}
|
|
3551
|
+
}, [streamingMessage]);
|
|
3482
3552
|
(0, import_react11.useLayoutEffect)(() => {
|
|
3483
3553
|
if (reservedSpaceFrameRef.current !== null) {
|
|
3484
3554
|
window.cancelAnimationFrame(reservedSpaceFrameRef.current);
|
|
@@ -3488,6 +3558,7 @@ var ChatThreadView = ({
|
|
|
3488
3558
|
reservedSpaceFrameRef.current = window.requestAnimationFrame(() => {
|
|
3489
3559
|
reservedSpaceFrameRef.current = null;
|
|
3490
3560
|
setLatestTurnMinHeight((current) => current === 0 ? current : 0);
|
|
3561
|
+
scrollToBottom();
|
|
3491
3562
|
});
|
|
3492
3563
|
return () => {
|
|
3493
3564
|
if (reservedSpaceFrameRef.current !== null) {
|
|
@@ -3499,7 +3570,7 @@ var ChatThreadView = ({
|
|
|
3499
3570
|
reservedSpaceFrameRef.current = window.requestAnimationFrame(() => {
|
|
3500
3571
|
reservedSpaceFrameRef.current = null;
|
|
3501
3572
|
measureLatestTurnMinHeight();
|
|
3502
|
-
|
|
3573
|
+
scrollToBottom();
|
|
3503
3574
|
});
|
|
3504
3575
|
return () => {
|
|
3505
3576
|
if (reservedSpaceFrameRef.current !== null) {
|
|
@@ -3507,13 +3578,18 @@ var ChatThreadView = ({
|
|
|
3507
3578
|
reservedSpaceFrameRef.current = null;
|
|
3508
3579
|
}
|
|
3509
3580
|
};
|
|
3510
|
-
}, [latestUserMessageId, measureLatestTurnMinHeight,
|
|
3581
|
+
}, [latestTurn, latestUserMessageId, error, measureLatestTurnMinHeight, scrollToBottom]);
|
|
3511
3582
|
(0, import_react11.useLayoutEffect)(() => {
|
|
3512
|
-
if (!
|
|
3583
|
+
if (!latestTurn)
|
|
3513
3584
|
return;
|
|
3514
3585
|
const handleResize = () => {
|
|
3586
|
+
if (!latestUserMessageId) {
|
|
3587
|
+
setLatestTurnMinHeight((current) => current === 0 ? current : 0);
|
|
3588
|
+
scrollToBottom();
|
|
3589
|
+
return;
|
|
3590
|
+
}
|
|
3515
3591
|
measureLatestTurnMinHeight();
|
|
3516
|
-
|
|
3592
|
+
scrollToBottom();
|
|
3517
3593
|
};
|
|
3518
3594
|
const container = containerRef.current;
|
|
3519
3595
|
let resizeObserver = null;
|
|
@@ -3528,57 +3604,101 @@ var ChatThreadView = ({
|
|
|
3528
3604
|
resizeObserver?.disconnect();
|
|
3529
3605
|
window.removeEventListener("resize", handleResize);
|
|
3530
3606
|
};
|
|
3531
|
-
}, [latestUserMessageId, measureLatestTurnMinHeight,
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3607
|
+
}, [latestTurn, latestUserMessageId, measureLatestTurnMinHeight, scrollToBottom]);
|
|
3608
|
+
(0, import_react11.useLayoutEffect)(() => {
|
|
3609
|
+
const latestTurnElement = latestTurnRef.current;
|
|
3610
|
+
if (!latestTurnElement || typeof ResizeObserver === "undefined") {
|
|
3611
|
+
return;
|
|
3612
|
+
}
|
|
3613
|
+
const observer = new ResizeObserver(() => {
|
|
3614
|
+
scrollToBottom();
|
|
3615
|
+
});
|
|
3616
|
+
observer.observe(latestTurnElement);
|
|
3617
|
+
return () => {
|
|
3618
|
+
observer.disconnect();
|
|
3619
|
+
};
|
|
3620
|
+
}, [latestTurn, scrollToBottom]);
|
|
3621
|
+
(0, import_react11.useLayoutEffect)(() => {
|
|
3622
|
+
const latestTurnElement = latestTurnRef.current;
|
|
3623
|
+
if (!latestTurnElement || typeof MutationObserver === "undefined") {
|
|
3624
|
+
return;
|
|
3625
|
+
}
|
|
3626
|
+
const observer = new MutationObserver(() => {
|
|
3627
|
+
scrollToBottom();
|
|
3628
|
+
});
|
|
3629
|
+
observer.observe(latestTurnElement, {
|
|
3630
|
+
childList: true,
|
|
3631
|
+
subtree: true,
|
|
3632
|
+
characterData: true
|
|
3633
|
+
});
|
|
3634
|
+
return () => {
|
|
3635
|
+
observer.disconnect();
|
|
3636
|
+
};
|
|
3637
|
+
}, [latestTurn, scrollToBottom]);
|
|
3638
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(ThreadViewport, { children: [
|
|
3639
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Container, { ref: containerRef, "data-testid": "chat-thread", onScroll: handleContainerScroll, children: [
|
|
3640
|
+
previousTurns.map((turn) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(ConversationTurn, { "data-testid": "chat-thread-turn", children: [
|
|
3641
|
+
turn.userMessage ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MessageSlot, { children: renderChatMessage({
|
|
3642
|
+
message: turn.userMessage,
|
|
3643
|
+
mode: activeSessionMode,
|
|
3644
|
+
onConfirmationSubmit,
|
|
3645
|
+
onQuestionnaireSubmit,
|
|
3646
|
+
renderMessageBlock
|
|
3647
|
+
}) }) : null,
|
|
3648
|
+
turn.responseMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MessageSlot, { children: renderChatMessage({
|
|
3649
|
+
message,
|
|
3650
|
+
mode: activeSessionMode,
|
|
3651
|
+
onConfirmationSubmit,
|
|
3652
|
+
onQuestionnaireSubmit,
|
|
3653
|
+
renderMessageBlock
|
|
3654
|
+
}) }, message.id))
|
|
3655
|
+
] }, turn.id)),
|
|
3656
|
+
latestTurn ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
3657
|
+
ConversationTurn,
|
|
3658
|
+
{
|
|
3659
|
+
ref: latestTurnRef,
|
|
3660
|
+
"data-testid": "chat-thread-latest-turn",
|
|
3661
|
+
style: latestTurnMinHeight > 0 ? { minHeight: `${latestTurnMinHeight}px` } : void 0,
|
|
3662
|
+
children: [
|
|
3663
|
+
latestTurn.userMessage ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3664
|
+
MessageSlot,
|
|
3665
|
+
{
|
|
3666
|
+
"data-testid": "chat-latest-user-anchor",
|
|
3667
|
+
style: { scrollMarginTop: `${CHAT_THREAD_SCROLL_TOP_GAP}px` },
|
|
3668
|
+
children: renderChatMessage({
|
|
3669
|
+
message: latestTurn.userMessage,
|
|
3670
|
+
mode: activeSessionMode,
|
|
3671
|
+
onConfirmationSubmit,
|
|
3672
|
+
onQuestionnaireSubmit,
|
|
3673
|
+
renderMessageBlock
|
|
3674
|
+
})
|
|
3675
|
+
}
|
|
3676
|
+
) : null,
|
|
3677
|
+
latestTurn.responseMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MessageSlot, { children: renderChatMessage({
|
|
3678
|
+
message,
|
|
3679
|
+
mode: activeSessionMode,
|
|
3680
|
+
onConfirmationSubmit,
|
|
3681
|
+
onQuestionnaireSubmit,
|
|
3682
|
+
renderMessageBlock
|
|
3683
|
+
}) }, message.id)),
|
|
3684
|
+
error ? renderErrorState({ error, onRetry, retryButtonLabel }) : null
|
|
3685
|
+
]
|
|
3686
|
+
}
|
|
3687
|
+
) : null,
|
|
3688
|
+
!latestTurn && error ? renderErrorState({ error, onRetry, retryButtonLabel }) : null
|
|
3689
|
+
] }),
|
|
3690
|
+
isDetached ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ScrollToLatestOverlay, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
3691
|
+
ScrollToLatestButton,
|
|
3551
3692
|
{
|
|
3552
|
-
|
|
3553
|
-
|
|
3693
|
+
type: "button",
|
|
3694
|
+
"data-testid": "chat-thread-scroll-to-latest",
|
|
3695
|
+
onClick: () => scrollToBottomAndPin(true),
|
|
3554
3696
|
children: [
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
{
|
|
3558
|
-
ref: latestUserMessageRef,
|
|
3559
|
-
"data-testid": "chat-latest-user-anchor",
|
|
3560
|
-
style: { scrollMarginTop: `${CHAT_THREAD_SCROLL_TOP_GAP}px` },
|
|
3561
|
-
children: renderChatMessage({
|
|
3562
|
-
message: latestTurn.userMessage,
|
|
3563
|
-
mode: activeSessionMode,
|
|
3564
|
-
onConfirmationSubmit,
|
|
3565
|
-
onQuestionnaireSubmit,
|
|
3566
|
-
renderMessageBlock
|
|
3567
|
-
})
|
|
3568
|
-
}
|
|
3569
|
-
) : null,
|
|
3570
|
-
latestTurn.responseMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MessageSlot, { children: renderChatMessage({
|
|
3571
|
-
message,
|
|
3572
|
-
mode: activeSessionMode,
|
|
3573
|
-
onConfirmationSubmit,
|
|
3574
|
-
onQuestionnaireSubmit,
|
|
3575
|
-
renderMessageBlock
|
|
3576
|
-
}) }, message.id)),
|
|
3577
|
-
error ? renderErrorState({ error, onRetry, retryButtonLabel }) : null
|
|
3697
|
+
scrollToLatestLabel,
|
|
3698
|
+
pendingNewMessageCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ScrollToLatestBadge, { "data-testid": "chat-thread-scroll-to-latest-count", children: pendingNewMessageCount }) : null
|
|
3578
3699
|
]
|
|
3579
3700
|
}
|
|
3580
|
-
) : null
|
|
3581
|
-
!latestTurn && error ? renderErrorState({ error, onRetry, retryButtonLabel }) : null
|
|
3701
|
+
) }) : null
|
|
3582
3702
|
] });
|
|
3583
3703
|
};
|
|
3584
3704
|
var EMPTY_MESSAGES = [];
|
|
@@ -3666,13 +3786,21 @@ var ChatThread = () => {
|
|
|
3666
3786
|
streamingMessage,
|
|
3667
3787
|
error,
|
|
3668
3788
|
retryButtonLabel: labels.retryButton,
|
|
3789
|
+
scrollToLatestLabel: labels.scrollToLatest,
|
|
3669
3790
|
onRetry: handleRetry,
|
|
3670
3791
|
onConfirmationSubmit: handleConfirmation,
|
|
3671
3792
|
onQuestionnaireSubmit: handleQuestionnaireSubmit,
|
|
3672
3793
|
renderMessageBlock
|
|
3673
|
-
}
|
|
3794
|
+
},
|
|
3795
|
+
activeSessionId ?? "chat-thread-empty"
|
|
3674
3796
|
);
|
|
3675
3797
|
};
|
|
3798
|
+
var ThreadViewport = import_styled9.default.div`
|
|
3799
|
+
position: relative;
|
|
3800
|
+
display: flex;
|
|
3801
|
+
flex: 1;
|
|
3802
|
+
min-height: 0;
|
|
3803
|
+
`;
|
|
3676
3804
|
var Container = import_styled9.default.div`
|
|
3677
3805
|
display: flex;
|
|
3678
3806
|
flex: 1;
|
|
@@ -3680,8 +3808,7 @@ var Container = import_styled9.default.div`
|
|
|
3680
3808
|
gap: 18px;
|
|
3681
3809
|
min-height: 0;
|
|
3682
3810
|
overflow: auto;
|
|
3683
|
-
padding: 24px;
|
|
3684
|
-
margin-bottom: 24px;
|
|
3811
|
+
padding: 24px 24px 88px;
|
|
3685
3812
|
overscroll-behavior: contain;
|
|
3686
3813
|
|
|
3687
3814
|
&::-webkit-scrollbar {
|
|
@@ -3733,6 +3860,48 @@ var RetryButton = import_styled9.default.button`
|
|
|
3733
3860
|
background: rgba(255, 255, 255, 0.08);
|
|
3734
3861
|
}
|
|
3735
3862
|
`;
|
|
3863
|
+
var ScrollToLatestOverlay = import_styled9.default.div`
|
|
3864
|
+
position: absolute;
|
|
3865
|
+
right: 24px;
|
|
3866
|
+
bottom: 24px;
|
|
3867
|
+
left: 24px;
|
|
3868
|
+
display: flex;
|
|
3869
|
+
justify-content: center;
|
|
3870
|
+
pointer-events: none;
|
|
3871
|
+
`;
|
|
3872
|
+
var ScrollToLatestButton = import_styled9.default.button`
|
|
3873
|
+
display: inline-flex;
|
|
3874
|
+
align-items: center;
|
|
3875
|
+
gap: 8px;
|
|
3876
|
+
border: 1px solid rgba(255, 255, 255, 0.14);
|
|
3877
|
+
border-radius: 999px;
|
|
3878
|
+
background: rgba(17, 18, 21, 0.92);
|
|
3879
|
+
color: rgba(255, 255, 255, 0.9);
|
|
3880
|
+
font-size: 12px;
|
|
3881
|
+
line-height: 1;
|
|
3882
|
+
padding: 10px 14px;
|
|
3883
|
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.28);
|
|
3884
|
+
cursor: pointer;
|
|
3885
|
+
z-index: 1;
|
|
3886
|
+
pointer-events: auto;
|
|
3887
|
+
|
|
3888
|
+
&:hover {
|
|
3889
|
+
background: rgba(28, 30, 36, 0.96);
|
|
3890
|
+
}
|
|
3891
|
+
`;
|
|
3892
|
+
var ScrollToLatestBadge = import_styled9.default.span`
|
|
3893
|
+
display: inline-flex;
|
|
3894
|
+
min-width: 18px;
|
|
3895
|
+
height: 18px;
|
|
3896
|
+
align-items: center;
|
|
3897
|
+
justify-content: center;
|
|
3898
|
+
padding: 0 6px;
|
|
3899
|
+
border-radius: 999px;
|
|
3900
|
+
background: rgba(109, 170, 255, 0.2);
|
|
3901
|
+
color: #9ac0ff;
|
|
3902
|
+
font-size: 11px;
|
|
3903
|
+
font-weight: 600;
|
|
3904
|
+
`;
|
|
3736
3905
|
|
|
3737
3906
|
// src/components/chat-composer/index.tsx
|
|
3738
3907
|
var import_react15 = require("react");
|
package/dist/index.mjs
CHANGED
|
@@ -21,6 +21,7 @@ var DEFAULT_AI_CHAT_LABELS = {
|
|
|
21
21
|
sendButton: "Send",
|
|
22
22
|
stopButton: "Stop",
|
|
23
23
|
retryButton: "Retry",
|
|
24
|
+
scrollToLatest: "Jump to latest",
|
|
24
25
|
placeholder: "Ask something...",
|
|
25
26
|
modeLabelAsk: "Ask",
|
|
26
27
|
modeLabelPlan: "Plan",
|
|
@@ -3317,6 +3318,8 @@ var HeroSubtitle = styled8.p`
|
|
|
3317
3318
|
|
|
3318
3319
|
// src/components/chat-thread/index.tsx
|
|
3319
3320
|
import { jsx as jsx10, jsxs as jsxs7 } from "@emotion/react/jsx-runtime";
|
|
3321
|
+
var CHAT_THREAD_PINNED_THRESHOLD_PX = 32;
|
|
3322
|
+
var isThreadPinnedToBottom = (container) => container.scrollHeight - container.clientHeight - container.scrollTop <= CHAT_THREAD_PINNED_THRESHOLD_PX;
|
|
3320
3323
|
var renderChatMessage = ({
|
|
3321
3324
|
message,
|
|
3322
3325
|
mode,
|
|
@@ -3390,6 +3393,7 @@ var ChatThreadView = ({
|
|
|
3390
3393
|
streamingMessage,
|
|
3391
3394
|
error,
|
|
3392
3395
|
retryButtonLabel,
|
|
3396
|
+
scrollToLatestLabel,
|
|
3393
3397
|
onRetry,
|
|
3394
3398
|
onConfirmationSubmit,
|
|
3395
3399
|
onQuestionnaireSubmit,
|
|
@@ -3403,9 +3407,15 @@ var ChatThreadView = ({
|
|
|
3403
3407
|
const latestTurn = conversationTurns[conversationTurns.length - 1];
|
|
3404
3408
|
const previousTurns = conversationTurns.slice(0, -1);
|
|
3405
3409
|
const latestUserMessageId = latestTurn?.userMessage?.id;
|
|
3406
|
-
const
|
|
3410
|
+
const latestHistoryMessage = historyMessages[historyMessages.length - 1];
|
|
3411
|
+
const latestTurnRef = useRef5(null);
|
|
3407
3412
|
const reservedSpaceFrameRef = useRef5(null);
|
|
3413
|
+
const isPinnedRef = useRef5(true);
|
|
3414
|
+
const lastHistoryMessageIdRef = useRef5(latestHistoryMessage?.id);
|
|
3415
|
+
const lastStreamingMessageIdRef = useRef5(streamingMessage?.id);
|
|
3408
3416
|
const [latestTurnMinHeight, setLatestTurnMinHeight] = useState4(0);
|
|
3417
|
+
const [isDetached, setIsDetached] = useState4(false);
|
|
3418
|
+
const [pendingNewMessageCount, setPendingNewMessageCount] = useState4(0);
|
|
3409
3419
|
const measureLatestTurnMinHeight = useCallback3(() => {
|
|
3410
3420
|
const container = containerRef.current;
|
|
3411
3421
|
if (!container)
|
|
@@ -3416,26 +3426,86 @@ var ChatThreadView = ({
|
|
|
3416
3426
|
const nextMinHeight = Math.max(0, container.clientHeight - paddingTop - paddingBottom);
|
|
3417
3427
|
setLatestTurnMinHeight((current) => current === nextMinHeight ? current : nextMinHeight);
|
|
3418
3428
|
}, []);
|
|
3419
|
-
const
|
|
3429
|
+
const scrollToBottom = useCallback3((force = false) => {
|
|
3420
3430
|
const container = containerRef.current;
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
const
|
|
3426
|
-
|
|
3427
|
-
0,
|
|
3428
|
-
container.scrollTop + (targetRect.top - containerRect.top) - CHAT_THREAD_SCROLL_TOP_GAP
|
|
3429
|
-
);
|
|
3431
|
+
if (!container)
|
|
3432
|
+
return false;
|
|
3433
|
+
if (!force && !isPinnedRef.current)
|
|
3434
|
+
return false;
|
|
3435
|
+
const nextScrollTop = Math.max(0, container.scrollHeight - container.clientHeight);
|
|
3436
|
+
container.scrollTop = nextScrollTop;
|
|
3430
3437
|
if (typeof container.scrollTo === "function") {
|
|
3431
3438
|
container.scrollTo({
|
|
3432
3439
|
top: nextScrollTop,
|
|
3433
3440
|
behavior: "auto"
|
|
3434
3441
|
});
|
|
3442
|
+
}
|
|
3443
|
+
return true;
|
|
3444
|
+
}, []);
|
|
3445
|
+
const markThreadPinned = useCallback3(() => {
|
|
3446
|
+
isPinnedRef.current = true;
|
|
3447
|
+
setIsDetached(false);
|
|
3448
|
+
setPendingNewMessageCount(0);
|
|
3449
|
+
}, []);
|
|
3450
|
+
const scrollToBottomAndPin = useCallback3(
|
|
3451
|
+
(force = false) => {
|
|
3452
|
+
const didScroll = scrollToBottom(force);
|
|
3453
|
+
if (!didScroll)
|
|
3454
|
+
return;
|
|
3455
|
+
markThreadPinned();
|
|
3456
|
+
},
|
|
3457
|
+
[markThreadPinned, scrollToBottom]
|
|
3458
|
+
);
|
|
3459
|
+
const handleContainerScroll = useCallback3(() => {
|
|
3460
|
+
const container = containerRef.current;
|
|
3461
|
+
if (!container)
|
|
3435
3462
|
return;
|
|
3463
|
+
const nextPinned = isThreadPinnedToBottom(container);
|
|
3464
|
+
isPinnedRef.current = nextPinned;
|
|
3465
|
+
setIsDetached(!nextPinned);
|
|
3466
|
+
if (nextPinned) {
|
|
3467
|
+
setPendingNewMessageCount(0);
|
|
3436
3468
|
}
|
|
3437
|
-
container.scrollTop = nextScrollTop;
|
|
3438
3469
|
}, []);
|
|
3470
|
+
useLayoutEffect2(() => {
|
|
3471
|
+
const nextHistoryMessageId = latestHistoryMessage?.id;
|
|
3472
|
+
if (lastHistoryMessageIdRef.current === nextHistoryMessageId) {
|
|
3473
|
+
return;
|
|
3474
|
+
}
|
|
3475
|
+
lastHistoryMessageIdRef.current = nextHistoryMessageId;
|
|
3476
|
+
if (!latestHistoryMessage) {
|
|
3477
|
+
return;
|
|
3478
|
+
}
|
|
3479
|
+
if (latestHistoryMessage.role === "user") {
|
|
3480
|
+
window.requestAnimationFrame(() => {
|
|
3481
|
+
if (!scrollToBottom(true)) {
|
|
3482
|
+
return;
|
|
3483
|
+
}
|
|
3484
|
+
markThreadPinned();
|
|
3485
|
+
});
|
|
3486
|
+
return;
|
|
3487
|
+
}
|
|
3488
|
+
if (!isPinnedRef.current && latestHistoryMessage.role === "assistant" && latestHistoryMessage.id !== lastStreamingMessageIdRef.current) {
|
|
3489
|
+
window.requestAnimationFrame(() => {
|
|
3490
|
+
setPendingNewMessageCount((current) => current + 1);
|
|
3491
|
+
});
|
|
3492
|
+
}
|
|
3493
|
+
}, [latestHistoryMessage, markThreadPinned, scrollToBottom]);
|
|
3494
|
+
useLayoutEffect2(() => {
|
|
3495
|
+
const nextStreamingMessageId = streamingMessage?.id;
|
|
3496
|
+
if (lastStreamingMessageIdRef.current === nextStreamingMessageId) {
|
|
3497
|
+
return;
|
|
3498
|
+
}
|
|
3499
|
+
lastStreamingMessageIdRef.current = nextStreamingMessageId;
|
|
3500
|
+
if (!streamingMessage || streamingMessage.role !== "assistant") {
|
|
3501
|
+
return;
|
|
3502
|
+
}
|
|
3503
|
+
if (!isPinnedRef.current) {
|
|
3504
|
+
window.requestAnimationFrame(() => {
|
|
3505
|
+
setPendingNewMessageCount((current) => current + 1);
|
|
3506
|
+
});
|
|
3507
|
+
}
|
|
3508
|
+
}, [streamingMessage]);
|
|
3439
3509
|
useLayoutEffect2(() => {
|
|
3440
3510
|
if (reservedSpaceFrameRef.current !== null) {
|
|
3441
3511
|
window.cancelAnimationFrame(reservedSpaceFrameRef.current);
|
|
@@ -3445,6 +3515,7 @@ var ChatThreadView = ({
|
|
|
3445
3515
|
reservedSpaceFrameRef.current = window.requestAnimationFrame(() => {
|
|
3446
3516
|
reservedSpaceFrameRef.current = null;
|
|
3447
3517
|
setLatestTurnMinHeight((current) => current === 0 ? current : 0);
|
|
3518
|
+
scrollToBottom();
|
|
3448
3519
|
});
|
|
3449
3520
|
return () => {
|
|
3450
3521
|
if (reservedSpaceFrameRef.current !== null) {
|
|
@@ -3456,7 +3527,7 @@ var ChatThreadView = ({
|
|
|
3456
3527
|
reservedSpaceFrameRef.current = window.requestAnimationFrame(() => {
|
|
3457
3528
|
reservedSpaceFrameRef.current = null;
|
|
3458
3529
|
measureLatestTurnMinHeight();
|
|
3459
|
-
|
|
3530
|
+
scrollToBottom();
|
|
3460
3531
|
});
|
|
3461
3532
|
return () => {
|
|
3462
3533
|
if (reservedSpaceFrameRef.current !== null) {
|
|
@@ -3464,13 +3535,18 @@ var ChatThreadView = ({
|
|
|
3464
3535
|
reservedSpaceFrameRef.current = null;
|
|
3465
3536
|
}
|
|
3466
3537
|
};
|
|
3467
|
-
}, [latestUserMessageId, measureLatestTurnMinHeight,
|
|
3538
|
+
}, [latestTurn, latestUserMessageId, error, measureLatestTurnMinHeight, scrollToBottom]);
|
|
3468
3539
|
useLayoutEffect2(() => {
|
|
3469
|
-
if (!
|
|
3540
|
+
if (!latestTurn)
|
|
3470
3541
|
return;
|
|
3471
3542
|
const handleResize = () => {
|
|
3543
|
+
if (!latestUserMessageId) {
|
|
3544
|
+
setLatestTurnMinHeight((current) => current === 0 ? current : 0);
|
|
3545
|
+
scrollToBottom();
|
|
3546
|
+
return;
|
|
3547
|
+
}
|
|
3472
3548
|
measureLatestTurnMinHeight();
|
|
3473
|
-
|
|
3549
|
+
scrollToBottom();
|
|
3474
3550
|
};
|
|
3475
3551
|
const container = containerRef.current;
|
|
3476
3552
|
let resizeObserver = null;
|
|
@@ -3485,57 +3561,101 @@ var ChatThreadView = ({
|
|
|
3485
3561
|
resizeObserver?.disconnect();
|
|
3486
3562
|
window.removeEventListener("resize", handleResize);
|
|
3487
3563
|
};
|
|
3488
|
-
}, [latestUserMessageId, measureLatestTurnMinHeight,
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3564
|
+
}, [latestTurn, latestUserMessageId, measureLatestTurnMinHeight, scrollToBottom]);
|
|
3565
|
+
useLayoutEffect2(() => {
|
|
3566
|
+
const latestTurnElement = latestTurnRef.current;
|
|
3567
|
+
if (!latestTurnElement || typeof ResizeObserver === "undefined") {
|
|
3568
|
+
return;
|
|
3569
|
+
}
|
|
3570
|
+
const observer = new ResizeObserver(() => {
|
|
3571
|
+
scrollToBottom();
|
|
3572
|
+
});
|
|
3573
|
+
observer.observe(latestTurnElement);
|
|
3574
|
+
return () => {
|
|
3575
|
+
observer.disconnect();
|
|
3576
|
+
};
|
|
3577
|
+
}, [latestTurn, scrollToBottom]);
|
|
3578
|
+
useLayoutEffect2(() => {
|
|
3579
|
+
const latestTurnElement = latestTurnRef.current;
|
|
3580
|
+
if (!latestTurnElement || typeof MutationObserver === "undefined") {
|
|
3581
|
+
return;
|
|
3582
|
+
}
|
|
3583
|
+
const observer = new MutationObserver(() => {
|
|
3584
|
+
scrollToBottom();
|
|
3585
|
+
});
|
|
3586
|
+
observer.observe(latestTurnElement, {
|
|
3587
|
+
childList: true,
|
|
3588
|
+
subtree: true,
|
|
3589
|
+
characterData: true
|
|
3590
|
+
});
|
|
3591
|
+
return () => {
|
|
3592
|
+
observer.disconnect();
|
|
3593
|
+
};
|
|
3594
|
+
}, [latestTurn, scrollToBottom]);
|
|
3595
|
+
return /* @__PURE__ */ jsxs7(ThreadViewport, { children: [
|
|
3596
|
+
/* @__PURE__ */ jsxs7(Container, { ref: containerRef, "data-testid": "chat-thread", onScroll: handleContainerScroll, children: [
|
|
3597
|
+
previousTurns.map((turn) => /* @__PURE__ */ jsxs7(ConversationTurn, { "data-testid": "chat-thread-turn", children: [
|
|
3598
|
+
turn.userMessage ? /* @__PURE__ */ jsx10(MessageSlot, { children: renderChatMessage({
|
|
3599
|
+
message: turn.userMessage,
|
|
3600
|
+
mode: activeSessionMode,
|
|
3601
|
+
onConfirmationSubmit,
|
|
3602
|
+
onQuestionnaireSubmit,
|
|
3603
|
+
renderMessageBlock
|
|
3604
|
+
}) }) : null,
|
|
3605
|
+
turn.responseMessages.map((message) => /* @__PURE__ */ jsx10(MessageSlot, { children: renderChatMessage({
|
|
3606
|
+
message,
|
|
3607
|
+
mode: activeSessionMode,
|
|
3608
|
+
onConfirmationSubmit,
|
|
3609
|
+
onQuestionnaireSubmit,
|
|
3610
|
+
renderMessageBlock
|
|
3611
|
+
}) }, message.id))
|
|
3612
|
+
] }, turn.id)),
|
|
3613
|
+
latestTurn ? /* @__PURE__ */ jsxs7(
|
|
3614
|
+
ConversationTurn,
|
|
3615
|
+
{
|
|
3616
|
+
ref: latestTurnRef,
|
|
3617
|
+
"data-testid": "chat-thread-latest-turn",
|
|
3618
|
+
style: latestTurnMinHeight > 0 ? { minHeight: `${latestTurnMinHeight}px` } : void 0,
|
|
3619
|
+
children: [
|
|
3620
|
+
latestTurn.userMessage ? /* @__PURE__ */ jsx10(
|
|
3621
|
+
MessageSlot,
|
|
3622
|
+
{
|
|
3623
|
+
"data-testid": "chat-latest-user-anchor",
|
|
3624
|
+
style: { scrollMarginTop: `${CHAT_THREAD_SCROLL_TOP_GAP}px` },
|
|
3625
|
+
children: renderChatMessage({
|
|
3626
|
+
message: latestTurn.userMessage,
|
|
3627
|
+
mode: activeSessionMode,
|
|
3628
|
+
onConfirmationSubmit,
|
|
3629
|
+
onQuestionnaireSubmit,
|
|
3630
|
+
renderMessageBlock
|
|
3631
|
+
})
|
|
3632
|
+
}
|
|
3633
|
+
) : null,
|
|
3634
|
+
latestTurn.responseMessages.map((message) => /* @__PURE__ */ jsx10(MessageSlot, { children: renderChatMessage({
|
|
3635
|
+
message,
|
|
3636
|
+
mode: activeSessionMode,
|
|
3637
|
+
onConfirmationSubmit,
|
|
3638
|
+
onQuestionnaireSubmit,
|
|
3639
|
+
renderMessageBlock
|
|
3640
|
+
}) }, message.id)),
|
|
3641
|
+
error ? renderErrorState({ error, onRetry, retryButtonLabel }) : null
|
|
3642
|
+
]
|
|
3643
|
+
}
|
|
3644
|
+
) : null,
|
|
3645
|
+
!latestTurn && error ? renderErrorState({ error, onRetry, retryButtonLabel }) : null
|
|
3646
|
+
] }),
|
|
3647
|
+
isDetached ? /* @__PURE__ */ jsx10(ScrollToLatestOverlay, { children: /* @__PURE__ */ jsxs7(
|
|
3648
|
+
ScrollToLatestButton,
|
|
3508
3649
|
{
|
|
3509
|
-
|
|
3510
|
-
|
|
3650
|
+
type: "button",
|
|
3651
|
+
"data-testid": "chat-thread-scroll-to-latest",
|
|
3652
|
+
onClick: () => scrollToBottomAndPin(true),
|
|
3511
3653
|
children: [
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
{
|
|
3515
|
-
ref: latestUserMessageRef,
|
|
3516
|
-
"data-testid": "chat-latest-user-anchor",
|
|
3517
|
-
style: { scrollMarginTop: `${CHAT_THREAD_SCROLL_TOP_GAP}px` },
|
|
3518
|
-
children: renderChatMessage({
|
|
3519
|
-
message: latestTurn.userMessage,
|
|
3520
|
-
mode: activeSessionMode,
|
|
3521
|
-
onConfirmationSubmit,
|
|
3522
|
-
onQuestionnaireSubmit,
|
|
3523
|
-
renderMessageBlock
|
|
3524
|
-
})
|
|
3525
|
-
}
|
|
3526
|
-
) : null,
|
|
3527
|
-
latestTurn.responseMessages.map((message) => /* @__PURE__ */ jsx10(MessageSlot, { children: renderChatMessage({
|
|
3528
|
-
message,
|
|
3529
|
-
mode: activeSessionMode,
|
|
3530
|
-
onConfirmationSubmit,
|
|
3531
|
-
onQuestionnaireSubmit,
|
|
3532
|
-
renderMessageBlock
|
|
3533
|
-
}) }, message.id)),
|
|
3534
|
-
error ? renderErrorState({ error, onRetry, retryButtonLabel }) : null
|
|
3654
|
+
scrollToLatestLabel,
|
|
3655
|
+
pendingNewMessageCount > 0 ? /* @__PURE__ */ jsx10(ScrollToLatestBadge, { "data-testid": "chat-thread-scroll-to-latest-count", children: pendingNewMessageCount }) : null
|
|
3535
3656
|
]
|
|
3536
3657
|
}
|
|
3537
|
-
) : null
|
|
3538
|
-
!latestTurn && error ? renderErrorState({ error, onRetry, retryButtonLabel }) : null
|
|
3658
|
+
) }) : null
|
|
3539
3659
|
] });
|
|
3540
3660
|
};
|
|
3541
3661
|
var EMPTY_MESSAGES = [];
|
|
@@ -3623,13 +3743,21 @@ var ChatThread = () => {
|
|
|
3623
3743
|
streamingMessage,
|
|
3624
3744
|
error,
|
|
3625
3745
|
retryButtonLabel: labels.retryButton,
|
|
3746
|
+
scrollToLatestLabel: labels.scrollToLatest,
|
|
3626
3747
|
onRetry: handleRetry,
|
|
3627
3748
|
onConfirmationSubmit: handleConfirmation,
|
|
3628
3749
|
onQuestionnaireSubmit: handleQuestionnaireSubmit,
|
|
3629
3750
|
renderMessageBlock
|
|
3630
|
-
}
|
|
3751
|
+
},
|
|
3752
|
+
activeSessionId ?? "chat-thread-empty"
|
|
3631
3753
|
);
|
|
3632
3754
|
};
|
|
3755
|
+
var ThreadViewport = styled9.div`
|
|
3756
|
+
position: relative;
|
|
3757
|
+
display: flex;
|
|
3758
|
+
flex: 1;
|
|
3759
|
+
min-height: 0;
|
|
3760
|
+
`;
|
|
3633
3761
|
var Container = styled9.div`
|
|
3634
3762
|
display: flex;
|
|
3635
3763
|
flex: 1;
|
|
@@ -3637,8 +3765,7 @@ var Container = styled9.div`
|
|
|
3637
3765
|
gap: 18px;
|
|
3638
3766
|
min-height: 0;
|
|
3639
3767
|
overflow: auto;
|
|
3640
|
-
padding: 24px;
|
|
3641
|
-
margin-bottom: 24px;
|
|
3768
|
+
padding: 24px 24px 88px;
|
|
3642
3769
|
overscroll-behavior: contain;
|
|
3643
3770
|
|
|
3644
3771
|
&::-webkit-scrollbar {
|
|
@@ -3690,6 +3817,48 @@ var RetryButton = styled9.button`
|
|
|
3690
3817
|
background: rgba(255, 255, 255, 0.08);
|
|
3691
3818
|
}
|
|
3692
3819
|
`;
|
|
3820
|
+
var ScrollToLatestOverlay = styled9.div`
|
|
3821
|
+
position: absolute;
|
|
3822
|
+
right: 24px;
|
|
3823
|
+
bottom: 24px;
|
|
3824
|
+
left: 24px;
|
|
3825
|
+
display: flex;
|
|
3826
|
+
justify-content: center;
|
|
3827
|
+
pointer-events: none;
|
|
3828
|
+
`;
|
|
3829
|
+
var ScrollToLatestButton = styled9.button`
|
|
3830
|
+
display: inline-flex;
|
|
3831
|
+
align-items: center;
|
|
3832
|
+
gap: 8px;
|
|
3833
|
+
border: 1px solid rgba(255, 255, 255, 0.14);
|
|
3834
|
+
border-radius: 999px;
|
|
3835
|
+
background: rgba(17, 18, 21, 0.92);
|
|
3836
|
+
color: rgba(255, 255, 255, 0.9);
|
|
3837
|
+
font-size: 12px;
|
|
3838
|
+
line-height: 1;
|
|
3839
|
+
padding: 10px 14px;
|
|
3840
|
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.28);
|
|
3841
|
+
cursor: pointer;
|
|
3842
|
+
z-index: 1;
|
|
3843
|
+
pointer-events: auto;
|
|
3844
|
+
|
|
3845
|
+
&:hover {
|
|
3846
|
+
background: rgba(28, 30, 36, 0.96);
|
|
3847
|
+
}
|
|
3848
|
+
`;
|
|
3849
|
+
var ScrollToLatestBadge = styled9.span`
|
|
3850
|
+
display: inline-flex;
|
|
3851
|
+
min-width: 18px;
|
|
3852
|
+
height: 18px;
|
|
3853
|
+
align-items: center;
|
|
3854
|
+
justify-content: center;
|
|
3855
|
+
padding: 0 6px;
|
|
3856
|
+
border-radius: 999px;
|
|
3857
|
+
background: rgba(109, 170, 255, 0.2);
|
|
3858
|
+
color: #9ac0ff;
|
|
3859
|
+
font-size: 11px;
|
|
3860
|
+
font-weight: 600;
|
|
3861
|
+
`;
|
|
3693
3862
|
|
|
3694
3863
|
// src/components/chat-composer/index.tsx
|
|
3695
3864
|
import { useEffect as useEffect7, useLayoutEffect as useLayoutEffect3, useRef as useRef8, useState as useState8 } from "react";
|