@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.
@@ -14,11 +14,13 @@
14
14
  min-width: 0;
15
15
  max-width: 100%;
16
16
  }
17
- .g-aikit-base-message_btn-hover .g-aikit-base-message__actions {
18
- visibility: hidden;
19
- }
20
- .g-aikit-base-message_btn-hover:hover .g-aikit-base-message__actions {
21
- visibility: visible;
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":"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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 //\n // The children are observed alongside the container, because a re-layout can change the\n // content height while the container's own box stays the same: the messages re-wrap, the\n // browser keeps `scrollTop`, and the list ends up in the middle of the history with nothing\n // to bring it back - `scroll` never fires, since `scrollTop` itself did not move. iOS Safari\n // does exactly that while the system snapshots the page for a screenshot. The children span\n // the whole scrollable content, so their heights are what `scrollHeight` is made of.\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 // Only the difference is (un)observed: `observe()` re-delivers the current size for an\n // already observed target, which would re-pin the list on every DOM mutation.\n const observedChildren = new Set<Element>();\n\n const syncObservedChildren = () => {\n const children = new Set<Element>(Array.from(container.children));\n\n for (const child of children) {\n if (!observedChildren.has(child)) {\n observer.observe(child);\n observedChildren.add(child);\n }\n }\n\n for (const child of observedChildren) {\n if (!children.has(child)) {\n observer.unobserve(child);\n observedChildren.delete(child);\n }\n }\n };\n\n syncObservedChildren();\n\n const childrenObserver =\n typeof MutationObserver === 'undefined'\n ? undefined\n : new MutationObserver(syncObservedChildren);\n\n childrenObserver?.observe(container, {childList: true});\n\n return () => {\n observer.disconnect();\n childrenObserver?.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"]}
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"]}
@@ -1 +1 @@
1
- {"version":"2.20.2-beta.de6d28351afafadd45e672108ecf935b8e837979.0","type":"commonjs","sideEffects":["*.css","*.scss"]}
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
- .g-aikit-base-message_btn-hover .g-aikit-base-message__actions {
18
- visibility: hidden;
19
- }
20
- .g-aikit-base-message_btn-hover:hover .g-aikit-base-message__actions {
21
- visibility: visible;
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":"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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 //\n // The children are observed alongside the container, because a re-layout can change the\n // content height while the container's own box stays the same: the messages re-wrap, the\n // browser keeps `scrollTop`, and the list ends up in the middle of the history with nothing\n // to bring it back - `scroll` never fires, since `scrollTop` itself did not move. iOS Safari\n // does exactly that while the system snapshots the page for a screenshot. The children span\n // the whole scrollable content, so their heights are what `scrollHeight` is made of.\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 // Only the difference is (un)observed: `observe()` re-delivers the current size for an\n // already observed target, which would re-pin the list on every DOM mutation.\n const observedChildren = new Set<Element>();\n\n const syncObservedChildren = () => {\n const children = new Set<Element>(Array.from(container.children));\n\n for (const child of children) {\n if (!observedChildren.has(child)) {\n observer.observe(child);\n observedChildren.add(child);\n }\n }\n\n for (const child of observedChildren) {\n if (!children.has(child)) {\n observer.unobserve(child);\n observedChildren.delete(child);\n }\n }\n };\n\n syncObservedChildren();\n\n const childrenObserver =\n typeof MutationObserver === 'undefined'\n ? undefined\n : new MutationObserver(syncObservedChildren);\n\n childrenObserver?.observe(container, {childList: true});\n\n return () => {\n observer.disconnect();\n childrenObserver?.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"]}
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"]}
@@ -1 +1 @@
1
- {"version":"2.20.2-beta.de6d28351afafadd45e672108ecf935b8e837979.0","type":"module","sideEffects":["*.css","*.scss"]}
1
+ {"version":"2.20.2","type":"module","sideEffects":["*.css","*.scss"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/aikit",
3
- "version": "2.20.2-beta.de6d28351afafadd45e672108ecf935b8e837979.0",
3
+ "version": "2.20.2",
4
4
  "description": "Gravity UI base kit for building ai assistant chats",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",