@gravity-ui/aikit 2.20.2-beta.de6d28351afafadd45e672108ecf935b8e837979.0 → 2.20.2
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/build/cjs/components/molecules/BaseMessage/BaseMessage.css +7 -5
- package/build/cjs/hooks/useSmartScroll.js +0 -31
- package/build/cjs/hooks/useSmartScroll.js.map +1 -1
- package/build/cjs/package.json +1 -1
- package/build/esm/components/molecules/BaseMessage/BaseMessage.css +7 -5
- package/build/esm/hooks/useSmartScroll.js +0 -31
- package/build/esm/hooks/useSmartScroll.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/package.json +1 -1
|
@@ -14,11 +14,13 @@
|
|
|
14
14
|
min-width: 0;
|
|
15
15
|
max-width: 100%;
|
|
16
16
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
@media (hover: hover) {
|
|
18
|
+
.g-aikit-base-message_btn-hover .g-aikit-base-message__actions {
|
|
19
|
+
visibility: hidden;
|
|
20
|
+
}
|
|
21
|
+
.g-aikit-base-message_btn-hover:hover .g-aikit-base-message__actions {
|
|
22
|
+
visibility: visible;
|
|
23
|
+
}
|
|
22
24
|
}
|
|
23
25
|
.g-aikit-base-message__actions {
|
|
24
26
|
display: flex;
|
|
@@ -44,13 +44,6 @@ function useSmartScroll({ isStreaming = false, messagesCount, status, }) {
|
|
|
44
44
|
// Keep the last message visible when the scroll viewport itself shrinks - the on-screen
|
|
45
45
|
// keyboard opening on mobile is the common case. The browser preserves `scrollTop`, so the
|
|
46
46
|
// bottom of the list would otherwise slide out of view.
|
|
47
|
-
//
|
|
48
|
-
// The children are observed alongside the container, because a re-layout can change the
|
|
49
|
-
// content height while the container's own box stays the same: the messages re-wrap, the
|
|
50
|
-
// browser keeps `scrollTop`, and the list ends up in the middle of the history with nothing
|
|
51
|
-
// to bring it back - `scroll` never fires, since `scrollTop` itself did not move. iOS Safari
|
|
52
|
-
// does exactly that while the system snapshots the page for a screenshot. The children span
|
|
53
|
-
// the whole scrollable content, so their heights are what `scrollHeight` is made of.
|
|
54
47
|
(0, react_1.useEffect)(() => {
|
|
55
48
|
const container = containerRef.current;
|
|
56
49
|
if (!container || typeof ResizeObserver === 'undefined') {
|
|
@@ -58,32 +51,8 @@ function useSmartScroll({ isStreaming = false, messagesCount, status, }) {
|
|
|
58
51
|
}
|
|
59
52
|
const observer = new ResizeObserver(() => scrollToBottom('instant'));
|
|
60
53
|
observer.observe(container);
|
|
61
|
-
// Only the difference is (un)observed: `observe()` re-delivers the current size for an
|
|
62
|
-
// already observed target, which would re-pin the list on every DOM mutation.
|
|
63
|
-
const observedChildren = new Set();
|
|
64
|
-
const syncObservedChildren = () => {
|
|
65
|
-
const children = new Set(Array.from(container.children));
|
|
66
|
-
for (const child of children) {
|
|
67
|
-
if (!observedChildren.has(child)) {
|
|
68
|
-
observer.observe(child);
|
|
69
|
-
observedChildren.add(child);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
for (const child of observedChildren) {
|
|
73
|
-
if (!children.has(child)) {
|
|
74
|
-
observer.unobserve(child);
|
|
75
|
-
observedChildren.delete(child);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
syncObservedChildren();
|
|
80
|
-
const childrenObserver = typeof MutationObserver === 'undefined'
|
|
81
|
-
? undefined
|
|
82
|
-
: new MutationObserver(syncObservedChildren);
|
|
83
|
-
childrenObserver === null || childrenObserver === void 0 ? void 0 : childrenObserver.observe(container, { childList: true });
|
|
84
54
|
return () => {
|
|
85
55
|
observer.disconnect();
|
|
86
|
-
childrenObserver === null || childrenObserver === void 0 ? void 0 : childrenObserver.disconnect();
|
|
87
56
|
};
|
|
88
57
|
}, [scrollToBottom]);
|
|
89
58
|
// Handle DOM mutations during streaming
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSmartScroll.js","sourceRoot":"../../../src","sources":["hooks/useSmartScroll.tsx"],"names":[],"mappings":";;AAWA,
|
|
1
|
+
{"version":3,"file":"useSmartScroll.js","sourceRoot":"../../../src","sources":["hooks/useSmartScroll.tsx"],"names":[],"mappings":"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","sourcesContent":["import {type RefObject, useCallback, useEffect, useRef} from 'react';\n\nimport type {ChatStatus} from '../types';\n\nexport interface UseSmartScrollReturn<T extends HTMLElement> {\n containerRef: RefObject<T>;\n scrollToBottom: (behavior?: ScrollBehavior) => void;\n}\n\nconst SCROLL_THRESHOLD = 10;\n\nexport function useSmartScroll<T extends HTMLElement>({\n isStreaming = false,\n messagesCount,\n status,\n}: {\n isStreaming?: boolean;\n messagesCount: number;\n status?: ChatStatus;\n}): UseSmartScrollReturn<T> {\n const containerRef = useRef<T>(null);\n const userScrolledUpRef = useRef(false);\n\n const scrollToBottom = useCallback((behavior: ScrollBehavior = 'instant') => {\n if (!userScrolledUpRef.current) {\n const container = containerRef.current;\n if (container) {\n container.scrollTo({\n top: container.scrollHeight,\n behavior,\n });\n }\n }\n }, []);\n\n // Initial scroll to bottom\n useEffect(() => {\n scrollToBottom();\n }, []);\n\n // Handle user scroll events\n useEffect(() => {\n const container = containerRef.current;\n if (!container) {\n return undefined;\n }\n\n const handleScroll = () => {\n if (!container) {\n return;\n }\n\n const {scrollTop, scrollHeight, clientHeight} = container;\n const distanceFromBottom = scrollHeight - scrollTop - clientHeight;\n\n const scrolledUp = distanceFromBottom > SCROLL_THRESHOLD;\n userScrolledUpRef.current = scrolledUp;\n };\n\n container.addEventListener('scroll', handleScroll, {passive: true});\n return () => {\n container.removeEventListener('scroll', handleScroll);\n };\n }, []);\n\n // Keep the last message visible when the scroll viewport itself shrinks - the on-screen\n // keyboard opening on mobile is the common case. The browser preserves `scrollTop`, so the\n // bottom of the list would otherwise slide out of view.\n useEffect(() => {\n const container = containerRef.current;\n if (!container || typeof ResizeObserver === 'undefined') {\n return undefined;\n }\n\n const observer = new ResizeObserver(() => scrollToBottom('instant'));\n observer.observe(container);\n\n return () => {\n observer.disconnect();\n };\n }, [scrollToBottom]);\n\n // Handle DOM mutations during streaming\n useEffect(() => {\n const container = containerRef.current;\n if (!container || !isStreaming) {\n return undefined;\n }\n\n const observer = new MutationObserver(() => {\n scrollToBottom('instant');\n });\n\n observer.observe(container, {\n childList: true,\n subtree: true,\n attributes: true,\n characterData: true,\n });\n\n return () => {\n observer.disconnect();\n };\n }, [isStreaming]);\n\n // Handle status changes\n useEffect(() => {\n scrollToBottom('smooth');\n }, [status]);\n\n useEffect(() => {\n if (messagesCount) {\n scrollToBottom('smooth');\n }\n }, [messagesCount]);\n\n return {\n containerRef,\n scrollToBottom,\n };\n}\n"]}
|
package/build/cjs/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"2.20.2
|
|
1
|
+
{"version":"2.20.2","type":"commonjs","sideEffects":["*.css","*.scss"]}
|
|
@@ -14,11 +14,13 @@
|
|
|
14
14
|
min-width: 0;
|
|
15
15
|
max-width: 100%;
|
|
16
16
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
@media (hover: hover) {
|
|
18
|
+
.g-aikit-base-message_btn-hover .g-aikit-base-message__actions {
|
|
19
|
+
visibility: hidden;
|
|
20
|
+
}
|
|
21
|
+
.g-aikit-base-message_btn-hover:hover .g-aikit-base-message__actions {
|
|
22
|
+
visibility: visible;
|
|
23
|
+
}
|
|
22
24
|
}
|
|
23
25
|
.g-aikit-base-message__actions {
|
|
24
26
|
display: flex;
|
|
@@ -41,13 +41,6 @@ export function useSmartScroll({ isStreaming = false, messagesCount, status, })
|
|
|
41
41
|
// Keep the last message visible when the scroll viewport itself shrinks - the on-screen
|
|
42
42
|
// keyboard opening on mobile is the common case. The browser preserves `scrollTop`, so the
|
|
43
43
|
// bottom of the list would otherwise slide out of view.
|
|
44
|
-
//
|
|
45
|
-
// The children are observed alongside the container, because a re-layout can change the
|
|
46
|
-
// content height while the container's own box stays the same: the messages re-wrap, the
|
|
47
|
-
// browser keeps `scrollTop`, and the list ends up in the middle of the history with nothing
|
|
48
|
-
// to bring it back - `scroll` never fires, since `scrollTop` itself did not move. iOS Safari
|
|
49
|
-
// does exactly that while the system snapshots the page for a screenshot. The children span
|
|
50
|
-
// the whole scrollable content, so their heights are what `scrollHeight` is made of.
|
|
51
44
|
useEffect(() => {
|
|
52
45
|
const container = containerRef.current;
|
|
53
46
|
if (!container || typeof ResizeObserver === 'undefined') {
|
|
@@ -55,32 +48,8 @@ export function useSmartScroll({ isStreaming = false, messagesCount, status, })
|
|
|
55
48
|
}
|
|
56
49
|
const observer = new ResizeObserver(() => scrollToBottom('instant'));
|
|
57
50
|
observer.observe(container);
|
|
58
|
-
// Only the difference is (un)observed: `observe()` re-delivers the current size for an
|
|
59
|
-
// already observed target, which would re-pin the list on every DOM mutation.
|
|
60
|
-
const observedChildren = new Set();
|
|
61
|
-
const syncObservedChildren = () => {
|
|
62
|
-
const children = new Set(Array.from(container.children));
|
|
63
|
-
for (const child of children) {
|
|
64
|
-
if (!observedChildren.has(child)) {
|
|
65
|
-
observer.observe(child);
|
|
66
|
-
observedChildren.add(child);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
for (const child of observedChildren) {
|
|
70
|
-
if (!children.has(child)) {
|
|
71
|
-
observer.unobserve(child);
|
|
72
|
-
observedChildren.delete(child);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
syncObservedChildren();
|
|
77
|
-
const childrenObserver = typeof MutationObserver === 'undefined'
|
|
78
|
-
? undefined
|
|
79
|
-
: new MutationObserver(syncObservedChildren);
|
|
80
|
-
childrenObserver === null || childrenObserver === void 0 ? void 0 : childrenObserver.observe(container, { childList: true });
|
|
81
51
|
return () => {
|
|
82
52
|
observer.disconnect();
|
|
83
|
-
childrenObserver === null || childrenObserver === void 0 ? void 0 : childrenObserver.disconnect();
|
|
84
53
|
};
|
|
85
54
|
}, [scrollToBottom]);
|
|
86
55
|
// Handle DOM mutations during streaming
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSmartScroll.js","sourceRoot":"../../../src","sources":["hooks/useSmartScroll.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAiB,WAAW,EAAE,SAAS,EAAE,MAAM,EAAC,MAAM,OAAO,CAAC;AASrE,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAE5B,MAAM,UAAU,cAAc,CAAwB,EAClD,WAAW,GAAG,KAAK,EACnB,aAAa,EACb,MAAM,GAKT;IACG,MAAM,YAAY,GAAG,MAAM,CAAI,IAAI,CAAC,CAAC;IACrC,MAAM,iBAAiB,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAExC,MAAM,cAAc,GAAG,WAAW,CAAC,CAAC,WAA2B,SAAS,EAAE,EAAE;QACxE,IAAI,CAAC,iBAAiB,CAAC,OAAO,EAAE,CAAC;YAC7B,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC;YACvC,IAAI,SAAS,EAAE,CAAC;gBACZ,SAAS,CAAC,QAAQ,CAAC;oBACf,GAAG,EAAE,SAAS,CAAC,YAAY;oBAC3B,QAAQ;iBACX,CAAC,CAAC;YACP,CAAC;QACL,CAAC;IACL,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,2BAA2B;IAC3B,SAAS,CAAC,GAAG,EAAE;QACX,cAAc,EAAE,CAAC;IACrB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,4BAA4B;IAC5B,SAAS,CAAC,GAAG,EAAE;QACX,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC;QACvC,IAAI,CAAC,SAAS,EAAE,CAAC;YACb,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,MAAM,YAAY,GAAG,GAAG,EAAE;YACtB,IAAI,CAAC,SAAS,EAAE,CAAC;gBACb,OAAO;YACX,CAAC;YAED,MAAM,EAAC,SAAS,EAAE,YAAY,EAAE,YAAY,EAAC,GAAG,SAAS,CAAC;YAC1D,MAAM,kBAAkB,GAAG,YAAY,GAAG,SAAS,GAAG,YAAY,CAAC;YAEnE,MAAM,UAAU,GAAG,kBAAkB,GAAG,gBAAgB,CAAC;YACzD,iBAAiB,CAAC,OAAO,GAAG,UAAU,CAAC;QAC3C,CAAC,CAAC;QAEF,SAAS,CAAC,gBAAgB,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC,CAAC;QACpE,OAAO,GAAG,EAAE;YACR,SAAS,CAAC,mBAAmB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QAC1D,CAAC,CAAC;IACN,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,wFAAwF;IACxF,2FAA2F;IAC3F,wDAAwD;IACxD,
|
|
1
|
+
{"version":3,"file":"useSmartScroll.js","sourceRoot":"../../../src","sources":["hooks/useSmartScroll.tsx"],"names":[],"mappings":"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","sourcesContent":["import {type RefObject, useCallback, useEffect, useRef} from 'react';\n\nimport type {ChatStatus} from '../types';\n\nexport interface UseSmartScrollReturn<T extends HTMLElement> {\n containerRef: RefObject<T>;\n scrollToBottom: (behavior?: ScrollBehavior) => void;\n}\n\nconst SCROLL_THRESHOLD = 10;\n\nexport function useSmartScroll<T extends HTMLElement>({\n isStreaming = false,\n messagesCount,\n status,\n}: {\n isStreaming?: boolean;\n messagesCount: number;\n status?: ChatStatus;\n}): UseSmartScrollReturn<T> {\n const containerRef = useRef<T>(null);\n const userScrolledUpRef = useRef(false);\n\n const scrollToBottom = useCallback((behavior: ScrollBehavior = 'instant') => {\n if (!userScrolledUpRef.current) {\n const container = containerRef.current;\n if (container) {\n container.scrollTo({\n top: container.scrollHeight,\n behavior,\n });\n }\n }\n }, []);\n\n // Initial scroll to bottom\n useEffect(() => {\n scrollToBottom();\n }, []);\n\n // Handle user scroll events\n useEffect(() => {\n const container = containerRef.current;\n if (!container) {\n return undefined;\n }\n\n const handleScroll = () => {\n if (!container) {\n return;\n }\n\n const {scrollTop, scrollHeight, clientHeight} = container;\n const distanceFromBottom = scrollHeight - scrollTop - clientHeight;\n\n const scrolledUp = distanceFromBottom > SCROLL_THRESHOLD;\n userScrolledUpRef.current = scrolledUp;\n };\n\n container.addEventListener('scroll', handleScroll, {passive: true});\n return () => {\n container.removeEventListener('scroll', handleScroll);\n };\n }, []);\n\n // Keep the last message visible when the scroll viewport itself shrinks - the on-screen\n // keyboard opening on mobile is the common case. The browser preserves `scrollTop`, so the\n // bottom of the list would otherwise slide out of view.\n useEffect(() => {\n const container = containerRef.current;\n if (!container || typeof ResizeObserver === 'undefined') {\n return undefined;\n }\n\n const observer = new ResizeObserver(() => scrollToBottom('instant'));\n observer.observe(container);\n\n return () => {\n observer.disconnect();\n };\n }, [scrollToBottom]);\n\n // Handle DOM mutations during streaming\n useEffect(() => {\n const container = containerRef.current;\n if (!container || !isStreaming) {\n return undefined;\n }\n\n const observer = new MutationObserver(() => {\n scrollToBottom('instant');\n });\n\n observer.observe(container, {\n childList: true,\n subtree: true,\n attributes: true,\n characterData: true,\n });\n\n return () => {\n observer.disconnect();\n };\n }, [isStreaming]);\n\n // Handle status changes\n useEffect(() => {\n scrollToBottom('smooth');\n }, [status]);\n\n useEffect(() => {\n if (messagesCount) {\n scrollToBottom('smooth');\n }\n }, [messagesCount]);\n\n return {\n containerRef,\n scrollToBottom,\n };\n}\n"]}
|
package/build/esm/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"2.20.2
|
|
1
|
+
{"version":"2.20.2","type":"module","sideEffects":["*.css","*.scss"]}
|
package/package.json
CHANGED