@portablecore/chat 0.2.6 → 0.2.8
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/chat-interface-core.d.ts.map +1 -1
- package/dist/chat-interface-core.js +1 -2
- package/dist/chat-interface-core.js.map +1 -1
- package/dist/components/message-list.d.ts +1 -4
- package/dist/components/message-list.d.ts.map +1 -1
- package/dist/components/message-list.js +2 -2
- package/dist/components/message-list.js.map +1 -1
- package/dist/hooks/use-chat-scroll.d.ts +0 -16
- package/dist/hooks/use-chat-scroll.d.ts.map +1 -1
- package/dist/hooks/use-chat-scroll.js +16 -138
- package/dist/hooks/use-chat-scroll.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-interface-core.d.ts","sourceRoot":"","sources":["../src/chat-interface-core.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAa,MAAM,YAAY,CAAA;AAWhE,UAAU,sBAAsB;IAC9B,MAAM,EAAE,mBAAmB,CAAA;IAE3B,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CACzC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,kBAAkB,GACnB,EAAE,sBAAsB,
|
|
1
|
+
{"version":3,"file":"chat-interface-core.d.ts","sourceRoot":"","sources":["../src/chat-interface-core.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAa,MAAM,YAAY,CAAA;AAWhE,UAAU,sBAAsB;IAC9B,MAAM,EAAE,mBAAmB,CAAA;IAE3B,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CACzC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,kBAAkB,GACnB,EAAE,sBAAsB,2CAyGxB"}
|
|
@@ -33,7 +33,6 @@ function ChatInterfaceCore({ config, markdownComponents, }) {
|
|
|
33
33
|
const variant = config.variant || "full";
|
|
34
34
|
const isCompact = variant === "compact";
|
|
35
35
|
const handleSend = (content) => {
|
|
36
|
-
scroll.expandSpacer();
|
|
37
36
|
session.send(content);
|
|
38
37
|
attachmentState.clear();
|
|
39
38
|
};
|
|
@@ -50,7 +49,7 @@ function ChatInterfaceCore({ config, markdownComponents, }) {
|
|
|
50
49
|
}
|
|
51
50
|
return ((0, jsx_runtime_1.jsx)("div", { className: `${indicatorMaxWidth} mx-auto px-4`, children: (0, jsx_runtime_1.jsx)(bouncing_dots_js_1.BouncingDots, { expertName: config.expert?.name, expertAvatarUrl: config.expert?.avatarUrl, expertEmoji: config.expert?.emoji }) }));
|
|
52
51
|
};
|
|
53
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: rootClasses, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col flex-1 min-w-0", children: [(0, jsx_runtime_1.jsx)(message_list_js_1.MessageList, { messages: session.messages, streaming: session.streaming, config: config, markdownComponents: markdownComponents, scrollRef: scroll.scrollRef,
|
|
52
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: rootClasses, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col flex-1 min-w-0", children: [(0, jsx_runtime_1.jsx)(message_list_js_1.MessageList, { messages: session.messages, streaming: session.streaming, config: config, markdownComponents: markdownComponents, scrollRef: scroll.scrollRef, emptyState: slots.emptyState || ((0, jsx_runtime_1.jsx)(empty_state_js_1.EmptyState, { expertName: config.expert?.name })), beforeMessageList: slots.beforeMessageList, afterMessageList: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [renderIndicator(), slots.afterMessageList] }) }), (0, jsx_runtime_1.jsx)(input_area_js_1.InputArea, { onSend: handleSend, onAbort: session.abort, streaming: session.streaming, sending: session.sending, placeholder: config.expert
|
|
54
53
|
? `Message ${config.expert.name}...`
|
|
55
54
|
: "Send a message...", prefillText: config.prefillText, inputPrefix: slots.inputPrefix, inputSuffix: slots.inputSuffix, attachments: attachmentState.attachments, uploading: attachmentState.uploading, onAddFiles: attachmentState.addFiles, onRemoveAttachment: attachmentState.remove, hasDocumentResolver: !!config.resolvers?.documentResolver, variant: variant, classNames: config.classNames, mentionResolver: config.resolvers?.mentionResolver, skillResolver: config.resolvers?.skillResolver })] }), slots.sidePanel && ((0, jsx_runtime_1.jsx)("div", { className: "w-80 border-l border-border overflow-y-auto", children: slots.sidePanel }))] }));
|
|
56
55
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-interface-core.js","sourceRoot":"","sources":["../src/chat-interface-core.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;;AAiCZ,
|
|
1
|
+
{"version":3,"file":"chat-interface-core.js","sourceRoot":"","sources":["../src/chat-interface-core.tsx"],"names":[],"mappings":"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"}
|
|
@@ -10,9 +10,6 @@ interface MessageListProps {
|
|
|
10
10
|
beforeMessageList?: ReactNode;
|
|
11
11
|
afterMessageList?: ReactNode;
|
|
12
12
|
scrollRef: RefObject<HTMLDivElement>;
|
|
13
|
-
messagesEndRef: RefObject<HTMLDivElement>;
|
|
14
|
-
spacerExpanded: boolean;
|
|
15
|
-
spacerHeight: number;
|
|
16
13
|
}
|
|
17
14
|
/**
|
|
18
15
|
* Scrollable message list with auto-scroll, date separators,
|
|
@@ -22,6 +19,6 @@ interface MessageListProps {
|
|
|
22
19
|
* - "full": max-w-5xl, wider padding, extra bottom space for scroll
|
|
23
20
|
* - "compact": max-w-3xl, tighter padding
|
|
24
21
|
*/
|
|
25
|
-
export declare function MessageList({ messages, streaming, config, markdownComponents, emptyState, beforeMessageList, afterMessageList, scrollRef,
|
|
22
|
+
export declare function MessageList({ messages, streaming, config, markdownComponents, emptyState, beforeMessageList, afterMessageList, scrollRef, }: MessageListProps): import("react/jsx-runtime").JSX.Element;
|
|
26
23
|
export {};
|
|
27
24
|
//# sourceMappingURL=message-list.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-list.d.ts","sourceRoot":"","sources":["../../src/components/message-list.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAW,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAItD,UAAU,gBAAgB;IACxB,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,SAAS,EAAE,OAAO,CAAA;IAClB,MAAM,EAAE,mBAAmB,CAAA;IAE3B,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACxC,UAAU,CAAC,EAAE,SAAS,CAAA;IACtB,iBAAiB,CAAC,EAAE,SAAS,CAAA;IAC7B,gBAAgB,CAAC,EAAE,SAAS,CAAA;IAC5B,SAAS,EAAE,SAAS,CAAC,cAAc,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"message-list.d.ts","sourceRoot":"","sources":["../../src/components/message-list.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAW,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAItD,UAAU,gBAAgB;IACxB,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,SAAS,EAAE,OAAO,CAAA;IAClB,MAAM,EAAE,mBAAmB,CAAA;IAE3B,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACxC,UAAU,CAAC,EAAE,SAAS,CAAA;IACtB,iBAAiB,CAAC,EAAE,SAAS,CAAA;IAC7B,gBAAgB,CAAC,EAAE,SAAS,CAAA;IAC5B,SAAS,EAAE,SAAS,CAAC,cAAc,CAAC,CAAA;CACrC;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,SAAS,EACT,MAAM,EACN,kBAAkB,EAClB,UAAU,EACV,iBAAiB,EACjB,gBAAgB,EAChB,SAAS,GACV,EAAE,gBAAgB,2CA4DlB"}
|
|
@@ -14,7 +14,7 @@ const message_bubble_js_1 = require("./message-bubble.js");
|
|
|
14
14
|
* - "full": max-w-5xl, wider padding, extra bottom space for scroll
|
|
15
15
|
* - "compact": max-w-3xl, tighter padding
|
|
16
16
|
*/
|
|
17
|
-
function MessageList({ messages, streaming, config, markdownComponents, emptyState, beforeMessageList, afterMessageList, scrollRef,
|
|
17
|
+
function MessageList({ messages, streaming, config, markdownComponents, emptyState, beforeMessageList, afterMessageList, scrollRef, }) {
|
|
18
18
|
const variant = config.variant || "full";
|
|
19
19
|
const isCompact = variant === "compact";
|
|
20
20
|
const renderableMessages = (0, react_1.useMemo)(() => (0, message_grouping_js_1.groupMessages)(messages), [messages]);
|
|
@@ -26,6 +26,6 @@ function MessageList({ messages, streaming, config, markdownComponents, emptySta
|
|
|
26
26
|
? `max-w-3xl mx-auto space-y-0 ${config.classNames?.messageListInner || ""}`
|
|
27
27
|
: `max-w-5xl mx-auto px-4 sm:px-6 pt-2 pb-12 sm:pb-4 space-y-0 ${config.classNames?.messageListInner || ""}`;
|
|
28
28
|
const listClasses = config.classNames?.messageList || "";
|
|
29
|
-
return ((0, jsx_runtime_1.jsxs)("div", { ref: scrollRef, className: `${scrollClasses} ${listClasses}`, children: [beforeMessageList, isEmpty && emptyState ? ((0, jsx_runtime_1.jsx)("div", { className: "flex items-center justify-center h-full", children: emptyState })) : ((0, jsx_runtime_1.
|
|
29
|
+
return ((0, jsx_runtime_1.jsxs)("div", { ref: scrollRef, className: `${scrollClasses} ${listClasses}`, children: [beforeMessageList, isEmpty && emptyState ? ((0, jsx_runtime_1.jsx)("div", { className: "flex items-center justify-center h-full", children: emptyState })) : ((0, jsx_runtime_1.jsx)("div", { className: innerClasses, children: renderableMessages.map((rm) => ((0, jsx_runtime_1.jsxs)("div", { children: [rm.showDateSeparator && rm.dateSeparatorLabel && ((0, jsx_runtime_1.jsx)("div", { className: `flex items-center justify-center ${isCompact ? "py-4" : "my-6"}`, children: (0, jsx_runtime_1.jsx)("div", { className: `px-3 py-1 rounded-full text-xs font-medium bg-slate-100 dark:bg-slate-800 text-slate-500 dark:text-slate-400 ${config.classNames?.dateSeparator || ""}`, children: rm.dateSeparatorLabel }) })), (0, jsx_runtime_1.jsx)(message_bubble_js_1.MessageBubble, { message: rm.message, isFirstInGroup: rm.isFirstInGroup, isLastInGroup: rm.isLastInGroup, config: config, markdownComponents: markdownComponents })] }, rm.message.id))) })), afterMessageList] }));
|
|
30
30
|
}
|
|
31
31
|
//# sourceMappingURL=message-list.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-list.js","sourceRoot":"","sources":["../../src/components/message-list.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;;
|
|
1
|
+
{"version":3,"file":"message-list.js","sourceRoot":"","sources":["../../src/components/message-list.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;;AA4BZ,kCAqEC;;AA/FD,iCAA+D;AAG/D,sEAA4D;AAC5D,2DAAmD;AAcnD;;;;;;;GAOG;AACH,SAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,SAAS,EACT,MAAM,EACN,kBAAkB,EAClB,UAAU,EACV,iBAAiB,EACjB,gBAAgB,EAChB,SAAS,GACQ;IACjB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,MAAM,CAAA;IACxC,MAAM,SAAS,GAAG,OAAO,KAAK,SAAS,CAAA;IAEvC,MAAM,kBAAkB,GAAG,IAAA,eAAO,EAChC,GAAG,EAAE,CAAC,IAAA,mCAAa,EAAC,QAAQ,CAAC,EAC7B,CAAC,QAAQ,CAAC,CACX,CAAA;IAED,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAA;IAErC,MAAM,aAAa,GAAG,SAAS;QAC7B,CAAC,CAAC,kCAAkC;QACpC,CAAC,CAAC,wBAAwB,CAAA;IAE5B,MAAM,YAAY,GAAG,SAAS;QAC5B,CAAC,CAAC,+BAA+B,MAAM,CAAC,UAAU,EAAE,gBAAgB,IAAI,EAAE,EAAE;QAC5E,CAAC,CAAC,+DAA+D,MAAM,CAAC,UAAU,EAAE,gBAAgB,IAAI,EAAE,EAAE,CAAA;IAE9G,MAAM,WAAW,GAAG,MAAM,CAAC,UAAU,EAAE,WAAW,IAAI,EAAE,CAAA;IAExD,OAAO,CACL,iCACE,GAAG,EAAE,SAAS,EACd,SAAS,EAAE,GAAG,aAAa,IAAI,WAAW,EAAE,aAE3C,iBAAiB,EAEjB,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CACvB,gCAAK,SAAS,EAAC,yCAAyC,YACrD,UAAU,GACP,CACP,CAAC,CAAC,CAAC,CACF,gCAAK,SAAS,EAAE,YAAY,YACzB,kBAAkB,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAC9B,4CACG,EAAE,CAAC,iBAAiB,IAAI,EAAE,CAAC,kBAAkB,IAAI,CAChD,gCAAK,SAAS,EAAE,oCAAoC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,YAC/E,gCACE,SAAS,EAAE,gHAAgH,MAAM,CAAC,UAAU,EAAE,aAAa,IAAI,EAAE,EAAE,YAElK,EAAE,CAAC,kBAAkB,GAClB,GACF,CACP,EACD,uBAAC,iCAAa,IACZ,OAAO,EAAE,EAAE,CAAC,OAAO,EACnB,cAAc,EAAE,EAAE,CAAC,cAAc,EACjC,aAAa,EAAE,EAAE,CAAC,aAAa,EAC/B,MAAM,EAAE,MAAM,EACd,kBAAkB,EAAE,kBAAkB,GACtC,KAhBM,EAAE,CAAC,OAAO,CAAC,EAAE,CAiBjB,CACP,CAAC,GACE,CACP,EAEA,gBAAgB,IACb,CACP,CAAA;AACH,CAAC"}
|
|
@@ -5,25 +5,9 @@ interface UseChatScrollOptions {
|
|
|
5
5
|
}
|
|
6
6
|
interface UseChatScrollReturn {
|
|
7
7
|
scrollRef: React.RefObject<HTMLDivElement>;
|
|
8
|
-
messagesEndRef: React.RefObject<HTMLDivElement>;
|
|
9
8
|
scrollToBottom: (behavior?: ScrollBehavior) => void;
|
|
10
9
|
isNearBottom: () => boolean;
|
|
11
|
-
spacerExpanded: boolean;
|
|
12
|
-
spacerHeight: number;
|
|
13
|
-
expandSpacer: () => void;
|
|
14
10
|
}
|
|
15
|
-
/**
|
|
16
|
-
* Manages auto-scroll behavior and the spacer system for a chat message list.
|
|
17
|
-
*
|
|
18
|
-
* The spacer system (ported from legacy ChatInterface):
|
|
19
|
-
* - When user sends a message, the spacer expands to 100vh, then the user
|
|
20
|
-
* message is scrolled to the TOP of the viewport (not the bottom). This
|
|
21
|
-
* makes room below for the expert's response to stream in.
|
|
22
|
-
* - During streaming, auto-scroll keeps the growing response visible.
|
|
23
|
-
* - When streaming ends, the spacer shrinks to the exact height needed to
|
|
24
|
-
* keep messages in their current position ("lock" behavior).
|
|
25
|
-
* - When user scrolls up past the lock point, the spacer collapses completely.
|
|
26
|
-
*/
|
|
27
11
|
export declare function useChatScroll({ messages, streaming, }: UseChatScrollOptions): UseChatScrollReturn;
|
|
28
12
|
export {};
|
|
29
13
|
//# sourceMappingURL=use-chat-scroll.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-chat-scroll.d.ts","sourceRoot":"","sources":["../../src/hooks/use-chat-scroll.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-chat-scroll.d.ts","sourceRoot":"","sources":["../../src/hooks/use-chat-scroll.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAE3D,UAAU,oBAAoB;IAC5B,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,SAAS,EAAE,OAAO,CAAA;CACnB;AAED,UAAU,mBAAmB;IAC3B,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAA;IAC1C,cAAc,EAAE,CAAC,QAAQ,CAAC,EAAE,cAAc,KAAK,IAAI,CAAA;IACnD,YAAY,EAAE,MAAM,OAAO,CAAA;CAC5B;AAED,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,SAAS,GACV,EAAE,oBAAoB,GAAG,mBAAmB,CA0D5C"}
|
|
@@ -2,30 +2,24 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.useChatScroll = useChatScroll;
|
|
5
|
-
const react_1 = require("react");
|
|
6
5
|
/**
|
|
7
|
-
*
|
|
6
|
+
* useChatScroll — Simple, reliable scroll management for chat.
|
|
7
|
+
*
|
|
8
|
+
* Auto-scrolls to bottom when:
|
|
9
|
+
* - Component mounts (instant)
|
|
10
|
+
* - A new message arrives (smooth, if user was near bottom)
|
|
11
|
+
* - Streaming content grows (smooth, if user was near bottom)
|
|
8
12
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* - When streaming ends, the spacer shrinks to the exact height needed to
|
|
15
|
-
* keep messages in their current position ("lock" behavior).
|
|
16
|
-
* - When user scrolls up past the lock point, the spacer collapses completely.
|
|
13
|
+
* Respects user intent: if user scrolled up, don't pull them down.
|
|
14
|
+
*
|
|
15
|
+
* The spacer system from legacy ChatInterface is intentionally NOT
|
|
16
|
+
* included here. It depends on the virtualizer (item 9) and will
|
|
17
|
+
* be ported together with virtualization.
|
|
17
18
|
*/
|
|
19
|
+
const react_1 = require("react");
|
|
18
20
|
function useChatScroll({ messages, streaming, }) {
|
|
19
21
|
const scrollRef = (0, react_1.useRef)(null);
|
|
20
|
-
const messagesEndRef = (0, react_1.useRef)(null);
|
|
21
22
|
const wasNearBottom = (0, react_1.useRef)(true);
|
|
22
|
-
const wasStreamingRef = (0, react_1.useRef)(false);
|
|
23
|
-
const lockedScrollPosition = (0, react_1.useRef)(null);
|
|
24
|
-
const lastMessageCountRef = (0, react_1.useRef)(messages.length);
|
|
25
|
-
const lastProgrammaticScroll = (0, react_1.useRef)(0);
|
|
26
|
-
const pendingUserMessageScroll = (0, react_1.useRef)(false);
|
|
27
|
-
const [spacerExpanded, setSpacerExpanded] = (0, react_1.useState)(false);
|
|
28
|
-
const [spacerHeight, setSpacerHeight] = (0, react_1.useState)(0);
|
|
29
23
|
const isNearBottom = (0, react_1.useCallback)(() => {
|
|
30
24
|
const el = scrollRef.current;
|
|
31
25
|
if (!el)
|
|
@@ -37,78 +31,25 @@ function useChatScroll({ messages, streaming, }) {
|
|
|
37
31
|
const el = scrollRef.current;
|
|
38
32
|
if (!el)
|
|
39
33
|
return;
|
|
40
|
-
lastProgrammaticScroll.current = Date.now();
|
|
41
34
|
el.scrollTo({ top: el.scrollHeight, behavior });
|
|
42
35
|
}, []);
|
|
43
|
-
|
|
44
|
-
setSpacerExpanded(true);
|
|
45
|
-
setSpacerHeight(0);
|
|
46
|
-
lockedScrollPosition.current = null;
|
|
47
|
-
pendingUserMessageScroll.current = true;
|
|
48
|
-
}, []);
|
|
49
|
-
// After spacer expands and messages render, scroll the user message to top.
|
|
50
|
-
// Uses a RAF loop to wait for the DOM to settle (matching legacy behavior).
|
|
51
|
-
(0, react_1.useEffect)(() => {
|
|
52
|
-
if (!pendingUserMessageScroll.current || !spacerExpanded)
|
|
53
|
-
return;
|
|
54
|
-
pendingUserMessageScroll.current = false;
|
|
55
|
-
const container = scrollRef.current;
|
|
56
|
-
if (!container)
|
|
57
|
-
return;
|
|
58
|
-
let rafId;
|
|
59
|
-
let attempts = 0;
|
|
60
|
-
const maxAttempts = 30;
|
|
61
|
-
const attemptScroll = () => {
|
|
62
|
-
attempts++;
|
|
63
|
-
// Find the last user message element in the container
|
|
64
|
-
const userMessages = container.querySelectorAll("[data-role='user']");
|
|
65
|
-
const lastUserEl = userMessages[userMessages.length - 1];
|
|
66
|
-
if (lastUserEl) {
|
|
67
|
-
const topOffset = 16;
|
|
68
|
-
const containerRect = container.getBoundingClientRect();
|
|
69
|
-
const elementRect = lastUserEl.getBoundingClientRect();
|
|
70
|
-
const scrollOffset = elementRect.top - containerRect.top + container.scrollTop - topOffset;
|
|
71
|
-
lastProgrammaticScroll.current = Date.now();
|
|
72
|
-
container.scrollTo({
|
|
73
|
-
top: Math.max(0, scrollOffset),
|
|
74
|
-
behavior: "smooth",
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
else if (attempts < maxAttempts) {
|
|
78
|
-
rafId = requestAnimationFrame(attemptScroll);
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
// Small delay to let the spacer expand in the DOM
|
|
82
|
-
const timeoutId = setTimeout(() => {
|
|
83
|
-
rafId = requestAnimationFrame(attemptScroll);
|
|
84
|
-
}, 16);
|
|
85
|
-
return () => {
|
|
86
|
-
clearTimeout(timeoutId);
|
|
87
|
-
if (rafId)
|
|
88
|
-
cancelAnimationFrame(rafId);
|
|
89
|
-
};
|
|
90
|
-
}, [spacerExpanded, messages.length]);
|
|
91
|
-
// Track scroll position for auto-scroll gating
|
|
36
|
+
// Track whether user is near bottom
|
|
92
37
|
(0, react_1.useEffect)(() => {
|
|
93
38
|
const el = scrollRef.current;
|
|
94
39
|
if (!el)
|
|
95
40
|
return;
|
|
96
41
|
const onScroll = () => {
|
|
97
|
-
if (Date.now() - lastProgrammaticScroll.current < 200)
|
|
98
|
-
return;
|
|
99
42
|
wasNearBottom.current = isNearBottom();
|
|
100
43
|
};
|
|
101
44
|
el.addEventListener("scroll", onScroll, { passive: true });
|
|
102
45
|
return () => el.removeEventListener("scroll", onScroll);
|
|
103
46
|
}, [isNearBottom]);
|
|
104
|
-
// Auto-scroll on new messages
|
|
47
|
+
// Auto-scroll on new messages
|
|
105
48
|
(0, react_1.useEffect)(() => {
|
|
106
|
-
if (spacerExpanded)
|
|
107
|
-
return;
|
|
108
49
|
if (wasNearBottom.current) {
|
|
109
50
|
scrollToBottom("smooth");
|
|
110
51
|
}
|
|
111
|
-
}, [messages.length, scrollToBottom
|
|
52
|
+
}, [messages.length, scrollToBottom]);
|
|
112
53
|
// Auto-scroll during streaming if near bottom
|
|
113
54
|
(0, react_1.useEffect)(() => {
|
|
114
55
|
if (!streaming)
|
|
@@ -125,69 +66,6 @@ function useChatScroll({ messages, streaming, }) {
|
|
|
125
66
|
scrollToBottom("instant");
|
|
126
67
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
127
68
|
}, []);
|
|
128
|
-
|
|
129
|
-
(0, react_1.useEffect)(() => {
|
|
130
|
-
if (streaming) {
|
|
131
|
-
wasStreamingRef.current = true;
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
if (wasStreamingRef.current && spacerExpanded) {
|
|
135
|
-
wasStreamingRef.current = false;
|
|
136
|
-
const container = scrollRef.current;
|
|
137
|
-
const endMarker = messagesEndRef.current;
|
|
138
|
-
if (container && endMarker) {
|
|
139
|
-
const containerRect = container.getBoundingClientRect();
|
|
140
|
-
const endRect = endMarker.getBoundingClientRect();
|
|
141
|
-
const spaceBelow = containerRect.bottom - endRect.bottom;
|
|
142
|
-
const newSpacerHeight = Math.max(0, spaceBelow);
|
|
143
|
-
setSpacerHeight(newSpacerHeight);
|
|
144
|
-
setSpacerExpanded(false);
|
|
145
|
-
lockedScrollPosition.current = container.scrollTop;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}, [streaming, spacerExpanded]);
|
|
149
|
-
// Fallback: collapse spacer if new assistant message arrives without streaming
|
|
150
|
-
(0, react_1.useEffect)(() => {
|
|
151
|
-
const messageCount = messages.length;
|
|
152
|
-
const hadNewMessage = messageCount > lastMessageCountRef.current;
|
|
153
|
-
lastMessageCountRef.current = messageCount;
|
|
154
|
-
if (spacerExpanded && !streaming && hadNewMessage) {
|
|
155
|
-
const lastMessage = messages[messages.length - 1];
|
|
156
|
-
if (lastMessage?.role === "assistant") {
|
|
157
|
-
const timeoutId = setTimeout(() => {
|
|
158
|
-
if (!streaming && spacerExpanded) {
|
|
159
|
-
setSpacerExpanded(false);
|
|
160
|
-
setSpacerHeight(16);
|
|
161
|
-
}
|
|
162
|
-
}, 500);
|
|
163
|
-
return () => clearTimeout(timeoutId);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}, [messages.length, spacerExpanded, streaming, messages]);
|
|
167
|
-
// Collapse spacer when user scrolls up past lock point
|
|
168
|
-
(0, react_1.useEffect)(() => {
|
|
169
|
-
const container = scrollRef.current;
|
|
170
|
-
if (!container)
|
|
171
|
-
return;
|
|
172
|
-
const handleScroll = () => {
|
|
173
|
-
if (lockedScrollPosition.current === null || spacerHeight <= 16)
|
|
174
|
-
return;
|
|
175
|
-
if (container.scrollTop < lockedScrollPosition.current - 50) {
|
|
176
|
-
setSpacerHeight(16);
|
|
177
|
-
lockedScrollPosition.current = null;
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
container.addEventListener("scroll", handleScroll, { passive: true });
|
|
181
|
-
return () => container.removeEventListener("scroll", handleScroll);
|
|
182
|
-
}, [spacerHeight]);
|
|
183
|
-
return {
|
|
184
|
-
scrollRef,
|
|
185
|
-
messagesEndRef,
|
|
186
|
-
scrollToBottom,
|
|
187
|
-
isNearBottom,
|
|
188
|
-
spacerExpanded,
|
|
189
|
-
spacerHeight,
|
|
190
|
-
expandSpacer,
|
|
191
|
-
};
|
|
69
|
+
return { scrollRef, scrollToBottom, isNearBottom };
|
|
192
70
|
}
|
|
193
71
|
//# sourceMappingURL=use-chat-scroll.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-chat-scroll.js","sourceRoot":"","sources":["../../src/hooks/use-chat-scroll.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;;;
|
|
1
|
+
{"version":3,"file":"use-chat-scroll.js","sourceRoot":"","sources":["../../src/hooks/use-chat-scroll.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;;;AA+BZ,sCA6DC;AA1FD;;;;;;;;;;;;;GAaG;AAEH,iCAAsD;AActD,SAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,SAAS,GACY;IACrB,MAAM,SAAS,GAAG,IAAA,cAAM,EAAiB,IAAK,CAAC,CAAA;IAC/C,MAAM,aAAa,GAAG,IAAA,cAAM,EAAC,IAAI,CAAC,CAAA;IAElC,MAAM,YAAY,GAAG,IAAA,mBAAW,EAAC,GAAG,EAAE;QACpC,MAAM,EAAE,GAAG,SAAS,CAAC,OAAO,CAAA;QAC5B,IAAI,CAAC,EAAE;YAAE,OAAO,IAAI,CAAA;QACpB,MAAM,SAAS,GAAG,GAAG,CAAA;QACrB,OAAO,EAAE,CAAC,YAAY,GAAG,EAAE,CAAC,SAAS,GAAG,EAAE,CAAC,YAAY,GAAG,SAAS,CAAA;IACrE,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,cAAc,GAAG,IAAA,mBAAW,EAChC,CAAC,WAA2B,QAAQ,EAAE,EAAE;QACtC,MAAM,EAAE,GAAG,SAAS,CAAC,OAAO,CAAA;QAC5B,IAAI,CAAC,EAAE;YAAE,OAAM;QACf,EAAE,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,YAAY,EAAE,QAAQ,EAAE,CAAC,CAAA;IACjD,CAAC,EACD,EAAE,CACH,CAAA;IAED,oCAAoC;IACpC,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,MAAM,EAAE,GAAG,SAAS,CAAC,OAAO,CAAA;QAC5B,IAAI,CAAC,EAAE;YAAE,OAAM;QAEf,MAAM,QAAQ,GAAG,GAAG,EAAE;YACpB,aAAa,CAAC,OAAO,GAAG,YAAY,EAAE,CAAA;QACxC,CAAC,CAAA;QAED,EAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;QAC1D,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IACzD,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAA;IAElB,8BAA8B;IAC9B,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;YAC1B,cAAc,CAAC,QAAQ,CAAC,CAAA;QAC1B,CAAC;IACH,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAA;IAErC,8CAA8C;IAC9C,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,CAAC,SAAS;YAAE,OAAM;QACtB,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;YAChC,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;gBAC1B,cAAc,CAAC,QAAQ,CAAC,CAAA;YAC1B,CAAC;QACH,CAAC,EAAE,GAAG,CAAC,CAAA;QACP,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;IACtC,CAAC,EAAE,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,CAAA;IAE/B,0BAA0B;IAC1B,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,cAAc,CAAC,SAAS,CAAC,CAAA;QACzB,uDAAuD;IACzD,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,YAAY,EAAE,CAAA;AACpD,CAAC"}
|
package/package.json
CHANGED