@epam/ai-dial-chat-hooks 1.2.0-dev.158 → 1.2.0-dev.162

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.
@@ -1,20 +1,20 @@
1
1
  import { formatDateYM as e } from "../conversation-transfer/date.js";
2
2
  import { ConversationTransferWarningCode as t } from "../conversation-transfer/types.js";
3
- import { UnsupportedImportFormatError as n, getFolderBreadcrumb as r, parseImportEnvelope as i, planAttachmentUploads as a, rebaseConversationId as o, rewriteAttachmentUrls as s } from "../conversation-transfer/import-conversation.js";
4
- import { createUploadPathAllocator as c } from "../conversation-transfer/build-upload-path.js";
5
- import { runWithConcurrency as l } from "../conversation-transfer/async.js";
6
- import { ConversationTransferKind as u, ConversationTransferPhase as d, buildTransferProgress as f } from "../conversation-transfer/progress.js";
7
- import { useConversationTransferQueue as p } from "../conversation-transfer/queue.js";
8
- import { parseDialArchive as m } from "../conversation-transfer/zip-import.js";
9
- import { ConversationTransferErrorCode as h, ConversationTransferSubjectKind as g, ConversationTransferUnitKind as _, generateUUID as v } from "@epam/ai-dial-chat-shared";
10
- import { useCallback as y } from "react";
3
+ import { UnsupportedImportFormatError as n, dropForeignAttachmentRefs as r, getFolderBreadcrumb as i, parseImportEnvelope as a, planAttachmentUploads as o, rebaseConversationId as s, rewriteAttachmentUrls as c } from "../conversation-transfer/import-conversation.js";
4
+ import { createUploadPathAllocator as l } from "../conversation-transfer/build-upload-path.js";
5
+ import { runWithConcurrency as u } from "../conversation-transfer/async.js";
6
+ import { ConversationTransferKind as d, ConversationTransferPhase as f, buildTransferProgress as p } from "../conversation-transfer/progress.js";
7
+ import { useConversationTransferQueue as m } from "../conversation-transfer/queue.js";
8
+ import { parseDialArchive as h } from "../conversation-transfer/zip-import.js";
9
+ import { ConversationTransferErrorCode as g, ConversationTransferSubjectKind as _, ConversationTransferUnitKind as v, generateUUID as y } from "@epam/ai-dial-chat-shared";
10
+ import { useCallback as b } from "react";
11
11
  //#region src/conversation/useConversationImport/useConversationImport.ts
