@epam/ai-dial-chat-hooks 1.1.0-dev.194

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/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './useConversationScroll/useConversationScroll';
2
+ //# sourceMappingURL=index.d.ts.map
package/index.d.ts.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,+CAA+C,CAAC"}
package/index.js ADDED
@@ -0,0 +1,122 @@
1
+ import { useCallback as e, useEffect as t, useLayoutEffect as n, useRef as r, useState as i } from "react";
2
+ //#region src/useConversationScroll/useConversationScroll.ts
3
+ var a = 80, o = 1, s = ({ messages: s, isAssistantTyping: c, conversationId: l }) => {
4
+ let [u, d] = i(!1), f = r(null), p = r(null), m = r(null), h = r(!1), g = r(!1), _ = r(null), v = r(/* @__PURE__ */ new Map()), y = e((e, t) => {
5
+ t ? v.current.set(e, t) : v.current.delete(e);
6
+ }, []), b = r(null), x = e((e) => {
7
+ b.current = e;
8
+ }, []), S = e((e, t) => {
9
+ let n = f.current;
10
+ n && n.scrollTo({
11
+ top: e,
12
+ behavior: t ? "instant" : "smooth"
13
+ });
14
+ }, []), C = e(() => {
15
+ m.current && (m.current.style.height = "0px"), h.current = !1, g.current = !1, _.current = null;
16
+ }, []), w = e(() => {
17
+ let e = f.current, t = p.current;
18
+ if (!e || !t) return null;
19
+ let n = e.getBoundingClientRect(), r = t.getBoundingClientRect();
20
+ return e.scrollTop + (r.bottom - n.top) - e.clientHeight;
21
+ }, []), T = e((e = !1) => {
22
+ let t = w();
23
+ t != null && S(Math.max(t, 0), e);
24
+ }, [S, w]), E = e(() => {
25
+ let e = f.current, t = p.current;
26
+ if (!e || !t) return null;
27
+ let n = e.getBoundingClientRect();
28
+ return t.getBoundingClientRect().bottom - n.top - e.clientHeight;
29
+ }, []), D = e(() => {
30
+ if (!g.current) return;
31
+ let e = E();
32
+ e == null || e < -1 || C();
33
+ }, [C, E]), O = e((e) => {
34
+ let t = f.current, n = v.current.get(e);
35
+ if (!t || !n) return null;
36
+ let r = t.getBoundingClientRect(), i = n.getBoundingClientRect(), a = t.scrollTop + (i.top - r.top);
37
+ return Math.max(a, 0);
38
+ }, []), k = e((e, t = !1) => {
39
+ let n = O(e);
40
+ n != null && S(n, t);
41
+ }, [O, S]), A = e(() => {
42
+ let e = w();
43
+ return e == null ? null : Math.max(e, 0);
44
+ }, [w]), j = e(() => {
45
+ if (!h.current) return null;
46
+ let e = _.current, t = A();
47
+ return e == null || t == null ? null : Math.max(e, t);
48
+ }, [A]), M = e(() => {
49
+ let e = f.current, t = j();
50
+ !e || t == null || e.scrollTop > t + o && (e.scrollTop = t);
51
+ }, [j]), N = e((e) => {
52
+ let t = m.current, n = A();
53
+ if (!t || n == null) return;
54
+ let r = Math.max(e - n, 0);
55
+ t.style.height = `${r}px`, h.current = r > 0, g.current = !1, _.current = r > 0 ? e : null;
56
+ }, [A]), P = e((e) => {
57
+ let t = O(e);
58
+ t != null && (N(t), k(e, !1));
59
+ }, [
60
+ N,
61
+ O,
62
+ k
63
+ ]), F = e(() => {
64
+ let e = E();
65
+ e != null && d(e >= a);
66
+ }, [E]), I = r(s.length), L = r(null);
67
+ n(() => {
68
+ let e = s.length !== I.current, t = l !== L.current, n = () => {
69
+ I.current = s.length, L.current = l;
70
+ };
71
+ t && C();
72
+ let r = b.current;
73
+ if (r != null && v.current.has(r)) {
74
+ b.current = null, P(r), n();
75
+ return;
76
+ }
77
+ c || ((e || t) && T(!1), n());
78
+ }, [
79
+ s,
80
+ c,
81
+ l,
82
+ T,
83
+ P,
84
+ C
85
+ ]), n(() => {
86
+ !c && h.current && (g.current = !0, D());
87
+ }, [c, D]);
88
+ let R = e(() => {
89
+ M(), D(), F();
90
+ }, [
91
+ M,
92
+ D,
93
+ F
94
+ ]);
95
+ return t(() => {
96
+ let e = f.current;
97
+ if (e) return e.addEventListener("scroll", R, { passive: !0 }), () => e.removeEventListener("scroll", R);
98
+ }, [R]), t(() => {
99
+ let e = p.current;
100
+ if (!e) return;
101
+ let t = new ResizeObserver(() => {
102
+ M(), D(), F();
103
+ });
104
+ return t.observe(e), () => t.disconnect();
105
+ }, [
106
+ M,
107
+ D,
108
+ F
109
+ ]), {
110
+ containerRef: f,
111
+ contentRef: p,
112
+ spacerRef: m,
113
+ setMessageRef: y,
114
+ isScrollButtonVisible: u,
115
+ scrollToBottom: e(() => {
116
+ T(!1);
117
+ }, [T]),
118
+ armAnchor: x
119
+ };
120
+ };
121
+ //#endregion
122
+ export { s as useConversationScroll };
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@epam/ai-dial-chat-hooks",
3
+ "description": "Framework-level React hooks extracted from AI DIAL Chat for building custom chat interfaces",
4
+ "version": "1.1.0-dev.194",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "main": "./index.js",
8
+ "module": "./index.js",
9
+ "types": "./index.d.ts",
10
+ "exports": {
11
+ "./package.json": "./package.json",
12
+ ".": {
13
+ "types": "./index.d.ts",
14
+ "import": "./index.js",
15
+ "default": "./index.js"
16
+ }
17
+ },
18
+ "peerDependencies": {
19
+ "react": "^19.2.6"
20
+ },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/epam/ai-dial-chat.git",
24
+ "directory": "libs/ai-dial-chat-hooks"
25
+ }
26
+ }
@@ -0,0 +1,46 @@
1
+ import { RefObject } from 'react';
2
+ /** Parameters for {@link useConversationScroll}. `T` is never read — only `messages.length` is used to detect growth/reset. */
3
+ export interface UseConversationScrollParams<T> {
4
+ /** Messages currently rendered in the list. */
5
+ messages: T[];
6
+ /** Whether an assistant response is currently streaming in. */
7
+ isAssistantTyping: boolean;
8
+ /** Identifier of the conversation being displayed. */
9
+ conversationId: string;
10
+ }
11
+ /** Return value of {@link useConversationScroll}. */
12
+ export interface UseConversationScrollResult {
13
+ /** Attach to the scrollable message-list element. */
14
+ containerRef: RefObject<HTMLDivElement | null>;
15
+ /** Attach to the element wrapping all rendered messages (used to detect content growth). */
16
+ contentRef: RefObject<HTMLDivElement | null>;
17
+ /**
18
+ * Attach to a spacer element, a sibling rendered right after `contentRef`
19
+ * inside `containerRef`. Its height is set imperatively, not via React
20
+ * state. Render it with `shrink-0` and an initial `style={{ height: 0 }}`.
21
+ */
22
+ spacerRef: RefObject<HTMLDivElement | null>;
23
+ /** Callback ref to register/unregister a rendered message's DOM node by index. */
24
+ setMessageRef: (index: number, el: HTMLDivElement | null) => void;
25
+ /** Whether the scroll-to-bottom button should be shown. */
26
+ isScrollButtonVisible: boolean;
27
+ /** Smoothly scrolls to the current bottom of the actual message content. */
28
+ scrollToBottom: () => void;
29
+ /**
30
+ * Arms the message at `index` to be scrolled near the top of the viewport
31
+ * on the next render — call right before triggering a send, regenerate,
32
+ * or edit-and-resubmit so the resulting message anchors near the top.
33
+ */
34
+ armAnchor: (index: number) => void;
35
+ }
36
+ /**
37
+ * Owns chat-list scrolling: anchors a new turn near the viewport top, keeps
38
+ * the position stable while the response streams, and reports whether the
39
+ * scroll-to-bottom button should be shown.
40
+ *
41
+ * The temporary spacer is technical scroll room, not user-visible content:
42
+ * it is sized to the minimum required to make the anchor reachable and manual
43
+ * scrolling is clamped before the user can move past the real message content.
44
+ */
45
+ export declare const useConversationScroll: <T>({ messages, isAssistantTyping, conversationId, }: UseConversationScrollParams<T>) => UseConversationScrollResult;
46
+ //# sourceMappingURL=useConversationScroll.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useConversationScroll.d.ts","sourceRoot":"","sources":["../../src/useConversationScroll/useConversationScroll.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAMf,+HAA+H;AAC/H,MAAM,WAAW,2BAA2B,CAAC,CAAC;IAC5C,+CAA+C;IAC/C,QAAQ,EAAE,CAAC,EAAE,CAAC;IACd,+DAA+D;IAC/D,iBAAiB,EAAE,OAAO,CAAC;IAC3B,sDAAsD;IACtD,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,qDAAqD;AACrD,MAAM,WAAW,2BAA2B;IAC1C,qDAAqD;IACrD,YAAY,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC/C,4FAA4F;IAC5F,UAAU,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC7C;;;;OAIG;IACH,SAAS,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC5C,kFAAkF;IAClF,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,GAAG,IAAI,KAAK,IAAI,CAAC;IAClE,2DAA2D;IAC3D,qBAAqB,EAAE,OAAO,CAAC;IAC/B,4EAA4E;IAC5E,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B;;;;OAIG;IACH,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,GAAI,CAAC,EAAE,kDAItC,2BAA2B,CAAC,CAAC,CAAC,KAAG,2BAySnC,CAAC"}