@epam/ai-dial-chat-hooks 1.1.0-dev.248 → 1.1.0-dev.250

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.
@@ -0,0 +1,34 @@
1
+ import { DisplayAttachment } from '@epam/ai-dial-chat-shared';
2
+ /** Resolves a DIAL Core file id to a downloadable URL. Host-owned — encodes the app's own file-download endpoint. */
3
+ export type ResolveDownloadUrl = (fileId: string) => string | undefined;
4
+ /** Parameters for {@link useAttachmentAction}. */
5
+ export interface UseAttachmentActionParams {
6
+ /** Resolves a DIAL Core file id to a downloadable URL. */
7
+ resolveDownloadUrl: ResolveDownloadUrl;
8
+ }
9
+ /** Return value of {@link useAttachmentAction}. */
10
+ export interface UseAttachmentActionResult {
11
+ /** Default click handler for an attachment tile: download, canvas-preview, or open-in-browser. */
12
+ handleAttachmentClick: (attachment: DisplayAttachment) => void;
13
+ }
14
+ /** Returns true when a value looks like a DIAL Core file id (`files/{bucket}/{path}`). */
15
+ export declare const isDialFileId: (url: string) => boolean;
16
+ /** Returns true when `downloadAttachment` can trigger a download for `attachment`. */
17
+ export declare const isDownloadableAttachment: (attachment: DisplayAttachment) => boolean;
18
+ /**
19
+ * Downloads an attachment that carries its own content: a DIAL-hosted file
20
+ * (`url` resolves to a DIAL file id) or an inline `data` payload. Returns
21
+ * whether a download was actually triggered, so callers can skip
22
+ * non-downloadable (reference-only) attachments without duplicating the
23
+ * DIAL-file-id / URL-resolution checks.
24
+ */
25
+ export declare const downloadAttachment: (attachment: DisplayAttachment, resolveDownloadUrl: ResolveDownloadUrl) => boolean;
26
+ /**
27
+ * Default click behavior for a plain attachment tile: downloads DIAL-hosted
28
+ * and inline (`data`) files, and for reference-only attachments (no `url`/`data`,
29
+ * only `referenceUrl` — e.g. RAG/search-grounding chunks), opens a PDF
30
+ * `referenceUrl` in the canvas scrolled to its referenced page when present,
31
+ * otherwise opens/downloads the `referenceUrl` as-is.
32
+ */
33
+ export declare const useAttachmentAction: ({ resolveDownloadUrl, }: UseAttachmentActionParams) => UseAttachmentActionResult;
34
+ //# sourceMappingURL=useAttachmentAction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAttachmentAction.d.ts","sourceRoot":"","sources":["../../../src/attachment/useAttachmentAction/useAttachmentAction.ts"],"names":[],"mappings":"AAKA,OAAO,EAML,KAAK,iBAAiB,EACvB,MAAM,2BAA2B,CAAC;AAInC,qHAAqH;AACrH,MAAM,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;AAExE,kDAAkD;AAClD,MAAM,WAAW,yBAAyB;IACxC,0DAA0D;IAC1D,kBAAkB,EAAE,kBAAkB,CAAC;CACxC;AAED,mDAAmD;AACnD,MAAM,WAAW,yBAAyB;IACxC,kGAAkG;IAClG,qBAAqB,EAAE,CAAC,UAAU,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAChE;AAED,0FAA0F;AAC1F,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,KAAG,OAAmC,CAAC;AAE/E,sFAAsF;AACtF,eAAO,MAAM,wBAAwB,GACnC,YAAY,iBAAiB,KAC5B,OAEsB,CAAC;AAE1B;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,GAC7B,YAAY,iBAAiB,EAC7B,oBAAoB,kBAAkB,KACrC,OAuBF,CAAC;AAqDF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAAI,yBAEjC,yBAAyB,KAAG,yBAmC9B,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { FilesApi } from '@epam/ai-dial-chat-api-client';
2
+ import { Attachment } from '@epam/ai-dial-chat-shared';
3
+ /** Parameters for {@link useAttachmentUpload}. */
4
+ export interface UseAttachmentUploadParams {
5
+ /** Already-configured generated-client instance used to upload the file. */
6
+ filesApi: Pick<FilesApi, 'uploadFile'>;
7
+ /** DIAL Core bucket the file is uploaded into. */
8
+ bucket: string | undefined;
9
+ /** Called with batched filenames after a burst of network-error upload failures. */
10
+ onNetworkError?: (filenames: string[]) => void;
11
+ /** Debounce window, in ms, for coalescing offline-failure batches. Defaults to `700`. */
12
+ debounceMs?: number;
13
+ }
14
+ /** Return value of {@link useAttachmentUpload}. */
15
+ export interface UseAttachmentUploadResult {
16
+ /** Uploads the given attachment's file and resolves to its DIAL Core file URL. */
17
+ handleUploadAttachment: (attachment: Attachment) => Promise<string>;
18
+ }
19
+ /**
20
+ * Uploads an attachment's file to DIAL Core storage against an
21
+ * already-configured `FilesApi` instance, coalescing a burst of
22
+ * offline/network upload failures into a single debounced callback rather
23
+ * than firing one notification per failed file.
24
+ */
25
+ export declare const useAttachmentUpload: ({ filesApi, bucket, onNetworkError, debounceMs, }: UseAttachmentUploadParams) => UseAttachmentUploadResult;
26
+ //# sourceMappingURL=useAttachmentUpload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAttachmentUpload.d.ts","sourceRoot":"","sources":["../../../src/conversation/useAttachmentUpload/useAttachmentUpload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAEL,KAAK,UAAU,EAChB,MAAM,2BAA2B,CAAC;AAKnC,kDAAkD;AAClD,MAAM,WAAW,yBAAyB;IACxC,4EAA4E;IAC5E,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IACvC,kDAAkD;IAClD,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,oFAAoF;IACpF,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC/C,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,mDAAmD;AACnD,MAAM,WAAW,yBAAyB;IACxC,kFAAkF;IAClF,sBAAsB,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACrE;AAgBD;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAAI,mDAKjC,yBAAyB,KAAG,yBA0C9B,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useConversationScroll.d.ts","sourceRoot":"","sources":["../../../src/conversation/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"}
@@ -0,0 +1,20 @@
1
+ import { AttachmentDisplayResolvers, DisplayAttachment, Message } from '@epam/ai-dial-chat-shared';
2
+ import { QuotationSource } from '@epam/ai-dial-source-panel';
3
+ /** Return value of {@link useConversationSources}. */
4
+ export interface UseConversationSourcesResult {
5
+ /** Deduplicated attachments the user uploaded across the conversation. */
6
+ uploaded: DisplayAttachment[];
7
+ /** Deduplicated attachments the assistant generated across the conversation. */
8
+ generated: DisplayAttachment[];
9
+ /** Quotation sources referenced by assistant messages, deduplicated by URL. */
10
+ sources: QuotationSource[];
11
+ }
12
+ /**
13
+ * Derives uploaded (user), generated (assistant) attachment lists, and
14
+ * quotation sources from a conversation's message array. All lists are
15
+ * memoised on the `messages` reference. `resolvers` are forwarded to
16
+ * `@epam/ai-dial-chat-shared`'s attachment mapper for preview/play URL
17
+ * resolution — pass an empty object when the host has no such resolution.
18
+ */
19
+ export declare const useConversationSources: (messages: Message[], resolvers?: AttachmentDisplayResolvers) => UseConversationSourcesResult;
20
+ //# sourceMappingURL=useConversationSources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useConversationSources.d.ts","sourceRoot":"","sources":["../../../src/conversation-sources/useConversationSources/useConversationSources.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,0BAA0B,EAC1B,iBAAiB,EACjB,OAAO,EACR,MAAM,2BAA2B,CAAC;AASnC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AASlE,sDAAsD;AACtD,MAAM,WAAW,4BAA4B;IAC3C,0EAA0E;IAC1E,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,gFAAgF;IAChF,SAAS,EAAE,iBAAiB,EAAE,CAAC;IAC/B,+EAA+E;IAC/E,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,GACjC,UAAU,OAAO,EAAE,EACnB,YAAW,0BAA4C,KACtD,4BA2DF,CAAC"}
package/index.d.ts CHANGED
@@ -1,2 +1,10 @@
1
- export * from './useConversationScroll/useConversationScroll';
1
+ export * from './attachment/useAttachmentAction/useAttachmentAction';
2
+ export * from './conversation/useAttachmentUpload/useAttachmentUpload';
3
+ export * from './conversation/useConversationScroll/useConversationScroll';
4
+ export * from './conversation-sources/useConversationSources/useConversationSources';
5
+ export * from './usePageFileDrag/usePageFileDrag';
6
+ export * from './usePanelMaxWidth/usePanelMaxWidth';
7
+ export * from './useShareLink/useShareLink';
8
+ export * from './useShareRecipientsCount/useShareRecipientsCount';
9
+ export * from './useViewportWidth/useViewportWidth';
2
10
  //# sourceMappingURL=index.d.ts.map