12
- var b = 5, x = 5, S = (e) => /\.(dial|zip)$/i.test(e.name), C = (e) => new Promise((t, n) => {
12
+ var x = 5, S = 5, C = (e) => /\.(dial|zip)$/i.test(e.name), w = (e) => new Promise((t, n) => {
13
13
  let r = new FileReader();
14
14
  r.onload = () => t(r.result), r.onerror = () => n(r.error), r.readAsText(e);
15
- }), w = async (e) => {
16
- if (S(e)) {
17
- let { envelope: t, attachments: n } = await m(e);
15
+ }), T = async (e) => {
16
+ if (C(e)) {
17
+ let { envelope: t, attachments: n } = await h(e);
18
18
  return {
19
19
  history: t.history,
20
20
  attachments: n,
@@ -22,15 +22,15 @@ var b = 5, x = 5, S = (e) => /\.(dial|zip)$/i.test(e.name), C = (e) => new Promi
22
22
  };
23
23
  }
24
24
  return {
25
- history: i(await C(e)).history,
25
+ history: a(await w(e)).history,
26
26
  attachments: /* @__PURE__ */ new Map(),
27
27
  isArchive: !1
28
28
  };
29
- }, T = async (e, t, n, r, i, o, s, c) => {
30
- let { plan: u, skippedNames: d } = a(e, t, r), f = !1, p = await l(u, b, async (e) => {
29
+ }, E = async (e, t, n, r, i, a, s, c) => {
30
+ let { plan: l, skippedNames: d } = o(e, t, r), f = !1, p = await u(l, x, async (e) => {
31
31
  if (c.aborted || f) return;
32
32
  let t = e.allocated;
33
- for (let a = 0; a <= x; a += 1) try {
33
+ for (let o = 0; o <= S; o += 1) try {
34
34
  let r = new File([new Uint8Array(e.bytes)], t.fileName), a = await i.uploadFile({
35
35
  bucket: n,
36
36
  path: t.path,
@@ -45,11 +45,11 @@ var b = 5, x = 5, S = (e) => /\.(dial|zip)$/i.test(e.name), C = (e) => new Promi
45
45
  };
46
46
  } catch (n) {
47
47
  if (c.aborted) return;
48
- if (o(n)) {
48
+ if (a(n)) {
49
49
  f = !0;
50
50
  return;
51
51
  }
52
- if (!(typeof n == "object" && n && "response" in n && n.response?.status === 409) || a === x) return s(), {
52
+ if (!(typeof n == "object" && n && "response" in n && n.response?.status === 409) || o === S) return s(), {
53
53
  kind: "skipped",
54
54
  name: e.originalFileName
55
55
  };
@@ -69,171 +69,173 @@ var b = 5, x = 5, S = (e) => /\.(dial|zip)$/i.test(e.name), C = (e) => new Promi
69
69
  skippedNames: c.aborted || f ? [] : h,
70
70
  isUnauthorized: !c.aborted && f
71
71
  };
72
- }, E = ({ conversationsApi: i, filesApi: a, bucket: l, onImported: m, classifyTransferError: b = () => ({}), resolveErrorTraceId: x = async () => void 0, onSuccess: S, onWarning: C, onError: E }) => {
73
- let D = p(), O = y(async (n, r, p) => {
74
- if (!l) {
75
- E?.({
72
+ }, D = ({ conversationsApi: a, filesApi: o, bucket: u, onImported: h, classifyTransferError: x = () => ({}), resolveErrorTraceId: S = async () => void 0, onSuccess: C, onWarning: w, onError: D }) => {
73
+ let O = m(), k = b(async (n, i, m) => {
74
+ if (!u) {
75
+ D?.({
76
76
  jobId: n,
77
- code: h.MissingBucket
78
- }), D.failJob(n, h.MissingBucket);
77
+ code: g.MissingBucket
78
+ }), O.failJob(n, g.MissingBucket);
79
79
  return;
80
80
  }
81
- let g = u.Import, v = r.attachments.size, y = 0, w = () => {
82
- y += 1, D.setJobProgress(n, f({
83
- kind: g,
84
- phase: d.Transfer,
85
- completed: y,
86
- total: v
81
+ let _ = d.Import, y = i.attachments.size, b = 0, T = () => {
82
+ b += 1, O.setJobProgress(n, p({
83
+ kind: _,
84
+ phase: f.Transfer,
85
+ completed: b,
86
+ total: y
87
87
  }, {
88
- completed: Math.min(y, v),
89
- total: v,
90
- kind: _.Attachment
88
+ completed: Math.min(b, y),
89
+ total: y,
90
+ kind: v.Attachment
91
91
  }));
92
- }, O = (e) => {
93
- D.setJobProgress(n, f({
94
- kind: g,
95
- phase: d.Finalize,
92
+ }, k = (e) => {
93
+ O.setJobProgress(n, p({
94
+ kind: _,
95
+ phase: f.Finalize,
96
96
  completed: e,
97
- total: r.history.length
97
+ total: i.history.length
98
98
  }, {
99
99
  completed: e,
100
- total: r.history.length,
101
- kind: _.Conversation
100
+ total: i.history.length,
101
+ kind: v.Conversation
102
102
  }));
103
- }, k = [], A = [], j = /* @__PURE__ */ new Set(), M = !1, N, P = /* @__PURE__ */ new Date(), F = (e) => !!b(e).isUnauthorized, I;
104
- if (r.isArchive) {
105
- let t = `uploads/${e(P)}`, r = [];
103
+ }, A = [], j = [], M = /* @__PURE__ */ new Set(), N = !1, P, F = /* @__PURE__ */ new Date(), I = (e) => !!x(e).isUnauthorized, L;
104
+ if (i.isArchive) {
105
+ let t = `uploads/${e(F)}`, r = [];
106
106
  try {
107
- r = (await a.listFiles({
108
- bucket: l,
107
+ r = (await o.listFiles({
108
+ bucket: u,
109
109
  path: t
110
- }, { signal: p })).items.map((e) => e.name);
110
+ }, { signal: m })).items.map((e) => e.name);
111
111
  } catch (e) {
112
- if (p.aborted) return;
113
- if (F(e)) {
114
- E?.({
112
+ if (m.aborted) return;
113
+ if (I(e)) {
114
+ D?.({
115
115
  jobId: n,
116
- code: h.Unauthorized
117
- }), D.failJob(n, h.Unauthorized);
116
+ code: g.Unauthorized
117
+ }), O.failJob(n, g.Unauthorized);
118
118
  return;
119
119
  }
120
120
  console.error("Failed to list the upload folder for attachment name disambiguation", e);
121
121
  }
122
- I = c({
123
- date: P,
122
+ L = l({
123
+ date: F,
124
124
  existingNames: r
125
125
  });
126
126
  }
127
- D.setJobProgress(n, f({
128
- kind: g,
129
- phase: d.Prepare
130
- })), v === 0 && D.setJobProgress(n, f({
131
- kind: g,
132
- phase: d.Transfer
127
+ O.setJobProgress(n, p({
128
+ kind: _,
129
+ phase: f.Prepare
130
+ })), y === 0 && O.setJobProgress(n, p({
131
+ kind: _,
132
+ phase: f.Transfer
133
133
  }));
134
- for (let e of r.history) {
135
- if (p.aborted) return;
136
- if (M) break;
134
+ for (let e of i.history) {
135
+ if (m.aborted) return;
136
+ if (N) break;
137
137
  try {
138
138
  let t = /* @__PURE__ */ new Map();
139
- if (r.isArchive && I) {
140
- let n = await T(e, r.attachments, l, I, a, F, w, p);
141
- if (p.aborted) return;
139
+ if (i.isArchive && L) {
140
+ let n = await E(e, i.attachments, u, L, o, I, T, m);
141
+ if (m.aborted) return;
142
142
  if (n.isUnauthorized) {
143
- M = !0;
143
+ N = !0;
144
144
  break;
145
145
  }
146
- for (let e of n.skippedNames) j.add(e);
146
+ for (let e of n.skippedNames) M.add(e);
147
147
  t = n.targetMap;
148
148
  }
149
- let { conversation: n, subPath: c } = o(s(e, t), l);
150
- if (await i.saveConversation({
151
- path: c,
149
+ let { conversation: n, droppedNames: l } = r(c(e, t), u);
150
+ for (let e of l) M.add(e);
151
+ let { conversation: d, subPath: f } = s(n, u);
152
+ if (await a.saveConversation({
153
+ path: f,
152
154
  saveConversationBodyDto: { conversation: {
153
- ...n,
155
+ ...d,
154
156
  llmNamingDone: !0
155
157
  } }
156
- }, { signal: p }), p.aborted) return;
157
- k.push(n.name);
158
+ }, { signal: m }), m.aborted) return;
159
+ A.push(d.name);
158
160
  } catch (t) {
159
- if (p.aborted) return;
160
- if (F(t)) {
161
- M = !0;
161
+ if (m.aborted) return;
162
+ if (I(t)) {
163
+ N = !0;
162
164
  break;
163
165
  }
164
- A.push(e.name), N === void 0 && (N = await x(t)), console.error("Failed to import a conversation", t);
166
+ j.push(e.name), P === void 0 && (P = await S(t)), console.error("Failed to import a conversation", t);
165
167
  }
166
- O(k.length + A.length);
168
+ k(A.length + j.length);
167
169
  }
168
- if (!p.aborted) {
169
- if (M) {
170
- E?.({
170
+ if (!m.aborted) {
171
+ if (N) {
172
+ D?.({
171
173
  jobId: n,
172
- code: h.Unauthorized
173
- }), D.failJob(n, h.Unauthorized);
174
+ code: g.Unauthorized
175
+ }), O.failJob(n, g.Unauthorized);
174
176
  return;
175
177
  }
176
- if (k.length > 0) {
178
+ if (A.length > 0) {
177
179
  try {
178
- await m?.();
180
+ await h?.();
179
181
  } catch {}
180
- S?.({
182
+ C?.({
181
183
  jobId: n,
182
- titles: k
184
+ titles: A
183
185
  });
184
186
  }
185
- A.length > 0 && E?.({
187
+ j.length > 0 && D?.({
186
188
  jobId: n,
187
- code: h.Unknown,
188
- titles: A,
189
- traceId: N
190
- }), j.size > 0 && C?.({
189
+ code: g.Unknown,
190
+ titles: j,
191
+ traceId: P
192
+ }), M.size > 0 && w?.({
191
193
  jobId: n,
192
194
  code: t.AttachmentSkipped,
193
- names: [...j]
194
- }), A.length > 0 ? D.failJob(n, h.Unknown) : j.size > 0 ? D.warnJob(n, t.AttachmentSkipped, [...j]) : D.succeedJob(n);
195
+ names: [...M]
196
+ }), j.length > 0 ? O.failJob(n, g.Unknown) : M.size > 0 ? O.warnJob(n, t.AttachmentSkipped, [...M]) : O.succeedJob(n);
195
197
  }
196
198
  }, [
197
- l,
198
- b,
199
- i,
199
+ u,
200
+ x,
200
201
  a,
201
- E,
202
- m,
203
- S,
204
- C,
202
+ o,
205
203
  D,
206
- x
207
- ]), k = y(async (e) => {
204
+ h,
205
+ C,
206
+ w,
207
+ O,
208
+ S
209
+ ]), A = b(async (e) => {
208
210
  let t;
209
211
  try {
210
- t = await w(e);
212
+ t = await T(e);
211
213
  } catch (e) {
212
- E?.({
213
- jobId: v(),
214
- code: h.UnsupportedFormat
214
+ D?.({
215
+ jobId: y(),
216
+ code: g.UnsupportedFormat
215
217
  }), e instanceof n || console.error("Failed to parse import file", e);
216
218
  return;
217
219
  }
218
- let i = t.history.length > 1, a = t.history[0], o = i ? { kind: g.All } : {
219
- kind: g.Single,
220
+ let r = t.history.length > 1, a = t.history[0], o = r ? { kind: _.All } : {
221
+ kind: _.Single,
220
222
  title: a?.name ?? e.name,
221
- sourceBreadcrumb: a ? r(a) : void 0
222
- }, s = D.addJob(o, e.name);
223
- return D.startJob(s, (e) => O(s, t, e));
223
+ sourceBreadcrumb: a ? i(a) : void 0
224
+ }, s = O.addJob(o, e.name);
225
+ return O.startJob(s, (e) => k(s, t, e));
224
226
  }, [
225
- E,
226
227
  D,
227
- O
228
+ O,
229
+ k
228
230
  ]);
229
231
  return {
230
- jobs: D.jobs,
231
- importConversations: k,
232
- cancelJob: D.cancelJob,
233
- dismissJob: D.dismissJob,
234
- retryJob: D.retryJob,
235
- dismissAll: D.dismissAll
232
+ jobs: O.jobs,
233
+ importConversations: A,
234
+ cancelJob: O.cancelJob,
235
+ dismissJob: O.dismissJob,
236
+ retryJob: O.retryJob,
237
+ dismissAll: O.dismissAll
236
238
  };
237
239
  };
238
240
  //#endregion
239
- export { E as useConversationImport };
241
+ export { D as useConversationImport };
@@ -0,0 +1,189 @@
1
+ import { useCallback as e, useEffect as t, useLayoutEffect as n, useMemo as r, useRef as i, useState as a } from "react";
2
+ //#region src/conversation/useMessageSelectionReply/useMessageSelectionReply.ts
3
+ var o = "button,input,textarea,select,iframe,[role=\"button\"],[contenteditable=\"true\"],[hidden],[inert],[aria-hidden=\"true\"]", s = [], c = (e, t) => e?.startContainer === t.startContainer && e.startOffset === t.startOffset && e.endContainer === t.endContainer && e.endOffset === t.endOffset, l = (e, t) => {
4
+ let n = window.visualViewport, r = n?.offsetLeft ?? 0, i = n?.offsetTop ?? 0, a = r + (n?.width ?? document.documentElement.clientWidth), o = i + (n?.height ?? document.documentElement.clientHeight);
5
+ for (let e = t; e; e = e.parentElement) {
6
+ let t = getComputedStyle(e);
7
+ if (t.display === "none" || t.visibility === "hidden") return;
8
+ let n = e.getBoundingClientRect();
9
+ /(hidden|clip|auto|scroll)/.test(t.overflowX) && (r = Math.max(r, n.left), a = Math.min(a, n.right)), /(hidden|clip|auto|scroll)/.test(t.overflowY) && (i = Math.max(i, n.top), o = Math.min(o, n.bottom));
10
+ }
11
+ return Array.from(e.getClientRects()).find((e) => e.width > 0 && e.height > 0 && e.left < a && e.right > r && e.top < o && e.bottom > i);
12
+ }, u = ({ conversationId: u, enabled: d, droppedFiles: f, onDroppedFilesConsumed: p }) => {
13
+ let m = i(/* @__PURE__ */ new Set()), h = i(null), g = i(null), _ = i(null), [v, y] = a(null), [b, x] = a({
14
+ conversationId: u,
15
+ files: []
16
+ }), [S, C] = a(), [w, T] = a(0), E = i(/* @__PURE__ */ new WeakSet()), D = i({
17
+ conversationId: u,
18
+ enabled: d
19
+ });
20
+ n(() => {
21
+ D.current = {
22
+ conversationId: u,
23
+ enabled: d
24
+ };
25
+ }, [u, d]);
26
+ let O = e(() => {
27
+ _.current = g.current?.range.cloneRange() ?? null, g.current = null, y(null);
28
+ }, []), k = e((e) => {
29
+ if (e) return m.current.add(e), () => {
30
+ m.current.delete(e), g.current?.body === e && O();
31
+ };
32
+ }, [O]);
33
+ t(() => {
34
+ O(), _.current = null, E.current = /* @__PURE__ */ new WeakSet(), x({
35
+ conversationId: u,
36
+ files: []
37
+ }), T(0), C(void 0);
38
+ }, [u, O]), t(() => {
39
+ if (!d) {
40
+ O(), x({
41
+ conversationId: u,
42
+ files: []
43
+ });
44
+ return;
45
+ }
46
+ let e = 0, t = !1, n = !1, r = !1, i = () => {
47
+ if (e = 0, t || n || r || h.current?.contains(document.activeElement)) return;
48
+ let i = window.getSelection();
49
+ if (!i || i.rangeCount !== 1 || i.isCollapsed || !i.toString().trim()) {
50
+ O();
51
+ return;
52
+ }
53
+ let a = i.getRangeAt(0);
54
+ if (c(_.current, a)) return;
55
+ let s = [...m.current].find((e) => e.contains(a.startContainer) && e.contains(a.endContainer));
56
+ if (!s || !s.isConnected) {
57
+ O();
58
+ return;
59
+ }
60
+ let d = a.cloneContents();
61
+ d.querySelectorAll(o).forEach((e) => e.remove());
62
+ let f = a.commonAncestorContainer;
63
+ if ((f instanceof Element ? f : f.parentElement)?.closest(o) || !d.textContent?.trim()) {
64
+ O();
65
+ return;
66
+ }
67
+ let p = l(a, s);
68
+ if (!p) {
69
+ O();
70
+ return;
71
+ }
72
+ let v = {
73
+ conversationId: u,
74
+ body: s,
75
+ range: a.cloneRange(),
76
+ text: i.toString(),
77
+ rect: p
78
+ };
79
+ g.current = v, y(v);
80
+ }, a = () => {
81
+ e && cancelAnimationFrame(e), e = requestAnimationFrame(i);
82
+ }, s = () => {
83
+ e && cancelAnimationFrame(e), e = requestAnimationFrame(() => {
84
+ e = 0;
85
+ let t = g.current;
86
+ if (!t) return;
87
+ let n = l(t.range, t.body);
88
+ if (!n) O();
89
+ else {
90
+ let e = {
91
+ ...t,
92
+ rect: n
93
+ };
94
+ g.current = e, y(e);
95
+ }
96
+ });
97
+ }, f = (e) => {
98
+ t = h.current?.contains(e.target) ?? !1, n = !t, t || (O(), [...m.current].some((t) => t.contains(e.target)) && (_.current = null));
99
+ }, p = () => {
100
+ n = !1, t || a(), t = !1;
101
+ }, v = () => {
102
+ n = !1, r = !1, t = !1, O();
103
+ let e = window.getSelection();
104
+ _.current = e?.rangeCount ? e.getRangeAt(0).cloneRange() : null;
105
+ }, b = (e) => {
106
+ if (!h.current?.contains(document.activeElement) && (e.shiftKey && [
107
+ "ArrowLeft",
108
+ "ArrowRight",
109
+ "ArrowUp",
110
+ "ArrowDown",
111
+ "Home",
112
+ "End",
113
+ "PageUp",
114
+ "PageDown"
115
+ ].includes(e.key) || (e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a")) {
116
+ r = !0, O(), _.current = null;
117
+ return;
118
+ }
119
+ if (g.current) {
120
+ if (e.key === "Escape") {
121
+ O();
122
+ return;
123
+ }
124
+ if (e.key === "Tab" && !e.shiftKey && !h.current?.contains(document.activeElement)) {
125
+ let t = h.current?.querySelector("button");
126
+ t && (e.preventDefault(), t.focus());
127
+ }
128
+ }
129
+ }, S = (e) => {
130
+ r && !e.shiftKey && (r = !1), e.key !== "Escape" && e.key !== "Tab" && a();
131
+ }, C = new MutationObserver((e) => {
132
+ let t = g.current;
133
+ t && (!t.body.isConnected || e.some((e) => t.body.contains(e.target))) && O();
134
+ });
135
+ return C.observe(document.body, {
136
+ subtree: !0,
137
+ childList: !0,
138
+ characterData: !0,
139
+ attributes: !0,
140
+ attributeFilter: [
141
+ "class",
142
+ "style",
143
+ "hidden",
144
+ "inert"
145
+ ]
146
+ }), document.addEventListener("selectionchange", a), document.addEventListener("pointerdown", f, !0), document.addEventListener("pointerup", p, !0), document.addEventListener("pointercancel", v, !0), window.addEventListener("blur", v), document.addEventListener("keydown", b), document.addEventListener("keyup", S), document.addEventListener("scroll", s, !0), window.addEventListener("resize", s), window.visualViewport?.addEventListener("resize", s), window.visualViewport?.addEventListener("scroll", s), () => {
147
+ cancelAnimationFrame(e), C.disconnect(), document.removeEventListener("selectionchange", a), document.removeEventListener("pointerdown", f, !0), document.removeEventListener("pointerup", p, !0), document.removeEventListener("pointercancel", v, !0), window.removeEventListener("blur", v), document.removeEventListener("keydown", b), document.removeEventListener("keyup", S), document.removeEventListener("scroll", s, !0), window.removeEventListener("resize", s), window.visualViewport?.removeEventListener("resize", s), window.visualViewport?.removeEventListener("scroll", s);
148
+ };
149
+ }, [
150
+ u,
151
+ O,
152
+ d
153
+ ]);
154
+ let A = e(() => {
155
+ let e = g.current;
156
+ if (!D.current.enabled || !e || e.conversationId !== D.current.conversationId || !e.body.isConnected || !l(e.range, e.body)) return;
157
+ let t = new File([e.text], `reply-${crypto.randomUUID()}.txt`, { type: "text/plain" });
158
+ E.current.add(t), T(0), x((n) => ({
159
+ conversationId: e.conversationId,
160
+ files: [...n.conversationId === e.conversationId ? n.files : [], t]
161
+ })), C((e) => (e ?? 0) + 1), O(), window.getSelection()?.removeAllRanges();
162
+ }, [O]), j = d && b.conversationId === u ? b.files : s, M = r(() => [...f, ...j], [f, j]), N = e(() => {
163
+ f.length && p(), x((e) => ({
164
+ ...e,
165
+ files: e.files.filter((e) => !j.includes(e))
166
+ }));
167
+ }, [
168
+ f,
169
+ p,
170
+ j
171
+ ]), P = e((e) => {
172
+ let t = e.filter((e) => e.file && E.current.has(e.file));
173
+ t.length && (t.forEach((e) => E.current.delete(e.file)), T((e) => e + 1));
174
+ }, []);
175
+ return {
176
+ contentRef: k,
177
+ actionRef: h,
178
+ selection: d && v?.conversationId === u ? v : null,
179
+ onReply: A,
180
+ dismiss: O,
181
+ pendingFiles: M,
182
+ onFilesConsumed: N,
183
+ focusRequestId: S,
184
+ onAttachmentsChange: P,
185
+ addedRevision: w
186
+ };
187
+ };
188
+ //#endregion
189
+ export { u as useMessageSelectionReply };
@@ -139,7 +139,8 @@ export declare interface UseConversationExportResult {
139
139
  * `.dial`/`.zip`, v5 envelope, old- or new-chat origin) once per file, then
140
140
  * re-uploads any archive attachments to `uploads/<YYYY-MM>/` —
141
141
  * disambiguating a name collision with a ` (n)` suffix instead of rejecting
142
- * it — rewrites attachment references (and renamed titles), regenerates each
142
+ * it — rewrites attachment references (and renamed titles), drops any
143
+ * reference still pointing into another user's bucket, regenerates each
143
144
  * conversation's id/path with a fresh UUID (collision-free save, no replace
144
145
  * dialog), and persists every conversation through the injected
145
146
  * `conversationsApi`. One job per imported file, not per conversation.
package/conversation.d.ts CHANGED
@@ -633,6 +633,20 @@ export declare interface MessagePair {
633
633
  assistantMessage: Message;
634
634
  }
635
635
 
636
+ /** A stable snapshot of one eligible native text selection. */
637
+ export declare interface MessageSelectionReplySnapshot {
638
+ /** Scope that owned the selection when it was captured. */
639
+ conversationId: string;
640
+ /** Message text root containing the captured range. */
641
+ body: HTMLDivElement;
642
+ /** Captured native range. */
643
+ range: Range;
644
+ /** Native selected text preserved for the generated file. */
645
+ text: string;
646
+ /** Visible fragment of the selected range. */
647
+ rect: DOMRect;
648
+ }
649
+
636
650
  /**
637
651
  * Normalises a stored response-format string to the current enum. Legacy data
638
652
  * may contain `'Markdown'` or `'PlainText'` (capital-first) instead of the
@@ -1134,6 +1148,35 @@ export declare interface UseImportFilePickerResult {
1134
1148
  handleFileChange: (event: ChangeEvent<HTMLInputElement>) => void;
1135
1149
  }
1136
1150
 
1151
+ /**
1152
+ * Captures eligible message text and creates Reply files for an existing composer
1153
+ * attachment flow without acquiring host deployment, upload or localization knowledge.
1154
+ */
1155
+ export declare const useMessageSelectionReply: ({ conversationId, enabled, droppedFiles, onDroppedFilesConsumed, }: UseMessageSelectionReplyParams) => {
1156
+ contentRef: (node: HTMLDivElement | null) => (() => void) | undefined;
1157
+ actionRef: RefObject<HTMLDivElement | null>;
1158
+ selection: MessageSelectionReplySnapshot | null;
1159
+ onReply: () => void;
1160
+ dismiss: () => void;
1161
+ pendingFiles: File[];
1162
+ onFilesConsumed: () => void;
1163
+ focusRequestId: number | undefined;
1164
+ onAttachmentsChange: (attachments: Attachment[]) => void;
1165
+ addedRevision: number;
1166
+ };
1167
+
1168
+ /** Parameters accepted by {@link useMessageSelectionReply}. */
1169
+ export declare interface UseMessageSelectionReplyParams {
1170
+ /** Identifier used to discard selection and queued files after a scope change. */
1171
+ conversationId: string;
1172
+ /** Whether the host currently permits a Reply attachment. */
1173
+ enabled: boolean;
1174
+ /** Files from the host's ordinary page-drop queue. */
1175
+ droppedFiles: File[];
1176
+ /** Acknowledges the captured page-drop batch after the composer accepts it. */
1177
+ onDroppedFilesConsumed: () => void;
1178
+ }
1179
+
1137
1180
  /**
1138
1181
  * Derives the tools submenu from the active deployment's configuration schema:
1139
1182
  * every boolean property becomes a toggle. Manages toggle state locally, resets
package/conversation.js CHANGED
@@ -15,18 +15,19 @@ import { useAsyncConfirmDialog as k } from "./conversation/useAsyncConfirmDialog
15
15
  import { useConversationLookupMaps as A } from "./conversation/useConversationLookupMaps/useConversationLookupMaps.js";
16
16
  import { getConversationSource as j, useConversationPanelItems as M } from "./conversation/useConversationPanelItems/useConversationPanelItems.js";
17
17
  import { useImportFilePicker as N } from "./conversation/useImportFilePicker/useImportFilePicker.js";
18
- import { useAttachmentUpload as P } from "./conversation/useAttachmentUpload/useAttachmentUpload.js";
19
- import { AudioTranscriptionError as F, AudioTranscriptionErrorReason as I } from "./conversation/useTranscribeAudio/audio-transcription-error.js";
20
- import { useTranscribeAudio as L } from "./conversation/useTranscribeAudio/useTranscribeAudio.js";
21
- import { attachmentToDto as R, attachmentsToDtos as z } from "./conversation/useConversationHandlers/attachment-to-dto.js";
22
- import { createMessagePair as B } from "./conversation/useConversationHandlers/message-factory.js";
23
- import { hasActiveToolConfig as V, isAnswerIncomplete as H, isMessageChanged as U, shouldRerunGenerationOnEdit as W } from "./conversation/useConversationHandlers/message-utils.js";
24
- import { getStarterConversationText as G, getStarterDisplayText as K, getStarterSubmitText as q } from "./conversation/useConversationHandlers/starter-option.js";
25
- import { getConversationPath as J } from "./conversation/useConversationStream/conversation-path.js";
26
- import { useConversationHandlers as Y } from "./conversation/useConversationHandlers/useConversationHandlers.js";
27
- import { applyChunkToMessages as X, mergeStages as Z } from "./conversation/useConversationStream/apply-chunk.js";
28
- import { isAwaitingGenerationResume as Q } from "./conversation/useConversationStream/generation-resume.js";
29
- import { useConversationStream as $ } from "./conversation/useConversationStream/useConversationStream.js";
30
- import { useChatSettingsFormConfig as ee } from "./useChatSettingsFormConfig/useChatSettingsFormConfig.js";
31
- import { useToolsMenu as te } from "./useToolsMenu/useToolsMenu.js";
32
- export { F as AudioTranscriptionError, I as AudioTranscriptionErrorReason, a as DEFAULT_GENERATION_CONFLICT_MESSAGE, o as DEFAULT_GENERATION_PERSISTENCE_ERROR_MESSAGE, s as GenerationConflictError, c as GenerationPersistenceError, l as StreamUpstreamError, X as applyChunkToMessages, R as attachmentToDto, z as attachmentsToDtos, e as buildAnnouncementSignature, u as createChatStreamApi, g as createDeploymentChangedMessage, B as createMessagePair, D as deriveConversationRowActionState, f as formatAppVersion, J as getConversationPath, j as getConversationSource, _ as getLastDeploymentId, v as getLastUserMessageToolConfiguration, m as getModelIdFromConversationId, S as getQuickAppConversationStarters, G as getStarterConversationText, K as getStarterDisplayText, T as getStarterPopulateText, q as getStarterSubmitText, E as getStartersFromSchema, h as getTimeOfDayGreeting, V as hasActiveToolConfig, t as hasAnnouncementContent, n as hasStructuredAnnouncement, H as isAnswerIncomplete, Q as isAwaitingGenerationResume, U as isMessageChanged, y as isMessageStreaming, C as mapStages, Z as mergeStages, b as messageHasStages, x as normalizeResponseFormat, r as sanitizeAnnouncementHtml, i as sanitizeAnnouncementMessageHtml, p as sanitizeFooterHtml, W as shouldRerunGenerationOnEdit, d as shouldWatchForDisplayNameUpdate, w as toStage, O as useActiveConversationSync, k as useAsyncConfirmDialog, P as useAttachmentUpload, ee as useChatSettingsFormConfig, Y as useConversationHandlers, A as useConversationLookupMaps, M as useConversationPanelItems, $ as useConversationStream, N as useImportFilePicker, te as useToolsMenu, L as useTranscribeAudio };
18
+ import { useMessageSelectionReply as P } from "./conversation/useMessageSelectionReply/useMessageSelectionReply.js";
19
+ import { useAttachmentUpload as F } from "./conversation/useAttachmentUpload/useAttachmentUpload.js";
20
+ import { AudioTranscriptionError as I, AudioTranscriptionErrorReason as L } from "./conversation/useTranscribeAudio/audio-transcription-error.js";
21
+ import { useTranscribeAudio as R } from "./conversation/useTranscribeAudio/useTranscribeAudio.js";
22
+ import { attachmentToDto as z, attachmentsToDtos as B } from "./conversation/useConversationHandlers/attachment-to-dto.js";
23
+ import { createMessagePair as V } from "./conversation/useConversationHandlers/message-factory.js";
24
+ import { hasActiveToolConfig as H, isAnswerIncomplete as U, isMessageChanged as W, shouldRerunGenerationOnEdit as G } from "./conversation/useConversationHandlers/message-utils.js";
25
+ import { getStarterConversationText as K, getStarterDisplayText as q, getStarterSubmitText as J } from "./conversation/useConversationHandlers/starter-option.js";
26
+ import { getConversationPath as Y } from "./conversation/useConversationStream/conversation-path.js";
27
+ import { useConversationHandlers as X } from "./conversation/useConversationHandlers/useConversationHandlers.js";
28
+ import { applyChunkToMessages as Z, mergeStages as Q } from "./conversation/useConversationStream/apply-chunk.js";
29
+ import { isAwaitingGenerationResume as $ } from "./conversation/useConversationStream/generation-resume.js";
30
+ import { useConversationStream as ee } from "./conversation/useConversationStream/useConversationStream.js";
31
+ import { useChatSettingsFormConfig as te } from "./useChatSettingsFormConfig/useChatSettingsFormConfig.js";
32
+ import { useToolsMenu as ne } from "./useToolsMenu/useToolsMenu.js";
33
+ export { I as AudioTranscriptionError, L as AudioTranscriptionErrorReason, a as DEFAULT_GENERATION_CONFLICT_MESSAGE, o as DEFAULT_GENERATION_PERSISTENCE_ERROR_MESSAGE, s as GenerationConflictError, c as GenerationPersistenceError, l as StreamUpstreamError, Z as applyChunkToMessages, z as attachmentToDto, B as attachmentsToDtos, e as buildAnnouncementSignature, u as createChatStreamApi, g as createDeploymentChangedMessage, V as createMessagePair, D as deriveConversationRowActionState, f as formatAppVersion, Y as getConversationPath, j as getConversationSource, _ as getLastDeploymentId, v as getLastUserMessageToolConfiguration, m as getModelIdFromConversationId, S as getQuickAppConversationStarters, K as getStarterConversationText, q as getStarterDisplayText, T as getStarterPopulateText, J as getStarterSubmitText, E as getStartersFromSchema, h as getTimeOfDayGreeting, H as hasActiveToolConfig, t as hasAnnouncementContent, n as hasStructuredAnnouncement, U as isAnswerIncomplete, $ as isAwaitingGenerationResume, W as isMessageChanged, y as isMessageStreaming, C as mapStages, Q as mergeStages, b as messageHasStages, x as normalizeResponseFormat, r as sanitizeAnnouncementHtml, i as sanitizeAnnouncementMessageHtml, p as sanitizeFooterHtml, G as shouldRerunGenerationOnEdit, d as shouldWatchForDisplayNameUpdate, w as toStage, O as useActiveConversationSync, k as useAsyncConfirmDialog, F as useAttachmentUpload, te as useChatSettingsFormConfig, X as useConversationHandlers, A as useConversationLookupMaps, M as useConversationPanelItems, ee as useConversationStream, N as useImportFilePicker, P as useMessageSelectionReply, ne as useToolsMenu, R as useTranscribeAudio };