@_solaris/messenger-widget 0.1.0

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,4594 @@
1
+ import { reactive as Ae, openBlock as l, createElementBlock as d, createVNode as z, Transition as Se, withCtx as xe, withKeys as ue, withModifiers as J, createElementVNode as i, toDisplayString as y, createCommentVNode as v, normalizeStyle as H, normalizeClass as B, Fragment as E, renderList as F, resolveComponent as O, createBlock as $, createTextVNode as se, resolveDynamicComponent as Me, renderSlot as Te, withDirectives as q, vModelText as G, vModelCheckbox as Ie, vModelSelect as Oe, createStaticVNode as de, markRaw as he } from "vue";
2
+ const Ee = [
3
+ "connected",
4
+ "message",
5
+ "message_stream",
6
+ "conversation_updated",
7
+ "config_updated",
8
+ "action_status"
9
+ ], Be = 5 * 60 * 1e3, Le = 10 * 60 * 1e3, Re = 30 * 1e3;
10
+ function $e(t) {
11
+ const e = {
12
+ baseUrl: Fe(t.baseUrl || ""),
13
+ widgetId: t.widgetId || "",
14
+ userId: t.userId || "",
15
+ userHash: t.userHash || "",
16
+ listeners: /* @__PURE__ */ new Map(),
17
+ eventSource: null,
18
+ connection: "idle",
19
+ visibilityHandler: null,
20
+ started: !1,
21
+ // Hybrid push state
22
+ panelOpen: !1,
23
+ pollTimer: null,
24
+ burstTimer: null,
25
+ hiddenGraceTimer: null,
26
+ // Max conversation.last_message_at observed across polls — used as a
27
+ // cheap "anything new?" cursor since the server has no /me endpoint.
28
+ lastActivityAt: null
29
+ };
30
+ if (!e.baseUrl || !e.widgetId || !e.userId || !e.userHash)
31
+ throw new Error(
32
+ "[transport] baseUrl, widgetId, userId, userHash are all required"
33
+ );
34
+ function n(m, _) {
35
+ return e.listeners.has(m) || e.listeners.set(m, /* @__PURE__ */ new Set()), e.listeners.get(m).add(_), () => e.listeners.get(m).delete(_);
36
+ }
37
+ function a(m, _) {
38
+ const b = e.listeners.get(m);
39
+ b && b.forEach((S) => {
40
+ try {
41
+ S(_);
42
+ } catch (M) {
43
+ console.error("[transport] listener", m, M);
44
+ }
45
+ });
46
+ }
47
+ function r(m) {
48
+ e.connection !== m && (e.connection = m, a("connection", m));
49
+ }
50
+ function s() {
51
+ return {
52
+ "X-User-Id": e.userId,
53
+ "X-User-Hash": e.userHash,
54
+ "X-Widget-Id": e.widgetId
55
+ };
56
+ }
57
+ async function o(m, _, b) {
58
+ const S = await fetch(`${e.baseUrl}${_}`, {
59
+ method: m,
60
+ headers: { "Content-Type": "application/json", ...s() },
61
+ body: b !== void 0 ? JSON.stringify(b) : void 0
62
+ });
63
+ if (!S.ok) {
64
+ const M = await c(S), D = new Error(
65
+ `HTTP ${S.status} ${m} ${_} :: ${(M == null ? void 0 : M.error) || S.statusText}`
66
+ );
67
+ throw D.status = S.status, D.body = M, D;
68
+ }
69
+ return S.status === 204 ? null : S.json();
70
+ }
71
+ async function c(m) {
72
+ try {
73
+ return await m.json();
74
+ } catch {
75
+ return null;
76
+ }
77
+ }
78
+ async function p() {
79
+ const [m, _] = await Promise.all([
80
+ // Public — no HMAC required.
81
+ fetch(`${e.baseUrl}/widgets/${encodeURIComponent(e.widgetId)}/config`).then(async (b) => {
82
+ if (!b.ok) {
83
+ const S = await c(b);
84
+ throw new Error(`HTTP ${b.status} GET /widgets/:id/config :: ${(S == null ? void 0 : S.error) || b.statusText}`);
85
+ }
86
+ return b.json();
87
+ }),
88
+ // HMAC-protected.
89
+ o("GET", "/customers/me")
90
+ ]);
91
+ return {
92
+ config: (
93
+ /** @type {import('./types.js').BootstrapConfig} */
94
+ m
95
+ ),
96
+ customer: (_ == null ? void 0 : _.customer) ?? null
97
+ };
98
+ }
99
+ async function k() {
100
+ const m = await o("GET", "/customers/me");
101
+ return (m == null ? void 0 : m.customer) ?? null;
102
+ }
103
+ async function g(m) {
104
+ const _ = await o("PATCH", "/customers/me", m);
105
+ return (_ == null ? void 0 : _.customer) ?? null;
106
+ }
107
+ async function T() {
108
+ const m = await o("GET", "/conversations");
109
+ return (m == null ? void 0 : m.conversations) ?? [];
110
+ }
111
+ async function R(m = {}) {
112
+ return (await o("POST", "/conversations", m)).conversation;
113
+ }
114
+ async function x(m) {
115
+ return (await o("GET", `/conversations/${encodeURIComponent(m)}`)).conversation;
116
+ }
117
+ async function N(m, _) {
118
+ return (await o("PATCH", `/conversations/${encodeURIComponent(m)}`, _)).conversation;
119
+ }
120
+ async function I(m, _ = {}) {
121
+ const b = new URLSearchParams();
122
+ _.before && b.set("before", _.before), _.since && b.set("since", _.since), _.limit && b.set("limit", String(_.limit));
123
+ const S = b.toString() ? `?${b.toString()}` : "";
124
+ return o(
125
+ "GET",
126
+ `/conversations/${encodeURIComponent(m)}/messages${S}`
127
+ );
128
+ }
129
+ async function P(m, _) {
130
+ W();
131
+ const b = {
132
+ client_msg_id: _.client_msg_id,
133
+ type: "content",
134
+ text_md: _.text_md,
135
+ author: { id: e.userId, type: "user" },
136
+ created_at: _.created_at || (/* @__PURE__ */ new Date()).toISOString()
137
+ };
138
+ return Array.isArray(_.attachments) && _.attachments.length && (b.payload = { type: "content", attachments: _.attachments }), _.metadata && typeof _.metadata == "object" && (b.metadata = _.metadata), o(
139
+ "POST",
140
+ `/conversations/${encodeURIComponent(m)}/messages`,
141
+ b
142
+ );
143
+ }
144
+ async function U(m, _, b) {
145
+ return W(), o(
146
+ "POST",
147
+ `/messages/${encodeURIComponent(m)}/callbacks/${encodeURIComponent(_)}`,
148
+ b ? { inputs: b } : {}
149
+ );
150
+ }
151
+ async function V(m) {
152
+ const _ = (
153
+ /** @type {File} */
154
+ m.name || "attachment"
155
+ ), b = m.type || "application/octet-stream", S = m.size || 0, M = (
156
+ /** @type {import('./types.js').AttachmentUploadTicket} */
157
+ await o("POST", "/attachments", {
158
+ mime_type: b,
159
+ size_bytes: S,
160
+ name: _
161
+ })
162
+ ), D = await fetch(M.upload_url, {
163
+ method: "PUT",
164
+ headers: { "Content-Type": b },
165
+ body: m
166
+ });
167
+ if (!D.ok)
168
+ throw new Error(`HTTP ${D.status} PUT signed upload`);
169
+ return {
170
+ type: Ue(b),
171
+ path: M.path,
172
+ mime_type: b,
173
+ size_bytes: S
174
+ };
175
+ }
176
+ async function Q(m) {
177
+ return o(
178
+ "GET",
179
+ `/attachments/sign?path=${encodeURIComponent(m)}`
180
+ );
181
+ }
182
+ function Z() {
183
+ const m = new URLSearchParams({
184
+ userId: e.userId,
185
+ userHash: e.userHash,
186
+ widgetId: e.widgetId
187
+ }).toString();
188
+ return `${e.baseUrl}/stream?${m}`;
189
+ }
190
+ function K() {
191
+ if (!e.eventSource && !(typeof document < "u" && document.hidden))
192
+ try {
193
+ const m = new EventSource(Z());
194
+ for (const _ of Ee)
195
+ m.addEventListener(_, (b) => re(_, b.data));
196
+ m.addEventListener("error", () => a("error", new Error("SSE error"))), e.eventSource = m, r("open");
197
+ } catch (m) {
198
+ console.error("[transport] SSE open failed", m), a("error", m);
199
+ }
200
+ }
201
+ function re(m, _) {
202
+ try {
203
+ const b = JSON.parse(_), S = b && typeof b == "object" && "data" in b ? b.data : b;
204
+ a(m, S);
205
+ } catch (b) {
206
+ console.error("[transport] bad SSE payload", m, b);
207
+ }
208
+ }
209
+ function Y() {
210
+ e.eventSource && (e.eventSource.close(), e.eventSource = null), e.connection !== "paused" && r("idle");
211
+ }
212
+ function W() {
213
+ clearTimeout(e.burstTimer), K(), e.panelOpen || (e.burstTimer = setTimeout(() => {
214
+ e.panelOpen || Y();
215
+ }, Le));
216
+ }
217
+ function ie(m) {
218
+ e.panelOpen = !!m, e.panelOpen ? (clearTimeout(e.burstTimer), K()) : W();
219
+ }
220
+ async function u() {
221
+ try {
222
+ const m = await T(), _ = m.reduce((S, M) => {
223
+ const D = M == null ? void 0 : M.last_message_at;
224
+ return D && (!S || D > S) ? D : S;
225
+ }, null);
226
+ _ && (!e.lastActivityAt || _ > e.lastActivityAt) && (e.lastActivityAt = _, a("activity", { conversations: m, latestAt: _ }), W());
227
+ } catch (m) {
228
+ console.error("[transport] poll failed", m);
229
+ }
230
+ }
231
+ function h() {
232
+ f(), !(typeof document < "u" && document.hidden) && (e.pollTimer = setInterval(u, Be));
233
+ }
234
+ function f() {
235
+ e.pollTimer && (clearInterval(e.pollTimer), e.pollTimer = null);
236
+ }
237
+ function w() {
238
+ if (document.hidden)
239
+ clearTimeout(e.hiddenGraceTimer), e.hiddenGraceTimer = setTimeout(() => {
240
+ e.hiddenGraceTimer = null, document.hidden && (f(), Y(), r("paused"));
241
+ }, Re);
242
+ else {
243
+ if (e.hiddenGraceTimer) {
244
+ clearTimeout(e.hiddenGraceTimer), e.hiddenGraceTimer = null;
245
+ return;
246
+ }
247
+ r("idle"), h(), e.panelOpen && K();
248
+ }
249
+ }
250
+ async function A() {
251
+ if (e.started) return e.lastBootstrap;
252
+ e.started = !0;
253
+ const m = await p();
254
+ e.lastBootstrap = m;
255
+ try {
256
+ const _ = await T();
257
+ e.lastActivityAt = _.reduce((b, S) => {
258
+ const M = S == null ? void 0 : S.last_message_at;
259
+ return M && (!b || M > b) ? M : b;
260
+ }, null);
261
+ } catch (_) {
262
+ console.error("[transport] initial /conversations failed", _);
263
+ }
264
+ return typeof document < "u" && (e.visibilityHandler = w, document.addEventListener("visibilitychange", e.visibilityHandler)), h(), m;
265
+ }
266
+ function C() {
267
+ f(), clearTimeout(e.burstTimer), clearTimeout(e.hiddenGraceTimer), e.hiddenGraceTimer = null, Y(), e.visibilityHandler && (document.removeEventListener("visibilitychange", e.visibilityHandler), e.visibilityHandler = null), e.started = !1;
268
+ }
269
+ return {
270
+ on: n,
271
+ start: A,
272
+ stop: C,
273
+ setPanelOpen: ie,
274
+ // REST
275
+ bootstrap: p,
276
+ getCustomer: k,
277
+ patchCustomer: g,
278
+ listConversations: T,
279
+ createConversation: R,
280
+ getConversation: x,
281
+ patchConversation: N,
282
+ listMessages: I,
283
+ postMessage: P,
284
+ postCallback: U,
285
+ uploadAttachment: V,
286
+ signAttachment: Q,
287
+ // Read-only state
288
+ get connection() {
289
+ return e.connection;
290
+ }
291
+ };
292
+ }
293
+ function Fe(t) {
294
+ return t.endsWith("/") ? t.slice(0, -1) : t;
295
+ }
296
+ function Ue(t) {
297
+ return t.startsWith("image/") ? "image" : t.startsWith("video/") ? "video" : t.startsWith("audio/") ? "audio" : "file";
298
+ }
299
+ function De() {
300
+ if (typeof crypto < "u" && typeof crypto.randomUUID == "function")
301
+ return crypto.randomUUID();
302
+ const t = new Uint8Array(16);
303
+ if (typeof crypto < "u" && crypto.getRandomValues)
304
+ crypto.getRandomValues(t);
305
+ else
306
+ for (let n = 0; n < 16; n++) t[n] = Math.floor(Math.random() * 256);
307
+ t[6] = t[6] & 15 | 64, t[8] = t[8] & 63 | 128;
308
+ const e = [...t].map((n) => n.toString(16).padStart(2, "0"));
309
+ return e.slice(0, 4).join("") + "-" + e.slice(4, 6).join("") + "-" + e.slice(6, 8).join("") + "-" + e.slice(8, 10).join("") + "-" + e.slice(10, 16).join("");
310
+ }
311
+ function je(t) {
312
+ const e = Ae({
313
+ ready: !1,
314
+ error: null,
315
+ config: null,
316
+ // BootstrapConfig
317
+ customer: null,
318
+ // Customer | null
319
+ conversations: [],
320
+ // Conversation[]
321
+ messagesByConv: {},
322
+ // id → Message[]
323
+ // id → { nextCursor: string|null, loading: boolean, loaded: boolean }
324
+ // History pagination cursor. `nextCursor === null` means "we've reached
325
+ // the start of the conversation" — used by the UI to stop firing
326
+ // load-more on scroll-up.
327
+ paginationByConv: {},
328
+ // message_id (UUID) → accumulated text_md tokens for an in-flight
329
+ // agent reply. Cleared when the final `message` event lands.
330
+ streamingByMsgId: {},
331
+ // Action message ids whose approve/reject callback was just clicked.
332
+ // Treated as "no longer pending" by the UI until the server echoes
333
+ // back the updated state — without this, the ApprovalCard stays
334
+ // mounted for the SSE round-trip and the user sees nothing happen.
335
+ awaitingCallback: {},
336
+ // conversation_id → { action_id: action_name } map des actions dont
337
+ // le sub-agent serveur est en train de tourner. Alimenté par les
338
+ // events SSE `action_status` ({state: 'running'|'done'}). Pilote
339
+ // l'indicateur de frappe quand une action s'exécute.
340
+ runningActionsByConv: {},
341
+ connection: "idle"
342
+ }), n = [];
343
+ n.push(t.on("connection", (u) => {
344
+ e.connection = u;
345
+ })), n.push(t.on("message", (u) => {
346
+ const h = u == null ? void 0 : u.conversation_id, f = u == null ? void 0 : u.message;
347
+ !h || !(f != null && f.id) || (Z(h, f), f.client_msg_id && delete e.streamingByMsgId[f.client_msg_id], Y(h, f.created_at));
348
+ })), n.push(t.on("message_stream", (u) => {
349
+ const h = u == null ? void 0 : u.message_id, f = u == null ? void 0 : u.token;
350
+ !h || typeof f != "string" || (e.streamingByMsgId[h] = (e.streamingByMsgId[h] || "") + f);
351
+ })), n.push(t.on("conversation_updated", (u) => {
352
+ const h = u == null ? void 0 : u.conversation_id, f = u == null ? void 0 : u.changes;
353
+ if (!h || !f) return;
354
+ const w = e.conversations.findIndex((A) => A.id === h);
355
+ w !== -1 && (e.conversations[w] = { ...e.conversations[w], ...f });
356
+ })), n.push(t.on("config_updated", (u) => {
357
+ u != null && u.config && (e.config = u.config);
358
+ })), n.push(t.on("action_status", (u) => {
359
+ const h = u == null ? void 0 : u.conversation_id, f = u == null ? void 0 : u.action_id, w = u == null ? void 0 : u.action_name;
360
+ if (!h || !f) return;
361
+ const A = e.runningActionsByConv[h] || {};
362
+ u.state === "running" ? (A[f] = w || f, e.runningActionsByConv[h] = { ...A }) : u.state === "done" && (delete A[f], e.runningActionsByConv[h] = { ...A });
363
+ })), n.push(t.on("activity", (u) => {
364
+ Array.isArray(u == null ? void 0 : u.conversations) && (e.conversations = u.conversations);
365
+ }));
366
+ async function a() {
367
+ try {
368
+ const u = new Promise(
369
+ (f, w) => setTimeout(() => w(new Error("bootstrap timeout (15s) — check baseUrl, CORS, and network")), 15e3)
370
+ ), h = await Promise.race([t.start(), u]);
371
+ e.config = h.config, e.customer = h.customer, e.conversations = await Promise.race([t.listConversations(), u]), e.ready = !0;
372
+ } catch (u) {
373
+ console.error("[store] start failed", u), e.error = (u == null ? void 0 : u.message) || String(u);
374
+ }
375
+ }
376
+ function r() {
377
+ for (const u of n)
378
+ try {
379
+ u();
380
+ } catch {
381
+ }
382
+ t.stop();
383
+ }
384
+ async function s(u = {}) {
385
+ const h = await t.createConversation(u), f = e.conversations.findIndex((w) => w.id === h.id);
386
+ return f === -1 ? e.conversations = [h, ...e.conversations] : e.conversations[f] = h, h;
387
+ }
388
+ const o = 50;
389
+ async function c(u) {
390
+ if (e.messagesByConv[u]) return;
391
+ const h = await t.listMessages(u, { limit: o });
392
+ e.messagesByConv[u] = (h == null ? void 0 : h.messages) ?? [], k(u, {
393
+ nextCursor: (h == null ? void 0 : h.next_cursor) ?? null,
394
+ loading: !1,
395
+ loaded: !0
396
+ });
397
+ }
398
+ async function p(u) {
399
+ const h = e.paginationByConv[u];
400
+ if (!(!h || h.loading || !h.nextCursor)) {
401
+ k(u, { ...h, loading: !0 });
402
+ try {
403
+ let A = h.nextCursor, C = [], m = 0;
404
+ for (; m < 6 && A; m++) {
405
+ const M = await t.listMessages(u, {
406
+ before: A,
407
+ limit: o
408
+ }), D = (M == null ? void 0 : M.messages) ?? [];
409
+ if (C = [...D, ...C], A = (M == null ? void 0 : M.next_cursor) ?? null, !D.length || C.length >= 60) break;
410
+ }
411
+ const _ = e.messagesByConv[u] || [], b = new Set(_.map((M) => String(M == null ? void 0 : M.id))), S = [...C.filter((M) => !b.has(String(M == null ? void 0 : M.id))), ..._];
412
+ e.messagesByConv[u] = S, k(u, {
413
+ nextCursor: A,
414
+ loading: !1,
415
+ loaded: !0
416
+ });
417
+ } catch (f) {
418
+ console.error("[store] loadMore failed", f), k(u, { ...h, loading: !1 });
419
+ }
420
+ }
421
+ }
422
+ function k(u, h) {
423
+ e.paginationByConv = { ...e.paginationByConv, [u]: h };
424
+ }
425
+ async function g(u, h) {
426
+ const f = await t.patchConversation(u, h), w = e.conversations.findIndex((A) => A.id === u);
427
+ w !== -1 && (e.conversations[w] = f);
428
+ }
429
+ async function T(u, h, { attachments: f, metadata: w } = {}) {
430
+ var S;
431
+ const A = (h || "").trim(), C = Array.isArray(f) && f.length > 0;
432
+ if (!u || !A && !C) return;
433
+ const m = De(), _ = ie(u), b = {
434
+ id: m,
435
+ client_msg_id: m,
436
+ conversation_id: u,
437
+ type: "content",
438
+ text_md: A,
439
+ author: {
440
+ type: "user",
441
+ id: ((S = e.customer) == null ? void 0 : S.external_id) || null
442
+ },
443
+ created_at: _,
444
+ // Local-only flag — UI may render dimmed until the SSE echo lands.
445
+ _pending: !0,
446
+ ...C ? { payload: { type: "content", attachments: f } } : {},
447
+ ...w && typeof w == "object" ? { metadata: w } : {}
448
+ };
449
+ Z(u, b);
450
+ try {
451
+ await t.postMessage(u, {
452
+ client_msg_id: m,
453
+ text_md: A,
454
+ created_at: _,
455
+ ...C ? { attachments: f } : {},
456
+ ...w && typeof w == "object" ? { metadata: w } : {}
457
+ });
458
+ } catch (M) {
459
+ console.error("[store] send failed", M), re(u, m, { _failed: !0, _pending: !1 });
460
+ }
461
+ }
462
+ async function R(u, h, f) {
463
+ u != null && (e.awaitingCallback[u] = !0);
464
+ try {
465
+ await t.postCallback(u, h, f);
466
+ } catch (w) {
467
+ console.error("[store] callback failed", w), u != null && delete e.awaitingCallback[u];
468
+ }
469
+ }
470
+ const x = /* @__PURE__ */ new Map();
471
+ async function N(u) {
472
+ if (!u) return null;
473
+ const h = x.get(u);
474
+ if (h != null && h.url) {
475
+ const f = h.expires_at ? Date.parse(h.expires_at) : 0;
476
+ if (!f || f - Date.now() > 6e4) return h.url;
477
+ }
478
+ try {
479
+ const f = await t.signAttachment(u);
480
+ if (f != null && f.signed_url)
481
+ return x.set(u, { url: f.signed_url, expires_at: f.expires_at }), f.signed_url;
482
+ } catch (f) {
483
+ console.error("[store] sign attachment failed", f);
484
+ }
485
+ return null;
486
+ }
487
+ async function I(u, { rating: h, comment: f } = {}) {
488
+ const w = e.conversations.find((m) => m.id === u), C = {
489
+ ...(w == null ? void 0 : w.metadata) || {},
490
+ feedback: {
491
+ rating: h,
492
+ comment: f || null,
493
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString()
494
+ }
495
+ };
496
+ await g(u, { metadata: C });
497
+ }
498
+ function P(u) {
499
+ var f, w;
500
+ const h = e.messagesByConv[u] || [];
501
+ for (let A = h.length - 1; A >= 0; A--) {
502
+ const C = h[A];
503
+ if ((C == null ? void 0 : C.type) === "action" && ((f = C == null ? void 0 : C.payload) == null ? void 0 : f.type) === "action" && ((w = C == null ? void 0 : C.payload) == null ? void 0 : w.state) === "pending" && Array.isArray(C == null ? void 0 : C.callbacks) && C.callbacks.length > 0 && !e.awaitingCallback[C.id])
504
+ return C;
505
+ }
506
+ return null;
507
+ }
508
+ function U(u) {
509
+ var f, w, A;
510
+ const h = e.messagesByConv[u] || [];
511
+ for (let C = h.length - 1; C >= 0; C--) {
512
+ const m = h[C];
513
+ if (((f = m == null ? void 0 : m.author) == null ? void 0 : f.type) === "user" || (m == null ? void 0 : m.type) === "action" && ((w = m == null ? void 0 : m.payload) == null ? void 0 : w.state) === "pending") return null;
514
+ const _ = (A = m == null ? void 0 : m.metadata) == null ? void 0 : A.form;
515
+ if (_ && Array.isArray(_.fields) && _.fields.length > 0)
516
+ return { message: m, form: _ };
517
+ }
518
+ return null;
519
+ }
520
+ function V(u) {
521
+ const h = e.runningActionsByConv[u];
522
+ if (!h) return null;
523
+ const f = Object.keys(h);
524
+ if (f.length === 0) return null;
525
+ const w = f[0];
526
+ return { id: w, payload: { name: h[w] } };
527
+ }
528
+ function Q(u) {
529
+ var f, w, A, C;
530
+ const h = e.messagesByConv[u] || [];
531
+ for (let m = h.length - 1; m >= 0; m--) {
532
+ const _ = h[m];
533
+ if (((f = _ == null ? void 0 : _.author) == null ? void 0 : f.type) === "user") return [];
534
+ if ((_ == null ? void 0 : _.type) === "action" && ((w = _ == null ? void 0 : _.payload) == null ? void 0 : w.state) === "pending") return [];
535
+ if (((A = _ == null ? void 0 : _.author) == null ? void 0 : A.type) !== "agent_ia") continue;
536
+ const b = (C = _ == null ? void 0 : _.metadata) == null ? void 0 : C.suggested_replies;
537
+ return Array.isArray(b) && b.length ? b.map((S) => {
538
+ if (typeof S == "string") {
539
+ const M = S.trim();
540
+ return M ? { label: M, kind: null } : null;
541
+ }
542
+ if (S && typeof S == "object" && typeof S.label == "string") {
543
+ const M = S.label.trim();
544
+ if (!M) return null;
545
+ const D = S.kind === "cta" || S.kind === "choice" || S.kind === "followup" ? S.kind : null;
546
+ return { label: M, kind: D };
547
+ }
548
+ return null;
549
+ }).filter(Boolean).slice(0, 4) : [];
550
+ }
551
+ return [];
552
+ }
553
+ function Z(u, h) {
554
+ var C;
555
+ const f = e.messagesByConv[u] || [];
556
+ let w = -1;
557
+ h != null && h.client_msg_id && (w = f.findIndex(
558
+ (m) => (m == null ? void 0 : m.client_msg_id) && m.client_msg_id === h.client_msg_id
559
+ )), w === -1 && (h == null ? void 0 : h.id) !== void 0 && (h == null ? void 0 : h.id) !== null && (w = f.findIndex((m) => K(m == null ? void 0 : m.id, h.id)));
560
+ let A;
561
+ w === -1 ? A = [...f, h].sort(W) : (A = f.slice(), A[w] = { ...f[w], ...h, _pending: !1, _failed: !1 }), e.messagesByConv[u] = A, (h == null ? void 0 : h.type) === "action" && ((C = h == null ? void 0 : h.payload) != null && C.state) && h.payload.state !== "pending" && (h == null ? void 0 : h.id) != null && e.awaitingCallback[h.id] && delete e.awaitingCallback[h.id];
562
+ }
563
+ function K(u, h) {
564
+ return u === h ? !0 : u == null || h == null ? !1 : String(u) === String(h);
565
+ }
566
+ function re(u, h, f) {
567
+ const w = e.messagesByConv[u];
568
+ if (!w) return;
569
+ const A = w.findIndex((m) => m.id === h);
570
+ if (A === -1) return;
571
+ const C = w.slice();
572
+ C[A] = { ...w[A], ...f }, e.messagesByConv[u] = C;
573
+ }
574
+ function Y(u, h) {
575
+ const f = e.conversations.findIndex((A) => A.id === u);
576
+ if (f === -1) return;
577
+ const w = e.conversations[f];
578
+ if (h && (!w.last_message_at || h > w.last_message_at)) {
579
+ const A = e.conversations.slice();
580
+ A[f] = { ...w, last_message_at: h }, A.sort(
581
+ (C, m) => (m.last_message_at || "").localeCompare(C.last_message_at || "")
582
+ ), e.conversations = A;
583
+ }
584
+ }
585
+ function W(u, h) {
586
+ return (u.created_at || "").localeCompare(h.created_at || "");
587
+ }
588
+ function ie(u) {
589
+ const h = e.messagesByConv[u] || [];
590
+ let f = "";
591
+ for (const C of h)
592
+ C != null && C.created_at && C.created_at > f && (f = C.created_at);
593
+ const w = (/* @__PURE__ */ new Date()).toISOString();
594
+ return !f || w > f ? w : new Date(Date.parse(f) + 1).toISOString();
595
+ }
596
+ return {
597
+ state: e,
598
+ start: a,
599
+ destroy: r,
600
+ createConversation: s,
601
+ openConversation: c,
602
+ loadMore: p,
603
+ patchConversation: g,
604
+ send: T,
605
+ clickCallback: R,
606
+ signAttachment: N,
607
+ submitFeedback: I,
608
+ getPendingApproval: P,
609
+ getActionInFlight: V,
610
+ getLatestSuggestions: Q,
611
+ getLatestForm: U,
612
+ // Pass-through for panel open/close (controls SSE burst).
613
+ setPanelOpen: t.setPanelOpen
614
+ };
615
+ }
616
+ const j = {
617
+ w: "#ffffff",
618
+ g50: "#F9F9F7",
619
+ g100: "#F2F1EE",
620
+ g150: "#E8E6E2",
621
+ g200: "#D9D6CF",
622
+ g300: "#B4B0A8",
623
+ g400: "#888480",
624
+ g500: "#5A5753",
625
+ g700: "#2B2926",
626
+ g900: "#17161A",
627
+ accent: "#5B5FEF",
628
+ accentLight: "#EEEFFE",
629
+ green: "#22C55E",
630
+ red: "#B91C1C",
631
+ redBg: "#FDECEC"
632
+ }, ae = ["#5B5FEF", "#7C3AED", "#DB2777", "#0891B2", "#D97706", "#059669"];
633
+ function ve(t = "") {
634
+ return t ? ae[t.charCodeAt(0) % ae.length] : ae[0];
635
+ }
636
+ function ge(t = "") {
637
+ return t.split(" ").map((e) => e[0] || "").join("").toUpperCase().slice(0, 2);
638
+ }
639
+ function ye(t = /* @__PURE__ */ new Date()) {
640
+ return t.toLocaleTimeString("fr-FR", { hour: "2-digit", minute: "2-digit" });
641
+ }
642
+ const Ne = `
643
+ @import url('https://fonts.googleapis.com/css2?family=Geist:wght@300;400;500;600;700&family=Geist+Mono:wght@400;500&display=swap');
644
+
645
+ .wm-root {
646
+ --wm-f: 'Geist', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
647
+ --wm-fm: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
648
+ --wm-w: ${j.w};
649
+ --wm-g50: ${j.g50};
650
+ --wm-g100: ${j.g100};
651
+ --wm-g150: ${j.g150};
652
+ --wm-g200: ${j.g200};
653
+ --wm-g300: ${j.g300};
654
+ --wm-g400: ${j.g400};
655
+ --wm-g500: ${j.g500};
656
+ --wm-g700: ${j.g700};
657
+ --wm-g900: ${j.g900};
658
+ --wm-a: ${j.accent};
659
+ --wm-al: ${j.accentLight};
660
+ --wm-green: ${j.green};
661
+ --wm-red: ${j.red};
662
+ --wm-redBg: ${j.redBg};
663
+ --wm-sh1: 0 1px 3px rgba(0,0,0,.06);
664
+ --wm-sh2: 0 4px 24px rgba(0,0,0,.09), 0 1px 4px rgba(0,0,0,.05);
665
+ --wm-shPanel: 0 12px 40px rgba(0,0,0,.13), 0 2px 8px rgba(0,0,0,.06);
666
+ }
667
+
668
+ @keyframes wm-heroGlow {
669
+ 0%, 100% { box-shadow: 0 8px 32px rgba(0,0,0,0.28), 0 0 0 0 rgba(0,0,0,0.10); }
670
+ 50% { box-shadow: 0 14px 52px rgba(0,0,0,0.42), 0 0 36px 14px rgba(0,0,0,0.15); }
671
+ }
672
+ @keyframes wm-typingDot {
673
+ 0%, 60%, 100% { transform: translateY(0); opacity: 0.35; }
674
+ 30% { transform: translateY(-4px); opacity: 1; }
675
+ }
676
+ @keyframes wm-fadeUp {
677
+ from { opacity: 0; transform: translateY(6px); }
678
+ to { opacity: 1; transform: translateY(0); }
679
+ }
680
+ @keyframes wm-widgetPop {
681
+ 0% { transform: scale(0.85) translateY(10px); opacity: 0; }
682
+ 100% { transform: scale(1) translateY(0); opacity: 1; }
683
+ }
684
+ @keyframes wm-sheetSlide {
685
+ 0% { transform: translateX(110%); opacity: 0; }
686
+ 100% { transform: translateX(0); opacity: 1; }
687
+ }
688
+ `, L = (t, e) => {
689
+ const n = t.__vccOpts || t;
690
+ for (const [a, r] of e)
691
+ n[a] = r;
692
+ return n;
693
+ }, Pe = {
694
+ name: "WmLauncher",
695
+ props: {
696
+ // Nombre de conversations non lues — pilote la pastille.
697
+ unreadCount: { type: Number, default: 0 },
698
+ // Aperçu du dernier message non lu à afficher en étiquette.
699
+ // Chaîne vide => pas d'étiquette (launcher normal).
700
+ peek: { type: String, default: "" }
701
+ },
702
+ emits: ["open", "dismiss"]
703
+ }, He = { class: "wm-launcherWrap" }, ze = { class: "wm-peek__text" }, Ve = ["aria-label"];
704
+ function qe(t, e, n, a, r, s) {
705
+ return l(), d("div", He, [
706
+ z(Se, { name: "wm-peek" }, {
707
+ default: xe(() => [
708
+ n.peek ? (l(), d("div", {
709
+ key: 0,
710
+ class: "wm-peek",
711
+ role: "button",
712
+ tabindex: "0",
713
+ "aria-label": "Ouvrir le messenger sur le dernier message",
714
+ onClick: e[1] || (e[1] = (o) => t.$emit("open")),
715
+ onKeydown: [
716
+ e[2] || (e[2] = ue(J((o) => t.$emit("open"), ["prevent"]), ["enter"])),
717
+ e[3] || (e[3] = ue(J((o) => t.$emit("open"), ["prevent"]), ["space"]))
718
+ ]
719
+ }, [
720
+ i("p", ze, y(n.peek), 1),
721
+ i("button", {
722
+ type: "button",
723
+ class: "wm-peek__close",
724
+ "aria-label": "Ignorer",
725
+ onClick: e[0] || (e[0] = J((o) => t.$emit("dismiss"), ["stop"]))
726
+ }, [...e[5] || (e[5] = [
727
+ i("svg", {
728
+ width: "11",
729
+ height: "11",
730
+ viewBox: "0 0 24 24",
731
+ fill: "none",
732
+ stroke: "currentColor",
733
+ "stroke-width": "2.4",
734
+ "stroke-linecap": "round",
735
+ "stroke-linejoin": "round",
736
+ "aria-hidden": "true"
737
+ }, [
738
+ i("path", { d: "M18 6L6 18M6 6l12 12" })
739
+ ], -1)
740
+ ])])
741
+ ], 32)) : v("", !0)
742
+ ]),
743
+ _: 1
744
+ }),
745
+ i("button", {
746
+ type: "button",
747
+ class: "wm-launcher",
748
+ "aria-label": "Ouvrir le messenger",
749
+ onClick: e[4] || (e[4] = (o) => t.$emit("open"))
750
+ }, [
751
+ e[6] || (e[6] = i("svg", {
752
+ width: "20",
753
+ height: "20",
754
+ viewBox: "0 0 24 24",
755
+ fill: "none",
756
+ stroke: "currentColor",
757
+ "stroke-width": "1.7",
758
+ "stroke-linecap": "round",
759
+ "stroke-linejoin": "round",
760
+ "aria-hidden": "true"
761
+ }, [
762
+ i("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" })
763
+ ], -1)),
764
+ n.unreadCount > 0 ? (l(), d("span", {
765
+ key: 0,
766
+ class: "wm-launcher__badge",
767
+ "aria-label": `${n.unreadCount} conversation${n.unreadCount > 1 ? "s" : ""} non lue${n.unreadCount > 1 ? "s" : ""}`
768
+ }, y(n.unreadCount > 9 ? "9+" : n.unreadCount), 9, Ve)) : v("", !0)
769
+ ])
770
+ ]);
771
+ }
772
+ const We = /* @__PURE__ */ L(Pe, [["render", qe], ["__scopeId", "data-v-fabef371"]]), Ge = {
773
+ name: "WmAIAvatar",
774
+ props: {
775
+ size: { type: Number, default: 26 },
776
+ pulse: { type: Boolean, default: !1 }
777
+ }
778
+ }, Ke = ["width", "height"];
779
+ function Ye(t, e, n, a, r, s) {
780
+ return l(), d("div", {
781
+ class: "wm-aiav",
782
+ style: H({ width: n.size + "px", height: n.size + "px" })
783
+ }, [
784
+ n.pulse ? (l(), d("div", {
785
+ key: 0,
786
+ class: "wm-aiav__pulse",
787
+ style: H({ borderRadius: n.size * 0.32 + 3 + "px" })
788
+ }, null, 4)) : v("", !0),
789
+ i("div", {
790
+ class: B(["wm-aiav__inner", { "wm-aiav__inner--glow": n.pulse }]),
791
+ style: H({ borderRadius: n.size * 0.32 + "px" })
792
+ }, [
793
+ (l(), d("svg", {
794
+ width: n.size * 0.5,
795
+ height: n.size * 0.5,
796
+ viewBox: "0 0 24 24",
797
+ fill: "none",
798
+ "aria-hidden": "true"
799
+ }, [...e[0] || (e[0] = [
800
+ i("path", {
801
+ d: "M12 2 L13.5 9 L20 10.5 L13.5 12 L12 19 L10.5 12 L4 10.5 L10.5 9 Z",
802
+ fill: "white",
803
+ opacity: "0.92"
804
+ }, null, -1),
805
+ i("circle", {
806
+ cx: "19",
807
+ cy: "5",
808
+ r: "1.5",
809
+ fill: "white",
810
+ opacity: "0.55"
811
+ }, null, -1),
812
+ i("circle", {
813
+ cx: "5",
814
+ cy: "18",
815
+ r: "1",
816
+ fill: "white",
817
+ opacity: "0.38"
818
+ }, null, -1)
819
+ ])], 8, Ke))
820
+ ], 6)
821
+ ], 4);
822
+ }
823
+ const X = /* @__PURE__ */ L(Ge, [["render", Ye], ["__scopeId", "data-v-fe042be7"]]), Je = {
824
+ name: "WmHumanAvatar",
825
+ props: {
826
+ name: { type: String, default: "" },
827
+ avatarUrl: { type: String, default: null },
828
+ size: { type: Number, default: 26 }
829
+ },
830
+ computed: {
831
+ bg() {
832
+ return this.avatarUrl ? "transparent" : ve(this.name);
833
+ },
834
+ initials() {
835
+ return ge(this.name);
836
+ }
837
+ }
838
+ }, Xe = ["src", "alt"];
839
+ function Qe(t, e, n, a, r, s) {
840
+ return l(), d("div", {
841
+ class: "wm-huav",
842
+ style: H({
843
+ width: n.size + "px",
844
+ height: n.size + "px",
845
+ borderRadius: Math.round(n.size * 0.32) + "px",
846
+ background: s.bg
847
+ })
848
+ }, [
849
+ n.avatarUrl ? (l(), d("img", {
850
+ key: 0,
851
+ src: n.avatarUrl,
852
+ alt: n.name || ""
853
+ }, null, 8, Xe)) : (l(), d("span", {
854
+ key: 1,
855
+ style: H({ fontSize: n.size * 0.36 + "px" })
856
+ }, y(s.initials), 5))
857
+ ], 4);
858
+ }
859
+ const we = /* @__PURE__ */ L(Je, [["render", Qe], ["__scopeId", "data-v-79449443"]]), Ze = {
860
+ name: "WmTeamAvatars",
861
+ props: {
862
+ members: { type: Array, default: () => [] },
863
+ responseLabel: { type: String, default: "" }
864
+ },
865
+ computed: {
866
+ visible() {
867
+ return Array.isArray(this.members) && this.members.length > 0;
868
+ },
869
+ stackWidth() {
870
+ return Math.min(this.members.length, 3) === 1 ? 20 : 16 + Math.min(this.members.length, 3) * 13;
871
+ }
872
+ },
873
+ methods: {
874
+ colorFor(t) {
875
+ return t.avatar_url ? "transparent" : ve(t.name || "");
876
+ },
877
+ initialsFor(t) {
878
+ return ge(t.name || "");
879
+ }
880
+ }
881
+ }, et = {
882
+ key: 0,
883
+ class: "wm-team"
884
+ }, tt = ["src", "alt"], nt = { key: 1 }, st = {
885
+ key: 0,
886
+ class: "wm-team__label"
887
+ };
888
+ function rt(t, e, n, a, r, s) {
889
+ return s.visible ? (l(), d("div", et, [
890
+ i("div", {
891
+ class: "wm-team__stack",
892
+ style: H({ width: s.stackWidth + "px" })
893
+ }, [
894
+ (l(!0), d(E, null, F(n.members.slice(0, 3), (o, c) => (l(), d("div", {
895
+ key: c,
896
+ class: "wm-team__pill",
897
+ style: H({ left: c * 13 + "px", zIndex: 3 - c, background: s.colorFor(o) })
898
+ }, [
899
+ o.avatar_url ? (l(), d("img", {
900
+ key: 0,
901
+ src: o.avatar_url,
902
+ alt: o.name || ""
903
+ }, null, 8, tt)) : (l(), d("span", nt, y(s.initialsFor(o)), 1))
904
+ ], 4))), 128))
905
+ ], 4),
906
+ n.responseLabel ? (l(), d("span", st, y(n.responseLabel), 1)) : v("", !0)
907
+ ])) : v("", !0);
908
+ }
909
+ const it = /* @__PURE__ */ L(Ze, [["render", rt], ["__scopeId", "data-v-3659b9c1"]]), at = {
910
+ name: "WmHeader",
911
+ components: { AIAvatar: X, HumanAvatar: we, TeamAvatars: it },
912
+ props: {
913
+ title: { type: String, default: "Nouvelle conversation" },
914
+ escalated: { type: Boolean, default: !1 },
915
+ agentName: { type: String, default: "" },
916
+ agentAvatarUrl: { type: String, default: null },
917
+ showBack: { type: Boolean, default: !1 },
918
+ teamMembers: { type: Array, default: () => [] },
919
+ responseLabel: { type: String, default: "" },
920
+ showClose: { type: Boolean, default: !0 },
921
+ showMore: { type: Boolean, default: !0 },
922
+ moreActive: { type: Boolean, default: !1 },
923
+ // When false (welcome / no conversation) the avatar + title + status
924
+ // block is hidden so the header stays a minimal action bar.
925
+ showIdentity: { type: Boolean, default: !0 }
926
+ },
927
+ emits: ["back", "close", "more"],
928
+ computed: {
929
+ hasTeam() {
930
+ return Array.isArray(this.teamMembers) && this.teamMembers.length > 0;
931
+ },
932
+ // Show the sub-line only when there's a real presence to convey:
933
+ // configured team members, or a human agent who has taken over.
934
+ // Plain AI chat with no team shows nothing (no orphan "En ligne").
935
+ showPresence() {
936
+ return this.hasTeam || this.escalated && !!this.agentName;
937
+ },
938
+ statusText() {
939
+ return this.escalated && this.agentName ? this.agentName : "En ligne";
940
+ }
941
+ }
942
+ }, ot = { class: "wm-header" }, lt = {
943
+ key: 1,
944
+ style: { width: "30px", height: "30px", "flex-shrink": "0" }
945
+ }, dt = { class: "wm-header__avatar" }, ct = { class: "wm-header__main" }, ut = { class: "wm-header__title" }, ht = {
946
+ key: 0,
947
+ class: "wm-header__sub"
948
+ }, mt = { class: "wm-header__status" }, ft = {
949
+ key: 3,
950
+ class: "wm-header__fill"
951
+ }, _t = { class: "wm-header__actions" };
952
+ function pt(t, e, n, a, r, s) {
953
+ const o = O("HumanAvatar"), c = O("AIAvatar"), p = O("TeamAvatars");
954
+ return l(), d("div", ot, [
955
+ n.showBack ? (l(), d("button", {
956
+ key: 0,
957
+ type: "button",
958
+ class: "wm-header__icon",
959
+ "aria-label": "Retour à l'accueil",
960
+ onClick: e[0] || (e[0] = (k) => t.$emit("back"))
961
+ }, [...e[3] || (e[3] = [
962
+ i("svg", {
963
+ width: "13",
964
+ height: "13",
965
+ viewBox: "0 0 24 24",
966
+ fill: "none",
967
+ stroke: "currentColor",
968
+ "stroke-width": "1.8",
969
+ "stroke-linecap": "round",
970
+ "stroke-linejoin": "round",
971
+ "aria-hidden": "true"
972
+ }, [
973
+ i("path", { d: "M19 12H5M12 5l-7 7 7 7" })
974
+ ], -1)
975
+ ])])) : (l(), d("div", lt)),
976
+ n.showIdentity ? (l(), d(E, { key: 2 }, [
977
+ i("div", dt, [
978
+ n.escalated ? (l(), $(o, {
979
+ key: 0,
980
+ name: n.agentName,
981
+ "avatar-url": n.agentAvatarUrl,
982
+ size: 34
983
+ }, null, 8, ["name", "avatar-url"])) : (l(), $(c, {
984
+ key: 1,
985
+ size: 34
986
+ }))
987
+ ]),
988
+ i("div", ct, [
989
+ i("div", ut, y(n.title), 1),
990
+ s.showPresence ? (l(), d("div", ht, [
991
+ s.hasTeam ? (l(), $(p, {
992
+ key: 0,
993
+ members: n.teamMembers,
994
+ "response-label": n.responseLabel
995
+ }, null, 8, ["members", "response-label"])) : v("", !0),
996
+ i("span", mt, [
997
+ e[4] || (e[4] = i("span", { class: "wm-header__dot" }, null, -1)),
998
+ se(" " + y(s.statusText), 1)
999
+ ])
1000
+ ])) : v("", !0)
1001
+ ])
1002
+ ], 64)) : (l(), d("div", ft)),
1003
+ i("div", _t, [
1004
+ n.showMore ? (l(), d("button", {
1005
+ key: 0,
1006
+ type: "button",
1007
+ class: B(["wm-header__icon", { "wm-header__icon--active": n.moreActive }]),
1008
+ "aria-label": "Plus d'options",
1009
+ title: "Plus d'options",
1010
+ onClick: e[1] || (e[1] = (k) => t.$emit("more"))
1011
+ }, [...e[5] || (e[5] = [
1012
+ i("svg", {
1013
+ width: "13",
1014
+ height: "13",
1015
+ viewBox: "0 0 24 24",
1016
+ fill: "currentColor",
1017
+ "aria-hidden": "true"
1018
+ }, [
1019
+ i("circle", {
1020
+ cx: "12",
1021
+ cy: "5",
1022
+ r: "1.6"
1023
+ }),
1024
+ i("circle", {
1025
+ cx: "12",
1026
+ cy: "12",
1027
+ r: "1.6"
1028
+ }),
1029
+ i("circle", {
1030
+ cx: "12",
1031
+ cy: "19",
1032
+ r: "1.6"
1033
+ })
1034
+ ], -1)
1035
+ ])], 2)) : v("", !0),
1036
+ n.showClose ? (l(), d("button", {
1037
+ key: 1,
1038
+ type: "button",
1039
+ class: "wm-header__icon",
1040
+ "aria-label": "Fermer le widget",
1041
+ title: "Fermer le widget",
1042
+ onClick: e[2] || (e[2] = (k) => t.$emit("close"))
1043
+ }, [...e[6] || (e[6] = [
1044
+ i("svg", {
1045
+ width: "13",
1046
+ height: "13",
1047
+ viewBox: "0 0 24 24",
1048
+ fill: "none",
1049
+ stroke: "currentColor",
1050
+ "stroke-width": "2",
1051
+ "stroke-linecap": "round",
1052
+ "stroke-linejoin": "round",
1053
+ "aria-hidden": "true"
1054
+ }, [
1055
+ i("path", { d: "M18 6L6 18M6 6l12 12" })
1056
+ ], -1)
1057
+ ])])) : v("", !0)
1058
+ ])
1059
+ ]);
1060
+ }
1061
+ const vt = /* @__PURE__ */ L(at, [["render", pt], ["__scopeId", "data-v-b5f5f6a9"]]), me = {
1062
+ book: "M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253",
1063
+ changelog: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2",
1064
+ status: "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z",
1065
+ chat: "M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z",
1066
+ link: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"
1067
+ }, gt = {
1068
+ name: "WmOnboarding",
1069
+ components: { AIAvatar: X },
1070
+ props: {
1071
+ welcomeMessage: { type: String, default: "" },
1072
+ agentName: { type: String, default: "" },
1073
+ quickLinks: { type: Array, default: () => [] },
1074
+ unreadThreads: { type: Array, default: () => [] },
1075
+ busy: { type: Boolean, default: !1 }
1076
+ },
1077
+ emits: ["start", "select", "resume"],
1078
+ computed: {
1079
+ heroTitle() {
1080
+ return this.welcomeMessage ? this.welcomeMessage : this.agentName ? `Bonjour, je suis ${this.agentName}` : "Bonjour, je suis votre assistant";
1081
+ },
1082
+ heroSub() {
1083
+ return `Posez-moi n'importe quelle question.
1084
+ Je réponds en quelques secondes.`;
1085
+ }
1086
+ },
1087
+ methods: {
1088
+ iconPath(t) {
1089
+ return me[t] || me.link;
1090
+ }
1091
+ }
1092
+ }, yt = { class: "wm-onb" }, wt = { class: "wm-onb__hero" }, bt = { class: "wm-onb__title" }, kt = { class: "wm-onb__sub" }, Ct = {
1093
+ key: 0,
1094
+ class: "wm-onb__section"
1095
+ }, At = { class: "wm-onb__links" }, St = ["onClick"], xt = { class: "wm-onb__resume-body" }, Mt = { class: "wm-onb__resume-title" }, Tt = { class: "wm-onb__resume-preview" }, It = {
1096
+ key: 1,
1097
+ class: "wm-onb__section"
1098
+ }, Ot = { class: "wm-onb__links" }, Et = ["onClick"], Bt = { class: "wm-onb__link-icon" }, Lt = {
1099
+ width: "14",
1100
+ height: "14",
1101
+ viewBox: "0 0 24 24",
1102
+ fill: "none",
1103
+ stroke: "currentColor",
1104
+ "stroke-width": "1.7",
1105
+ "stroke-linecap": "round",
1106
+ "stroke-linejoin": "round",
1107
+ "aria-hidden": "true"
1108
+ }, Rt = ["d"], $t = { class: "wm-onb__link-label" }, Ft = { class: "wm-onb__cta" }, Ut = ["disabled"];
1109
+ function Dt(t, e, n, a, r, s) {
1110
+ const o = O("AIAvatar");
1111
+ return l(), d("div", yt, [
1112
+ i("div", wt, [
1113
+ z(o, {
1114
+ size: 56,
1115
+ pulse: !0
1116
+ }),
1117
+ i("div", bt, y(s.heroTitle), 1),
1118
+ i("div", kt, y(s.heroSub), 1)
1119
+ ]),
1120
+ n.unreadThreads.length ? (l(), d("div", Ct, [
1121
+ e[3] || (e[3] = i("div", { class: "wm-onb__section-title" }, "Messages non lus", -1)),
1122
+ i("div", At, [
1123
+ (l(!0), d(E, null, F(n.unreadThreads, (c) => (l(), d("button", {
1124
+ key: c.id,
1125
+ type: "button",
1126
+ class: "wm-onb__link wm-onb__resume",
1127
+ onClick: (p) => t.$emit("resume", c)
1128
+ }, [
1129
+ e[1] || (e[1] = i("span", { class: "wm-onb__link-icon wm-onb__resume-icon" }, [
1130
+ i("svg", {
1131
+ width: "14",
1132
+ height: "14",
1133
+ viewBox: "0 0 24 24",
1134
+ fill: "none",
1135
+ stroke: "currentColor",
1136
+ "stroke-width": "1.7",
1137
+ "stroke-linecap": "round",
1138
+ "stroke-linejoin": "round",
1139
+ "aria-hidden": "true"
1140
+ }, [
1141
+ i("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" })
1142
+ ]),
1143
+ i("span", {
1144
+ class: "wm-onb__resume-dot",
1145
+ "aria-label": "Non lu"
1146
+ })
1147
+ ], -1)),
1148
+ i("span", xt, [
1149
+ i("span", Mt, y(c.title), 1),
1150
+ i("span", Tt, y(c.preview), 1)
1151
+ ]),
1152
+ e[2] || (e[2] = i("svg", {
1153
+ width: "13",
1154
+ height: "13",
1155
+ viewBox: "0 0 24 24",
1156
+ fill: "none",
1157
+ stroke: "currentColor",
1158
+ "stroke-width": "1.8",
1159
+ "stroke-linecap": "round",
1160
+ "stroke-linejoin": "round",
1161
+ class: "wm-onb__link-chev",
1162
+ "aria-hidden": "true"
1163
+ }, [
1164
+ i("path", { d: "M9 18l6-6-6-6" })
1165
+ ], -1))
1166
+ ], 8, St))), 128))
1167
+ ])
1168
+ ])) : v("", !0),
1169
+ n.quickLinks.length ? (l(), d("div", It, [
1170
+ e[5] || (e[5] = i("div", { class: "wm-onb__section-title" }, "Accès rapide", -1)),
1171
+ i("div", Ot, [
1172
+ (l(!0), d(E, null, F(n.quickLinks, (c, p) => (l(), d("button", {
1173
+ key: p,
1174
+ type: "button",
1175
+ class: "wm-onb__link",
1176
+ onClick: (k) => t.$emit("select", c)
1177
+ }, [
1178
+ i("span", Bt, [
1179
+ (l(), d("svg", Lt, [
1180
+ i("path", {
1181
+ d: s.iconPath(c.icon)
1182
+ }, null, 8, Rt)
1183
+ ]))
1184
+ ]),
1185
+ i("span", $t, y(c.label), 1),
1186
+ e[4] || (e[4] = i("svg", {
1187
+ width: "13",
1188
+ height: "13",
1189
+ viewBox: "0 0 24 24",
1190
+ fill: "none",
1191
+ stroke: "currentColor",
1192
+ "stroke-width": "1.8",
1193
+ "stroke-linecap": "round",
1194
+ "stroke-linejoin": "round",
1195
+ class: "wm-onb__link-chev",
1196
+ "aria-hidden": "true"
1197
+ }, [
1198
+ i("path", { d: "M9 18l6-6-6-6" })
1199
+ ], -1))
1200
+ ], 8, Et))), 128))
1201
+ ])
1202
+ ])) : v("", !0),
1203
+ i("div", Ft, [
1204
+ i("button", {
1205
+ type: "button",
1206
+ class: "wm-onb__startBtn",
1207
+ disabled: n.busy,
1208
+ onClick: e[0] || (e[0] = (c) => t.$emit("start"))
1209
+ }, y(n.busy ? "…" : "Commencer une conversation"), 9, Ut)
1210
+ ])
1211
+ ]);
1212
+ }
1213
+ const jt = /* @__PURE__ */ L(gt, [["render", Dt], ["__scopeId", "data-v-bd89bc8f"]]);
1214
+ function Nt(t) {
1215
+ return String(t).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
1216
+ }
1217
+ function Pt(t) {
1218
+ return /^(https?:\/\/|mailto:|tel:)/i.test(String(t).trim());
1219
+ }
1220
+ const fe = "";
1221
+ function ee(t) {
1222
+ let e = t;
1223
+ const n = [];
1224
+ return e = e.replace(/`([^`\n]+)`/g, (a, r) => {
1225
+ const s = n.length;
1226
+ return n.push(r), `${fe}CODE${s}${fe}`;
1227
+ }), e = e.replace(/\[([^\]\n]+)\]\(([^)\s]+)\)/g, (a, r, s) => Pt(s) ? `<a href="${s}" target="_blank" rel="noopener noreferrer">${r}</a>` : r), e = e.replace(/\*\*([^\n*][^\n]*?)\*\*/g, "<strong>$1</strong>"), e = e.replace(/__([^\n_][^\n]*?)__/g, "<u>$1</u>"), e = e.replace(/~~([^\n~][^\n]*?)~~/g, "<s>$1</s>"), e = e.replace(/(^|[^\w*])\*([^\n*]+?)\*(?!\w)/g, "$1<em>$2</em>"), e = e.replace(/(^|[^\w_])_([^\n_]+?)_(?!\w)/g, "$1<em>$2</em>"), e = e.replace(/CODE(\d+)/g, (a, r) => `<code class="wm-md-code">${n[+r]}</code>`), e;
1228
+ }
1229
+ function be(t) {
1230
+ if (!t) return "";
1231
+ const e = Nt(t).split(`
1232
+ `), n = [];
1233
+ let a = 0;
1234
+ for (; a < e.length; ) {
1235
+ const s = e[a];
1236
+ if (/^\s*```([\w-]*)\s*$/.exec(s)) {
1237
+ a++;
1238
+ const k = [];
1239
+ for (; a < e.length && !/^\s*```\s*$/.test(e[a]); )
1240
+ k.push(e[a]), a++;
1241
+ a < e.length && a++, n.push({
1242
+ type: "block",
1243
+ html: `<pre class="wm-md-pre"><code>${k.join(`
1244
+ `)}</code></pre>`
1245
+ });
1246
+ continue;
1247
+ }
1248
+ if (/^\s*[-*]\s+/.test(s)) {
1249
+ const k = [];
1250
+ for (; a < e.length; ) {
1251
+ const T = /^\s*[-*]\s+(.*)$/.exec(e[a]);
1252
+ if (!T) break;
1253
+ k.push(T[1]), a++;
1254
+ }
1255
+ const g = k.map((T) => `<li>${ee(T)}</li>`).join("");
1256
+ n.push({ type: "block", html: `<ul class="wm-md-ul">${g}</ul>` });
1257
+ continue;
1258
+ }
1259
+ const c = /^\s*(\d+)\.\s+(.*)$/.exec(s);
1260
+ if (c) {
1261
+ const k = parseInt(c[1], 10), g = [c[2]];
1262
+ for (a++; a < e.length; ) {
1263
+ const x = /^\s*\d+\.\s+(.*)$/.exec(e[a]);
1264
+ if (!x) break;
1265
+ g.push(x[1]), a++;
1266
+ }
1267
+ const T = g.map((x) => `<li>${ee(x)}</li>`).join(""), R = k !== 1 ? ` start="${k}"` : "";
1268
+ n.push({ type: "block", html: `<ol class="wm-md-ol"${R}>${T}</ol>` });
1269
+ continue;
1270
+ }
1271
+ const p = /^(#{1,6})\s+(.*)$/.exec(s);
1272
+ if (p) {
1273
+ const k = p[1].length;
1274
+ n.push({
1275
+ type: "block",
1276
+ html: `<h${k} class="wm-md-h wm-md-h${k}">${ee(p[2])}</h${k}>`
1277
+ }), a++;
1278
+ continue;
1279
+ }
1280
+ n.push({ type: "text", html: ee(s) }), a++;
1281
+ }
1282
+ let r = "";
1283
+ for (let s = 0; s < n.length; s++) {
1284
+ const o = n[s];
1285
+ r += o.html;
1286
+ const c = n[s + 1];
1287
+ c && o.type !== "block" && c.type !== "block" && (r += `
1288
+ `);
1289
+ }
1290
+ return r;
1291
+ }
1292
+ const Ht = {
1293
+ name: "WmArtifactFormResponse",
1294
+ props: {
1295
+ data: { type: Object, required: !0 }
1296
+ },
1297
+ computed: {
1298
+ fields() {
1299
+ var t;
1300
+ return Array.isArray((t = this.data) == null ? void 0 : t.fields) ? this.data.fields : [];
1301
+ }
1302
+ }
1303
+ }, zt = { class: "wm-art wm-art--formResponse" }, Vt = { class: "wm-art__head" }, qt = { class: "wm-art__title" }, Wt = { class: "wm-art__body" }, Gt = { class: "wm-art__fieldLabel" };
1304
+ function Kt(t, e, n, a, r, s) {
1305
+ return l(), d("div", zt, [
1306
+ i("div", Vt, [
1307
+ i("div", qt, y(n.data.title || "Formulaire"), 1),
1308
+ e[0] || (e[0] = i("span", { class: "wm-art__badge wm-art__badge--success" }, [
1309
+ i("svg", {
1310
+ width: "11",
1311
+ height: "11",
1312
+ viewBox: "0 0 24 24",
1313
+ fill: "none",
1314
+ stroke: "currentColor",
1315
+ "stroke-width": "2.5",
1316
+ "stroke-linecap": "round",
1317
+ "stroke-linejoin": "round",
1318
+ "aria-hidden": "true"
1319
+ }, [
1320
+ i("polyline", { points: "20 6 9 17 4 12" })
1321
+ ]),
1322
+ se(" Envoyé ")
1323
+ ], -1))
1324
+ ]),
1325
+ i("div", Wt, [
1326
+ (l(!0), d(E, null, F(s.fields, (o, c) => (l(), d("div", {
1327
+ key: c,
1328
+ class: "wm-art__field"
1329
+ }, [
1330
+ i("div", Gt, y(o.label), 1),
1331
+ i("div", {
1332
+ class: B(["wm-art__fieldValue", { "wm-art__fieldValue--multi": o.multiline }])
1333
+ }, y(o.value), 3)
1334
+ ]))), 128))
1335
+ ])
1336
+ ]);
1337
+ }
1338
+ const Yt = /* @__PURE__ */ L(Ht, [["render", Kt], ["__scopeId", "data-v-812bda8b"]]), Jt = {
1339
+ name: "WmArtifactInfoCard",
1340
+ props: {
1341
+ data: { type: Object, required: !0 }
1342
+ },
1343
+ computed: {
1344
+ fields() {
1345
+ var t;
1346
+ return Array.isArray((t = this.data) == null ? void 0 : t.fields) ? this.data.fields : [];
1347
+ },
1348
+ hasBody() {
1349
+ var t;
1350
+ return !!((t = this.data) != null && t.body) || this.fields.length > 0;
1351
+ }
1352
+ }
1353
+ }, Xt = { class: "wm-art wm-art--infoCard" }, Qt = {
1354
+ key: 0,
1355
+ class: "wm-art__image"
1356
+ }, Zt = ["src", "alt"], en = { class: "wm-art__head" }, tn = { class: "wm-art__headMain" }, nn = { class: "wm-art__title" }, sn = {
1357
+ key: 0,
1358
+ class: "wm-art__subtitle"
1359
+ }, rn = {
1360
+ key: 1,
1361
+ class: "wm-art__body"
1362
+ }, an = {
1363
+ key: 0,
1364
+ class: "wm-art__text"
1365
+ }, on = { class: "wm-art__fieldLabel" };
1366
+ function ln(t, e, n, a, r, s) {
1367
+ return l(), d("div", Xt, [
1368
+ n.data.image_url ? (l(), d("figure", Qt, [
1369
+ i("img", {
1370
+ src: n.data.image_url,
1371
+ alt: n.data.title || "",
1372
+ loading: "lazy"
1373
+ }, null, 8, Zt)
1374
+ ])) : v("", !0),
1375
+ i("div", en, [
1376
+ i("div", tn, [
1377
+ i("div", nn, y(n.data.title), 1),
1378
+ n.data.subtitle ? (l(), d("div", sn, y(n.data.subtitle), 1)) : v("", !0)
1379
+ ]),
1380
+ n.data.badge && n.data.badge.label ? (l(), d("span", {
1381
+ key: 0,
1382
+ class: B(["wm-art__badge", `wm-art__badge--${n.data.badge.tone || "neutral"}`])
1383
+ }, y(n.data.badge.label), 3)) : v("", !0)
1384
+ ]),
1385
+ s.hasBody ? (l(), d("div", rn, [
1386
+ n.data.body ? (l(), d("div", an, y(n.data.body), 1)) : v("", !0),
1387
+ s.fields.length ? (l(!0), d(E, { key: 1 }, F(s.fields, (o, c) => (l(), d("div", {
1388
+ key: c,
1389
+ class: "wm-art__field"
1390
+ }, [
1391
+ i("div", on, y(o.label), 1),
1392
+ i("div", {
1393
+ class: B(["wm-art__fieldValue", { "wm-art__fieldValue--multi": o.multiline }])
1394
+ }, y(o.value), 3)
1395
+ ]))), 128)) : v("", !0)
1396
+ ])) : v("", !0)
1397
+ ]);
1398
+ }
1399
+ const dn = /* @__PURE__ */ L(Jt, [["render", ln], ["__scopeId", "data-v-d7369333"]]);
1400
+ function cn(t) {
1401
+ if (!t) return "";
1402
+ const e = new Date(t);
1403
+ if (Number.isNaN(e.getTime())) return t;
1404
+ const n = e.toLocaleDateString("fr-FR", { day: "numeric", month: "long", year: "numeric" }), a = e.toLocaleTimeString("fr-FR", { hour: "2-digit", minute: "2-digit" });
1405
+ return `${n} à ${a}`;
1406
+ }
1407
+ const un = {
1408
+ name: "WmArtifactTicket",
1409
+ props: {
1410
+ data: { type: Object, required: !0 }
1411
+ },
1412
+ computed: {
1413
+ fields() {
1414
+ var t;
1415
+ return Array.isArray((t = this.data) == null ? void 0 : t.fields) ? this.data.fields : [];
1416
+ },
1417
+ hasBody() {
1418
+ var t;
1419
+ return !!((t = this.data) != null && t.body) || this.fields.length > 0;
1420
+ },
1421
+ formattedDate() {
1422
+ var t;
1423
+ return cn((t = this.data) == null ? void 0 : t.created_at);
1424
+ }
1425
+ },
1426
+ methods: {
1427
+ // Détection souple par pattern : tout libellé qui ressemble à
1428
+ // "Priorité"/"Priority"/"Prio" déclenche l'icône signal.
1429
+ isPriority(t) {
1430
+ return /priorit|^prio$/i.test(String(t || "").trim());
1431
+ },
1432
+ // 3 niveaux dérivés librement de la valeur ("Moyenne (Medium)",
1433
+ // "Élevée", "Critical"…). Défaut : niveau moyen — non-reconnu vaut
1434
+ // mieux que muet.
1435
+ priorityLevel(t) {
1436
+ const e = String(t || "").toLowerCase();
1437
+ return /high|haute|élev|elev|critic|critiq|urgent/.test(e) ? 3 : /low|basse|faible|minor/.test(e) ? 1 : 2;
1438
+ }
1439
+ }
1440
+ }, hn = { class: "wm-art wm-art--ticket" }, mn = { class: "wm-art__head wm-tk__head" }, fn = { class: "wm-art__title wm-tk__title" }, _n = { class: "wm-tk__sub" }, pn = { class: "wm-tk__ref" }, vn = {
1441
+ key: 0,
1442
+ class: "wm-art__body"
1443
+ }, gn = {
1444
+ key: 0,
1445
+ class: "wm-tk__text"
1446
+ }, yn = { class: "wm-art__fieldLabel" }, wn = ["data-level"], bn = {
1447
+ key: 1,
1448
+ class: "wm-art__footer wm-tk__footer"
1449
+ };
1450
+ function kn(t, e, n, a, r, s) {
1451
+ return l(), d("div", hn, [
1452
+ i("div", mn, [
1453
+ i("div", fn, y(n.data.title), 1),
1454
+ i("div", _n, [
1455
+ i("div", pn, [
1456
+ e[0] || (e[0] = i("svg", {
1457
+ width: "11",
1458
+ height: "11",
1459
+ viewBox: "0 0 24 24",
1460
+ fill: "none",
1461
+ stroke: "currentColor",
1462
+ "stroke-width": "2",
1463
+ "stroke-linecap": "round",
1464
+ "stroke-linejoin": "round",
1465
+ "aria-hidden": "true"
1466
+ }, [
1467
+ i("path", { d: "M20 12a2 2 0 0 1 2-2V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v4a2 2 0 0 1 0 4v4a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-4a2 2 0 0 1-2-2z" }),
1468
+ i("path", { d: "M13 5v2M13 17v2M13 11v2" })
1469
+ ], -1)),
1470
+ i("span", null, y(n.data.reference), 1)
1471
+ ]),
1472
+ i("span", {
1473
+ class: B(["wm-art__badge", "wm-tk__badge", `wm-art__badge--${n.data.status.tone || "neutral"}`])
1474
+ }, [
1475
+ e[1] || (e[1] = i("span", {
1476
+ class: "wm-tk__dot",
1477
+ "aria-hidden": "true"
1478
+ }, null, -1)),
1479
+ se(" " + y(n.data.status.label), 1)
1480
+ ], 2)
1481
+ ])
1482
+ ]),
1483
+ s.hasBody ? (l(), d("div", vn, [
1484
+ n.data.body ? (l(), d("div", gn, y(n.data.body), 1)) : v("", !0),
1485
+ s.fields.length ? (l(!0), d(E, { key: 1 }, F(s.fields, (o, c) => (l(), d("div", {
1486
+ key: c,
1487
+ class: "wm-art__field"
1488
+ }, [
1489
+ i("div", yn, y(o.label), 1),
1490
+ i("div", {
1491
+ class: B(["wm-art__fieldValue", { "wm-art__fieldValue--multi": o.multiline }])
1492
+ }, [
1493
+ s.isPriority(o.label) ? (l(), d("svg", {
1494
+ key: 0,
1495
+ class: "wm-tk__prio",
1496
+ "data-level": s.priorityLevel(o.value),
1497
+ width: "12",
1498
+ height: "12",
1499
+ viewBox: "0 0 12 12",
1500
+ "aria-hidden": "true"
1501
+ }, [...e[2] || (e[2] = [
1502
+ i("rect", {
1503
+ x: "1",
1504
+ y: "8",
1505
+ width: "2",
1506
+ height: "3",
1507
+ rx: "0.5"
1508
+ }, null, -1),
1509
+ i("rect", {
1510
+ x: "5",
1511
+ y: "5",
1512
+ width: "2",
1513
+ height: "6",
1514
+ rx: "0.5"
1515
+ }, null, -1),
1516
+ i("rect", {
1517
+ x: "9",
1518
+ y: "2",
1519
+ width: "2",
1520
+ height: "9",
1521
+ rx: "0.5"
1522
+ }, null, -1)
1523
+ ])], 8, wn)) : v("", !0),
1524
+ i("span", null, y(o.value), 1)
1525
+ ], 2)
1526
+ ]))), 128)) : v("", !0)
1527
+ ])) : v("", !0),
1528
+ n.data.created_at ? (l(), d("div", bn, [
1529
+ e[3] || (e[3] = i("svg", {
1530
+ width: "11",
1531
+ height: "11",
1532
+ viewBox: "0 0 24 24",
1533
+ fill: "none",
1534
+ stroke: "currentColor",
1535
+ "stroke-width": "2",
1536
+ "stroke-linecap": "round",
1537
+ "stroke-linejoin": "round",
1538
+ "aria-hidden": "true"
1539
+ }, [
1540
+ i("rect", {
1541
+ x: "3",
1542
+ y: "4",
1543
+ width: "18",
1544
+ height: "18",
1545
+ rx: "2"
1546
+ }),
1547
+ i("path", { d: "M16 2v4M8 2v4M3 10h18" })
1548
+ ], -1)),
1549
+ i("span", null, y(s.formattedDate), 1)
1550
+ ])) : v("", !0)
1551
+ ]);
1552
+ }
1553
+ const Cn = /* @__PURE__ */ L(un, [["render", kn], ["__scopeId", "data-v-f0495924"]]), An = {
1554
+ form_response: Yt,
1555
+ info_card: dn,
1556
+ ticket: Cn
1557
+ }, Sn = {
1558
+ name: "WmArtifactRenderer",
1559
+ props: {
1560
+ // Forme : { kind: string, data: any } (le `data` est validé
1561
+ // serveur contre le schéma du `kind` correspondant).
1562
+ artifact: { type: Object, required: !0 }
1563
+ },
1564
+ computed: {
1565
+ component() {
1566
+ var e;
1567
+ const t = (e = this.artifact) == null ? void 0 : e.kind;
1568
+ return t && An[t] || null;
1569
+ }
1570
+ }
1571
+ };
1572
+ function xn(t, e, n, a, r, s) {
1573
+ return s.component ? (l(), $(Me(s.component), {
1574
+ key: 0,
1575
+ data: n.artifact.data
1576
+ }, null, 8, ["data"])) : v("", !0);
1577
+ }
1578
+ const ke = /* @__PURE__ */ L(Sn, [["render", xn]]), Mn = {
1579
+ name: "WmActionResult",
1580
+ components: { ArtifactRenderer: ke },
1581
+ props: {
1582
+ state: { type: String, default: "success" },
1583
+ // 'success' | 'failure' | 'rejected'
1584
+ label: { type: String, required: !0 },
1585
+ detail: { type: String, default: "" },
1586
+ // Artifact attaché au résultat (forme `{ kind, data }`, posé par
1587
+ // le sub-agent via `set_result_artifact`). `null` si rien à afficher.
1588
+ artifact: { type: Object, default: null }
1589
+ },
1590
+ computed: {
1591
+ ok() {
1592
+ return this.state === "success";
1593
+ },
1594
+ // Sub-agent summaries arrive as free-form text often containing
1595
+ // bullet lists / bold emphasis / inline code. Render them through
1596
+ // the shared markdown pipeline so they read like the chat bubbles.
1597
+ detailHtml() {
1598
+ return be(this.detail);
1599
+ }
1600
+ }
1601
+ }, Tn = { class: "wm-result-wrap" }, In = { class: "wm-result" }, On = {
1602
+ key: 0,
1603
+ width: "10",
1604
+ height: "10",
1605
+ viewBox: "0 0 24 24",
1606
+ fill: "none",
1607
+ stroke: "#fff",
1608
+ "stroke-width": "2.5",
1609
+ "stroke-linecap": "round",
1610
+ "stroke-linejoin": "round",
1611
+ "aria-hidden": "true"
1612
+ }, En = {
1613
+ key: 1,
1614
+ width: "10",
1615
+ height: "10",
1616
+ viewBox: "0 0 24 24",
1617
+ fill: "none",
1618
+ stroke: "currentColor",
1619
+ "stroke-width": "2.5",
1620
+ "stroke-linecap": "round",
1621
+ "stroke-linejoin": "round",
1622
+ "aria-hidden": "true"
1623
+ }, Bn = { class: "wm-result__body" }, Ln = ["innerHTML"], Rn = {
1624
+ key: 0,
1625
+ class: "wm-result-art"
1626
+ };
1627
+ function $n(t, e, n, a, r, s) {
1628
+ const o = O("ArtifactRenderer");
1629
+ return l(), d("div", Tn, [
1630
+ i("div", In, [
1631
+ i("div", {
1632
+ class: B(["wm-result__check", { "wm-result__check--ok": s.ok }])
1633
+ }, [
1634
+ s.ok ? (l(), d("svg", On, [...e[0] || (e[0] = [
1635
+ i("path", { d: "M20 6L9 17l-5-5" }, null, -1)
1636
+ ])])) : (l(), d("svg", En, [...e[1] || (e[1] = [
1637
+ i("path", { d: "M18 6L6 18M6 6l12 12" }, null, -1)
1638
+ ])]))
1639
+ ], 2),
1640
+ i("div", Bn, [
1641
+ i("div", {
1642
+ class: B(["wm-result__label", { "wm-result__label--struck": !s.ok }])
1643
+ }, y(n.label), 3),
1644
+ n.detail ? (l(), d("div", {
1645
+ key: 0,
1646
+ class: "wm-result__detail",
1647
+ innerHTML: s.detailHtml
1648
+ }, null, 8, Ln)) : v("", !0)
1649
+ ])
1650
+ ]),
1651
+ n.artifact ? (l(), d("div", Rn, [
1652
+ z(o, { artifact: n.artifact }, null, 8, ["artifact"])
1653
+ ])) : v("", !0)
1654
+ ]);
1655
+ }
1656
+ const Fn = /* @__PURE__ */ L(Mn, [["render", $n], ["__scopeId", "data-v-037fd69e"]]), Un = {
1657
+ name: "WmAttachmentPreview",
1658
+ inject: {
1659
+ signAttachmentFn: { default: null }
1660
+ },
1661
+ props: {
1662
+ attachment: { type: Object, required: !0 }
1663
+ },
1664
+ data() {
1665
+ return { url: null, loading: !1 };
1666
+ },
1667
+ computed: {
1668
+ kind() {
1669
+ var n, a;
1670
+ const t = (n = this.attachment) == null ? void 0 : n.type;
1671
+ if (t) return t;
1672
+ const e = (((a = this.attachment) == null ? void 0 : a.mime_type) || "").toLowerCase();
1673
+ return e.startsWith("image/") ? "image" : e.startsWith("audio/") ? "audio" : e.startsWith("video/") ? "video" : "file";
1674
+ },
1675
+ displayName() {
1676
+ var t, e, n;
1677
+ return ((e = (t = this.attachment) == null ? void 0 : t.metadata) == null ? void 0 : e.name) || ((n = this.attachment) == null ? void 0 : n.name) || this.guessNameFromPath() || "Pièce jointe";
1678
+ },
1679
+ sizeLabel() {
1680
+ var e;
1681
+ const t = (e = this.attachment) == null ? void 0 : e.size_bytes;
1682
+ return t ? t < 1024 ? `${t} o` : t < 1024 * 1024 ? `${(t / 1024).toFixed(0)} ko` : `${(t / (1024 * 1024)).toFixed(1)} Mo` : "";
1683
+ }
1684
+ },
1685
+ watch: {
1686
+ "attachment.path": { handler() {
1687
+ this.refresh();
1688
+ }, immediate: !0 }
1689
+ },
1690
+ methods: {
1691
+ guessNameFromPath() {
1692
+ var n;
1693
+ return ((((n = this.attachment) == null ? void 0 : n.path) || "").split("/").pop() || "").replace(/^[0-9a-f-]{8,}-/, "");
1694
+ },
1695
+ async refresh() {
1696
+ var t;
1697
+ if (!(!((t = this.attachment) != null && t.path) || !this.signAttachmentFn)) {
1698
+ this.loading = !0;
1699
+ try {
1700
+ this.url = await this.signAttachmentFn(this.attachment.path);
1701
+ } finally {
1702
+ this.loading = !1;
1703
+ }
1704
+ }
1705
+ },
1706
+ onFileClick(t) {
1707
+ this.url || t.preventDefault();
1708
+ }
1709
+ }
1710
+ }, Dn = ["href"], jn = ["src", "alt"], Nn = ["src"], Pn = ["src"], Hn = ["href", "download"], zn = { class: "wm-att__main" }, Vn = { class: "wm-att__name" }, qn = {
1711
+ key: 0,
1712
+ class: "wm-att__meta"
1713
+ }, Wn = {
1714
+ key: 0,
1715
+ class: "wm-att__spin",
1716
+ "aria-hidden": "true"
1717
+ };
1718
+ function Gn(t, e, n, a, r, s) {
1719
+ return l(), d("div", {
1720
+ class: B(["wm-att", ["wm-att--" + (s.kind || "file")]])
1721
+ }, [
1722
+ s.kind === "image" && r.url ? (l(), d("a", {
1723
+ key: 0,
1724
+ href: r.url,
1725
+ target: "_blank",
1726
+ rel: "noopener",
1727
+ class: "wm-att__imgWrap"
1728
+ }, [
1729
+ i("img", {
1730
+ src: r.url,
1731
+ alt: s.displayName,
1732
+ loading: "lazy"
1733
+ }, null, 8, jn)
1734
+ ], 8, Dn)) : s.kind === "audio" && r.url ? (l(), d("audio", {
1735
+ key: 1,
1736
+ src: r.url,
1737
+ controls: "",
1738
+ preload: "metadata"
1739
+ }, null, 8, Nn)) : s.kind === "video" && r.url ? (l(), d("video", {
1740
+ key: 2,
1741
+ src: r.url,
1742
+ controls: "",
1743
+ preload: "metadata"
1744
+ }, null, 8, Pn)) : (l(), d("a", {
1745
+ key: 3,
1746
+ class: "wm-att__file",
1747
+ href: r.url || "#",
1748
+ download: s.displayName,
1749
+ target: "_blank",
1750
+ rel: "noopener",
1751
+ onClick: e[0] || (e[0] = (...o) => s.onFileClick && s.onFileClick(...o))
1752
+ }, [
1753
+ e[1] || (e[1] = i("span", { class: "wm-att__icon" }, [
1754
+ i("svg", {
1755
+ width: "14",
1756
+ height: "14",
1757
+ viewBox: "0 0 24 24",
1758
+ fill: "none",
1759
+ stroke: "currentColor",
1760
+ "stroke-width": "1.7",
1761
+ "stroke-linecap": "round",
1762
+ "stroke-linejoin": "round",
1763
+ "aria-hidden": "true"
1764
+ }, [
1765
+ i("path", { d: "M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z" }),
1766
+ i("path", { d: "M14 2v6h6" })
1767
+ ])
1768
+ ], -1)),
1769
+ i("span", zn, [
1770
+ i("span", Vn, y(s.displayName), 1),
1771
+ s.sizeLabel ? (l(), d("span", qn, y(s.sizeLabel), 1)) : v("", !0)
1772
+ ]),
1773
+ r.loading ? (l(), d("span", Wn)) : v("", !0)
1774
+ ], 8, Hn))
1775
+ ], 2);
1776
+ }
1777
+ const Kn = /* @__PURE__ */ L(Un, [["render", Gn], ["__scopeId", "data-v-1cd1267b"]]), Yn = {
1778
+ name: "WmBubble",
1779
+ props: {
1780
+ role: { type: String, default: "ai" },
1781
+ // 'user' | 'ai' | 'human'
1782
+ text: { type: String, default: "" }
1783
+ },
1784
+ computed: {
1785
+ rendered() {
1786
+ return be(this.text);
1787
+ }
1788
+ }
1789
+ }, Jn = ["innerHTML"];
1790
+ function Xn(t, e, n, a, r, s) {
1791
+ return l(), d("div", {
1792
+ class: B(["wm-bubble", "wm-bubble--" + n.role])
1793
+ }, [
1794
+ Te(t.$slots, "default", {}, () => [
1795
+ i("span", { innerHTML: s.rendered }, null, 8, Jn)
1796
+ ], !0)
1797
+ ], 2);
1798
+ }
1799
+ const Qn = /* @__PURE__ */ L(Yn, [["render", Xn], ["__scopeId", "data-v-bcbe5969"]]), Zn = { name: "WmTyping" }, es = { class: "wm-typing" };
1800
+ function ts(t, e, n, a, r, s) {
1801
+ return l(), d("div", es, [...e[0] || (e[0] = [
1802
+ i("span", { style: { "animation-delay": "0s" } }, null, -1),
1803
+ i("span", { style: { "animation-delay": "0.2s" } }, null, -1),
1804
+ i("span", { style: { "animation-delay": "0.4s" } }, null, -1)
1805
+ ])]);
1806
+ }
1807
+ const ns = /* @__PURE__ */ L(Zn, [["render", ts], ["__scopeId", "data-v-df2447fd"]]);
1808
+ function oe(t) {
1809
+ return t ? t.client_msg_id || t.id : "";
1810
+ }
1811
+ const ss = {
1812
+ transferred_to_human: "Conversation transférée à un humain",
1813
+ assigned: "{name} a rejoint la conversation",
1814
+ unassigned: "L'agent a quitté la conversation",
1815
+ resolved: "Conversation résolue",
1816
+ reopened: "Conversation rouverte",
1817
+ idle: "Conversation en pause"
1818
+ }, rs = 80, is = 200, as = {
1819
+ name: "WmMessageList",
1820
+ components: { AIAvatar: X, HumanAvatar: we, Bubble: Qn, Typing: ns, ActionResult: Fn, AttachmentPreview: Kn, ArtifactRenderer: ke },
1821
+ props: {
1822
+ messages: { type: Array, default: () => [] },
1823
+ streamingActive: { type: Boolean, default: !1 },
1824
+ dateLabel: { type: String, default: "" },
1825
+ // Conversation identity — used to detect a thread switch so the
1826
+ // pinned-scroll logic doesn't carry over from the previous thread.
1827
+ conversationId: { type: [String, Number], default: null },
1828
+ // True while older messages are being fetched.
1829
+ loadingMore: { type: Boolean, default: !1 },
1830
+ // True when the server still has older messages to return.
1831
+ hasMore: { type: Boolean, default: !1 }
1832
+ },
1833
+ emits: ["load-more"],
1834
+ data() {
1835
+ return {
1836
+ // True while a load-more is in flight and for a short grace
1837
+ // window after, suppressing the per-row fade-up animation so
1838
+ // a freshly prepended history page doesn't visibly flicker
1839
+ // in 50-at-once.
1840
+ silentFades: !1
1841
+ };
1842
+ },
1843
+ computed: {
1844
+ historyExhausted() {
1845
+ return !this.hasMore && !this.loadingMore && this.messages.length >= 20;
1846
+ },
1847
+ groups() {
1848
+ var e, n, a;
1849
+ const t = [];
1850
+ for (const r of this.messages) {
1851
+ const s = this.roleOf(r);
1852
+ if (s === "system") {
1853
+ t.push({
1854
+ key: `sys-${oe(r)}`,
1855
+ role: s,
1856
+ messages: [r],
1857
+ systemLabel: this.systemLabel(r)
1858
+ });
1859
+ continue;
1860
+ }
1861
+ const o = t[t.length - 1];
1862
+ o && o.role === s && (s === "ai" || o.agentName === (((e = r == null ? void 0 : r.author) == null ? void 0 : e.name) || "")) ? o.messages.push(r) : t.push({
1863
+ key: `g-${oe(r)}`,
1864
+ role: s,
1865
+ agentName: ((n = r == null ? void 0 : r.author) == null ? void 0 : n.name) || "",
1866
+ agentAvatarUrl: ((a = r == null ? void 0 : r.author) == null ? void 0 : a.avatar_url) || null,
1867
+ messages: [r]
1868
+ });
1869
+ }
1870
+ return t;
1871
+ }
1872
+ },
1873
+ watch: {
1874
+ messages: { handler: "snapshotAndRestore", deep: !0 },
1875
+ loadingMore(t) {
1876
+ t || (this._pendingLoadMore = !1), this.silentFades = !0, clearTimeout(this._silenceTimer), t || (this._silenceTimer = setTimeout(() => {
1877
+ this.silentFades = !1;
1878
+ }, 250)), this.snapshotAndRestore();
1879
+ },
1880
+ streamingActive: "snapshotAndRestore"
1881
+ },
1882
+ mounted() {
1883
+ this._lastSeenConvId = this.conversationId, this.scrollToBottom();
1884
+ },
1885
+ methods: {
1886
+ messageKey: oe,
1887
+ isAtBottom(t) {
1888
+ return t.scrollHeight - t.scrollTop - t.clientHeight <= rs;
1889
+ },
1890
+ onScroll() {
1891
+ const t = this.$refs.scrollEl;
1892
+ t && (this.loadingMore || !this.hasMore || this._pendingLoadMore || t.scrollTop <= is && (this._pendingLoadMore = !0, this.$emit("load-more")));
1893
+ },
1894
+ // Capture pre-patch scroll state and restore it on the next tick.
1895
+ //
1896
+ // Three regimes, in priority order:
1897
+ // 1. Conv switch → always snap to bottom.
1898
+ // 2. User was pinned → snap to bottom (new message at the end).
1899
+ // 3. User scrolled up → keep an anchor element pinned at its
1900
+ // exact viewport offset. We track the
1901
+ // anchor's position *relative to the
1902
+ // scroll container* (via
1903
+ // getBoundingClientRect) rather than an
1904
+ // offsetTop delta — that makes the
1905
+ // correction idempotent so we can replay
1906
+ // it across the next few frames and
1907
+ // absorb late layout shifts (images,
1908
+ // markdown reflow) in the freshly
1909
+ // prepended page. That replay is what
1910
+ // removes the residual "half flash".
1911
+ //
1912
+ // The `_scrollSnap` guard coalesces multiple watchers firing in the
1913
+ // same tick (e.g. `messages` + `loadingMore` both change when a page
1914
+ // of history lands) so we only apply the correction once.
1915
+ snapshotAndRestore() {
1916
+ const t = this.$refs.scrollEl;
1917
+ if (!t || this._scrollSnap) return;
1918
+ const e = this._lastSeenConvId !== this.conversationId;
1919
+ this._lastSeenConvId = this.conversationId;
1920
+ const n = this.isAtBottom(t);
1921
+ let a = null;
1922
+ if (!e && !n) {
1923
+ const s = this.pickAnchor(t);
1924
+ if (s != null && s.el) {
1925
+ const o = t.getBoundingClientRect().top;
1926
+ a = {
1927
+ el: s.el,
1928
+ relY: s.el.getBoundingClientRect().top - o
1929
+ };
1930
+ }
1931
+ }
1932
+ const r = { forceBottom: e, wasPinned: n, anchor: a };
1933
+ this._scrollSnap = r, this.$nextTick(() => {
1934
+ if (this._scrollSnap = null, r.forceBottom || r.wasPinned) {
1935
+ t.scrollTop = t.scrollHeight;
1936
+ return;
1937
+ }
1938
+ if (!r.anchor) return;
1939
+ const s = () => {
1940
+ var k;
1941
+ const o = r.anchor;
1942
+ if (!((k = o.el) != null && k.isConnected)) return;
1943
+ const p = o.el.getBoundingClientRect().top - t.getBoundingClientRect().top - o.relY;
1944
+ Math.abs(p) > 0.5 && (t.scrollTop += p);
1945
+ };
1946
+ s(), requestAnimationFrame(() => {
1947
+ s(), requestAnimationFrame(s);
1948
+ });
1949
+ });
1950
+ },
1951
+ // Pick a *stable* DOM element to use as a scroll anchor — one
1952
+ // that will still be mounted after the upcoming patch and whose
1953
+ // `offsetTop` moves in lockstep with content being prepended.
1954
+ //
1955
+ // We must skip:
1956
+ // - the loader (`wm-list__loadMore`): unmounts when loading
1957
+ // ends, leaving `anchor.el.isConnected === false`;
1958
+ // - the history-end marker (`wm-list__historyEnd`): same story;
1959
+ // - the date sep (`wm-list__sep`): it stays glued to the very
1960
+ // top of the scroller regardless of prepends, so its
1961
+ // `offsetTop` delta is always 0 → preservation does nothing
1962
+ // and the user stays pinned to the top, which is exactly the
1963
+ // bug we're trying to avoid.
1964
+ //
1965
+ // Falling back to the *first* message group (rather than the
1966
+ // first one past `scrollTop`) when the user is at the very top
1967
+ // is intentional: it's the most reliable anchor and its
1968
+ // `offsetTop` grows by the full prepended height.
1969
+ pickAnchor(t) {
1970
+ const e = t.scrollTop;
1971
+ let n = null;
1972
+ for (const a of t.children)
1973
+ if (a.classList.contains("wm-list__group") && (n || (n = a), a.offsetTop + a.offsetHeight >= e))
1974
+ return { el: a, offsetTop: a.offsetTop };
1975
+ return n ? { el: n, offsetTop: n.offsetTop } : null;
1976
+ },
1977
+ roleOf(t) {
1978
+ var n, a;
1979
+ if ((t == null ? void 0 : t.type) === "system" || ((n = t == null ? void 0 : t.payload) == null ? void 0 : n.type) === "system") return "system";
1980
+ const e = (a = t == null ? void 0 : t.author) == null ? void 0 : a.type;
1981
+ return e === "user" ? "user" : e === "agent_human" ? "human" : "ai";
1982
+ },
1983
+ roleLabel(t) {
1984
+ return t.role === "human" ? t.agentName || "Agent" : "Assistant IA";
1985
+ },
1986
+ // Visible "kind" of a message — drives corner-radius adjacency
1987
+ // logic. `null` means the item renders nothing (no body, no
1988
+ // bubble) and shouldn't count as a neighbour.
1989
+ itemKindOf(t) {
1990
+ return t ? t.type === "action" && t.payload && t.payload.state !== "pending" || this.artifactOf(t) ? "card" : t.text_md ? "bubble" : null : null;
1991
+ },
1992
+ // Per-corner border radius for an item inside a multi-item
1993
+ // cluster. Geometric model:
1994
+ // - The two corners on the *shared* edge (left for ai/human,
1995
+ // right for user) become small whenever a neighbour exists
1996
+ // on that side — the items are flush along that edge.
1997
+ // - The two corners on the *opposite* edge stay rounded
1998
+ // unless the neighbour is a strictly-wider item (card vs
1999
+ // bubble) that physically covers them.
2000
+ // Standalone bubbles (no neighbours) keep the tail design: a
2001
+ // small TL for ai/human, small BR for user.
2002
+ cornersFor(t, e) {
2003
+ const n = t.messages, a = this.itemKindOf(n[e]), r = this.itemKindOf(n[e - 1]), s = this.itemKindOf(n[e + 1]), o = t.role === "user", c = 14, p = 4, k = (N, I) => N === "card" && I === "bubble";
2004
+ let g = c, T = c, R = c, x = c;
2005
+ return o ? (r && (T = p, k(r, a) && (g = p)), s && (R = p, k(s, a) && (x = p)), !r && !s && a === "bubble" && (R = p)) : (r && (g = p, k(r, a) && (T = p)), s && (x = p, k(s, a) && (R = p)), !r && !s && a === "bubble" && (g = p)), { tl: g, tr: T, br: R, bl: x };
2006
+ },
2007
+ // Inline style emitting the four corner CSS variables. Set on
2008
+ // `.wm-list__row` so they cascade to Bubble/ActionResult/
2009
+ // ArtifactRenderer. Returns null for single-item clusters so
2010
+ // those components fall back to their native border-radius.
2011
+ cornersStyle(t, e) {
2012
+ if (t.messages.reduce(
2013
+ (r, s) => r + (this.itemKindOf(s) ? 1 : 0),
2014
+ 0
2015
+ ) < 2) return null;
2016
+ const a = this.cornersFor(t, e);
2017
+ return {
2018
+ "--wm-r-tl": `${a.tl}px`,
2019
+ "--wm-r-tr": `${a.tr}px`,
2020
+ "--wm-r-br": `${a.br}px`,
2021
+ "--wm-r-bl": `${a.bl}px`
2022
+ };
2023
+ },
2024
+ lastTimeOf(t) {
2025
+ const e = t.messages[t.messages.length - 1];
2026
+ if (!(e != null && e.created_at)) return "";
2027
+ try {
2028
+ return ye(new Date(e.created_at));
2029
+ } catch {
2030
+ return "";
2031
+ }
2032
+ },
2033
+ attachmentsOf(t) {
2034
+ var n;
2035
+ const e = (n = t == null ? void 0 : t.payload) == null ? void 0 : n.attachments;
2036
+ return Array.isArray(e) ? e : [];
2037
+ },
2038
+ // Returns the artifact descriptor `{ kind, data }` if this message
2039
+ // carries one in `metadata.artifact`. Used to swap the default text
2040
+ // bubble for a dedicated card via `ArtifactRenderer`. Le registry
2041
+ // côté ArtifactRenderer décide quoi afficher pour chaque `kind`.
2042
+ artifactOf(t) {
2043
+ var n;
2044
+ const e = (n = t == null ? void 0 : t.metadata) == null ? void 0 : n.artifact;
2045
+ return !e || typeof e != "object" || typeof e.kind != "string" ? null : e;
2046
+ },
2047
+ actionLabel(t) {
2048
+ var e;
2049
+ return ((e = t == null ? void 0 : t.payload) == null ? void 0 : e.name) || (t == null ? void 0 : t.text_md) || "Action";
2050
+ },
2051
+ actionDetail(t) {
2052
+ var n, a, r, s, o, c;
2053
+ const e = t == null ? void 0 : t.payload;
2054
+ return e ? e.state === "success" ? ((n = e.success) == null ? void 0 : n.summary) || ((r = (a = e.success) == null ? void 0 : a.metadata) == null ? void 0 : r.description) || "" : e.state === "rejected" ? ((s = e.rejected) == null ? void 0 : s.reason) || "Action annulée par l'utilisateur." : e.state === "failure" && (((o = e.failure) == null ? void 0 : o.summary) || ((c = e.failure) == null ? void 0 : c.error)) || "" : "";
2055
+ },
2056
+ actionArtifact(t) {
2057
+ var n, a;
2058
+ const e = (a = (n = t == null ? void 0 : t.payload) == null ? void 0 : n.success) == null ? void 0 : a.artifact;
2059
+ return !e || typeof e != "object" || typeof e.kind != "string" ? null : e;
2060
+ },
2061
+ systemLabel(t) {
2062
+ var r, s, o;
2063
+ const e = (r = t == null ? void 0 : t.payload) == null ? void 0 : r.event, n = ss[e] || (t == null ? void 0 : t.text_md) || "Mise à jour de la conversation", a = ((s = t == null ? void 0 : t.metadata) == null ? void 0 : s.agent_name) || ((o = t == null ? void 0 : t.author) == null ? void 0 : o.name) || "";
2064
+ return n.replace("{name}", a || "Un agent");
2065
+ },
2066
+ scrollToBottom() {
2067
+ const t = this.$refs.scrollEl;
2068
+ t && (t.scrollTop = t.scrollHeight);
2069
+ }
2070
+ }
2071
+ }, os = {
2072
+ key: 0,
2073
+ class: "wm-list__loadMore",
2074
+ role: "status",
2075
+ "aria-live": "polite"
2076
+ }, ls = {
2077
+ key: 1,
2078
+ class: "wm-list__historyEnd"
2079
+ }, ds = {
2080
+ key: 2,
2081
+ class: "wm-list__sep"
2082
+ }, cs = { class: "wm-list__sep-label" }, us = {
2083
+ key: 0,
2084
+ class: "wm-list__sysep"
2085
+ }, hs = { class: "wm-list__sysep-label" }, ms = {
2086
+ key: 0,
2087
+ class: "wm-list__role"
2088
+ }, fs = {
2089
+ key: 0,
2090
+ class: "wm-list__avatarSlot"
2091
+ }, _s = {
2092
+ key: 3,
2093
+ class: "wm-list__body"
2094
+ }, ps = {
2095
+ key: 3,
2096
+ class: "wm-list__row wm-list__row--ai fade-up"
2097
+ }, vs = { class: "wm-list__avatarSlot" };
2098
+ function gs(t, e, n, a, r, s) {
2099
+ const o = O("AIAvatar"), c = O("HumanAvatar"), p = O("ActionResult"), k = O("ArtifactRenderer"), g = O("Bubble"), T = O("AttachmentPreview"), R = O("Typing");
2100
+ return l(), d("div", {
2101
+ ref: "scrollEl",
2102
+ class: B(["wm-list", { "wm-list--silent": r.silentFades }]),
2103
+ onScrollPassive: e[0] || (e[0] = (...x) => s.onScroll && s.onScroll(...x))
2104
+ }, [
2105
+ n.loadingMore ? (l(), d("div", os, [...e[1] || (e[1] = [
2106
+ i("span", {
2107
+ class: "wm-list__loadMore-spinner",
2108
+ "aria-hidden": "true"
2109
+ }, null, -1),
2110
+ i("span", { class: "wm-list__loadMore-lbl" }, "Chargement de l'historique…", -1)
2111
+ ])])) : s.historyExhausted ? (l(), d("div", ls, "Début de la conversation")) : v("", !0),
2112
+ n.dateLabel ? (l(), d("div", ds, [
2113
+ e[2] || (e[2] = i("div", { class: "wm-list__line" }, null, -1)),
2114
+ i("span", cs, y(n.dateLabel), 1),
2115
+ e[3] || (e[3] = i("div", { class: "wm-list__line" }, null, -1))
2116
+ ])) : v("", !0),
2117
+ (l(!0), d(E, null, F(s.groups, (x, N) => (l(), d("div", {
2118
+ key: x.key,
2119
+ class: B(["wm-list__group", "wm-list__group--" + x.role])
2120
+ }, [
2121
+ x.role === "system" ? (l(), d("div", us, [
2122
+ e[4] || (e[4] = i("div", { class: "wm-list__line wm-list__line--strong" }, null, -1)),
2123
+ i("span", hs, y(x.systemLabel), 1),
2124
+ e[5] || (e[5] = i("div", { class: "wm-list__line wm-list__line--strong" }, null, -1))
2125
+ ])) : (l(), d(E, { key: 1 }, [
2126
+ x.role !== "user" ? (l(), d("div", ms, y(s.roleLabel(x)), 1)) : v("", !0),
2127
+ (l(!0), d(E, null, F(x.messages, (I, P) => (l(), d("div", {
2128
+ key: s.messageKey(I),
2129
+ class: B(["wm-list__row fade-up", ["wm-list__row--" + x.role, { "is-pending": I._pending, "is-failed": I._failed }]]),
2130
+ style: H(s.cornersStyle(x, P))
2131
+ }, [
2132
+ x.role !== "user" ? (l(), d("div", fs, [
2133
+ P === x.messages.length - 1 ? (l(), d(E, { key: 0 }, [
2134
+ x.role === "ai" ? (l(), $(o, {
2135
+ key: 0,
2136
+ size: 26
2137
+ })) : (l(), $(c, {
2138
+ key: 1,
2139
+ name: x.agentName,
2140
+ "avatar-url": x.agentAvatarUrl,
2141
+ size: 26
2142
+ }, null, 8, ["name", "avatar-url"]))
2143
+ ], 64)) : v("", !0)
2144
+ ])) : v("", !0),
2145
+ I.type === "action" && I.payload && I.payload.state !== "pending" ? (l(), $(p, {
2146
+ key: 1,
2147
+ state: I.payload.state,
2148
+ label: s.actionLabel(I),
2149
+ detail: s.actionDetail(I),
2150
+ artifact: s.actionArtifact(I)
2151
+ }, null, 8, ["state", "label", "detail", "artifact"])) : s.artifactOf(I) ? (l(), $(k, {
2152
+ key: 2,
2153
+ artifact: s.artifactOf(I)
2154
+ }, null, 8, ["artifact"])) : (l(), d("div", _s, [
2155
+ I.text_md ? (l(), $(g, {
2156
+ key: 0,
2157
+ role: x.role,
2158
+ text: I.text_md
2159
+ }, null, 8, ["role", "text"])) : v("", !0),
2160
+ s.attachmentsOf(I).length ? (l(), d("div", {
2161
+ key: 1,
2162
+ class: B(["wm-list__atts", { "wm-list__atts--align-end": x.role === "user" }])
2163
+ }, [
2164
+ (l(!0), d(E, null, F(s.attachmentsOf(I), (U, V) => (l(), $(T, {
2165
+ key: `${s.messageKey(I)}-att-${V}`,
2166
+ attachment: U
2167
+ }, null, 8, ["attachment"]))), 128))
2168
+ ], 2)) : v("", !0)
2169
+ ]))
2170
+ ], 6))), 128)),
2171
+ s.lastTimeOf(x) ? (l(), d("div", {
2172
+ key: 1,
2173
+ class: B(["wm-list__time", { "wm-list__time--right": x.role === "user" }])
2174
+ }, y(s.lastTimeOf(x)), 3)) : v("", !0)
2175
+ ], 64))
2176
+ ], 2))), 128)),
2177
+ n.streamingActive ? (l(), d("div", ps, [
2178
+ i("div", vs, [
2179
+ z(o, { size: 26 })
2180
+ ]),
2181
+ z(R)
2182
+ ])) : v("", !0)
2183
+ ], 34);
2184
+ }
2185
+ const ys = /* @__PURE__ */ L(as, [["render", gs], ["__scopeId", "data-v-f23f2f8c"]]), te = typeof navigator < "u" && !!navigator.mediaDevices && typeof navigator.mediaDevices.getDisplayMedia == "function", ce = typeof window < "u" && typeof window.MediaRecorder < "u";
2186
+ function ws() {
2187
+ return ce && [
2188
+ "video/webm;codecs=vp9,opus",
2189
+ "video/webm;codecs=vp8,opus",
2190
+ "video/webm",
2191
+ "video/mp4"
2192
+ ].find((e) => {
2193
+ var n, a;
2194
+ return (a = (n = window.MediaRecorder).isTypeSupported) == null ? void 0 : a.call(n, e);
2195
+ }) || "";
2196
+ }
2197
+ function Ce({ audio: t }) {
2198
+ return {
2199
+ video: !0,
2200
+ audio: !!t,
2201
+ selfBrowserSurface: "include",
2202
+ surfaceSwitching: "include",
2203
+ systemAudio: t ? "include" : "exclude"
2204
+ };
2205
+ }
2206
+ function Bi(t) {
2207
+ return t ? t.startsWith("image/") ? "image" : t.startsWith("video/") ? "video" : "file" : "file";
2208
+ }
2209
+ async function bs() {
2210
+ if (!te) return null;
2211
+ let t;
2212
+ try {
2213
+ t = await navigator.mediaDevices.getDisplayMedia(Ce({ audio: !1 }));
2214
+ } catch (e) {
2215
+ return (e == null ? void 0 : e.name) !== "NotAllowedError" && console.error("[media] screenshot picker", e), null;
2216
+ }
2217
+ try {
2218
+ return await ks(t);
2219
+ } catch (e) {
2220
+ return console.error("[media] screenshot capture", e), null;
2221
+ } finally {
2222
+ t.getTracks().forEach((e) => {
2223
+ e.stop();
2224
+ });
2225
+ }
2226
+ }
2227
+ async function ks(t) {
2228
+ const e = document.createElement("video");
2229
+ e.muted = !0, e.playsInline = !0, e.srcObject = t, await e.play(), await new Promise((c) => requestAnimationFrame(c));
2230
+ const n = e.videoWidth || 1280, a = e.videoHeight || 720, r = document.createElement("canvas");
2231
+ r.width = n, r.height = a, r.getContext("2d").drawImage(e, 0, 0, n, a);
2232
+ const s = await new Promise((c, p) => {
2233
+ r.toBlob((k) => k ? c(k) : p(new Error("toBlob failed")), "image/png");
2234
+ }), o = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
2235
+ return new File([s], `capture-${o}.png`, { type: "image/png" });
2236
+ }
2237
+ async function Cs(t = {}) {
2238
+ var k;
2239
+ if (!te || !ce) return null;
2240
+ let e;
2241
+ try {
2242
+ e = await navigator.mediaDevices.getDisplayMedia(Ce({ audio: !0 }));
2243
+ } catch (g) {
2244
+ return (g == null ? void 0 : g.name) !== "NotAllowedError" && console.error("[media] record picker", g), null;
2245
+ }
2246
+ const n = ws();
2247
+ let a;
2248
+ try {
2249
+ a = n ? new window.MediaRecorder(e, { mimeType: n }) : new window.MediaRecorder(e);
2250
+ } catch (g) {
2251
+ return console.error("[media] recorder init", g), e.getTracks().forEach((T) => {
2252
+ T.stop();
2253
+ }), null;
2254
+ }
2255
+ const r = [];
2256
+ let s = null, o = !1;
2257
+ a.addEventListener("dataavailable", (g) => {
2258
+ g.data && g.data.size > 0 && r.push(g.data);
2259
+ }), a.addEventListener("stop", () => {
2260
+ var g, T;
2261
+ if (s && clearInterval(s), e.getTracks().forEach((R) => {
2262
+ R.stop();
2263
+ }), r.length) {
2264
+ const R = a.mimeType || n || "video/webm", x = new Blob(r, { type: R }), N = /mp4/.test(R) ? "mp4" : "webm", I = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19), P = new File([x], `ecran-${I}.${N}`, { type: R });
2265
+ (g = t.onfinalize) == null || g.call(t, P);
2266
+ } else
2267
+ (T = t.oncancel) == null || T.call(t);
2268
+ }), e.getVideoTracks().forEach((g) => {
2269
+ g.addEventListener("ended", () => c(), { once: !0 });
2270
+ });
2271
+ function c() {
2272
+ if (!o && (o = !0, a.state !== "inactive"))
2273
+ try {
2274
+ a.stop();
2275
+ } catch (g) {
2276
+ console.error("[media] recorder stop", g);
2277
+ }
2278
+ }
2279
+ try {
2280
+ a.start(1e3);
2281
+ } catch (g) {
2282
+ return console.error("[media] recorder start", g), e.getTracks().forEach((T) => {
2283
+ T.stop();
2284
+ }), null;
2285
+ }
2286
+ (k = t.onstart) == null || k.call(t);
2287
+ const p = Date.now();
2288
+ return s = setInterval(() => {
2289
+ var g;
2290
+ (g = t.ontick) == null || g.call(t, Date.now() - p);
2291
+ }, 500), {
2292
+ stop: c,
2293
+ get state() {
2294
+ return a.state;
2295
+ }
2296
+ };
2297
+ }
2298
+ const As = [
2299
+ {
2300
+ action: "file",
2301
+ label: "Joindre un fichier",
2302
+ path: "M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48"
2303
+ },
2304
+ {
2305
+ action: "screenshot",
2306
+ label: "Capture d'écran",
2307
+ path: "M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z M12 17a4 4 0 100-8 4 4 0 000 8z"
2308
+ },
2309
+ {
2310
+ action: "record",
2311
+ label: "Enregistrer l'écran",
2312
+ path: "M23 7l-7 5 7 5V7z M1 5a2 2 0 012-2h12a2 2 0 012 2v14a2 2 0 01-2 2H3a2 2 0 01-2-2V5z"
2313
+ }
2314
+ ], Ss = {
2315
+ name: "WmComposer",
2316
+ props: {
2317
+ modelValue: { type: String, default: "" },
2318
+ placeholder: { type: String, default: "Écrivez votre message…" },
2319
+ disabled: { type: Boolean, default: !1 },
2320
+ attachLabel: { type: String, default: "Joindre" }
2321
+ },
2322
+ emits: ["update:modelValue", "send", "attach"],
2323
+ data() {
2324
+ return {
2325
+ local: this.modelValue,
2326
+ attachOpen: !1,
2327
+ recording: !1,
2328
+ recordingElapsed: 0,
2329
+ recorder: null
2330
+ };
2331
+ },
2332
+ computed: {
2333
+ canSend() {
2334
+ return !this.disabled && !!this.local.trim();
2335
+ },
2336
+ attachItems() {
2337
+ return As.map((t) => ({
2338
+ ...t,
2339
+ disabled: t.action === "screenshot" && !te || t.action === "record" && (!te || !ce)
2340
+ }));
2341
+ },
2342
+ recordingElapsedLabel() {
2343
+ const t = Math.floor(this.recordingElapsed / 1e3), e = Math.floor(t / 60), n = t % 60;
2344
+ return `${e}:${n.toString().padStart(2, "0")}`;
2345
+ }
2346
+ },
2347
+ watch: {
2348
+ modelValue(t) {
2349
+ t !== this.local && (this.local = t, this.$nextTick(() => this.autosize()));
2350
+ },
2351
+ local(t) {
2352
+ this.$emit("update:modelValue", t);
2353
+ }
2354
+ },
2355
+ mounted() {
2356
+ this.autosize();
2357
+ },
2358
+ beforeUnmount() {
2359
+ if (this.recorder)
2360
+ try {
2361
+ this.recorder.stop();
2362
+ } catch {
2363
+ }
2364
+ },
2365
+ methods: {
2366
+ // Imperatively move the caret into the textarea — called by the
2367
+ // parent right after a fresh conversation is started so the user
2368
+ // can type immediately without an extra click.
2369
+ focus() {
2370
+ const t = this.$refs.inputEl;
2371
+ if (!(!t || this.disabled))
2372
+ try {
2373
+ t.focus({ preventScroll: !0 });
2374
+ } catch {
2375
+ t.focus();
2376
+ }
2377
+ },
2378
+ onSubmit() {
2379
+ if (!this.canSend) return;
2380
+ const t = this.local.trim();
2381
+ this.local = "", this.$emit("send", t), this.$nextTick(() => this.autosize());
2382
+ },
2383
+ // Enter sends; Shift+Enter (or Ctrl/Cmd+Enter) inserts a newline so
2384
+ // the user can compose multi-line messages without losing the
2385
+ // single-key send affordance.
2386
+ onKeydown(t) {
2387
+ t.key === "Enter" && !t.shiftKey && !t.ctrlKey && !t.metaKey && !t.isComposing && (t.preventDefault(), this.onSubmit());
2388
+ },
2389
+ // Auto-resize the textarea to fit its content, capped by the CSS
2390
+ // max-height (~6 lines) — the overflow then scrolls.
2391
+ autosize() {
2392
+ const t = this.$refs.inputEl;
2393
+ t && (t.style.height = "auto", t.style.height = `${t.scrollHeight}px`);
2394
+ },
2395
+ onFile(t) {
2396
+ const e = Array.from(t.target.files || []);
2397
+ for (const n of e) this.$emit("attach", n);
2398
+ t.target.value = "";
2399
+ },
2400
+ onAttachAction(t) {
2401
+ this.attachOpen = !1, t === "file" ? this.pickFile() : t === "screenshot" ? this.captureScreenshot() : t === "record" && this.startRecording();
2402
+ },
2403
+ pickFile() {
2404
+ var t;
2405
+ (t = this.$refs.fileEl) == null || t.click();
2406
+ },
2407
+ async captureScreenshot() {
2408
+ if (this.disabled) return;
2409
+ const t = await bs();
2410
+ t && this.$emit("attach", t);
2411
+ },
2412
+ async startRecording() {
2413
+ if (this.recording || this.disabled) return;
2414
+ this.recordingElapsed = 0;
2415
+ const t = await Cs({
2416
+ onstart: () => {
2417
+ this.recording = !0;
2418
+ },
2419
+ ontick: (e) => {
2420
+ this.recordingElapsed = e;
2421
+ },
2422
+ onfinalize: (e) => {
2423
+ this.$emit("attach", e), this.recording = !1, this.recordingElapsed = 0, this.recorder = null;
2424
+ },
2425
+ oncancel: () => {
2426
+ this.recording = !1, this.recordingElapsed = 0, this.recorder = null;
2427
+ }
2428
+ });
2429
+ t && (this.recorder = t);
2430
+ },
2431
+ stopRecording() {
2432
+ if (this.recorder)
2433
+ try {
2434
+ this.recorder.stop();
2435
+ } catch (t) {
2436
+ console.error("[composer] stop recording", t);
2437
+ }
2438
+ }
2439
+ }
2440
+ }, xs = { class: "wm-compose-wrap" }, Ms = {
2441
+ key: 0,
2442
+ class: "wm-rec"
2443
+ }, Ts = { class: "wm-rec__lbl" }, Is = {
2444
+ key: 1,
2445
+ class: "wm-compose__menu",
2446
+ role: "menu"
2447
+ }, Os = ["disabled", "onClick"], Es = { class: "wm-compose__menuIcon" }, Bs = {
2448
+ viewBox: "0 0 24 24",
2449
+ width: "14",
2450
+ height: "14",
2451
+ "aria-hidden": "true"
2452
+ }, Ls = ["d"], Rs = ["placeholder", "disabled"], $s = { class: "wm-compose__actions" }, Fs = ["title", "aria-label", "disabled"], Us = ["disabled"];
2453
+ function Ds(t, e, n, a, r, s) {
2454
+ return l(), d("div", xs, [
2455
+ r.recording ? (l(), d("div", Ms, [
2456
+ e[8] || (e[8] = i("span", {
2457
+ class: "wm-rec__dot",
2458
+ "aria-hidden": "true"
2459
+ }, null, -1)),
2460
+ i("span", Ts, "Enregistrement · " + y(s.recordingElapsedLabel), 1),
2461
+ i("button", {
2462
+ type: "button",
2463
+ class: "wm-rec__stop",
2464
+ onClick: e[0] || (e[0] = (...o) => s.stopRecording && s.stopRecording(...o))
2465
+ }, "Arrêter")
2466
+ ])) : v("", !0),
2467
+ i("form", {
2468
+ class: B(["wm-compose", { "has-attach": r.attachOpen }]),
2469
+ onSubmit: e[7] || (e[7] = J((...o) => s.onSubmit && s.onSubmit(...o), ["prevent"]))
2470
+ }, [
2471
+ i("input", {
2472
+ ref: "fileEl",
2473
+ type: "file",
2474
+ hidden: "",
2475
+ multiple: "",
2476
+ onChange: e[1] || (e[1] = (...o) => s.onFile && s.onFile(...o))
2477
+ }, null, 544),
2478
+ r.attachOpen ? (l(), d("div", {
2479
+ key: 0,
2480
+ class: "wm-compose__overlay",
2481
+ onClick: e[2] || (e[2] = (o) => r.attachOpen = !1)
2482
+ })) : v("", !0),
2483
+ r.attachOpen ? (l(), d("div", Is, [
2484
+ (l(!0), d(E, null, F(s.attachItems, (o) => (l(), d("button", {
2485
+ key: o.action,
2486
+ type: "button",
2487
+ class: "wm-compose__menuItem",
2488
+ disabled: o.disabled,
2489
+ onClick: (c) => s.onAttachAction(o.action)
2490
+ }, [
2491
+ i("span", Es, [
2492
+ (l(), d("svg", Bs, [
2493
+ i("path", {
2494
+ d: o.path,
2495
+ stroke: "currentColor",
2496
+ "stroke-width": "1.8",
2497
+ "stroke-linecap": "round",
2498
+ "stroke-linejoin": "round",
2499
+ fill: "none"
2500
+ }, null, 8, Ls)
2501
+ ]))
2502
+ ]),
2503
+ i("span", null, y(o.label), 1)
2504
+ ], 8, Os))), 128))
2505
+ ])) : v("", !0),
2506
+ q(i("textarea", {
2507
+ ref: "inputEl",
2508
+ "onUpdate:modelValue": e[3] || (e[3] = (o) => r.local = o),
2509
+ class: "wm-compose__input",
2510
+ rows: "3",
2511
+ placeholder: n.placeholder,
2512
+ disabled: n.disabled,
2513
+ onKeydown: e[4] || (e[4] = (...o) => s.onKeydown && s.onKeydown(...o)),
2514
+ onInput: e[5] || (e[5] = (...o) => s.autosize && s.autosize(...o))
2515
+ }, null, 40, Rs), [
2516
+ [G, r.local]
2517
+ ]),
2518
+ i("div", $s, [
2519
+ i("button", {
2520
+ type: "button",
2521
+ class: B(["wm-compose__icon", { "is-open": r.attachOpen }]),
2522
+ title: n.attachLabel,
2523
+ "aria-label": n.attachLabel,
2524
+ disabled: r.recording,
2525
+ onClick: e[6] || (e[6] = (o) => r.attachOpen = !r.attachOpen)
2526
+ }, [...e[9] || (e[9] = [
2527
+ i("svg", {
2528
+ width: "13",
2529
+ height: "13",
2530
+ viewBox: "0 0 24 24",
2531
+ fill: "none",
2532
+ stroke: "currentColor",
2533
+ "stroke-width": "1.8",
2534
+ "stroke-linecap": "round",
2535
+ "stroke-linejoin": "round",
2536
+ "aria-hidden": "true"
2537
+ }, [
2538
+ i("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48" })
2539
+ ], -1)
2540
+ ])], 10, Fs),
2541
+ i("button", {
2542
+ type: "submit",
2543
+ class: B(["wm-compose__send", { "is-empty": !s.canSend }]),
2544
+ disabled: !s.canSend,
2545
+ "aria-label": "Envoyer"
2546
+ }, [...e[10] || (e[10] = [
2547
+ i("svg", {
2548
+ width: "13",
2549
+ height: "13",
2550
+ viewBox: "0 0 24 24",
2551
+ fill: "none",
2552
+ stroke: "currentColor",
2553
+ "stroke-width": "2",
2554
+ "stroke-linecap": "round",
2555
+ "stroke-linejoin": "round",
2556
+ "aria-hidden": "true"
2557
+ }, [
2558
+ i("path", { d: "M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" })
2559
+ ], -1)
2560
+ ])], 10, Us)
2561
+ ])
2562
+ ], 34)
2563
+ ]);
2564
+ }
2565
+ const js = /* @__PURE__ */ L(Ss, [["render", Ds], ["__scopeId", "data-v-14fa9ec0"]]), Ns = {
2566
+ name: "WmSuggestionChips",
2567
+ props: {
2568
+ items: { type: Array, default: () => [] },
2569
+ // Lets the chips wait for the latest message to land before they pop
2570
+ // in. Bumped by the parent on each new suggestion set.
2571
+ baseDelay: { type: Number, default: 360 },
2572
+ stepDelay: { type: Number, default: 90 }
2573
+ },
2574
+ emits: ["select"],
2575
+ computed: {
2576
+ // Re-key the whole strip on any content change so Vue tears down
2577
+ // the chips and the entry animation replays for the new set.
2578
+ batchKey() {
2579
+ return this.items.map((t) => (t == null ? void 0 : t.label) || (t == null ? void 0 : t.text) || "").join("§");
2580
+ }
2581
+ }
2582
+ }, Ps = ["onClick"];
2583
+ function Hs(t, e, n, a, r, s) {
2584
+ return n.items.length ? (l(), d("div", {
2585
+ key: s.batchKey,
2586
+ class: "wm-chips"
2587
+ }, [
2588
+ (l(!0), d(E, null, F(n.items, (o, c) => (l(), d("button", {
2589
+ key: c,
2590
+ type: "button",
2591
+ class: "wm-chip",
2592
+ style: H({ animationDelay: n.baseDelay + c * n.stepDelay + "ms" }),
2593
+ onClick: (p) => t.$emit("select", o)
2594
+ }, y(o.label), 13, Ps))), 128))
2595
+ ])) : v("", !0);
2596
+ }
2597
+ const zs = /* @__PURE__ */ L(Ns, [["render", Hs], ["__scopeId", "data-v-55aa529d"]]), Vs = {
2598
+ name: "WmApprovalCard",
2599
+ components: { AIAvatar: X },
2600
+ props: {
2601
+ action: { type: String, required: !0 },
2602
+ detail: { type: String, default: "" },
2603
+ callbacks: { type: Array, default: () => [] }
2604
+ },
2605
+ emits: ["callback"],
2606
+ computed: {
2607
+ approveCallback() {
2608
+ return this.callbacks.find((t) => t.id === "approve" || t.style === "primary") || this.callbacks[0];
2609
+ },
2610
+ rejectCallback() {
2611
+ return this.callbacks.find((t) => t.id === "reject" || t.style === "danger" || t.style === "neutral") || (this.callbacks.length > 1 ? this.callbacks[1] : null);
2612
+ },
2613
+ approveId() {
2614
+ var t;
2615
+ return (t = this.approveCallback) == null ? void 0 : t.id;
2616
+ },
2617
+ rejectId() {
2618
+ var t;
2619
+ return (t = this.rejectCallback) == null ? void 0 : t.id;
2620
+ },
2621
+ approveLabel() {
2622
+ var t;
2623
+ return ((t = this.approveCallback) == null ? void 0 : t.label) || "Autoriser";
2624
+ },
2625
+ rejectLabel() {
2626
+ var t;
2627
+ return ((t = this.rejectCallback) == null ? void 0 : t.label) || "Refuser";
2628
+ }
2629
+ }
2630
+ }, qs = { class: "wm-approval" }, Ws = { class: "wm-approval__head" }, Gs = { class: "wm-approval__icon" }, Ks = { class: "wm-approval__main" }, Ys = { class: "wm-approval__title" }, Js = {
2631
+ key: 0,
2632
+ class: "wm-approval__detail"
2633
+ }, Xs = { class: "wm-approval__actions" };
2634
+ function Qs(t, e, n, a, r, s) {
2635
+ const o = O("AIAvatar");
2636
+ return l(), d("div", qs, [
2637
+ i("div", Ws, [
2638
+ i("div", Gs, [
2639
+ z(o, { size: 24 })
2640
+ ]),
2641
+ i("div", Ks, [
2642
+ i("div", Ys, y(n.action), 1),
2643
+ n.detail ? (l(), d("div", Js, y(n.detail), 1)) : v("", !0)
2644
+ ])
2645
+ ]),
2646
+ i("div", Xs, [
2647
+ s.rejectId ? (l(), d("button", {
2648
+ key: 0,
2649
+ type: "button",
2650
+ class: "wm-approval__btn wm-approval__btn--neutral",
2651
+ onClick: e[0] || (e[0] = (c) => t.$emit("callback", s.rejectId))
2652
+ }, y(s.rejectLabel), 1)) : v("", !0),
2653
+ s.approveId ? (l(), d("button", {
2654
+ key: 1,
2655
+ type: "button",
2656
+ class: "wm-approval__btn wm-approval__btn--primary",
2657
+ onClick: e[1] || (e[1] = (c) => t.$emit("callback", s.approveId))
2658
+ }, y(s.approveLabel), 1)) : v("", !0)
2659
+ ])
2660
+ ]);
2661
+ }
2662
+ const Zs = /* @__PURE__ */ L(Vs, [["render", Qs], ["__scopeId", "data-v-b1be139c"]]);
2663
+ let _e = 0;
2664
+ const er = /* @__PURE__ */ new Set([
2665
+ "text",
2666
+ "textarea",
2667
+ "number",
2668
+ "boolean",
2669
+ "select",
2670
+ "multiselect",
2671
+ "date"
2672
+ ]), tr = {
2673
+ name: "WmFormCard",
2674
+ components: { AIAvatar: X },
2675
+ props: {
2676
+ form: { type: Object, required: !0 },
2677
+ readOnly: { type: Boolean, default: !1 },
2678
+ initialValues: { type: Object, default: null }
2679
+ },
2680
+ emits: ["submit"],
2681
+ data() {
2682
+ return _e += 1, {
2683
+ _uid: _e,
2684
+ values: {},
2685
+ busy: !1,
2686
+ error: ""
2687
+ };
2688
+ },
2689
+ computed: {
2690
+ // Filtre les champs invalides (type inconnu, select/multiselect sans
2691
+ // options) plutôt que de planter le rendu si le LLM a passé un schéma
2692
+ // douteux.
2693
+ normalizedFields() {
2694
+ var e;
2695
+ return (Array.isArray((e = this.form) == null ? void 0 : e.fields) ? this.form.fields : []).filter((n) => !(!(n != null && n.key) || !(n != null && n.label) || !er.has(n == null ? void 0 : n.type) || (n.type === "select" || n.type === "multiselect") && (!Array.isArray(n.options) || n.options.length === 0)));
2696
+ }
2697
+ },
2698
+ created() {
2699
+ this.resetValues();
2700
+ },
2701
+ methods: {
2702
+ resetValues() {
2703
+ const t = this.initialValues && typeof this.initialValues == "object" ? this.initialValues : {}, e = {};
2704
+ for (const n of this.normalizedFields) {
2705
+ if (t[n.key] !== void 0) {
2706
+ e[n.key] = t[n.key];
2707
+ continue;
2708
+ }
2709
+ n.type === "boolean" ? e[n.key] = !1 : n.type === "multiselect" ? e[n.key] = [] : e[n.key] = "";
2710
+ }
2711
+ this.values = e, this.error = "";
2712
+ },
2713
+ toggleMulti(t, e, n) {
2714
+ const a = Array.isArray(this.values[t]) ? this.values[t].slice() : [], r = a.indexOf(e);
2715
+ n && r === -1 ? a.push(e) : !n && r !== -1 && a.splice(r, 1), this.values = { ...this.values, [t]: a };
2716
+ },
2717
+ validate() {
2718
+ for (const t of this.normalizedFields) {
2719
+ if (!t.required) continue;
2720
+ const e = this.values[t.key];
2721
+ if (t.type !== "boolean") {
2722
+ if (t.type === "multiselect") {
2723
+ if (!Array.isArray(e) || e.length === 0) return `« ${t.label} » est requis.`;
2724
+ continue;
2725
+ }
2726
+ if (e == null || e === "") return `« ${t.label} » est requis.`;
2727
+ }
2728
+ }
2729
+ return "";
2730
+ },
2731
+ async onSubmit() {
2732
+ if (this.readOnly || this.busy) return;
2733
+ const t = this.validate();
2734
+ if (t) {
2735
+ this.error = t;
2736
+ return;
2737
+ }
2738
+ this.error = "", this.busy = !0;
2739
+ try {
2740
+ await this.$emit("submit", { values: this.values });
2741
+ } finally {
2742
+ this.busy = !1;
2743
+ }
2744
+ }
2745
+ }
2746
+ }, nr = { class: "wm-form" }, sr = { class: "wm-form__head" }, rr = { class: "wm-form__icon" }, ir = { class: "wm-form__main" }, ar = { class: "wm-form__title" }, or = {
2747
+ key: 0,
2748
+ class: "wm-form__detail"
2749
+ }, lr = ["for"], dr = {
2750
+ key: 0,
2751
+ class: "wm-form__req",
2752
+ "aria-hidden": "true"
2753
+ }, cr = ["id", "onUpdate:modelValue", "placeholder", "required", "disabled"], ur = ["id", "onUpdate:modelValue", "placeholder", "required", "disabled"], hr = ["id", "onUpdate:modelValue", "placeholder", "required", "disabled"], mr = ["id", "onUpdate:modelValue", "required", "disabled"], fr = {
2754
+ key: 4,
2755
+ class: "wm-form__bool"
2756
+ }, _r = ["id", "onUpdate:modelValue", "disabled"], pr = ["id", "onUpdate:modelValue", "required", "disabled"], vr = {
2757
+ value: "",
2758
+ disabled: ""
2759
+ }, gr = ["value"], yr = {
2760
+ key: 6,
2761
+ class: "wm-form__multi"
2762
+ }, wr = ["value", "checked", "disabled", "onChange"], br = {
2763
+ key: 0,
2764
+ class: "wm-form__err"
2765
+ }, kr = ["disabled"], Cr = {
2766
+ key: 0,
2767
+ class: "wm-form__spinner",
2768
+ "aria-hidden": "true"
2769
+ }, Ar = {
2770
+ key: 2,
2771
+ class: "wm-form__doneLbl"
2772
+ };
2773
+ function Sr(t, e, n, a, r, s) {
2774
+ const o = O("AIAvatar");
2775
+ return l(), d("div", nr, [
2776
+ i("div", sr, [
2777
+ i("div", rr, [
2778
+ z(o, { size: 24 })
2779
+ ]),
2780
+ i("div", ir, [
2781
+ i("div", ar, y(n.form.title || "Formulaire"), 1),
2782
+ n.form.description ? (l(), d("div", or, y(n.form.description), 1)) : v("", !0)
2783
+ ])
2784
+ ]),
2785
+ i("form", {
2786
+ class: "wm-form__body",
2787
+ onSubmit: e[0] || (e[0] = J((...c) => s.onSubmit && s.onSubmit(...c), ["prevent"]))
2788
+ }, [
2789
+ (l(!0), d(E, null, F(s.normalizedFields, (c) => (l(), d("div", {
2790
+ key: c.key,
2791
+ class: "wm-form__field"
2792
+ }, [
2793
+ i("label", {
2794
+ for: `wm-f-${r._uid}-${c.key}`,
2795
+ class: "wm-form__label"
2796
+ }, [
2797
+ se(y(c.label), 1),
2798
+ c.required ? (l(), d("span", dr, "*")) : v("", !0)
2799
+ ], 8, lr),
2800
+ c.type === "text" ? q((l(), d("input", {
2801
+ key: 0,
2802
+ id: `wm-f-${r._uid}-${c.key}`,
2803
+ "onUpdate:modelValue": (p) => r.values[c.key] = p,
2804
+ type: "text",
2805
+ class: "wm-form__input",
2806
+ placeholder: c.placeholder || "",
2807
+ required: c.required,
2808
+ disabled: n.readOnly || r.busy
2809
+ }, null, 8, cr)), [
2810
+ [G, r.values[c.key]]
2811
+ ]) : c.type === "textarea" ? q((l(), d("textarea", {
2812
+ key: 1,
2813
+ id: `wm-f-${r._uid}-${c.key}`,
2814
+ "onUpdate:modelValue": (p) => r.values[c.key] = p,
2815
+ class: "wm-form__textarea",
2816
+ rows: "3",
2817
+ placeholder: c.placeholder || "",
2818
+ required: c.required,
2819
+ disabled: n.readOnly || r.busy
2820
+ }, null, 8, ur)), [
2821
+ [G, r.values[c.key]]
2822
+ ]) : c.type === "number" ? q((l(), d("input", {
2823
+ key: 2,
2824
+ id: `wm-f-${r._uid}-${c.key}`,
2825
+ "onUpdate:modelValue": (p) => r.values[c.key] = p,
2826
+ type: "number",
2827
+ class: "wm-form__input",
2828
+ placeholder: c.placeholder || "",
2829
+ required: c.required,
2830
+ disabled: n.readOnly || r.busy
2831
+ }, null, 8, hr)), [
2832
+ [
2833
+ G,
2834
+ r.values[c.key],
2835
+ void 0,
2836
+ { number: !0 }
2837
+ ]
2838
+ ]) : c.type === "date" ? q((l(), d("input", {
2839
+ key: 3,
2840
+ id: `wm-f-${r._uid}-${c.key}`,
2841
+ "onUpdate:modelValue": (p) => r.values[c.key] = p,
2842
+ type: "date",
2843
+ class: "wm-form__input",
2844
+ required: c.required,
2845
+ disabled: n.readOnly || r.busy
2846
+ }, null, 8, mr)), [
2847
+ [G, r.values[c.key]]
2848
+ ]) : c.type === "boolean" ? (l(), d("label", fr, [
2849
+ q(i("input", {
2850
+ id: `wm-f-${r._uid}-${c.key}`,
2851
+ "onUpdate:modelValue": (p) => r.values[c.key] = p,
2852
+ type: "checkbox",
2853
+ disabled: n.readOnly || r.busy
2854
+ }, null, 8, _r), [
2855
+ [Ie, r.values[c.key]]
2856
+ ]),
2857
+ i("span", null, y(c.placeholder || "Oui"), 1)
2858
+ ])) : c.type === "select" ? q((l(), d("select", {
2859
+ key: 5,
2860
+ id: `wm-f-${r._uid}-${c.key}`,
2861
+ "onUpdate:modelValue": (p) => r.values[c.key] = p,
2862
+ class: "wm-form__select",
2863
+ required: c.required,
2864
+ disabled: n.readOnly || r.busy
2865
+ }, [
2866
+ i("option", vr, y(c.placeholder || "Choisir…"), 1),
2867
+ (l(!0), d(E, null, F(c.options, (p) => (l(), d("option", {
2868
+ key: p.value,
2869
+ value: p.value
2870
+ }, y(p.label), 9, gr))), 128))
2871
+ ], 8, pr)), [
2872
+ [Oe, r.values[c.key]]
2873
+ ]) : c.type === "multiselect" ? (l(), d("div", yr, [
2874
+ (l(!0), d(E, null, F(c.options, (p) => (l(), d("label", {
2875
+ key: p.value,
2876
+ class: "wm-form__multiItem"
2877
+ }, [
2878
+ i("input", {
2879
+ type: "checkbox",
2880
+ value: p.value,
2881
+ checked: Array.isArray(r.values[c.key]) && r.values[c.key].includes(p.value),
2882
+ disabled: n.readOnly || r.busy,
2883
+ onChange: (k) => s.toggleMulti(c.key, p.value, k.target.checked)
2884
+ }, null, 40, wr),
2885
+ i("span", null, y(p.label), 1)
2886
+ ]))), 128))
2887
+ ])) : v("", !0)
2888
+ ]))), 128)),
2889
+ r.error ? (l(), d("div", br, y(r.error), 1)) : v("", !0),
2890
+ n.readOnly ? (l(), d("div", Ar, "Réponse envoyée")) : (l(), d("button", {
2891
+ key: 1,
2892
+ type: "submit",
2893
+ class: "wm-form__submit",
2894
+ disabled: r.busy
2895
+ }, [
2896
+ r.busy ? (l(), d("span", Cr)) : v("", !0),
2897
+ i("span", null, y(r.busy ? "Envoi…" : n.form.submit_label || "Envoyer"), 1)
2898
+ ], 8, kr))
2899
+ ], 32)
2900
+ ]);
2901
+ }
2902
+ const xr = /* @__PURE__ */ L(tr, [["render", Sr], ["__scopeId", "data-v-64b40f76"]]), Mr = {
2903
+ name: "WmFeedback",
2904
+ props: {
2905
+ busy: { type: Boolean, default: !1 },
2906
+ done: { type: Boolean, default: !1 }
2907
+ },
2908
+ emits: ["submit"],
2909
+ data() {
2910
+ return {
2911
+ sel: null,
2912
+ options: [
2913
+ { v: 1, e: "😞", l: "Pas bien" },
2914
+ { v: 2, e: "😐", l: "Moyen" },
2915
+ { v: 3, e: "🙂", l: "Bien" },
2916
+ { v: 4, e: "😄", l: "Super" },
2917
+ { v: 5, e: "🤩", l: "Excellent" }
2918
+ ]
2919
+ };
2920
+ },
2921
+ methods: {
2922
+ onSend() {
2923
+ !this.sel || this.busy || this.$emit("submit", { rating: this.sel });
2924
+ }
2925
+ }
2926
+ }, Tr = { class: "wm-fb" }, Ir = { class: "wm-fb__row" }, Or = ["onClick"], Er = { class: "wm-fb__emoji" }, Br = { class: "wm-fb__label" }, Lr = ["disabled"], Rr = {
2927
+ key: 1,
2928
+ class: "wm-fb__done"
2929
+ };
2930
+ function $r(t, e, n, a, r, s) {
2931
+ return l(), d("div", Tr, [
2932
+ n.done ? (l(), d("div", Rr, [...e[3] || (e[3] = [
2933
+ de('<div class="wm-fb__check" data-v-6f45ff3b><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" data-v-6f45ff3b><path d="M20 6L9 17l-5-5" data-v-6f45ff3b></path></svg></div><div class="wm-fb__doneTitle" data-v-6f45ff3b>Merci pour votre retour !</div><div class="wm-fb__doneSub" data-v-6f45ff3b>Votre avis a bien été pris en compte.</div>', 3)
2934
+ ])])) : (l(), d(E, { key: 0 }, [
2935
+ e[1] || (e[1] = i("div", { class: "wm-fb__title" }, "Comment s'est passée cette conversation ?", -1)),
2936
+ e[2] || (e[2] = i("div", { class: "wm-fb__sub" }, "Votre avis nous aide à améliorer l'assistant.", -1)),
2937
+ i("div", Ir, [
2938
+ (l(!0), d(E, null, F(r.options, (o) => (l(), d("button", {
2939
+ key: o.v,
2940
+ type: "button",
2941
+ class: B(["wm-fb__opt", { "is-selected": r.sel === o.v }]),
2942
+ onClick: (c) => r.sel = o.v
2943
+ }, [
2944
+ i("span", Er, y(o.e), 1),
2945
+ i("span", Br, y(o.l), 1)
2946
+ ], 10, Or))), 128))
2947
+ ]),
2948
+ i("button", {
2949
+ type: "button",
2950
+ class: "wm-fb__send",
2951
+ disabled: !r.sel || n.busy,
2952
+ onClick: e[0] || (e[0] = (...o) => s.onSend && s.onSend(...o))
2953
+ }, y(n.busy ? "Envoi…" : "Envoyer mon avis"), 9, Lr)
2954
+ ], 64))
2955
+ ]);
2956
+ }
2957
+ const Fr = /* @__PURE__ */ L(Mr, [["render", $r], ["__scopeId", "data-v-6f45ff3b"]]);
2958
+ function Ur(t) {
2959
+ const e = new Date(t);
2960
+ return e.setHours(0, 0, 0, 0), e;
2961
+ }
2962
+ const Dr = {
2963
+ name: "WmHistoryDrawer",
2964
+ props: {
2965
+ conversations: { type: Array, default: () => [] },
2966
+ activeId: { type: [String, Number], default: null }
2967
+ },
2968
+ emits: ["close", "new", "pick"],
2969
+ data() {
2970
+ return { query: "" };
2971
+ },
2972
+ computed: {
2973
+ normalized() {
2974
+ return (Array.isArray(this.conversations) ? this.conversations.slice() : []).map((e) => {
2975
+ var r;
2976
+ const n = e.last_message_at || e.updated_at || e.created_at || null, a = n ? Date.parse(n) : 0;
2977
+ return {
2978
+ id: e.id,
2979
+ raw: e,
2980
+ title: e.name || "Nouvelle conversation",
2981
+ // `_preview` / `_unread` are computed by the parent
2982
+ // from the cached messages + local read state.
2983
+ preview: e._preview || e.last_message_preview || e.preview || ((r = e.metadata) == null ? void 0 : r.last_preview) || "",
2984
+ unread: !!e._unread,
2985
+ stamp: a
2986
+ };
2987
+ }).sort((e, n) => n.stamp - e.stamp);
2988
+ },
2989
+ filtered() {
2990
+ const t = this.query.trim().toLowerCase();
2991
+ return t ? this.normalized.filter(
2992
+ (e) => (e.title || "").toLowerCase().includes(t) || (e.preview || "").toLowerCase().includes(t)
2993
+ ) : this.normalized;
2994
+ },
2995
+ groups() {
2996
+ const e = Ur(/* @__PURE__ */ new Date()).getTime(), n = e - 6 * 864e5, a = [], r = [], s = [];
2997
+ for (const o of this.filtered) {
2998
+ if (!o.stamp) {
2999
+ s.push(o);
3000
+ continue;
3001
+ }
3002
+ o.stamp >= e ? a.push(o) : o.stamp >= n ? r.push(o) : s.push(o);
3003
+ }
3004
+ return [
3005
+ { key: "today", label: "Aujourd'hui", items: a },
3006
+ { key: "week", label: "Cette semaine", items: r },
3007
+ { key: "older", label: "Plus ancien", items: s }
3008
+ ];
3009
+ },
3010
+ hasAny() {
3011
+ return this.filtered.length > 0;
3012
+ }
3013
+ }
3014
+ }, jr = { class: "wm-hd" }, Nr = {
3015
+ class: "wm-hd__panel",
3016
+ role: "dialog",
3017
+ "aria-label": "Vos discussions"
3018
+ }, Pr = { class: "wm-hd__head" }, Hr = { class: "wm-hd__top" }, zr = { class: "wm-hd__search" }, Vr = { class: "wm-hd__list" }, qr = {
3019
+ key: 0,
3020
+ class: "wm-hd__group"
3021
+ }, Wr = { class: "wm-hd__groupLabel" }, Gr = { class: "wm-hd__rows" }, Kr = ["onClick"], Yr = { class: "wm-hd__rowIcon" }, Jr = {
3022
+ key: 0,
3023
+ class: "wm-hd__rowDot",
3024
+ "aria-label": "Message non lu"
3025
+ }, Xr = { class: "wm-hd__rowBody" }, Qr = { class: "wm-hd__rowTop" }, Zr = { class: "wm-hd__rowTitle" }, ei = { class: "wm-hd__rowPreview" }, ti = {
3026
+ key: 0,
3027
+ class: "wm-hd__empty"
3028
+ };
3029
+ function ni(t, e, n, a, r, s) {
3030
+ return l(), d("div", jr, [
3031
+ i("div", {
3032
+ class: "wm-hd__scrim",
3033
+ onClick: e[0] || (e[0] = (o) => t.$emit("close"))
3034
+ }),
3035
+ i("aside", Nr, [
3036
+ i("div", Pr, [
3037
+ e[5] || (e[5] = i("div", { class: "wm-hd__heading" }, [
3038
+ i("div", { class: "wm-hd__title" }, "Vos discussions"),
3039
+ i("div", { class: "wm-hd__sub" }, "Reprenez là où vous en étiez.")
3040
+ ], -1)),
3041
+ i("button", {
3042
+ type: "button",
3043
+ class: "wm-hd__close",
3044
+ "aria-label": "Fermer",
3045
+ onClick: e[1] || (e[1] = (o) => t.$emit("close"))
3046
+ }, [...e[4] || (e[4] = [
3047
+ i("svg", {
3048
+ width: "12",
3049
+ height: "12",
3050
+ viewBox: "0 0 24 24",
3051
+ fill: "none",
3052
+ stroke: "currentColor",
3053
+ "stroke-width": "2",
3054
+ "stroke-linecap": "round",
3055
+ "stroke-linejoin": "round",
3056
+ "aria-hidden": "true"
3057
+ }, [
3058
+ i("path", { d: "M18 6L6 18M6 6l12 12" })
3059
+ ], -1)
3060
+ ])])
3061
+ ]),
3062
+ i("div", Hr, [
3063
+ i("button", {
3064
+ type: "button",
3065
+ class: "wm-hd__new",
3066
+ onClick: e[2] || (e[2] = (o) => t.$emit("new"))
3067
+ }, [...e[6] || (e[6] = [
3068
+ i("span", { class: "wm-hd__newIcon" }, [
3069
+ i("svg", {
3070
+ width: "12",
3071
+ height: "12",
3072
+ viewBox: "0 0 24 24",
3073
+ fill: "none",
3074
+ stroke: "currentColor",
3075
+ "stroke-width": "2.2",
3076
+ "stroke-linecap": "round",
3077
+ "stroke-linejoin": "round",
3078
+ "aria-hidden": "true"
3079
+ }, [
3080
+ i("path", { d: "M12 5v14M5 12h14" })
3081
+ ])
3082
+ ], -1),
3083
+ i("span", null, "Démarrer un nouveau fil", -1)
3084
+ ])]),
3085
+ i("div", zr, [
3086
+ e[7] || (e[7] = i("span", { class: "wm-hd__searchIcon" }, [
3087
+ i("svg", {
3088
+ width: "12",
3089
+ height: "12",
3090
+ viewBox: "0 0 24 24",
3091
+ fill: "none",
3092
+ stroke: "currentColor",
3093
+ "stroke-width": "1.8",
3094
+ "stroke-linecap": "round",
3095
+ "stroke-linejoin": "round",
3096
+ "aria-hidden": "true"
3097
+ }, [
3098
+ i("path", { d: "M21 21l-4.35-4.35M11 17a6 6 0 100-12 6 6 0 000 12z" })
3099
+ ])
3100
+ ], -1)),
3101
+ q(i("input", {
3102
+ "onUpdate:modelValue": e[3] || (e[3] = (o) => r.query = o),
3103
+ type: "text",
3104
+ placeholder: "Rechercher dans vos messages",
3105
+ "aria-label": "Rechercher dans vos messages"
3106
+ }, null, 512), [
3107
+ [G, r.query]
3108
+ ])
3109
+ ])
3110
+ ]),
3111
+ i("div", Vr, [
3112
+ (l(!0), d(E, null, F(s.groups, (o) => (l(), d(E, {
3113
+ key: o.key
3114
+ }, [
3115
+ o.items.length ? (l(), d("div", qr, [
3116
+ i("div", Wr, y(o.label), 1),
3117
+ i("div", Gr, [
3118
+ (l(!0), d(E, null, F(o.items, (c) => (l(), d("button", {
3119
+ key: c.id,
3120
+ type: "button",
3121
+ class: B(["wm-hd__row", {
3122
+ "wm-hd__row--active": c.id === n.activeId,
3123
+ "wm-hd__row--unread": c.unread
3124
+ }]),
3125
+ onClick: (p) => t.$emit("pick", c)
3126
+ }, [
3127
+ i("div", Yr, [
3128
+ e[8] || (e[8] = i("svg", {
3129
+ width: "11",
3130
+ height: "11",
3131
+ viewBox: "0 0 24 24",
3132
+ fill: "none",
3133
+ "aria-hidden": "true"
3134
+ }, [
3135
+ i("path", {
3136
+ d: "M12 2 L13.5 9 L20 10.5 L13.5 12 L12 19 L10.5 12 L4 10.5 L10.5 9 Z",
3137
+ fill: "white",
3138
+ opacity: "0.92"
3139
+ })
3140
+ ], -1)),
3141
+ c.unread ? (l(), d("span", Jr)) : v("", !0)
3142
+ ]),
3143
+ i("div", Xr, [
3144
+ i("div", Qr, [
3145
+ i("span", Zr, y(c.title), 1)
3146
+ ]),
3147
+ i("div", ei, y(c.preview || "Aucun message"), 1)
3148
+ ])
3149
+ ], 10, Kr))), 128))
3150
+ ])
3151
+ ])) : v("", !0)
3152
+ ], 64))), 128)),
3153
+ s.hasAny ? v("", !0) : (l(), d("div", ti, " Aucun fil pour le moment. "))
3154
+ ])
3155
+ ])
3156
+ ]);
3157
+ }
3158
+ const si = /* @__PURE__ */ L(Dr, [["render", ni], ["__scopeId", "data-v-1259e822"]]), ri = {
3159
+ name: "WmMoreMenu",
3160
+ props: {
3161
+ canRename: { type: Boolean, default: !0 },
3162
+ canClear: { type: Boolean, default: !0 },
3163
+ canExport: { type: Boolean, default: !0 },
3164
+ notifEnabled: { type: Boolean, default: !0 },
3165
+ statusUrl: { type: String, default: "" },
3166
+ helpUrl: { type: String, default: "" }
3167
+ },
3168
+ emits: ["close", "action", "notif-toggle"],
3169
+ data() {
3170
+ return { notifOn: this.notifEnabled };
3171
+ },
3172
+ watch: {
3173
+ notifEnabled(t) {
3174
+ this.notifOn = !!t;
3175
+ }
3176
+ },
3177
+ methods: {
3178
+ emit(t) {
3179
+ this.$emit("action", t);
3180
+ },
3181
+ toggleNotif() {
3182
+ this.notifOn = !this.notifOn, this.$emit("notif-toggle", this.notifOn);
3183
+ }
3184
+ }
3185
+ }, ii = { class: "wm-mm" }, ai = {
3186
+ class: "wm-mm__pop",
3187
+ role: "menu"
3188
+ }, oi = { class: "wm-mm__section" }, li = { class: "wm-mm__section" }, di = { class: "wm-mm__section" };
3189
+ function ci(t, e, n, a, r, s) {
3190
+ return l(), d("div", ii, [
3191
+ i("div", {
3192
+ class: "wm-mm__scrim",
3193
+ onClick: e[0] || (e[0] = (o) => t.$emit("close"))
3194
+ }),
3195
+ i("div", ai, [
3196
+ i("div", oi, [
3197
+ i("button", {
3198
+ type: "button",
3199
+ class: "wm-mm__item",
3200
+ onClick: e[1] || (e[1] = (o) => s.emit("history"))
3201
+ }, [...e[8] || (e[8] = [
3202
+ i("span", { class: "wm-mm__icon" }, [
3203
+ i("svg", {
3204
+ width: "12",
3205
+ height: "12",
3206
+ viewBox: "0 0 24 24",
3207
+ fill: "none",
3208
+ stroke: "currentColor",
3209
+ "stroke-width": "1.8",
3210
+ "stroke-linecap": "round",
3211
+ "stroke-linejoin": "round",
3212
+ "aria-hidden": "true"
3213
+ }, [
3214
+ i("path", { d: "M3 3v5h5M3.05 13A9 9 0 1 0 6 5.3L3 8M12 7v5l4 2" })
3215
+ ])
3216
+ ], -1),
3217
+ i("span", { class: "wm-mm__label" }, "Historique des discussions", -1)
3218
+ ])]),
3219
+ n.canRename ? (l(), d("button", {
3220
+ key: 0,
3221
+ type: "button",
3222
+ class: "wm-mm__item",
3223
+ onClick: e[2] || (e[2] = (o) => s.emit("rename"))
3224
+ }, [...e[9] || (e[9] = [
3225
+ de('<span class="wm-mm__icon" data-v-c1bb81d2><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" data-v-c1bb81d2><path d="M12 20h9" data-v-c1bb81d2></path><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z" data-v-c1bb81d2></path></svg></span><span class="wm-mm__label" data-v-c1bb81d2>Renommer le fil</span>', 2)
3226
+ ])])) : v("", !0),
3227
+ n.canClear ? (l(), d("button", {
3228
+ key: 1,
3229
+ type: "button",
3230
+ class: "wm-mm__item",
3231
+ onClick: e[3] || (e[3] = (o) => s.emit("clear"))
3232
+ }, [...e[10] || (e[10] = [
3233
+ i("span", { class: "wm-mm__icon" }, [
3234
+ i("svg", {
3235
+ width: "12",
3236
+ height: "12",
3237
+ viewBox: "0 0 24 24",
3238
+ fill: "none",
3239
+ stroke: "currentColor",
3240
+ "stroke-width": "1.8",
3241
+ "stroke-linecap": "round",
3242
+ "stroke-linejoin": "round",
3243
+ "aria-hidden": "true"
3244
+ }, [
3245
+ i("path", { d: "M4 7h16M10 11v6M14 11v6M5 7l1 13a2 2 0 002 2h8a2 2 0 002-2l1-13M9 7V4a1 1 0 011-1h4a1 1 0 011 1v3" })
3246
+ ])
3247
+ ], -1),
3248
+ i("span", { class: "wm-mm__label" }, "Effacer ce fil", -1)
3249
+ ])])) : v("", !0),
3250
+ n.canExport ? (l(), d("button", {
3251
+ key: 2,
3252
+ type: "button",
3253
+ class: "wm-mm__item",
3254
+ onClick: e[4] || (e[4] = (o) => s.emit("export"))
3255
+ }, [...e[11] || (e[11] = [
3256
+ de('<span class="wm-mm__icon" data-v-c1bb81d2><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" data-v-c1bb81d2><path d="M4 12v8a2 2 0 002 2h12a2 2 0 002-2v-8M16 6l-4-4-4 4M12 2v13" data-v-c1bb81d2></path></svg></span><span class="wm-mm__label" data-v-c1bb81d2>Exporter la transcription</span><span class="wm-mm__hint" data-v-c1bb81d2>.txt</span>', 3)
3257
+ ])])) : v("", !0)
3258
+ ]),
3259
+ e[17] || (e[17] = i("div", { class: "wm-mm__sep" }, null, -1)),
3260
+ i("div", li, [
3261
+ i("button", {
3262
+ type: "button",
3263
+ class: "wm-mm__item",
3264
+ onClick: e[5] || (e[5] = (...o) => s.toggleNotif && s.toggleNotif(...o))
3265
+ }, [
3266
+ e[13] || (e[13] = i("span", { class: "wm-mm__icon" }, [
3267
+ i("svg", {
3268
+ width: "12",
3269
+ height: "12",
3270
+ viewBox: "0 0 24 24",
3271
+ fill: "none",
3272
+ stroke: "currentColor",
3273
+ "stroke-width": "1.8",
3274
+ "stroke-linecap": "round",
3275
+ "stroke-linejoin": "round",
3276
+ "aria-hidden": "true"
3277
+ }, [
3278
+ i("path", { d: "M15 17h5l-1.4-1.4A2 2 0 0118 14.2V11a6 6 0 10-12 0v3.2c0 .5-.2 1-.6 1.4L4 17h5m6 0a3 3 0 11-6 0" })
3279
+ ])
3280
+ ], -1)),
3281
+ e[14] || (e[14] = i("span", { class: "wm-mm__label" }, "Notifications", -1)),
3282
+ i("span", {
3283
+ class: B(["wm-mm__toggle", { "wm-mm__toggle--on": r.notifOn }])
3284
+ }, [...e[12] || (e[12] = [
3285
+ i("span", { class: "wm-mm__knob" }, null, -1)
3286
+ ])], 2)
3287
+ ])
3288
+ ]),
3289
+ e[18] || (e[18] = i("div", { class: "wm-mm__sep" }, null, -1)),
3290
+ i("div", di, [
3291
+ n.statusUrl ? (l(), d("button", {
3292
+ key: 0,
3293
+ type: "button",
3294
+ class: "wm-mm__item",
3295
+ onClick: e[6] || (e[6] = (o) => s.emit("status"))
3296
+ }, [...e[15] || (e[15] = [
3297
+ i("span", { class: "wm-mm__icon" }, [
3298
+ i("svg", {
3299
+ width: "12",
3300
+ height: "12",
3301
+ viewBox: "0 0 24 24",
3302
+ fill: "none",
3303
+ stroke: "currentColor",
3304
+ "stroke-width": "1.8",
3305
+ "stroke-linecap": "round",
3306
+ "stroke-linejoin": "round",
3307
+ "aria-hidden": "true"
3308
+ }, [
3309
+ i("path", { d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zM10 6v4l3 2" })
3310
+ ])
3311
+ ], -1),
3312
+ i("span", { class: "wm-mm__label" }, "Statut des services", -1)
3313
+ ])])) : v("", !0),
3314
+ n.helpUrl ? (l(), d("button", {
3315
+ key: 1,
3316
+ type: "button",
3317
+ class: "wm-mm__item",
3318
+ onClick: e[7] || (e[7] = (o) => s.emit("help"))
3319
+ }, [...e[16] || (e[16] = [
3320
+ i("span", { class: "wm-mm__icon" }, [
3321
+ i("svg", {
3322
+ width: "12",
3323
+ height: "12",
3324
+ viewBox: "0 0 24 24",
3325
+ fill: "none",
3326
+ stroke: "currentColor",
3327
+ "stroke-width": "1.8",
3328
+ "stroke-linecap": "round",
3329
+ "stroke-linejoin": "round",
3330
+ "aria-hidden": "true"
3331
+ }, [
3332
+ i("path", { d: "M9 11a3 3 0 116 0c0 2-3 2-3 5M12 19h.01M12 22a10 10 0 110-20 10 10 0 010 20z" })
3333
+ ])
3334
+ ], -1),
3335
+ i("span", { class: "wm-mm__label" }, "Centre d'aide", -1)
3336
+ ])])) : v("", !0)
3337
+ ])
3338
+ ])
3339
+ ]);
3340
+ }
3341
+ const ui = /* @__PURE__ */ L(ri, [["render", ci], ["__scopeId", "data-v-c1bb81d2"]]), pe = "ww-messenger-tokens";
3342
+ function le(t) {
3343
+ var n;
3344
+ const e = (n = t == null ? void 0 : t.author) == null ? void 0 : n.type;
3345
+ return e === "agent_ia" || e === "agent_human";
3346
+ }
3347
+ function hi(t, e) {
3348
+ if (!t || !e) return "";
3349
+ const n = Array.isArray(t.fields) ? t.fields : [], a = [];
3350
+ for (const r of n) {
3351
+ if (!(r != null && r.key) || !(r != null && r.label)) continue;
3352
+ const s = e[r.key];
3353
+ if (s == null || s === "") continue;
3354
+ let o;
3355
+ if (Array.isArray(s)) {
3356
+ if (o = s.map((c) => ne(r, String(c))).join(", "), !o) continue;
3357
+ } else typeof s == "boolean" ? o = s ? "Oui" : "Non" : o = ne(r, String(s));
3358
+ a.push(`${r.label} :
3359
+ ${o}`);
3360
+ }
3361
+ return a.join(`
3362
+
3363
+ `);
3364
+ }
3365
+ function ne(t, e) {
3366
+ if (!Array.isArray(t == null ? void 0 : t.options)) return e;
3367
+ const n = t.options.find((a) => (a == null ? void 0 : a.value) === e);
3368
+ return (n == null ? void 0 : n.label) || e;
3369
+ }
3370
+ function mi(t, e) {
3371
+ const n = [], a = Array.isArray(t == null ? void 0 : t.fields) ? t.fields : [];
3372
+ for (const r of a) {
3373
+ if (!(r != null && r.key) || !(r != null && r.label)) continue;
3374
+ const s = e == null ? void 0 : e[r.key];
3375
+ if (s == null || s === "") continue;
3376
+ let o;
3377
+ if (Array.isArray(s)) {
3378
+ if (o = s.map((p) => ne(r, String(p))).join(", "), !o) continue;
3379
+ } else typeof s == "boolean" ? o = s ? "Oui" : "Non" : o = ne(r, String(s));
3380
+ const c = r.type === "textarea" || typeof o == "string" && (o.length > 60 || o.includes(`
3381
+ `));
3382
+ n.push({ label: r.label, value: o, multiline: c });
3383
+ }
3384
+ return {
3385
+ kind: "form_response",
3386
+ data: {
3387
+ title: (t == null ? void 0 : t.title) || "Formulaire",
3388
+ fields: n
3389
+ }
3390
+ };
3391
+ }
3392
+ const fi = 450, _i = 50, pi = 900, vi = 12e3, gi = 300, yi = {
3393
+ name: "Messenger",
3394
+ components: {
3395
+ Launcher: We,
3396
+ Header: vt,
3397
+ Onboarding: jt,
3398
+ MessageList: ys,
3399
+ Composer: js,
3400
+ SuggestionChips: zs,
3401
+ ApprovalCard: Zs,
3402
+ FormCard: xr,
3403
+ Feedback: Fr,
3404
+ HistoryDrawer: si,
3405
+ MoreMenu: ui
3406
+ },
3407
+ // Make signAttachment available to deep children (AttachmentPreview)
3408
+ // without prop drilling. The store may not exist yet at provide-time
3409
+ // (it's created in mounted), so we go through a stable wrapper that
3410
+ // looks it up lazily.
3411
+ provide() {
3412
+ return {
3413
+ signAttachmentFn: (t) => {
3414
+ var e, n;
3415
+ return ((n = (e = this.store) == null ? void 0 : e.signAttachment) == null ? void 0 : n.call(e, t)) || null;
3416
+ }
3417
+ };
3418
+ },
3419
+ props: {
3420
+ baseUrl: { type: String, default: "" },
3421
+ widgetId: { type: String, default: "" },
3422
+ userId: { type: String, default: "" },
3423
+ userHash: { type: String, default: "" },
3424
+ // 'floating' (default) | 'sheet' | 'embedded'
3425
+ displayMode: { type: String, default: "floating" },
3426
+ // Replaces the old WeWeb `wwEditorState` editor-detection prop.
3427
+ devtools: { type: Boolean, default: !1 }
3428
+ },
3429
+ data() {
3430
+ return {
3431
+ transport: null,
3432
+ store: null,
3433
+ isOpen: !1,
3434
+ draft: "",
3435
+ busy: !1,
3436
+ bootError: null,
3437
+ pendingAttachments: [],
3438
+ feedbackBusy: !1,
3439
+ feedbackDone: !1,
3440
+ // Reveal pacing — agent (ai/human) messages don't pop in the
3441
+ // moment the server emits them; they're queued so consecutive
3442
+ // bubbles are spaced apart, mimicking a human writing several
3443
+ // messages in a row. Map: messageId → ms timestamp once shown.
3444
+ revealedAt: {},
3445
+ nextRevealAt: 0,
3446
+ revealTimers: [],
3447
+ // Set when the user opens the conversation (or when the panel
3448
+ // auto-opens in embedded mode). Any message stamped strictly
3449
+ // before this is treated as history and revealed instantly —
3450
+ // pacing only applies to messages that arrive live.
3451
+ convOpenedAt: 0,
3452
+ // Overlays anchored to the header. Only one open at a time.
3453
+ historyOpen: !1,
3454
+ moreOpen: !1,
3455
+ // When set, takes precedence over the latest-conversation
3456
+ // selection so the user can browse another thread from the
3457
+ // history drawer without losing live updates on the others.
3458
+ activeConvId: null,
3459
+ // When true, force the onboarding screen even if conversations
3460
+ // exist (e.g. user tapped the "back to home" header button).
3461
+ showOnboarding: !1,
3462
+ // Local UI preference; mirrors the toggle in MoreMenu.
3463
+ notifEnabled: !0,
3464
+ // True once the user has dismissed (×) the closed-launcher
3465
+ // étiquette for the current unread batch. Reset automatically
3466
+ // when a newer unread message arrives (see latestUnread watch).
3467
+ labelDismissed: !1,
3468
+ // conversation_id → ISO timestamp of the newest message the user
3469
+ // has seen for that thread. Persisted to localStorage so the
3470
+ // unread dot in the history drawer survives reloads. The server
3471
+ // has no per-customer read model, so this is purely client-side.
3472
+ readState: {},
3473
+ // Live-measured height of the floating overlay (chips / approval
3474
+ // / feedback). Reserved as bottom padding on the MessageList so
3475
+ // the latest message scrolls above the overlay instead of
3476
+ // landing behind it.
3477
+ floatHeight: 0,
3478
+ floatRO: null,
3479
+ // ── Approval card reveal pacing ────────────────────────────────
3480
+ // Goal: the approval card must only appear once the conversation
3481
+ // has visibly "settled" — last bubble done revealing, no new
3482
+ // stream in progress. The server may deliver the `action` and
3483
+ // its explanatory AI bubble in either order, and the bubble
3484
+ // itself goes through the typing-paced reveal queue, so any
3485
+ // logic that just gates on the *current* streamingActive will
3486
+ // either show the card too early (before the explanation
3487
+ // arrives) or flicker (show → message arrives → hide → show).
3488
+ //
3489
+ // The settle approach: every time streamingActive flips (in
3490
+ // either direction) while an approval is pending, bump a short
3491
+ // debounce timer. The card only reveals after that timer has
3492
+ // expired without further changes — i.e. after a quiet beat
3493
+ // following the very last reveal in the burst.
3494
+ //
3495
+ // Once the card has revealed for a given pending action it
3496
+ // *latches* (`approvalLatchId`): a late follow-up bubble in the
3497
+ // same burst no longer re-hides it. Without the latch the user
3498
+ // saw the documented show → hide → show flicker when the
3499
+ // explanation bubble's SSE arrived after the settle window had
3500
+ // already elapsed.
3501
+ approvalSettled: !0,
3502
+ approvalSettleTimer: null,
3503
+ // message id of the pending action the card is currently locked
3504
+ // open for. Cleared whenever the approval clears / conv switches.
3505
+ approvalLatchId: null
3506
+ };
3507
+ },
3508
+ computed: {
3509
+ isEditor() {
3510
+ return !!this.devtools;
3511
+ },
3512
+ isEmbedded() {
3513
+ return this.displayMode === "embedded";
3514
+ },
3515
+ error() {
3516
+ var t;
3517
+ return this.bootError || ((t = this.s) == null ? void 0 : t.error) || null;
3518
+ },
3519
+ canBoot() {
3520
+ return !!(this.baseUrl && this.widgetId && this.userId && this.userHash);
3521
+ },
3522
+ s() {
3523
+ var t;
3524
+ return ((t = this.store) == null ? void 0 : t.state) || null;
3525
+ },
3526
+ ready() {
3527
+ var t;
3528
+ return !!((t = this.s) != null && t.ready);
3529
+ },
3530
+ allConversations() {
3531
+ var e;
3532
+ const t = (e = this.s) == null ? void 0 : e.conversations;
3533
+ return Array.isArray(t) ? t : [];
3534
+ },
3535
+ // Conversations enriched for the history drawer: a one-line preview
3536
+ // of the last message and an unread flag (last activity newer than
3537
+ // what the user has seen). The server exposes no preview/read fields,
3538
+ // so both are derived client-side from the cached messages + the
3539
+ // local `readState`.
3540
+ drawerConversations() {
3541
+ var n;
3542
+ const t = this.readState, e = ((n = this.s) == null ? void 0 : n.messagesByConv) || {};
3543
+ return this.allConversations.map((a) => {
3544
+ const r = e[a.id] || [], s = this.convLastActivity(a, r), o = t[a.id] || "", c = this.lastMessageAuthorType(r), p = !!s && c !== "user" && (!o || s > o);
3545
+ return { ...a, _preview: this.convPreview(a, r), _unread: p };
3546
+ });
3547
+ },
3548
+ // How many threads have an unseen agent/human message. Drives the
3549
+ // pastille on the closed launcher.
3550
+ unreadCount() {
3551
+ return this.drawerConversations.filter((t) => t._unread).length;
3552
+ },
3553
+ // Unread threads, freshest first, trimmed to a handful — surfaced on
3554
+ // the onboarding/home screen so the user can jump straight back in.
3555
+ unreadThreads() {
3556
+ var e;
3557
+ const t = ((e = this.s) == null ? void 0 : e.messagesByConv) || {};
3558
+ return this.drawerConversations.filter((n) => n._unread).map((n) => ({
3559
+ id: n.id,
3560
+ title: n.name || "Nouvelle conversation",
3561
+ preview: n._preview || "Nouveau message",
3562
+ _ts: this.convLastActivity(n, t[n.id] || [])
3563
+ })).sort((n, a) => n._ts < a._ts ? 1 : n._ts > a._ts ? -1 : 0).slice(0, 3);
3564
+ },
3565
+ // The unread thread with the freshest activity — what the étiquette
3566
+ // previews. `{ convId, preview, ts }` or null when nothing's unread.
3567
+ latestUnread() {
3568
+ var n;
3569
+ const t = ((n = this.s) == null ? void 0 : n.messagesByConv) || {};
3570
+ let e = null;
3571
+ for (const a of this.drawerConversations) {
3572
+ if (!a._unread) continue;
3573
+ const r = this.convLastActivity(a, t[a.id] || []);
3574
+ (!e || r > e.ts) && (e = { convId: a.id, preview: a._preview, ts: r });
3575
+ }
3576
+ return e;
3577
+ },
3578
+ // Text shown in the closed-launcher étiquette: the last unread
3579
+ // message, only while the panel is closed and not dismissed. Empty
3580
+ // string => plain launcher.
3581
+ launcherPeek() {
3582
+ if (this.isOpen || this.isEmbedded || this.labelDismissed) return "";
3583
+ const t = this.latestUnread;
3584
+ return t ? t.preview || "Vous avez un nouveau message" : "";
3585
+ },
3586
+ currentConv() {
3587
+ if (this.showOnboarding) return null;
3588
+ const t = this.allConversations;
3589
+ if (!t.length) return null;
3590
+ if (this.activeConvId != null) {
3591
+ const e = t.find((n) => n.id === this.activeConvId);
3592
+ if (e) return e;
3593
+ }
3594
+ return t[0] || null;
3595
+ },
3596
+ canBack() {
3597
+ return !!this.currentConv;
3598
+ },
3599
+ statusUrl() {
3600
+ var n;
3601
+ const t = (n = this.widget) == null ? void 0 : n.quick_links;
3602
+ if (!Array.isArray(t)) return "";
3603
+ const e = t.find((a) => a.icon === "status" && a.url);
3604
+ return (e == null ? void 0 : e.url) || "";
3605
+ },
3606
+ helpUrl() {
3607
+ var n;
3608
+ const t = (n = this.widget) == null ? void 0 : n.quick_links;
3609
+ if (!Array.isArray(t)) return "";
3610
+ const e = t.find((a) => (a.icon === "chat" || a.icon === "help") && a.url);
3611
+ return (e == null ? void 0 : e.url) || "";
3612
+ },
3613
+ widget() {
3614
+ var t, e;
3615
+ return ((e = (t = this.s) == null ? void 0 : t.config) == null ? void 0 : e.widget) || null;
3616
+ },
3617
+ widgetWelcomeMessage() {
3618
+ var t;
3619
+ return ((t = this.widget) == null ? void 0 : t.welcome_message) || "";
3620
+ },
3621
+ agentName() {
3622
+ var t, e, n;
3623
+ return ((n = (e = (t = this.s) == null ? void 0 : t.config) == null ? void 0 : e.agent) == null ? void 0 : n.name) || "";
3624
+ },
3625
+ agentAvatarUrl() {
3626
+ var t, e, n;
3627
+ return ((n = (e = (t = this.s) == null ? void 0 : t.config) == null ? void 0 : e.agent) == null ? void 0 : n.avatar_url) || null;
3628
+ },
3629
+ quickLinks() {
3630
+ var e;
3631
+ const t = (e = this.widget) == null ? void 0 : e.quick_links;
3632
+ return Array.isArray(t) ? t : [];
3633
+ },
3634
+ teamMembers() {
3635
+ var e;
3636
+ const t = (e = this.widget) == null ? void 0 : e.team_members;
3637
+ return Array.isArray(t) ? t : [];
3638
+ },
3639
+ responseLabel() {
3640
+ var t;
3641
+ return ((t = this.widget) == null ? void 0 : t.response_time_label) || "";
3642
+ },
3643
+ humanMessageAuthor() {
3644
+ var e, n, a;
3645
+ if (!this.currentConv) return null;
3646
+ const t = ((e = this.s.messagesByConv) == null ? void 0 : e[this.currentConv.id]) || [];
3647
+ for (let r = t.length - 1; r >= 0; r--)
3648
+ if (((a = (n = t[r]) == null ? void 0 : n.author) == null ? void 0 : a.type) === "agent_human") return t[r].author;
3649
+ return null;
3650
+ },
3651
+ humanAgentName() {
3652
+ var t;
3653
+ return ((t = this.humanMessageAuthor) == null ? void 0 : t.name) || "";
3654
+ },
3655
+ humanAgentAvatarUrl() {
3656
+ var t;
3657
+ return ((t = this.humanMessageAuthor) == null ? void 0 : t.avatar_url) || null;
3658
+ },
3659
+ // True only once a human has actually posted — drives the avatar swap
3660
+ // in the header so we don't show a blank initial during the
3661
+ // "waiting for human" transitional phase.
3662
+ isEscalated() {
3663
+ return !!this.humanMessageAuthor;
3664
+ },
3665
+ isWaitingHuman() {
3666
+ var e;
3667
+ const t = (e = this.currentConv) == null ? void 0 : e.status;
3668
+ return t === "waiting" || t === "handled";
3669
+ },
3670
+ headerTitle() {
3671
+ var t, e;
3672
+ return ((t = this.currentConv) == null ? void 0 : t.name) || ((e = this.widget) == null ? void 0 : e.name) || "Nouvelle conversation";
3673
+ },
3674
+ composerPlaceholder() {
3675
+ return this.isEscalated && this.humanAgentName ? `Répondre à ${this.humanAgentName.split(" ")[0]}…` : this.isWaitingHuman ? "Un agent prend le relais…" : "Écrivez votre message…";
3676
+ },
3677
+ displayedMessages() {
3678
+ const t = this.currentConv;
3679
+ if (!t) return [];
3680
+ const e = (r) => {
3681
+ var s, o;
3682
+ return (r == null ? void 0 : r.type) === "action" || (r == null ? void 0 : r.type) === "system" || ((s = r == null ? void 0 : r.payload) == null ? void 0 : s.type) === "system" || Array.isArray((o = r == null ? void 0 : r.payload) == null ? void 0 : o.attachments) && r.payload.attachments.length ? !0 : typeof (r == null ? void 0 : r.text_md) == "string" && r.text_md.trim().length > 0;
3683
+ }, n = (r) => {
3684
+ var s;
3685
+ return (r == null ? void 0 : r.type) === "action" && ((s = r == null ? void 0 : r.payload) == null ? void 0 : s.state) === "pending";
3686
+ }, a = this.revealedAt;
3687
+ return (this.s.messagesByConv[t.id] || []).filter((r) => !n(r)).filter((r) => le(r) ? a[r.id] > 0 : !0).filter(e);
3688
+ },
3689
+ // True whenever we should show the "typing" indicator at the bottom
3690
+ // of the list: either the LLM is actively streaming tokens, or one
3691
+ // or more agent messages are queued and waiting for their reveal
3692
+ // slot to fire.
3693
+ streamingActive() {
3694
+ var n, a, r;
3695
+ const t = this.currentConv;
3696
+ return t ? Object.keys(((n = this.s) == null ? void 0 : n.streamingByMsgId) || {}).length > 0 ? !0 : this.actionInFlight ? !1 : (((r = (a = this.s) == null ? void 0 : a.messagesByConv) == null ? void 0 : r[t.id]) || []).some((s) => le(s) && !(this.revealedAt[s.id] > 0)) : !1;
3697
+ },
3698
+ // Internal: the raw persisted list for the current conversation. We
3699
+ // watch this to detect new agent messages that need to be paced.
3700
+ currentConvMessages() {
3701
+ var e, n, a;
3702
+ const t = (e = this.currentConv) == null ? void 0 : e.id;
3703
+ return t ? ((a = (n = this.s) == null ? void 0 : n.messagesByConv) == null ? void 0 : a[t]) || [] : [];
3704
+ },
3705
+ pendingApproval() {
3706
+ return !this.currentConv || !this.store || this.actionInFlight ? null : this.store.getPendingApproval(this.currentConv.id);
3707
+ },
3708
+ // The approval card sits on top of the chat as an overlay. It must
3709
+ // appear only once the conversation has visibly settled, never
3710
+ // alongside typing dots or before the explaining bubble. The two
3711
+ // guards :
3712
+ // - `streamingActive` — a bubble is currently typing / queued for
3713
+ // reveal. Always defer.
3714
+ // - `approvalSettled` — a short debounce that resets every time
3715
+ // streaming state changes (or a fresh approval arrives). It
3716
+ // fires after a quiet beat following the *last* transition,
3717
+ // ensuring we don't surface the card in the brief gap between
3718
+ // two consecutive reveals when the server sends action +
3719
+ // explanation back-to-back.
3720
+ approvalReady() {
3721
+ return this.pendingApproval ? this.pendingApproval.id === this.approvalLatchId ? !0 : !(this.streamingActive || !this.approvalSettled) : !1;
3722
+ },
3723
+ approvalTitle() {
3724
+ var t, e, n;
3725
+ return ((e = (t = this.pendingApproval) == null ? void 0 : t.payload) == null ? void 0 : e.name) || ((n = this.pendingApproval) == null ? void 0 : n.text_md) || "Confirmer l'action";
3726
+ },
3727
+ approvalDetail() {
3728
+ var a, r, s, o, c, p;
3729
+ const t = (s = (r = (a = this.pendingApproval) == null ? void 0 : a.payload) == null ? void 0 : r.pending) == null ? void 0 : s.user_explanation;
3730
+ if (typeof t == "string" && t.trim())
3731
+ return t.trim();
3732
+ const e = (p = (c = (o = this.pendingApproval) == null ? void 0 : o.payload) == null ? void 0 : c.pending) == null ? void 0 : p.prepared_params;
3733
+ if (!e || typeof e != "object") return "";
3734
+ const n = Object.entries(e);
3735
+ return n.length ? n.slice(0, 2).map(([k, g]) => `${k}: ${g}`).join(" · ") : "";
3736
+ },
3737
+ actionInFlight() {
3738
+ var t, e;
3739
+ return !this.currentConv || !this.store ? null : ((e = (t = this.store).getActionInFlight) == null ? void 0 : e.call(t, this.currentConv.id)) || null;
3740
+ },
3741
+ actionInFlightName() {
3742
+ var e;
3743
+ const t = this.actionInFlight;
3744
+ return t ? ((e = t.payload) == null ? void 0 : e.name) || t.text_md || "Action" : "";
3745
+ },
3746
+ suggestions() {
3747
+ return !this.currentConv || !this.store ? [] : this.streamingActive ? [] : this.pendingForm ? [] : this.store.getLatestSuggestions(this.currentConv.id);
3748
+ },
3749
+ // Dernier formulaire `request_form` actif sur la conv. Pilote la
3750
+ // FormCard. Même slot que l'ApprovalCard — priorité gérée dans
3751
+ // `floatVisible`. Suit le même pacing (`streamingActive` /
3752
+ // `approvalSettled`) que l'ApprovalCard pour ne pas surgir au-dessus
3753
+ // d'une bulle en cours d'écriture.
3754
+ pendingForm() {
3755
+ var t, e;
3756
+ return !this.currentConv || !this.store || this.pendingApproval || this.actionInFlight || this.streamingActive || !this.approvalSettled ? null : ((e = (t = this.store).getLatestForm) == null ? void 0 : e.call(t, this.currentConv.id)) || null;
3757
+ },
3758
+ showFeedback() {
3759
+ var e;
3760
+ const t = this.currentConv;
3761
+ return t ? this.feedbackDone ? !0 : t.status !== "resolved" ? !1 : !((e = t.metadata) != null && e.feedback) : !1;
3762
+ },
3763
+ floatVisible() {
3764
+ return this.approvalReady || !!this.pendingForm || this.showFeedback || this.suggestions.length > 0;
3765
+ },
3766
+ dateLabel() {
3767
+ var n, a, r;
3768
+ const t = this.currentConv;
3769
+ let e = /* @__PURE__ */ new Date();
3770
+ if (t) {
3771
+ const o = ((r = (((a = (n = this.s) == null ? void 0 : n.messagesByConv) == null ? void 0 : a[t.id]) || []).find((c) => c == null ? void 0 : c.created_at)) == null ? void 0 : r.created_at) || t.created_at;
3772
+ if (o) {
3773
+ const c = new Date(o);
3774
+ Number.isNaN(c.getTime()) || (e = c);
3775
+ }
3776
+ }
3777
+ return `Aujourd'hui · ${ye(e)}`;
3778
+ },
3779
+ // Pagination state for the active conversation. Drives the
3780
+ // MessageList's scroll-up history loader. Defaults are safe (no
3781
+ // history available, nothing loading) so the loader stays inert
3782
+ // until `openConversation` has populated the entry.
3783
+ paginationState() {
3784
+ var n, a, r;
3785
+ const t = (n = this.currentConv) == null ? void 0 : n.id, e = t ? (r = (a = this.s) == null ? void 0 : a.paginationByConv) == null ? void 0 : r[t] : null;
3786
+ return {
3787
+ loading: !!(e != null && e.loading),
3788
+ hasMore: !!(e != null && e.nextCursor)
3789
+ };
3790
+ }
3791
+ },
3792
+ watch: {
3793
+ // Closed-launcher étiquette: a dismissal only silences the *current*
3794
+ // unread batch. A fresh message (newer timestamp, or a different
3795
+ // thread) re-expands it; everything read again clears the flag so
3796
+ // it's primed for next time.
3797
+ latestUnread: {
3798
+ handler(t, e) {
3799
+ if (!t) {
3800
+ this.labelDismissed = !1;
3801
+ return;
3802
+ }
3803
+ (!e || t.convId !== e.convId || t.ts > e.ts) && (this.labelDismissed = !1);
3804
+ },
3805
+ deep: !0
3806
+ },
3807
+ // Conv switch (or first time it's set) — drop any in-flight reveal
3808
+ // timers, reset the queue and stamp the open time so the messages
3809
+ // we're about to load count as history and bypass pacing.
3810
+ "currentConv.id": {
3811
+ handler() {
3812
+ this.cancelReveals(), this.revealedAt = {}, this.nextRevealAt = 0, this.convOpenedAt = Date.now(), this.moreOpen = !1, this.resetApprovalPacing();
3813
+ },
3814
+ immediate: !0
3815
+ },
3816
+ // Fresh `pendingApproval` arrival : kick off the settle debounce so
3817
+ // the card waits for the conversation to go quiet. Historical
3818
+ // approvals (page reload, conv switch on a thread that already had
3819
+ // a pending action) bypass the wait — there's nothing to pace, the
3820
+ // user expects to see it on landing.
3821
+ pendingApproval: {
3822
+ handler(t, e) {
3823
+ if (t && !e) {
3824
+ const n = t != null && t.created_at ? Date.parse(t.created_at) : NaN;
3825
+ if (!(Number.isFinite(n) && n >= this.convOpenedAt)) {
3826
+ this.resetApprovalPacing();
3827
+ return;
3828
+ }
3829
+ this.bumpApprovalSettle(2600);
3830
+ } else !t && e && this.resetApprovalPacing();
3831
+ },
3832
+ immediate: !1
3833
+ },
3834
+ // Every flip of streamingActive while an approval is pending resets
3835
+ // the settle clock. This is what makes the card wait through the
3836
+ // entire burst : action queue → typing → bubble → maybe another
3837
+ // bubble → finally quiet → settle fires → card reveals. Bumping on
3838
+ // *both* directions covers the case where streaming starts shortly
3839
+ // after settle was about to fire on its own.
3840
+ streamingActive(t, e) {
3841
+ t !== e && this.pendingApproval && this.pendingApproval.id !== this.approvalLatchId && this.bumpApprovalSettle();
3842
+ },
3843
+ // Once the card is cleared to show, latch it to that action id so a
3844
+ // late bubble in the same burst can't toggle it back off.
3845
+ approvalReady(t) {
3846
+ t && this.pendingApproval && (this.approvalLatchId = this.pendingApproval.id);
3847
+ },
3848
+ // New messages in the current conv — non-agent ones are revealed
3849
+ // immediately; agent messages older than `convOpenedAt` are treated
3850
+ // as history (instant reveal too); only live agent messages go
3851
+ // through the pacing queue.
3852
+ // Track the floating overlay (chips / approval / feedback) so we can
3853
+ // reserve matching bottom space in the MessageList — otherwise the
3854
+ // last message lands behind the overlay.
3855
+ floatVisible: {
3856
+ handler(t) {
3857
+ this.$nextTick(() => this.syncFloatObserver(t));
3858
+ },
3859
+ immediate: !0
3860
+ },
3861
+ currentConvMessages(t) {
3862
+ var a;
3863
+ if (!Array.isArray(t) || !t.length) return;
3864
+ const e = Date.now(), n = { ...this.revealedAt };
3865
+ for (const r of t) {
3866
+ if ((r == null ? void 0 : r.id) == null || n[r.id] !== void 0) continue;
3867
+ if (!le(r)) {
3868
+ n[r.id] = e;
3869
+ continue;
3870
+ }
3871
+ const s = r != null && r.created_at ? Date.parse(r.created_at) : NaN;
3872
+ if (!Number.isFinite(s) || s < this.convOpenedAt) {
3873
+ n[r.id] = e;
3874
+ continue;
3875
+ }
3876
+ const o = typeof (r == null ? void 0 : r.text_md) == "string" && r.text_md.trim().length > 0, c = Array.isArray((a = r == null ? void 0 : r.payload) == null ? void 0 : a.attachments) && r.payload.attachments.length > 0;
3877
+ if (!o && !c) {
3878
+ n[r.id] = e;
3879
+ continue;
3880
+ }
3881
+ n[r.id] = 0, this.scheduleReveal(r);
3882
+ }
3883
+ this.revealedAt = n, (this.isOpen || this.isEmbedded) && !this.historyOpen && this.currentConv && this.markConvRead(this.currentConv);
3884
+ }
3885
+ },
3886
+ async mounted() {
3887
+ if (console.log("[messenger] mounted", {
3888
+ isEditor: this.isEditor,
3889
+ canBoot: this.canBoot,
3890
+ hasBaseUrl: !!this.baseUrl,
3891
+ hasWidgetId: !!this.widgetId,
3892
+ hasUserId: !!this.userId,
3893
+ hasUserHash: !!this.userHash
3894
+ }), typeof document < "u" && !document.getElementById(pe)) {
3895
+ const t = document.createElement("style");
3896
+ t.id = pe, t.textContent = Ne, document.head.appendChild(t);
3897
+ }
3898
+ this.hydrateReadState(), await this.boot(), this.isEmbedded && this.store && await this.open();
3899
+ },
3900
+ beforeUnmount() {
3901
+ this.cancelReveals(), this.disconnectFloatRO(), this.resetApprovalPacing(), this.store && this.store.destroy();
3902
+ },
3903
+ methods: {
3904
+ syncFloatObserver(t) {
3905
+ if (this.disconnectFloatRO(), !t) {
3906
+ this.floatHeight = 0;
3907
+ return;
3908
+ }
3909
+ const e = this.$refs.floatEl;
3910
+ if (e) {
3911
+ if (typeof ResizeObserver > "u") {
3912
+ this.floatHeight = Math.ceil(e.getBoundingClientRect().height + 8);
3913
+ return;
3914
+ }
3915
+ this.floatRO = new ResizeObserver((n) => {
3916
+ const a = n[0];
3917
+ if (!a) return;
3918
+ const r = Math.ceil(a.contentRect.height + 8);
3919
+ r !== this.floatHeight && (this.floatHeight = r, this.$nextTick(() => {
3920
+ var s, o;
3921
+ (o = (s = this.$refs.messageList) == null ? void 0 : s.scrollToBottom) == null || o.call(s);
3922
+ }));
3923
+ }), this.floatRO.observe(e);
3924
+ }
3925
+ },
3926
+ disconnectFloatRO() {
3927
+ if (this.floatRO) {
3928
+ try {
3929
+ this.floatRO.disconnect();
3930
+ } catch {
3931
+ }
3932
+ this.floatRO = null;
3933
+ }
3934
+ },
3935
+ // ── History drawer derivations + read tracking ────────────────────
3936
+ // Newest known activity timestamp for a conversation: the conv's
3937
+ // own `last_message_at` plus the latest cached message (covers the
3938
+ // optimistic-send / SSE gap before the list resorts) and the
3939
+ // create/update stamps as a floor.
3940
+ convLastActivity(t, e) {
3941
+ let n = (t == null ? void 0 : t.last_message_at) || (t == null ? void 0 : t.updated_at) || (t == null ? void 0 : t.created_at) || "";
3942
+ for (const a of e)
3943
+ a != null && a.created_at && a.created_at > n && (n = a.created_at);
3944
+ return n;
3945
+ },
3946
+ lastMessageAuthorType(t) {
3947
+ var e, n;
3948
+ for (let a = t.length - 1; a >= 0; a--) {
3949
+ const r = (n = (e = t[a]) == null ? void 0 : e.author) == null ? void 0 : n.type;
3950
+ if (r) return r;
3951
+ }
3952
+ return "";
3953
+ },
3954
+ // One-line preview = the start of the last meaningful message. Falls
3955
+ // back to whatever preview the server happened to attach when the
3956
+ // thread's messages aren't cached yet (never opened this session).
3957
+ convPreview(t, e) {
3958
+ var n, a, r;
3959
+ for (let s = e.length - 1; s >= 0; s--) {
3960
+ const o = e[s];
3961
+ if (!o) continue;
3962
+ const c = typeof o.text_md == "string" ? o.text_md.trim() : "";
3963
+ if (c)
3964
+ return (((n = o.author) == null ? void 0 : n.type) === "user" ? "Vous : " : "") + c.replace(/\s+/g, " ");
3965
+ const p = (a = o.payload) == null ? void 0 : a.attachments;
3966
+ if (Array.isArray(p) && p.length) return "📎 Pièce jointe";
3967
+ }
3968
+ return (t == null ? void 0 : t.last_message_preview) || (t == null ? void 0 : t.preview) || ((r = t == null ? void 0 : t.metadata) == null ? void 0 : r.last_preview) || "";
3969
+ },
3970
+ readStorageKey() {
3971
+ const t = this.widgetId || "", e = this.userId || "";
3972
+ return `wm:read:${t}:${e}`;
3973
+ },
3974
+ hydrateReadState() {
3975
+ try {
3976
+ if (typeof localStorage > "u") return;
3977
+ const t = localStorage.getItem(this.readStorageKey());
3978
+ if (!t) return;
3979
+ const e = JSON.parse(t);
3980
+ e && typeof e == "object" && (this.readState = e);
3981
+ } catch {
3982
+ }
3983
+ },
3984
+ persistReadState() {
3985
+ try {
3986
+ if (typeof localStorage > "u") return;
3987
+ localStorage.setItem(this.readStorageKey(), JSON.stringify(this.readState));
3988
+ } catch {
3989
+ }
3990
+ },
3991
+ // Stamp a conversation as read up to its newest known activity.
3992
+ // Called when the user opens/starts a thread and whenever the
3993
+ // actively-viewed thread receives new messages.
3994
+ markConvRead(t) {
3995
+ var a, r;
3996
+ if (!(t != null && t.id)) return;
3997
+ const e = ((r = (a = this.s) == null ? void 0 : a.messagesByConv) == null ? void 0 : r[t.id]) || [], n = this.convLastActivity(t, e);
3998
+ n && this.readState[t.id] !== n && (this.readState = { ...this.readState, [t.id]: n }, this.persistReadState());
3999
+ },
4000
+ cancelReveals() {
4001
+ for (const t of this.revealTimers)
4002
+ try {
4003
+ clearTimeout(t);
4004
+ } catch {
4005
+ }
4006
+ this.revealTimers = [];
4007
+ },
4008
+ // Clears any in-flight settle timer and returns the approval pacing
4009
+ // to its idle state (settled = true so an already-pending historical
4010
+ // approval can render immediately on mount/restore). Called on conv
4011
+ // switch, on `pendingApproval` cleared (approve/reject/expire), and
4012
+ // on unmount.
4013
+ resetApprovalPacing() {
4014
+ if (this.approvalSettleTimer) {
4015
+ try {
4016
+ clearTimeout(this.approvalSettleTimer);
4017
+ } catch {
4018
+ }
4019
+ this.approvalSettleTimer = null;
4020
+ }
4021
+ this.approvalSettled = !0, this.approvalLatchId = null;
4022
+ },
4023
+ // Pause the approval card for one settle period. A follow-on bubble
4024
+ // from the same SSE burst should land (and start streaming, which
4025
+ // re-bumps us) before it expires. `ms` defaults to a short beat for
4026
+ // streaming-driven re-bumps; the fresh-approval path passes a longer
4027
+ // grace to bridge the gap until the explaining bubble's SSE arrives.
4028
+ // Each call resets the timer; the card reveals only after a full
4029
+ // quiet period.
4030
+ bumpApprovalSettle(t = 800) {
4031
+ if (this.approvalSettled = !1, this.approvalSettleTimer)
4032
+ try {
4033
+ clearTimeout(this.approvalSettleTimer);
4034
+ } catch {
4035
+ }
4036
+ this.approvalSettleTimer = setTimeout(() => {
4037
+ this.approvalSettled = !0, this.approvalSettleTimer = null;
4038
+ }, t);
4039
+ },
4040
+ // Stagger the visibility of a freshly-arrived agent message:
4041
+ // - at least FIRST_REVEAL_DELAY_MS after "now"
4042
+ // - never overlap the previous reveal slot
4043
+ // - inter-bubble gap scales with message length (reading time),
4044
+ // bounded by MIN/MAX_BETWEEN_MS.
4045
+ scheduleReveal(t) {
4046
+ const e = Date.now(), n = ((t == null ? void 0 : t.text_md) || "").length, a = Math.min(
4047
+ vi,
4048
+ Math.max(pi, n * _i)
4049
+ ), s = Math.max(
4050
+ e + fi,
4051
+ this.nextRevealAt + gi
4052
+ ) + a;
4053
+ this.nextRevealAt = s;
4054
+ const o = Math.max(0, s - e), c = t.id, p = setTimeout(() => {
4055
+ this.revealedAt = { ...this.revealedAt, [c]: Date.now() }, this.revealTimers = this.revealTimers.filter((g) => g !== p);
4056
+ }, o);
4057
+ this.revealTimers.push(p);
4058
+ const k = setTimeout(() => {
4059
+ this.revealedAt[c] > 0 || (this.revealedAt = { ...this.revealedAt, [c]: Date.now() }), this.revealTimers = this.revealTimers.filter((g) => g !== k);
4060
+ }, o + 4e3);
4061
+ this.revealTimers.push(k);
4062
+ },
4063
+ async boot() {
4064
+ if (!this.canBoot) {
4065
+ console.warn("[messenger] boot skipped — missing creds", {
4066
+ baseUrl: this.baseUrl,
4067
+ widgetId: this.widgetId,
4068
+ userId: this.userId,
4069
+ hasUserHash: !!this.userHash
4070
+ });
4071
+ return;
4072
+ }
4073
+ try {
4074
+ console.log("[messenger] boot start", {
4075
+ baseUrl: this.baseUrl,
4076
+ widgetId: this.widgetId,
4077
+ userId: this.userId
4078
+ }), this.transport = he($e({
4079
+ baseUrl: this.baseUrl,
4080
+ widgetId: this.widgetId,
4081
+ userId: this.userId,
4082
+ userHash: this.userHash
4083
+ })), this.store = he(je(this.transport)), await this.store.start(), console.log("[ww-messenger] boot done", {
4084
+ ready: this.store.state.ready,
4085
+ error: this.store.state.error
4086
+ });
4087
+ } catch (t) {
4088
+ console.error("[ww-messenger] bootstrap failed", t), this.bootError = (t == null ? void 0 : t.message) || String(t);
4089
+ }
4090
+ },
4091
+ async refresh() {
4092
+ this.store && this.store.destroy(), this.cancelReveals(), this.revealedAt = {}, this.nextRevealAt = 0, this.convOpenedAt = 0, this.transport = null, this.store = null, this.isOpen = !1, this.draft = "", this.busy = !1, this.bootError = null, this.pendingAttachments = [], this.feedbackBusy = !1, this.feedbackDone = !1, this.historyOpen = !1, this.moreOpen = !1, this.activeConvId = null, this.showOnboarding = !1, await this.boot(), this.isEmbedded && this.store && await this.open();
4093
+ },
4094
+ // Opening straight from the étiquette: land on the unread thread it
4095
+ // was previewing rather than whatever was last active.
4096
+ async openFromPeek() {
4097
+ var e, n;
4098
+ const t = (e = this.latestUnread) == null ? void 0 : e.convId;
4099
+ t && t !== ((n = this.currentConv) == null ? void 0 : n.id) && (this.activeConvId = t, this.showOnboarding = !1), await this.open();
4100
+ },
4101
+ async open() {
4102
+ if (this.isOpen = !0, this.store && this.store.setPanelOpen(!0), this.currentConv) {
4103
+ try {
4104
+ await this.store.openConversation(this.currentConv.id);
4105
+ } catch (t) {
4106
+ console.error("[ww-messenger] load messages failed", t);
4107
+ }
4108
+ this.markConvRead(this.currentConv);
4109
+ }
4110
+ },
4111
+ close() {
4112
+ this.isOpen = !1, this.store && this.store.setPanelOpen(!1);
4113
+ },
4114
+ async startConv() {
4115
+ if (!this.busy) {
4116
+ this.busy = !0;
4117
+ try {
4118
+ const t = await this.store.createConversation({});
4119
+ this.activeConvId = t.id, this.showOnboarding = !1, await this.store.openConversation(t.id), this.markConvRead(t), this.focusComposer();
4120
+ } catch (t) {
4121
+ console.error("[ww-messenger] create conv failed", t), this.bootError = (t == null ? void 0 : t.message) || String(t);
4122
+ } finally {
4123
+ this.busy = !1;
4124
+ }
4125
+ }
4126
+ },
4127
+ // After a fresh conversation renders, drop the caret straight into
4128
+ // the composer so the user can type without an extra click. The
4129
+ // Composer only mounts once `currentConv` flips truthy, so wait a
4130
+ // tick for that render before reaching for its ref.
4131
+ focusComposer() {
4132
+ this.$nextTick(() => {
4133
+ var t;
4134
+ return (t = this.$refs.composer) == null ? void 0 : t.focus();
4135
+ });
4136
+ },
4137
+ toggleMore() {
4138
+ this.historyOpen = !1, this.moreOpen = !this.moreOpen;
4139
+ },
4140
+ goHome() {
4141
+ this.historyOpen = !1, this.moreOpen = !1, this.activeConvId = null, this.showOnboarding = !0;
4142
+ },
4143
+ async onDrawerPick(t) {
4144
+ if (!(t != null && t.id) || !this.store) return;
4145
+ this.historyOpen = !1, this.activeConvId = t.id, this.showOnboarding = !1;
4146
+ try {
4147
+ await this.store.openConversation(t.id);
4148
+ } catch (n) {
4149
+ console.error("[ww-messenger] open conv failed", n);
4150
+ }
4151
+ const e = this.allConversations.find((n) => n.id === t.id);
4152
+ e && this.markConvRead(e);
4153
+ },
4154
+ async onDrawerNew() {
4155
+ this.historyOpen = !1, await this.startConv();
4156
+ },
4157
+ onNotifToggle(t) {
4158
+ this.notifEnabled = !!t;
4159
+ },
4160
+ async onMoreAction(t) {
4161
+ switch (this.moreOpen = !1, t) {
4162
+ case "history":
4163
+ this.historyOpen = !0;
4164
+ break;
4165
+ case "rename":
4166
+ await this.renameCurrentConv();
4167
+ break;
4168
+ case "clear":
4169
+ await this.clearCurrentConv();
4170
+ break;
4171
+ case "export":
4172
+ this.exportCurrentConv();
4173
+ break;
4174
+ case "status":
4175
+ if (this.statusUrl)
4176
+ try {
4177
+ window.open(this.statusUrl, "_blank", "noopener");
4178
+ } catch {
4179
+ }
4180
+ break;
4181
+ case "help":
4182
+ if (this.helpUrl)
4183
+ try {
4184
+ window.open(this.helpUrl, "_blank", "noopener");
4185
+ } catch {
4186
+ }
4187
+ break;
4188
+ }
4189
+ },
4190
+ async renameCurrentConv() {
4191
+ const t = this.currentConv;
4192
+ if (!t || !this.store) return;
4193
+ let e;
4194
+ try {
4195
+ e = typeof window < "u" && typeof window.prompt == "function" ? window.prompt("Nouveau nom du fil", t.name || "") : null;
4196
+ } catch {
4197
+ e = null;
4198
+ }
4199
+ if (e == null) return;
4200
+ const n = e.trim();
4201
+ if (!(!n || n === t.name))
4202
+ try {
4203
+ await this.store.patchConversation(t.id, { name: n });
4204
+ } catch (a) {
4205
+ console.error("[ww-messenger] rename failed", a);
4206
+ }
4207
+ },
4208
+ async clearCurrentConv() {
4209
+ var n;
4210
+ const t = this.currentConv;
4211
+ !t || !this.store || !(!(typeof window < "u" && typeof window.confirm == "function") || window.confirm("Effacer ce fil de votre côté ? Cette action ne supprime pas les messages côté agent.")) || ((n = this.s) != null && n.messagesByConv && (this.s.messagesByConv[t.id] = []), this.activeConvId = null, this.showOnboarding = !0);
4212
+ },
4213
+ exportCurrentConv() {
4214
+ var r, s, o, c, p, k;
4215
+ const t = this.currentConv;
4216
+ if (!t) return;
4217
+ const e = (((s = (r = this.s) == null ? void 0 : r.messagesByConv) == null ? void 0 : s[t.id]) || []).slice(), n = [
4218
+ `# ${t.name || "Conversation"}`,
4219
+ t.created_at ? `Créée le : ${t.created_at}` : "",
4220
+ ""
4221
+ ];
4222
+ for (const g of e) {
4223
+ if (!g) continue;
4224
+ const T = ((o = g.author) == null ? void 0 : o.name) || (((c = g.author) == null ? void 0 : c.type) === "user" ? "Vous" : ((p = g.author) == null ? void 0 : p.type) === "agent_human" ? "Agent" : ((k = g.author) == null ? void 0 : k.type) === "agent_ia" ? "Assistant IA" : "Système"), R = g.created_at ? new Date(g.created_at).toLocaleString("fr-FR") : "", x = (g.text_md || "").trim();
4225
+ x && (n.push(`[${R}] ${T} :`), n.push(x), n.push(""));
4226
+ }
4227
+ const a = new Blob([n.join(`
4228
+ `)], { type: "text/plain;charset=utf-8" });
4229
+ try {
4230
+ const g = URL.createObjectURL(a), T = document.createElement("a");
4231
+ T.href = g, T.download = `${(t.name || "conversation").replace(/[^a-z0-9-_]+/gi, "_")}.txt`, document.body.appendChild(T), T.click(), document.body.removeChild(T), setTimeout(() => URL.revokeObjectURL(g), 1e3);
4232
+ } catch (g) {
4233
+ console.error("[ww-messenger] export failed", g);
4234
+ }
4235
+ },
4236
+ async onSend(t) {
4237
+ const e = this.currentConv;
4238
+ if (!e && (await this.startConv(), !this.currentConv))
4239
+ return;
4240
+ const n = (this.currentConv || e).id, a = this.pendingAttachments.slice();
4241
+ this.pendingAttachments = [], await this.store.send(n, t, { attachments: a.length ? a : void 0 });
4242
+ },
4243
+ async onSuggestion(t) {
4244
+ const e = t == null ? void 0 : t.label;
4245
+ e && await this.onSend(e);
4246
+ },
4247
+ async onLoadMore() {
4248
+ var e;
4249
+ const t = (e = this.currentConv) == null ? void 0 : e.id;
4250
+ !t || !this.store || await this.store.loadMore(t);
4251
+ },
4252
+ async onApprovalCallback(t) {
4253
+ const e = this.pendingApproval;
4254
+ e && await this.store.clickCallback(e.id, t);
4255
+ },
4256
+ async onFormSubmit({ values: t }) {
4257
+ const e = this.pendingForm;
4258
+ if (!(e != null && e.form)) return;
4259
+ const n = hi(e.form, t);
4260
+ if (!n) return;
4261
+ const a = this.currentConv;
4262
+ a && await this.store.send(a.id, n, {
4263
+ metadata: {
4264
+ artifact: mi(e.form, t)
4265
+ }
4266
+ });
4267
+ },
4268
+ async onAttach(t) {
4269
+ if (!(!t || !this.transport))
4270
+ try {
4271
+ const e = await this.transport.uploadAttachment(t);
4272
+ this.pendingAttachments.push({
4273
+ type: e.type || "file",
4274
+ path: e.path,
4275
+ name: t.name || "fichier",
4276
+ mime_type: t.type,
4277
+ size_bytes: t.size
4278
+ });
4279
+ } catch (e) {
4280
+ console.error("[ww-messenger] attachment upload failed", e);
4281
+ }
4282
+ },
4283
+ async onQuickLink(t) {
4284
+ if (t) {
4285
+ if (t.url) {
4286
+ try {
4287
+ window.open(t.url, "_blank", "noopener");
4288
+ } catch {
4289
+ }
4290
+ return;
4291
+ }
4292
+ await this.startConv(), this.currentConv && await this.store.send(this.currentConv.id, t.label);
4293
+ }
4294
+ },
4295
+ async onFeedback({ rating: t, comment: e }) {
4296
+ if (this.currentConv) {
4297
+ this.feedbackBusy = !0;
4298
+ try {
4299
+ await this.store.submitFeedback(this.currentConv.id, { rating: t, comment: e }), this.feedbackDone = !0;
4300
+ } catch (n) {
4301
+ console.error("[ww-messenger] feedback failed", n);
4302
+ } finally {
4303
+ this.feedbackBusy = !1;
4304
+ }
4305
+ }
4306
+ }
4307
+ }
4308
+ }, wi = {
4309
+ key: 0,
4310
+ class: "wm-loading",
4311
+ "aria-busy": "true",
4312
+ "aria-live": "polite"
4313
+ }, bi = {
4314
+ key: 0,
4315
+ class: "wm-state"
4316
+ }, ki = { class: "wm-state__err" }, Ci = { class: "wm-state__errSub" }, Ai = { class: "wm-bottom" }, Si = {
4317
+ key: 0,
4318
+ ref: "floatEl",
4319
+ class: "wm-float"
4320
+ }, xi = {
4321
+ key: 1,
4322
+ class: "wm-actionWait",
4323
+ role: "status",
4324
+ "aria-live": "polite"
4325
+ }, Mi = { class: "wm-actionWait__lbl" }, Ti = {
4326
+ key: 1,
4327
+ class: "wm-attached"
4328
+ }, Ii = ["onClick"];
4329
+ function Oi(t, e, n, a, r, s) {
4330
+ const o = O("Launcher"), c = O("Header"), p = O("Onboarding"), k = O("MessageList"), g = O("ApprovalCard"), T = O("FormCard"), R = O("Feedback"), x = O("SuggestionChips"), N = O("Composer"), I = O("MoreMenu"), P = O("HistoryDrawer");
4331
+ return l(), d("div", {
4332
+ class: B(["wm-root", `wm-root--${n.displayMode}`])
4333
+ }, [
4334
+ !r.isOpen && !s.isEmbedded ? (l(), $(o, {
4335
+ key: 0,
4336
+ "unread-count": s.unreadCount,
4337
+ peek: s.launcherPeek,
4338
+ onOpen: s.openFromPeek,
4339
+ onDismiss: e[0] || (e[0] = (U) => r.labelDismissed = !0)
4340
+ }, null, 8, ["unread-count", "peek", "onOpen"])) : v("", !0),
4341
+ r.isOpen || s.isEmbedded ? (l(), d("section", {
4342
+ key: 1,
4343
+ class: B(["wm-panel", `wm-panel--${n.displayMode}`]),
4344
+ style: H(r.floatHeight ? { "--wm-float-h": r.floatHeight + "px" } : null),
4345
+ role: "dialog",
4346
+ "aria-label": "Messenger"
4347
+ }, [
4348
+ !s.ready && !s.error ? (l(), d("div", wi, [
4349
+ s.isEmbedded ? v("", !0) : (l(), d("button", {
4350
+ key: 0,
4351
+ type: "button",
4352
+ class: "wm-loading__close",
4353
+ "aria-label": "Réduire",
4354
+ onClick: e[1] || (e[1] = (...U) => s.close && s.close(...U))
4355
+ }, [...e[6] || (e[6] = [
4356
+ i("svg", {
4357
+ width: "13",
4358
+ height: "13",
4359
+ viewBox: "0 0 24 24",
4360
+ fill: "none",
4361
+ stroke: "currentColor",
4362
+ "stroke-width": "2",
4363
+ "stroke-linecap": "round",
4364
+ "stroke-linejoin": "round",
4365
+ "aria-hidden": "true"
4366
+ }, [
4367
+ i("path", { d: "M18 6L6 18M6 6l12 12" })
4368
+ ], -1)
4369
+ ])])),
4370
+ e[7] || (e[7] = i("div", {
4371
+ class: "wm-loading__spinner",
4372
+ "aria-hidden": "true"
4373
+ }, null, -1))
4374
+ ])) : (l(), d(E, { key: 1 }, [
4375
+ z(c, {
4376
+ title: s.headerTitle,
4377
+ escalated: s.isEscalated,
4378
+ "agent-name": s.humanAgentName,
4379
+ "agent-avatar-url": s.humanAgentAvatarUrl,
4380
+ "team-members": s.teamMembers,
4381
+ "response-label": s.responseLabel,
4382
+ "show-identity": !!s.currentConv,
4383
+ "show-back": s.canBack,
4384
+ "show-close": !s.isEmbedded,
4385
+ "more-active": r.moreOpen,
4386
+ onBack: s.goHome,
4387
+ onMore: s.toggleMore,
4388
+ onClose: s.close
4389
+ }, null, 8, ["title", "escalated", "agent-name", "agent-avatar-url", "team-members", "response-label", "show-identity", "show-back", "show-close", "more-active", "onBack", "onMore", "onClose"]),
4390
+ s.error ? (l(), d("div", bi, [
4391
+ i("div", ki, [
4392
+ e[9] || (e[9] = i("div", { class: "wm-state__errIcon" }, [
4393
+ i("svg", {
4394
+ width: "14",
4395
+ height: "14",
4396
+ viewBox: "0 0 24 24",
4397
+ fill: "none",
4398
+ stroke: "currentColor",
4399
+ "stroke-width": "2",
4400
+ "stroke-linecap": "round",
4401
+ "stroke-linejoin": "round",
4402
+ "aria-hidden": "true"
4403
+ }, [
4404
+ i("path", { d: "M18 6L6 18M6 6l12 12" })
4405
+ ])
4406
+ ], -1)),
4407
+ i("div", null, [
4408
+ e[8] || (e[8] = i("div", { class: "wm-state__errTitle" }, "Connexion impossible", -1)),
4409
+ i("div", Ci, y(s.error), 1)
4410
+ ])
4411
+ ])
4412
+ ])) : s.currentConv ? (l(), d(E, { key: 2 }, [
4413
+ z(k, {
4414
+ ref: "messageList",
4415
+ messages: s.displayedMessages,
4416
+ "streaming-active": s.streamingActive,
4417
+ "date-label": s.dateLabel,
4418
+ "conversation-id": s.currentConv ? s.currentConv.id : null,
4419
+ "loading-more": s.paginationState.loading,
4420
+ "has-more": s.paginationState.hasMore,
4421
+ onLoadMore: s.onLoadMore
4422
+ }, null, 8, ["messages", "streaming-active", "date-label", "conversation-id", "loading-more", "has-more", "onLoadMore"]),
4423
+ i("div", Ai, [
4424
+ s.floatVisible ? (l(), d("div", Si, [
4425
+ s.approvalReady ? (l(), $(g, {
4426
+ key: 0,
4427
+ action: s.approvalTitle,
4428
+ detail: s.approvalDetail,
4429
+ callbacks: s.pendingApproval.callbacks,
4430
+ onCallback: s.onApprovalCallback
4431
+ }, null, 8, ["action", "detail", "callbacks", "onCallback"])) : s.pendingForm ? (l(), $(T, {
4432
+ key: s.pendingForm.message && s.pendingForm.message.id,
4433
+ form: s.pendingForm.form,
4434
+ onSubmit: s.onFormSubmit
4435
+ }, null, 8, ["form", "onSubmit"])) : s.showFeedback ? (l(), $(R, {
4436
+ key: 2,
4437
+ busy: r.feedbackBusy,
4438
+ done: r.feedbackDone,
4439
+ onSubmit: s.onFeedback
4440
+ }, null, 8, ["busy", "done", "onSubmit"])) : (l(), $(x, {
4441
+ key: 3,
4442
+ items: s.suggestions,
4443
+ onSelect: s.onSuggestion
4444
+ }, null, 8, ["items", "onSelect"]))
4445
+ ], 512)) : v("", !0),
4446
+ s.actionInFlight ? (l(), d("div", xi, [
4447
+ e[10] || (e[10] = i("span", {
4448
+ class: "wm-actionWait__spinner",
4449
+ "aria-hidden": "true"
4450
+ }, null, -1)),
4451
+ i("span", Mi, y(s.actionInFlightName) + " en cours, veuillez patienter…", 1)
4452
+ ])) : (l(), $(N, {
4453
+ key: 2,
4454
+ ref: "composer",
4455
+ modelValue: r.draft,
4456
+ "onUpdate:modelValue": e[2] || (e[2] = (U) => r.draft = U),
4457
+ placeholder: s.composerPlaceholder,
4458
+ disabled: !!s.pendingApproval,
4459
+ "attach-label": "Joindre un fichier",
4460
+ onSend: s.onSend,
4461
+ onAttach: s.onAttach
4462
+ }, null, 8, ["modelValue", "placeholder", "disabled", "onSend", "onAttach"]))
4463
+ ]),
4464
+ r.moreOpen ? (l(), $(I, {
4465
+ key: 0,
4466
+ "can-rename": !!s.currentConv,
4467
+ "can-clear": !!s.currentConv,
4468
+ "can-export": !!s.currentConv,
4469
+ "notif-enabled": r.notifEnabled,
4470
+ "status-url": s.statusUrl,
4471
+ "help-url": s.helpUrl,
4472
+ onClose: e[3] || (e[3] = (U) => r.moreOpen = !1),
4473
+ onNotifToggle: s.onNotifToggle,
4474
+ onAction: s.onMoreAction
4475
+ }, null, 8, ["can-rename", "can-clear", "can-export", "notif-enabled", "status-url", "help-url", "onNotifToggle", "onAction"])) : v("", !0),
4476
+ r.pendingAttachments.length ? (l(), d("div", Ti, [
4477
+ (l(!0), d(E, null, F(r.pendingAttachments, (U, V) => (l(), d("div", {
4478
+ key: V,
4479
+ class: "wm-attached__chip"
4480
+ }, [
4481
+ e[12] || (e[12] = i("svg", {
4482
+ width: "11",
4483
+ height: "11",
4484
+ viewBox: "0 0 24 24",
4485
+ fill: "none",
4486
+ stroke: "currentColor",
4487
+ "stroke-width": "1.8",
4488
+ "stroke-linecap": "round",
4489
+ "stroke-linejoin": "round",
4490
+ "aria-hidden": "true"
4491
+ }, [
4492
+ i("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48" })
4493
+ ], -1)),
4494
+ i("span", null, y(U.name), 1),
4495
+ i("button", {
4496
+ type: "button",
4497
+ "aria-label": "Retirer",
4498
+ onClick: (Q) => r.pendingAttachments.splice(V, 1)
4499
+ }, [...e[11] || (e[11] = [
4500
+ i("svg", {
4501
+ width: "10",
4502
+ height: "10",
4503
+ viewBox: "0 0 24 24",
4504
+ fill: "none",
4505
+ stroke: "currentColor",
4506
+ "stroke-width": "2",
4507
+ "stroke-linecap": "round",
4508
+ "stroke-linejoin": "round",
4509
+ "aria-hidden": "true"
4510
+ }, [
4511
+ i("path", { d: "M18 6L6 18M6 6l12 12" })
4512
+ ], -1)
4513
+ ])], 8, Ii)
4514
+ ]))), 128))
4515
+ ])) : v("", !0)
4516
+ ], 64)) : (l(), $(p, {
4517
+ key: 1,
4518
+ "welcome-message": s.widgetWelcomeMessage,
4519
+ "agent-name": s.agentName,
4520
+ "quick-links": s.quickLinks,
4521
+ "unread-threads": s.unreadThreads,
4522
+ busy: r.busy,
4523
+ onStart: s.startConv,
4524
+ onSelect: s.onQuickLink,
4525
+ onResume: s.onDrawerPick
4526
+ }, null, 8, ["welcome-message", "agent-name", "quick-links", "unread-threads", "busy", "onStart", "onSelect", "onResume"])),
4527
+ r.historyOpen ? (l(), $(P, {
4528
+ key: 3,
4529
+ conversations: s.drawerConversations,
4530
+ "active-id": s.currentConv ? s.currentConv.id : null,
4531
+ onClose: e[4] || (e[4] = (U) => r.historyOpen = !1),
4532
+ onNew: s.onDrawerNew,
4533
+ onPick: s.onDrawerPick
4534
+ }, null, 8, ["conversations", "active-id", "onNew", "onPick"])) : v("", !0),
4535
+ r.moreOpen && !s.currentConv ? (l(), $(I, {
4536
+ key: 4,
4537
+ "can-rename": !1,
4538
+ "can-clear": !1,
4539
+ "can-export": !1,
4540
+ "notif-enabled": r.notifEnabled,
4541
+ "status-url": s.statusUrl,
4542
+ "help-url": s.helpUrl,
4543
+ onClose: e[5] || (e[5] = (U) => r.moreOpen = !1),
4544
+ onNotifToggle: s.onNotifToggle,
4545
+ onAction: s.onMoreAction
4546
+ }, null, 8, ["notif-enabled", "status-url", "help-url", "onNotifToggle", "onAction"])) : v("", !0)
4547
+ ], 64))
4548
+ ], 6)) : v("", !0)
4549
+ ], 2);
4550
+ }
4551
+ const Li = /* @__PURE__ */ L(yi, [["render", Oi], ["__scopeId", "data-v-da51a155"]]);
4552
+ export {
4553
+ X as AIAvatar,
4554
+ ae as AVATAR_COLORS,
4555
+ Fn as ActionResult,
4556
+ Zs as ApprovalCard,
4557
+ Yt as ArtifactFormResponse,
4558
+ dn as ArtifactInfoCard,
4559
+ ke as ArtifactRenderer,
4560
+ Cn as ArtifactTicket,
4561
+ Kn as AttachmentPreview,
4562
+ Qn as Bubble,
4563
+ js as Composer,
4564
+ Fr as Feedback,
4565
+ xr as FormCard,
4566
+ vt as Header,
4567
+ si as HistoryDrawer,
4568
+ we as HumanAvatar,
4569
+ We as Launcher,
4570
+ ce as MEDIA_RECORDER_SUPPORTED,
4571
+ ys as MessageList,
4572
+ Li as Messenger,
4573
+ ui as MoreMenu,
4574
+ jt as Onboarding,
4575
+ te as SCREEN_CAPTURE_SUPPORTED,
4576
+ zs as SuggestionChips,
4577
+ it as TeamAvatars,
4578
+ ns as Typing,
4579
+ ve as avatarColor,
4580
+ ge as avatarInitials,
4581
+ bs as captureScreenshotFile,
4582
+ j as colors,
4583
+ je as createStore,
4584
+ $e as createTransport,
4585
+ Li as default,
4586
+ ye as formatTime,
4587
+ Bi as guessAttachmentKind,
4588
+ ws as pickRecorderMime,
4589
+ be as renderMarkdown,
4590
+ Cs as startScreenRecording,
4591
+ Ne as tokensCss,
4592
+ De as uuid,
4593
+ De as v4
4594
+ };