package/index.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,+CAA+C,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sDAAsD,CAAC;AACrE,cAAc,wDAAwD,CAAC;AACvE,cAAc,4DAA4D,CAAC;AAC3E,cAAc,sEAAsE,CAAC;AACrF,cAAc,mCAAmC,CAAC;AAClD,cAAc,qCAAqC,CAAC;AACpD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mDAAmD,CAAC;AAClE,cAAc,qCAAqC,CAAC"}
package/index.js CHANGED
@@ -1,122 +1,369 @@
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;
1
+ import { AttachmentContentType as e, useAttachmentCanvas as t } from "@epam/ai-dial-attachment-canvas";
2
+ import { AttachmentErrorReason as n, MIMEType as r, MessageRole as i, base64ToBlob as a, messageAttachmentToDisplayAttachment as o, triggerAnchorDownload as s, triggerBlobDownload as c } from "@epam/ai-dial-chat-shared";
3
+ import { isReferenceOnlyAttachment as l, parsePdfPageReference as u, resolveMessageAnnotations as d } from "@epam/ai-dial-quotations";
4
+ import { useCallback as f, useEffect as p, useLayoutEffect as m, useMemo as h, useRef as g, useState as _ } from "react";
5
+ import { ShareLinkResponseDtoAccessEnum as v } from "@epam/ai-dial-chat-api-client";
6
+ import { ShareLinkAccess as y } from "@epam/ai-dial-share";
7
+ //#region src/attachment/useAttachmentAction/useAttachmentAction.ts
8
+ var b = (e) => e.startsWith("files/"), x = (e) => e.url != null && b(e.url) || e.data != null, S = (e, t) => {
9
+ let { url: n, name: i, contentType: o, data: l } = e;
10
+ if (n != null && b(n)) {
11
+ let e = t(n);
12
+ return e == null ? !1 : (s(e, i), !0);
13
+ }
14
+ return l == null ? !1 : (c(a(l, o || r.Plain), i), !0);
15
+ }, C = (e, t) => {
16
+ let { url: n } = e;
17
+ if (n == null) return;
18
+ let r = n.split("#")[0];
19
+ if (b(r)) {
20
+ let n = t(r);
21
+ if (n == null) return;
22
+ s(n, e.title ?? r.split("/").pop() ?? "");
23
+ } else window.open(n, "_blank", "noopener,noreferrer");
24
+ }, w = (t, n) => {
25
+ let r = u(t.url);
26
+ if (r == null) return null;
27
+ let i = b(r.baseUrl) ? n(r.baseUrl) : r.baseUrl;
28
+ if (i == null) return null;
29
+ if (r.page == null) return {
30
+ type: e.Pdf,
31
+ url: i
32
+ };
33
+ let a = `reference-page-${r.page}`;
34
+ return {
35
+ type: e.Pdf,
36
+ url: i,
37
+ highlights: [{
38
+ id: a,
39
+ bboxes: [{
40
+ page: r.page,
41
+ x1: 0,
42
+ y1: 0,
43
+ x2: 0,
44
+ y2: 0
45
+ }],
46
+ style: {
47
+ backgroundColor: "transparent",
48
+ opacity: 0
49
+ }
50
+ }],
51
+ selectedHighlightId: a
52
+ };
53
+ }, T = ({ resolveDownloadUrl: e }) => {
54
+ let { openCanvas: n } = t();
55
+ return { handleAttachmentClick: f((t) => {
56
+ let { url: r, referenceUrl: i, data: a, name: o } = t;
57
+ if (r != null || a != null) {
58
+ S(t, e);
59
+ return;
60
+ }
61
+ if (i == null) return;
62
+ let s = {
63
+ type: t.contentType,
64
+ url: i,
65
+ title: o
66
+ }, c = w(s, e);
67
+ if (c) {
68
+ n(c, o);
69
+ return;
70
+ }
71
+ C(s, e);
72
+ }, [n, e]) };
73
+ }, E = 700, D = (e) => (e.split(/[\\/]/).filter(Boolean).pop() ?? "file").replace(/\.\.+/g, ".").replace(/^\.+/, "") || "file", O = (e, t = /* @__PURE__ */ new Date()) => `uploads/${t.getFullYear()}-${String(t.getMonth() + 1).padStart(2, "0")}/${encodeURIComponent(D(e))}`, k = ({ filesApi: e, bucket: t, onNetworkError: r, debounceMs: i = E }) => {
74
+ let a = g([]), o = g(null);
75
+ return { handleUploadAttachment: f(async (s) => {
76
+ if (!t) throw Error("User bucket is not available");
77
+ try {
78
+ return (await e.uploadFile({
79
+ bucket: t,
80
+ path: O(s.name),
81
+ file: s.file
82
+ })).url;
83
+ } catch (e) {
84
+ if (!navigator.onLine) {
85
+ a.current.push(s.name), o.current != null && clearTimeout(o.current), o.current = setTimeout(() => {
86
+ let e = a.current.splice(0);
87
+ r?.(e), o.current = null;
88
+ }, i);
89
+ let t = e instanceof Error ? e : /* @__PURE__ */ Error("Network upload failed");
90
+ throw t.errorReason = n.Network, t;
91
+ }
92
+ throw e;
93
+ }
94
+ }, [
95
+ t,
96
+ i,
97
+ e,
98
+ r
99
+ ]) };
100
+ }, A = 80, j = 1, M = ({ messages: e, isAssistantTyping: t, conversationId: n }) => {
101
+ let [r, i] = _(!1), a = g(null), o = g(null), s = g(null), c = g(!1), l = g(!1), u = g(null), d = g(/* @__PURE__ */ new Map()), h = f((e, t) => {
102
+ t ? d.current.set(e, t) : d.current.delete(e);
103
+ }, []), v = g(null), y = f((e) => {
104
+ v.current = e;
105
+ }, []), b = f((e, t) => {
106
+ let n = a.current;
10
107
  n && n.scrollTo({
11
108
  top: e,
12
109
  behavior: t ? "instant" : "smooth"
13
110
  });
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;
111
+ }, []), x = f(() => {
112
+ s.current && (s.current.style.height = "0px"), c.current = !1, l.current = !1, u.current = null;
113
+ }, []), S = f(() => {
114
+ let e = a.current, t = o.current;
18
115
  if (!e || !t) return null;
19
116
  let n = e.getBoundingClientRect(), r = t.getBoundingClientRect();
20
117
  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;
118
+ }, []), C = f((e = !1) => {
119
+ let t = S();
120
+ t != null && b(Math.max(t, 0), e);
121
+ }, [b, S]), w = f(() => {
122
+ let e = a.current, t = o.current;
26
123
  if (!e || !t) return null;
27
124
  let n = e.getBoundingClientRect();
28
125
  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(() => {
126
+ }, []), T = f(() => {
127
+ if (!l.current) return;
42
128
  let e = w();
129
+ e == null || e < -1 || x();
130
+ }, [x, w]), E = f((e) => {
131
+ let t = a.current, n = d.current.get(e);
132
+ if (!t || !n) return null;
133
+ let r = t.getBoundingClientRect(), i = n.getBoundingClientRect(), o = t.scrollTop + (i.top - r.top);
134
+ return Math.max(o, 0);
135
+ }, []), D = f((e, t = !1) => {
136
+ let n = E(e);
137
+ n != null && b(n, t);
138
+ }, [E, b]), O = f(() => {
139
+ let e = S();
43
140
  return e == null ? null : Math.max(e, 0);
44
- }, [w]), j = e(() => {
45
- if (!h.current) return null;
46
- let e = _.current, t = A();
141
+ }, [S]), k = f(() => {
142
+ if (!c.current) return null;
143
+ let e = u.current, t = O();
47
144
  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();
145
+ }, [O]), M = f(() => {
146
+ let e = a.current, t = k();
147
+ !e || t == null || e.scrollTop > t + j && (e.scrollTop = t);
148
+ }, [k]), N = f((e) => {
149
+ let t = s.current, n = O();
53
150
  if (!t || n == null) return;
54
151
  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));
152
+ t.style.height = `${r}px`, c.current = r > 0, l.current = !1, u.current = r > 0 ? e : null;
153
+ }, [O]), P = f((e) => {
154
+ let t = E(e);
155
+ t != null && (N(t), D(e, !1));
59
156
  }, [
60
157
  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;
158
+ E,
159
+ D
160
+ ]), F = f(() => {
161
+ let e = w();
162
+ e != null && i(e >= A);
163
+ }, [w]), I = g(e.length), L = g(null);
164
+ m(() => {
165
+ let r = e.length !== I.current, i = n !== L.current, a = () => {
166
+ I.current = e.length, L.current = n;
70
167
  };
71
- t && C();
72
- let r = b.current;
73
- if (r != null && v.current.has(r)) {
74
- b.current = null, P(r), n();
168
+ i && x();
169
+ let o = v.current;
170
+ if (o != null && d.current.has(o)) {
171
+ v.current = null, P(o), a();
75
172
  return;
76
173
  }
77
- c || ((e || t) && T(!1), n());
174
+ t || ((r || i) && C(!1), a());
78
175
  }, [
79
- s,
80
- c,
81
- l,
82
- T,
176
+ e,
177
+ t,
178
+ n,
179
+ C,
83
180
  P,
84
- C
85
- ]), n(() => {
86
- !c && h.current && (g.current = !0, D());
87
- }, [c, D]);
88
- let R = e(() => {
89
- M(), D(), F();
181
+ x
182
+ ]), m(() => {
183
+ !t && c.current && (l.current = !0, T());
184
+ }, [t, T]);
185
+ let R = f(() => {
186
+ M(), T(), F();
90
187
  }, [
91
188
  M,
92
- D,
189
+ T,
93
190
  F
94
191
  ]);
95
- return t(() => {
96
- let e = f.current;
192
+ return p(() => {
193
+ let e = a.current;
97
194
  if (e) return e.addEventListener("scroll", R, { passive: !0 }), () => e.removeEventListener("scroll", R);
98
- }, [R]), t(() => {
99
- let e = p.current;
195
+ }, [R]), p(() => {
196
+ let e = o.current;
100
197
  if (!e) return;
101
198
  let t = new ResizeObserver(() => {
102
- M(), D(), F();
199
+ M(), T(), F();
103
200
  });
104
201
  return t.observe(e), () => t.disconnect();
105
202
  }, [
106
203
  M,
107
- D,
204
+ T,
108
205
  F
109
206
  ]), {
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
207
+ containerRef: a,
208
+ contentRef: o,
209
+ spacerRef: s,
210
+ setMessageRef: h,
211
+ isScrollButtonVisible: r,
212
+ scrollToBottom: f(() => {
213
+ C(!1);
214
+ }, [C]),
215
+ armAnchor: y
216
+ };
217
+ }, N = {}, P = (e, t = N) => h(() => {
218
+ let n = [], r = [], a = [], s = /* @__PURE__ */ new Set(), c = /* @__PURE__ */ new Set(), u = /* @__PURE__ */ new Set();
219
+ for (let f of e) {
220
+ let e = f.custom_content?.attachments;
221
+ if (f.role === i.User) for (let r of e ?? []) {
222
+ let e = o(r, t);
223
+ c.has(e.id) || (c.add(e.id), n.push(e));
224
+ }
225
+ else if (f.role === i.Assistant) {
226
+ let n = e?.filter((e) => !l(e));
227
+ for (let e of n ?? []) {
228
+ let n = o(e, t);
229
+ u.has(n.id) || (u.add(n.id), r.push(n));
230
+ }
231
+ for (let t of e ?? []) {
232
+ if (!l(t)) continue;
233
+ let e = t.reference_url;
234
+ s.has(e) || (s.add(e), a.push({
235
+ url: e,
236
+ title: t.title ?? e,
237
+ contentType: t.reference_type ?? t.type ?? "",
238
+ quote: t.data
239
+ }));
240
+ }
241
+ for (let e of d(f)) {
242
+ let t = e?.body?.source?.attachment, n = t?.url;
243
+ !n || s.has(n) || (s.add(n), a.push({
244
+ url: n,
245
+ title: t?.title ?? n.split("/").pop() ?? n,
246
+ contentType: t?.type ?? "",
247
+ quote: e.body?.quote
248
+ }));
249
+ }
250
+ }
251
+ }
252
+ return {
253
+ uploaded: n,
254
+ generated: r,
255
+ sources: a
256
+ };
257
+ }, [e, t]), F = (e = !0, t = !0) => {
258
+ let [n, r] = _(!1), [i, a] = _([]), o = g(0), s = g(e), c = g(t);
259
+ m(() => {
260
+ s.current = e, c.current = t, t || (o.current = 0, r(!1));
261
+ }, [e, t]);
262
+ let l = f(() => {
263
+ a([]);
264
+ }, []);
265
+ return p(() => {
266
+ let e = (e) => !!e.dataTransfer?.types.includes("Files"), t = (t) => {
267
+ !c.current || !e(t) || (o.current += 1, r(!0));
268
+ }, n = (t) => {
269
+ !c.current || !e(t) || (--o.current, o.current <= 0 && (o.current = 0, r(!1)));
270
+ }, i = (t) => {
271
+ e(t) && (t.preventDefault(), c.current);
272
+ }, l = (t) => {
273
+ if (!(!e(t) || !c.current) && (t.preventDefault(), o.current = 0, r(!1), s.current)) {
274
+ let e = Array.from(t.dataTransfer?.files ?? []);
275
+ e.length > 0 && a(e);
276
+ }
277
+ };
278
+ return document.addEventListener("dragenter", t), document.addEventListener("dragleave", n), document.addEventListener("dragover", i), document.addEventListener("drop", l), () => {
279
+ document.removeEventListener("dragenter", t), document.removeEventListener("dragleave", n), document.removeEventListener("dragover", i), document.removeEventListener("drop", l);
280
+ };
281
+ }, []), {
282
+ isDragging: n,
283
+ pendingFiles: i,
284
+ onFilesConsumed: l
285
+ };
286
+ }, I = () => typeof window < "u" ? window.innerWidth : 1024, L = () => {
287
+ let [e, t] = _(I);
288
+ return p(() => {
289
+ if (typeof window > "u") return;
290
+ let e = () => t(window.innerWidth);
291
+ return window.addEventListener("resize", e), () => window.removeEventListener("resize", e);
292
+ }, []), e;
293
+ }, R = (e) => {
294
+ let t = L();
295
+ return Math.max(0, t - e);
296
+ }, z = (e) => e.map((e) => e === v.Edit ? y.Edit : y.View), B = (e, t) => {
297
+ let n = new URL(e, t);
298
+ return `${t}${n.pathname}${n.search}${n.hash}`;
299
+ }, V = (e, t, n, r = window.location.origin) => {
300
+ let [i, a] = _(), [o, s] = _(!0), [c, l] = _(null), u = g(0), d = f(async (i = [y.View]) => {
301
+ let o = ++u.current;
302
+ s(!0), l(null);
303
+ try {
304
+ let s = await e.createShareLink({ createShareLinkDto: {
305
+ itemId: t,
306
+ resourceKind: n,
307
+ access: i
308
+ } });
309
+ u.current === o && a({
310
+ url: B(s.url, r),
311
+ expiresInDays: s.expiresInDays,
312
+ access: z(s.access)
313
+ });
314
+ } catch (e) {
315
+ u.current === o && l(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to create share link"));
316
+ } finally {
317
+ u.current === o && s(!1);
318
+ }
319
+ }, [
320
+ t,
321
+ r,
322
+ n,
323
+ e
324
+ ]);
325
+ return p(() => {
326
+ a(void 0), d();
327
+ }, [t, d]), {
328
+ data: i,
329
+ isLoading: o,
330
+ error: c,
331
+ setAccess: f((e) => {
332
+ d(e);
333
+ }, [d])
334
+ };
335
+ }, H = /* @__PURE__ */ function(e) {
336
+ return e.Idle = "idle", e.Loading = "loading", e.Resolved = "resolved", e.Unknown = "unknown", e;
337
+ }({}), U = { status: "idle" }, W = (e) => {
338
+ let [t, n] = _({}), r = g(/* @__PURE__ */ new Set());
339
+ return {
340
+ requestRecipientsCount: f((t) => {
341
+ r.current.has(t) || (r.current.add(t), n((e) => ({
342
+ ...e,
343
+ [t]: { status: "loading" }
344
+ })), (async () => {
345
+ try {
346
+ let { recipientsCount: r } = await e.getShareRecipientsCount({ itemId: t });
347
+ n((e) => ({
348
+ ...e,
349
+ [t]: {
350
+ status: "resolved",
351
+ count: r
352
+ }
353
+ }));
354
+ } catch {
355
+ n((e) => ({
356
+ ...e,
357
+ [t]: { status: "unknown" }
358
+ }));
359
+ }
360
+ })());
361
+ }, [e]),
362
+ getRecipientsCount: f((e) => t[e] ?? U, [t]),
363
+ invalidateRecipientsCount: f((e) => {
364
+ r.current.delete(e), n(({ [e]: t, ...n }) => n);
365
+ }, [])
119
366
  };
120
367
  };
121
368
  //#endregion
122
- export { s as useConversationScroll };
369
+ export { H as RecipientsCountStatus, S as downloadAttachment, b as isDialFileId, x as isDownloadableAttachment, T as useAttachmentAction, k as useAttachmentUpload, M as useConversationScroll, P as useConversationSources, F as usePageFileDrag, R as usePanelMaxWidth, V as useShareLink, W as useShareRecipientsCount, L as useViewportWidth };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@epam/ai-dial-chat-hooks",
3
3
  "description": "Framework-level React hooks extracted from AI DIAL Chat for building custom chat interfaces",
4
- "version": "1.1.0-dev.248",
4
+ "version": "1.1.0-dev.250",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "main": "./index.js",
@@ -16,11 +16,17 @@
16
16
  }
17
17
  },
18
18
  "peerDependencies": {
19
- "react": "^19.2.6"
19
+ "react": "^19.2.6",
20
+ "@epam/ai-dial-attachment-canvas": "1.1.0-dev.250",
21
+ "@epam/ai-dial-chat-api-client": "1.1.0-dev.250",
22
+ "@epam/ai-dial-chat-shared": "1.1.0-dev.250",
23
+ "@epam/ai-dial-quotations": "1.1.0-dev.250",
24
+ "@epam/ai-dial-share": "1.1.0-dev.250",
25
+ "@epam/ai-dial-source-panel": "1.1.0-dev.250"
20
26
  },
21
27
  "repository": {
22
28
  "type": "git",
23
29
  "url": "git+https://github.com/epam/ai-dial-chat.git",
24
- "directory": "libs/ai-dial-chat-hooks"
30
+ "directory": "libs/chat-hooks"
25
31
  }
26
32
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=test-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-setup.d.ts","sourceRoot":"","sources":["../src/test-setup.ts"],"names":[],"mappings":""}
@@ -0,0 +1,16 @@
1
+ /** Return value of {@link usePageFileDrag}. */
2
+ export interface UsePageFileDragResult {
3
+ /** Whether a file drag is currently over the page. */
4
+ isDragging: boolean;
5
+ /** Files dropped on the page, pending consumption by the caller. */
6
+ pendingFiles: File[];
7
+ /** Clears `pendingFiles` after the caller has processed them. */
8
+ onFilesConsumed: () => void;
9
+ }
10
+ /**
11
+ * Detects files being dragged over the whole page and exposes the dropped
12
+ * files, using only `document`-level drag events with an enter/leave
13
+ * counter to avoid flicker from child-element boundary crossings.
14
+ */
15
+ export declare const usePageFileDrag: (isAttachmentsAllowed?: boolean, isEnabled?: boolean) => UsePageFileDragResult;
16
+ //# sourceMappingURL=usePageFileDrag.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePageFileDrag.d.ts","sourceRoot":"","sources":["../../src/usePageFileDrag/usePageFileDrag.ts"],"names":[],"mappings":"AAQA,+CAA+C;AAC/C,MAAM,WAAW,qBAAqB;IACpC,sDAAsD;IACtD,UAAU,EAAE,OAAO,CAAC;IACpB,oEAAoE;IACpE,YAAY,EAAE,IAAI,EAAE,CAAC;IACrB,iEAAiE;IACjE,eAAe,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAC1B,8BAA2B,EAC3B,mBAAgB,KACf,qBAwEF,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Returns the maximum pixel width a resizable side panel may occupy without
3
+ * collapsing the main content area below `minContentAreaWidth`.
4
+ */
5
+ export declare const usePanelMaxWidth: (minContentAreaWidth: number) => number;
6
+ //# sourceMappingURL=usePanelMaxWidth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePanelMaxWidth.d.ts","sourceRoot":"","sources":["../../src/usePanelMaxWidth/usePanelMaxWidth.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,qBAAqB,MAAM,KAAG,MAG9D,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { CreateShareLinkDtoResourceKindEnum, ShareApi } from '@epam/ai-dial-chat-api-client';
2
+ import { ShareLinkAccess, ShareLinkData } from '@epam/ai-dial-share';
3
+ /** Result returned by {@link useShareLink}. */
4
+ export interface UseShareLinkResult {
5
+ /** Resolved share-link data, or `undefined` while loading or on error. */
6
+ data: ShareLinkData | undefined;
7
+ /** Whether the share link is still being created. */
8
+ isLoading: boolean;
9
+ /** Set when the share link could not be created. */
10
+ error: Error | null;
11
+ /** Requests a new share link for the given access levels. */
12
+ setAccess: (access: ShareLinkAccess[]) => void;
13
+ }
14
+ /**
15
+ * Resolves and manages share-link data for a DIAL Core resource.
16
+ *
17
+ * Accepts an already-configured `ShareApi` instance — this hook owns only
18
+ * the request lifecycle (loading/error, a stale-response guard, and
19
+ * re-fetch on access change), not the client's base URL, auth, or CSRF
20
+ * setup, which stay the caller's responsibility.
21
+ */
22
+ export declare const useShareLink: (shareApi: Pick<ShareApi, "createShareLink">, itemId: string, resourceKind?: CreateShareLinkDtoResourceKindEnum, origin?: string) => UseShareLinkResult;
23
+ //# sourceMappingURL=useShareLink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useShareLink.d.ts","sourceRoot":"","sources":["../../src/useShareLink/useShareLink.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,kCAAkC,EACvC,KAAK,QAAQ,EACd,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAYrE,+CAA+C;AAC/C,MAAM,WAAW,kBAAkB;IACjC,0EAA0E;IAC1E,IAAI,EAAE,aAAa,GAAG,SAAS,CAAC;IAChC,qDAAqD;IACrD,SAAS,EAAE,OAAO,CAAC;IACnB,oDAAoD;IACpD,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,6DAA6D;IAC7D,SAAS,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,IAAI,CAAC;CAChD;AAcD;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GACvB,UAAU,IAAI,CAAC,QAAQ,EAAE,iBAAiB,CAAC,EAC3C,QAAQ,MAAM,EACd,eAAe,kCAAkC,EACjD,SAAQ,MAA+B,KACtC,kBAwDF,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { ShareApi } from '@epam/ai-dial-chat-api-client';
2
+ /** Lifecycle of an on-demand recipient-count lookup for one resource. */
3
+ export declare enum RecipientsCountStatus {
4
+ /** Nothing requested yet for this resource. */
5
+ Idle = "idle",
6
+ /** A lookup is in flight. */
7
+ Loading = "loading",
8
+ /** A count came back and is known exactly. */
9
+ Resolved = "resolved",
10
+ /** The lookup failed, so the count is unknown. */
11
+ Unknown = "unknown"
12
+ }
13
+ /** State of one resource's recipient-count lookup. */
14
+ export interface RecipientsCountEntry {
15
+ /** Where the lookup for this resource currently stands. */
16
+ status: RecipientsCountStatus;
17
+ /** Number of users holding access; present only once the status is `Resolved`. */
18
+ count?: number;
19
+ }
20
+ /** Per-resource recipient-count lookups resolved on demand. */
21
+ export interface UseShareRecipientsCountResult {
22
+ /** Starts a lookup for the resource unless one already ran for it. */
23
+ requestRecipientsCount: (itemId: string) => void;
24
+ /** Current lookup state for the resource. */
25
+ getRecipientsCount: (itemId: string) => RecipientsCountEntry;
26
+ /** Drops the resource's cached result so the next request fetches it again. */
27
+ invalidateRecipientsCount: (itemId: string) => void;
28
+ }
29
+ /**
30
+ * Resolves how many users hold shared access to a resource, one resource at
31
+ * a time and only when asked. Accepts an already-configured `ShareApi`
32
+ * instance — this hook owns only the dedup/cache state machine, not the
33
+ * client's base URL, auth, or CSRF setup.
34
+ */
35
+ export declare const useShareRecipientsCount: (shareApi: Pick<ShareApi, "getShareRecipientsCount">) => UseShareRecipientsCountResult;
36
+ //# sourceMappingURL=useShareRecipientsCount.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useShareRecipientsCount.d.ts","sourceRoot":"","sources":["../../src/useShareRecipientsCount/useShareRecipientsCount.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAG9D,yEAAyE;AACzE,oBAAY,qBAAqB;IAC/B,+CAA+C;IAC/C,IAAI,SAAS;IACb,6BAA6B;IAC7B,OAAO,YAAY;IACnB,8CAA8C;IAC9C,QAAQ,aAAa;IACrB,kDAAkD;IAClD,OAAO,YAAY;CACpB;AAED,sDAAsD;AACtD,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,MAAM,EAAE,qBAAqB,CAAC;IAC9B,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,+DAA+D;AAC/D,MAAM,WAAW,6BAA6B;IAC5C,sEAAsE;IACtE,sBAAsB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,6CAA6C;IAC7C,kBAAkB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,oBAAoB,CAAC;IAC7D,+EAA+E;IAC/E,yBAAyB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAMD;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAClC,UAAU,IAAI,CAAC,QAAQ,EAAE,yBAAyB,CAAC,KAClD,6BA0DF,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** Tracks `window.innerWidth`, updating on the browser `resize` event. */
2
+ export declare const useViewportWidth: () => number;
3
+ //# sourceMappingURL=useViewportWidth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useViewportWidth.d.ts","sourceRoot":"","sources":["../../src/useViewportWidth/useViewportWidth.ts"],"names":[],"mappings":"AAKA,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,QAAO,MAYnC,CAAC"}
@@ -1 +0,0 @@
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"}