@paramms/chat-widget 1.0.37 → 1.0.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
- var pe = Object.defineProperty;
2
- var he = (o, e, i) => e in o ? pe(o, e, { enumerable: !0, configurable: !0, writable: !0, value: i }) : o[e] = i;
3
- var y = (o, e, i) => he(o, typeof e != "symbol" ? e + "" : e, i);
4
- import { p as fe, r as ue, a as we } from "./codec.js";
5
- import { h as Me } from "./codec.js";
6
- import { P as be, C as xe, b as ge } from "./outbox.js";
7
- import { e as He } from "./outbox.js";
8
- import { E2ESession as me, extractX3DHInit as ye } from "./e2e.js";
9
- const te = "http://www.w3.org/2000/svg", ve = 8e3;
10
- class ke {
1
+ var Pe = Object.defineProperty;
2
+ var Le = (t, e, i) => e in t ? Pe(t, e, { enumerable: !0, configurable: !0, writable: !0, value: i }) : t[e] = i;
3
+ var y = (t, e, i) => Le(t, typeof e != "symbol" ? e + "" : e, i);
4
+ import { p as je, r as Ae, a as $e } from "./codec.js";
5
+ import { h as st } from "./codec.js";
6
+ import { P as Be, C as Me, b as Re } from "./outbox.js";
7
+ import { e as at } from "./outbox.js";
8
+ import { E2ESession as qe, extractX3DHInit as Oe } from "./e2e.js";
9
+ const xe = "http://www.w3.org/2000/svg", De = 8e3;
10
+ class He {
11
11
  constructor() {
12
12
  y(this, "svg", null);
13
13
  y(this, "timers", /* @__PURE__ */ new Set());
@@ -23,7 +23,7 @@ class ke {
23
23
  ensureSvg() {
24
24
  var i;
25
25
  if ((i = this.svg) != null && i.isConnected) return this.svg;
26
- const e = document.createElementNS(te, "svg");
26
+ const e = document.createElementNS(xe, "svg");
27
27
  return e.setAttribute("data-relay-annotations", ""), e.setAttribute("aria-hidden", "true"), e.setAttribute(
28
28
  "style",
29
29
  "position:fixed;inset:0;width:100vw;height:100vh;pointer-events:none;z-index:2147483000;"
@@ -32,15 +32,15 @@ class ke {
32
32
  draw(e) {
33
33
  var b;
34
34
  if (typeof document > "u" || !((b = e.points) != null && b.length)) return;
35
- const i = this.ensureSvg(), s = window.innerWidth, a = window.innerHeight, c = document.createElementNS(te, "polyline");
36
- c.setAttribute("points", e.points.map((d) => `${(d.x * s).toFixed(1)},${(d.y * a).toFixed(1)}`).join(" ")), c.setAttribute("fill", "none"), c.setAttribute("stroke", e.color || "#ff3b30"), c.setAttribute("stroke-width", String(e.width || 3)), c.setAttribute("stroke-linecap", "round"), c.setAttribute("stroke-linejoin", "round"), c.setAttribute("data-stroke-id", e.id), i.append(c);
35
+ const i = this.ensureSvg(), s = window.innerWidth, r = window.innerHeight, a = document.createElementNS(xe, "polyline");
36
+ a.setAttribute("points", e.points.map((d) => `${(d.x * s).toFixed(1)},${(d.y * r).toFixed(1)}`).join(" ")), a.setAttribute("fill", "none"), a.setAttribute("stroke", e.color || "#ff3b30"), a.setAttribute("stroke-width", String(e.width || 3)), a.setAttribute("stroke-linecap", "round"), a.setAttribute("stroke-linejoin", "round"), a.setAttribute("data-stroke-id", e.id), i.append(a);
37
37
  const f = setTimeout(() => {
38
- c.style.transition = "opacity 600ms", c.style.opacity = "0";
38
+ a.style.transition = "opacity 600ms", a.style.opacity = "0";
39
39
  const d = setTimeout(() => {
40
- c.remove(), this.timers.delete(d);
40
+ a.remove(), this.timers.delete(d);
41
41
  }, 650);
42
42
  this.timers.add(d), this.timers.delete(f);
43
- }, ve);
43
+ }, De);
44
44
  this.timers.add(f);
45
45
  }
46
46
  clear() {
@@ -53,19 +53,29 @@ class ke {
53
53
  this.timers.clear(), (e = this.svg) == null || e.remove(), this.svg = null;
54
54
  }
55
55
  }
56
- const oe = "objectchat-widget-styles", Ce = ["👍", "❤️", "😂", "😮", "😢", "🙏"], Te = `
56
+ const me = "objectchat-widget-styles", Ne = ["👍", "❤️", "😂", "😮", "😢", "🙏"], _e = 400, Fe = '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M22 2 11 13M22 2l-7 20-4-9-9-4 20-7z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>', Ue = `
57
57
  .ocw { --ocw-accent:#4F63F5; --ocw-bg:#f3efe9; --ocw-card:#fff; --ocw-line:#ececec; --ocw-ink:#1c1b1a; --ocw-mut:#9b9690;
58
58
  position:relative;
59
59
  display:flex; flex-direction:column; height:100%; min-height:320px; background:var(--ocw-bg);
60
60
  font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif; color:var(--ocw-ink); overflow:hidden; }
61
- @media (max-width:480px) {
62
- .ocw { min-height:100dvh; border-radius:0 !important; }
63
- .ocw-bubble { font-size:15px; }
64
- .ocw-input textarea { font-size:16px; } /* prevent iOS zoom on focus */
65
- .ocw-chip { padding:9px 14px; font-size:14px; }
66
- .ocw-modal-card { width:90%; }
67
- .ocw-row { max-width:94%; }
68
- }
61
+ /* ── Responsive sizing ──────────────────────────────────────────────────────
62
+ * Sizing is driven by the WIDGET'S OWN width (ResizeObserver toggles
63
+ * .ocw-compact below 400px), not the viewport — so a widget embedded in a
64
+ * narrow desktop sidebar sizes the same as one on a phone, and a tablet in
65
+ * landscape keeps comfortable desktop sizing. A viewport query alone can't
66
+ * see the container. Fullscreen (launcher on mobile) additionally gets
67
+ * .ocw-fs from the launcher, which is the only case that should remove the
68
+ * corner radius — an INLINE embed on a phone must NOT take over the page
69
+ * (the old blanket min-height:100dvh rule did exactly that). */
70
+ .ocw.ocw-fs { border-radius:0 !important; }
71
+ .ocw-compact .ocw-bubble { font-size:15px; }
72
+ .ocw-compact.ocw .ocw-input textarea { font-size:16px; } /* ≥16px prevents iOS zoom on focus */
73
+ .ocw-compact .ocw-chip { padding:9px 14px; font-size:14px; }
74
+ .ocw-compact .ocw-modal-card { width:90%; }
75
+ .ocw-compact .ocw-row { max-width:94%; }
76
+ .ocw-compact .ocw-sendbtn { min-width:44px; height:44px; }
77
+ .ocw-compact .ocw-back { width:34px; height:34px; }
78
+ .ocw-compact .ocw-quick button { padding:9px 15px; font-size:14px; }
69
79
  /* RTL support: when the host element has dir=rtl, flip layout direction */
70
80
  [dir="rtl"] .ocw-row.mine { flex-direction:row; }
71
81
  [dir="rtl"] .ocw-row.theirs { flex-direction:row-reverse; }
@@ -114,7 +124,6 @@ const oe = "objectchat-widget-styles", Ce = ["👍", "❤️", "😂", "😮", "
114
124
  .ocw-react-pill { display:inline-flex; align-items:center; gap:3px; border:1px solid #e3ded7; border-radius:999px; padding:2px 7px; background:#fff; font-size:12px; cursor:pointer; }
115
125
  .ocw-react-pill:hover { border-color:var(--ocw-accent); }
116
126
  .ocw-react-pill.mine { border-color:var(--ocw-accent); background:#fff8f5; }
117
- .ocw-react-add { display:none; position:absolute; bottom:100%; left:0; margin-bottom:4px; background:#fff; border:1px solid #e3ded7; border-radius:14px; padding:6px 8px; box-shadow:0 4px 16px rgba(0,0,0,.12); display:flex; gap:4px; z-index:10; }
118
127
  .ocw-react-wrap { position:relative; }
119
128
  .ocw-react-wrap:not(:hover) .ocw-react-picker { display:none; }
120
129
  .ocw-react-picker { position:absolute; bottom:calc(100% + 4px); left:0; background:#fff; border:1px solid #e3ded7; border-radius:14px; padding:6px 8px; box-shadow:0 4px 16px rgba(0,0,0,.12); display:flex; gap:4px; z-index:10; white-space:nowrap; }
@@ -203,10 +212,19 @@ const oe = "objectchat-widget-styles", Ce = ["👍", "❤️", "😂", "😮", "
203
212
  .ocw-footer a:hover { color:var(--ocw-accent); }
204
213
  .ocw-attach { background:none;border:none;cursor:pointer;font-size:18px;padding:4px 6px;opacity:.6;flex-none; }
205
214
  .ocw-attach:hover { opacity:1; }
206
- .ocw-input textarea { flex:1; resize:none; border:1px solid #e3ded7; border-radius:22px; padding:11px 16px; font:inherit; font-size:14px; background:#fff; outline:none; max-height:96px; }
215
+ .ocw-input textarea { flex:1; min-width:0; resize:none; border:1px solid #e3ded7; border-radius:22px; padding:11px 16px; font:inherit; font-size:14px; line-height:1.4; background:#fff; outline:none; max-height:120px; overflow-y:auto; }
207
216
  .ocw-input textarea:focus { border-color:var(--ocw-accent); }
208
- .ocw-sendbtn { width:42px; height:42px; border-radius:50%; border:none; background:var(--ocw-accent); color:#fff; font-size:18px; cursor:pointer; flex:none; display:flex; align-items:center; justify-content:center; }
217
+ /* Send button auto-sizes to its label: the default is a fixed circle around an
218
+ * inline SVG; a TEXT label (i18n.send: "Send", "보내기", "Enviar"…) switches to
219
+ * .ocw-sendbtn-label — a pill whose width follows the text. A fixed 42px
220
+ * circle with 18px type overflowed the moment anyone localized the label. */
221
+ .ocw-sendbtn { min-width:42px; height:42px; border-radius:999px; border:none; background:var(--ocw-accent); color:#fff; font:inherit; font-size:14px; font-weight:600; cursor:pointer; flex:none; display:flex; align-items:center; justify-content:center; padding:0; transition:opacity .15s, transform .1s; }
222
+ .ocw-sendbtn-label { padding:0 16px; white-space:nowrap; }
223
+ .ocw-sendbtn svg { width:19px; height:19px; display:block; }
224
+ .ocw-sendbtn:not(:disabled):hover { transform:scale(1.05); }
225
+ .ocw-sendbtn:not(:disabled):active { transform:scale(.96); }
209
226
  .ocw-sendbtn:disabled { opacity:.5; cursor:default; }
227
+ .ocw-sendbtn:focus-visible, .ocw-back:focus-visible, .ocw-chip:focus-visible { outline:2px solid var(--ocw-accent); outline-offset:2px; }
210
228
  .ocw-cobrowse-btn { display:none; }
211
229
  .ocw-cobrowse-btn.show { display:inline-flex; }
212
230
  .ocw-cobrowse-btn.on { color:var(--ocw-accent); border-color:var(--ocw-accent); }
@@ -226,42 +244,42 @@ const oe = "objectchat-widget-styles", Ce = ["👍", "❤️", "😂", "😮", "
226
244
  .ocw-bubble-wrap:hover .ocw-translate-btn { opacity:1; }
227
245
  .ocw-translated-tag { font-size:10px; color:var(--ocw-mut); margin-top:2px; }
228
246
  `;
229
- function Ie() {
230
- if (typeof document > "u" || document.getElementById(oe)) return;
231
- const o = document.createElement("style");
232
- o.id = oe, o.textContent = Te, document.head.appendChild(o);
247
+ function We() {
248
+ if (typeof document > "u" || document.getElementById(me)) return;
249
+ const t = document.createElement("style");
250
+ t.id = me, t.textContent = Ue, document.head.appendChild(t);
233
251
  }
234
- function t(o, e, i) {
235
- const s = document.createElement(o);
252
+ function o(t, e, i) {
253
+ const s = document.createElement(t);
236
254
  return e && (s.className = e), i !== void 0 && (s.textContent = i), s;
237
255
  }
238
- function Se(o) {
256
+ function Ve(t) {
239
257
  try {
240
- return new Date(o).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
258
+ return new Date(t).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
241
259
  } catch {
242
260
  return "";
243
261
  }
244
262
  }
245
- function W(o) {
263
+ function ie(t) {
246
264
  var e;
247
- switch (o.kind) {
265
+ switch (t.kind) {
248
266
  case "text":
249
- return o.text;
267
+ return t.text;
250
268
  case "system":
251
- return typeof ((e = o.data) == null ? void 0 : e.message) == "string" ? String(o.data.message) : o.event;
269
+ return typeof ((e = t.data) == null ? void 0 : e.message) == "string" ? String(t.data.message) : t.event;
252
270
  case "card":
253
- return [o.title, o.body].filter(Boolean).join(" — ");
271
+ return [t.title, t.body].filter(Boolean).join(" — ");
254
272
  case "attachment":
255
- return o.name ?? o.url;
273
+ return t.name ?? t.url;
256
274
  case "form":
257
- return o.prompt;
275
+ return t.prompt;
258
276
  case "appointment":
259
- return `📅 ${o.title} — ${new Date(o.startIso).toLocaleString()}`;
277
+ return `📅 ${t.title} — ${new Date(t.startIso).toLocaleString()}`;
260
278
  }
261
279
  }
262
- class Ee {
280
+ class Ye {
263
281
  /** Last seq the guest has seen per conversationId — used to compute unread badges. */
264
- constructor(e, i, s, a = {}) {
282
+ constructor(e, i, s, r = {}) {
265
283
  y(this, "scroll");
266
284
  y(this, "chips");
267
285
  y(this, "typing");
@@ -282,6 +300,9 @@ class Ee {
282
300
  y(this, "connStatus");
283
301
  y(this, "typingTimer", null);
284
302
  y(this, "csatSubmitted", !1);
303
+ y(this, "sendBtn");
304
+ /** Container-driven responsive sizing — toggles .ocw-compact (see CSS note). */
305
+ y(this, "compactObserver", null);
285
306
  // ── Co-browsing (shared whiteboard) ─────────────────────────────────────
286
307
  y(this, "cobrowseBtn");
287
308
  y(this, "cobrowseCanvas");
@@ -295,65 +316,77 @@ class Ee {
295
316
  // ── Live translation ──────────────────────────────────────────────────-
296
317
  y(this, "translationCache", /* @__PURE__ */ new Map());
297
318
  y(this, "showingTranslation", /* @__PURE__ */ new Set());
319
+ /** Whether the subject card (server Subject entity, mount config fallback)
320
+ * has been built — built once when data first arrives. */
298
321
  y(this, "subjectBuilt", !1);
299
- var m, h, I, T, $, A, P, z, k;
300
- this.root = e, this.me = i, this.h = s, this.cfg = a, Ie(), e.replaceChildren(), e.classList.add("ocw"), a.accent && e.style.setProperty("--ocw-accent", a.accent);
301
- const c = t("div", "ocw-head");
322
+ var k, I, j, C, R, A, O, U, T, B;
323
+ this.root = e, this.me = i, this.h = s, this.cfg = r, We(), e.replaceChildren(), e.classList.add("ocw"), r.accent && e.style.setProperty("--ocw-accent", r.accent);
324
+ const a = o("div", "ocw-head");
302
325
  if (this.h.onBack) {
303
- const x = t("button", "ocw-back", "‹");
304
- x.type = "button", x.setAttribute("aria-label", "Back"), x.addEventListener("click", () => this.h.onBack()), c.append(x);
326
+ const m = o("button", "ocw-back", "‹");
327
+ m.type = "button", m.setAttribute("aria-label", "Back"), m.addEventListener("click", () => this.h.onBack()), a.append(m);
305
328
  }
306
- const f = t("div", "ocw-avatar");
307
- if ((m = a.userInfo) != null && m.avatar) {
308
- const x = document.createElement("img");
309
- x.src = a.userInfo.avatar, x.alt = a.userInfo.name ?? "You", x.style.cssText = "width:100%;height:100%;border-radius:50%;object-fit:cover", f.append(x);
329
+ const f = o("div", "ocw-avatar");
330
+ if ((k = r.userInfo) != null && k.avatar) {
331
+ const m = document.createElement("img");
332
+ m.src = r.userInfo.avatar, m.alt = r.userInfo.name ?? "You", m.style.cssText = "width:100%;height:100%;border-radius:50%;object-fit:cover", f.append(m);
310
333
  } else
311
- f.textContent = (h = a.userInfo) != null && h.name ? a.userInfo.name[0].toUpperCase() : "🧑";
312
- c.append(f);
313
- const b = t("div", "ocw-head-main");
314
- this.headerName = t("div", "ocw-head-name", ((I = a.subject) == null ? void 0 : I.ownerLabel) ?? ((T = a.subject) == null ? void 0 : T.title) ?? ""), b.append(this.headerName), ($ = a.subject) != null && $.subtitle && b.append(t("div", "ocw-head-meta", a.subject.subtitle)), c.append(b), this.statusBadge = t("span", "ocw-badge", ((A = a.subject) == null ? void 0 : A.status) ?? ""), (P = a.subject) != null && P.status || (this.statusBadge.style.display = "none"), c.append(this.statusBadge), this.e2eBadge = t("span", "ocw-e2e", "🔒 E2E"), this.e2eBadge.style.display = "none", c.append(this.e2eBadge), this.connStatus = t("span", "ocw-conn-status"), this.connStatus.style.display = "none", c.append(this.connStatus), this.cobrowseBtn = t("button", "ocw-menu ocw-cobrowse-btn", "🖍"), this.cobrowseBtn.title = "Shared whiteboard — draw to point things out together", this.cobrowseBtn.addEventListener("click", () => {
334
+ f.textContent = (I = r.userInfo) != null && I.name ? r.userInfo.name[0].toUpperCase() : "🧑";
335
+ a.append(f);
336
+ const b = o("div", "ocw-head-main");
337
+ this.headerName = o("div", "ocw-head-name", ((j = r.subject) == null ? void 0 : j.ownerLabel) ?? ((C = r.subject) == null ? void 0 : C.title) ?? ""), b.append(this.headerName), (R = r.subject) != null && R.subtitle && b.append(o("div", "ocw-head-meta", r.subject.subtitle)), a.append(b), this.statusBadge = o("span", "ocw-badge", ((A = r.subject) == null ? void 0 : A.status) ?? ""), (O = r.subject) != null && O.status || (this.statusBadge.style.display = "none"), a.append(this.statusBadge), this.e2eBadge = o("span", "ocw-e2e", "🔒 E2E"), this.e2eBadge.style.display = "none", a.append(this.e2eBadge), this.connStatus = o("span", "ocw-conn-status"), this.connStatus.style.display = "none", a.append(this.connStatus), this.cobrowseBtn = o("button", "ocw-menu ocw-cobrowse-btn", "🖍"), this.cobrowseBtn.title = "Shared whiteboard — draw to point things out together", this.cobrowseBtn.addEventListener("click", () => {
315
338
  this.cobrowseActive = !this.cobrowseActive, this.updateCobrowseUI();
316
- }), c.append(this.cobrowseBtn), c.append(t("button", "ocw-menu", "⋯")), this.chips = t("div", "ocw-chiprow"), this.scroll = t("div", "ocw-scroll"), this.subjectCard = t("div", "ocw-subject"), this.typing = t("div", "ocw-typing");
317
- const d = t("div", "ocw-typing-bubble");
318
- d.append(t("div", "ocw-typing-dot"), t("div", "ocw-typing-dot"), t("div", "ocw-typing-dot")), this.typing.append(t("div", "ocw-dot", "🧑"), d), this.quick = t("div", "ocw-quick");
319
- for (const x of a.quickReplies ?? []) {
320
- const S = t("button", void 0, x);
339
+ }), a.append(this.cobrowseBtn), a.append(o("button", "ocw-menu", "⋯")), this.chips = o("div", "ocw-chiprow"), this.scroll = o("div", "ocw-scroll"), this.subjectCard = o("div", "ocw-subject"), this.typing = o("div", "ocw-typing");
340
+ const d = o("div", "ocw-typing-bubble");
341
+ d.append(o("div", "ocw-typing-dot"), o("div", "ocw-typing-dot"), o("div", "ocw-typing-dot")), this.typing.append(o("div", "ocw-dot", "🧑"), d), this.quick = o("div", "ocw-quick");
342
+ for (const m of r.quickReplies ?? []) {
343
+ const S = o("button", void 0, m);
321
344
  S.addEventListener("click", () => {
322
- this.h.onSend(x), this.quick.style.display = "none";
345
+ this.h.onSend(m), this.quick.style.display = "none";
323
346
  }), this.quick.append(S);
324
347
  }
325
- this.formHost = t("div", "ocw-form-host"), this.csatPanel = t("div", "ocw-csat"), this.csatPanel.style.display = "none", this.offlinePanel = t("div", "ocw-offline"), this.offlinePanel.style.display = "none", this.preChatPanel = t("div", "ocw-prechat"), this.preChatPanel.style.display = "none", this.deflectPanel = t("div", "ocw-deflect"), this.deflectPanel.style.display = "none", this.input = t("textarea", void 0), this.input.rows = 1, this.input.placeholder = "Message…";
326
- const w = t("button", "ocw-sendbtn", ((z = a.i18n) == null ? void 0 : z.send) ?? "➤");
327
- w.addEventListener("click", () => this.flushSend()), this.input.addEventListener("input", () => {
328
- var x, S;
329
- this.storeRef && !this.storeRef.messages().some((B) => B.senderRole === "guest") ? (S = (x = this.h).onDeflectQuery) == null || S.call(x, this.input.value) : this.hideDeflection();
330
- }), this.input.addEventListener("keydown", (x) => {
331
- x.isComposing || x.keyCode === 229 || (x.key === "Enter" && !x.shiftKey ? (x.preventDefault(), this.flushSend()) : this.signalTyping());
348
+ this.formHost = o("div", "ocw-form-host"), this.csatPanel = o("div", "ocw-csat"), this.csatPanel.style.display = "none", this.offlinePanel = o("div", "ocw-offline"), this.offlinePanel.style.display = "none", this.preChatPanel = o("div", "ocw-prechat"), this.preChatPanel.style.display = "none", this.deflectPanel = o("div", "ocw-deflect"), this.deflectPanel.style.display = "none", this.input = o("textarea", void 0), this.input.rows = 1, this.input.placeholder = ((U = r.i18n) == null ? void 0 : U.placeholder) ?? "Message…";
349
+ const h = o("button", "ocw-sendbtn");
350
+ h.type = "button";
351
+ const v = (T = r.i18n) == null ? void 0 : T.send;
352
+ v ? (h.textContent = v, h.classList.add("ocw-sendbtn-label"), h.setAttribute("aria-label", v)) : (h.innerHTML = Fe, h.setAttribute("aria-label", "Send message")), h.disabled = !0, this.sendBtn = h, h.addEventListener("click", () => this.flushSend()), this.input.addEventListener("input", () => {
353
+ var m, S;
354
+ this.sendBtn.disabled = this.input.value.trim().length === 0, this.autoGrowInput(), this.storeRef && !this.storeRef.messages().some((q) => q.senderRole === "guest") ? (S = (m = this.h).onDeflectQuery) == null || S.call(m, this.input.value) : this.hideDeflection();
355
+ }), this.input.addEventListener("keydown", (m) => {
356
+ m.isComposing || m.keyCode === 229 || (m.key === "Enter" && !m.shiftKey ? (m.preventDefault(), this.flushSend()) : this.signalTyping());
332
357
  });
333
- const v = t("button", "ocw-attach", "📎");
334
- v.title = "Attach image or file";
335
- const r = document.createElement("input");
336
- r.type = "file", r.accept = "image/*,.pdf,.txt,.doc,.docx", r.style.display = "none", v.addEventListener("click", () => r.click()), r.addEventListener("change", () => {
337
- var x;
338
- (x = r.files) != null && x[0] && this.h.onAttach && this.h.onAttach(r.files[0]), r.value = "";
358
+ const c = o("button", "ocw-attach", "📎");
359
+ c.title = "Attach image or file";
360
+ const u = document.createElement("input");
361
+ u.type = "file", u.accept = "image/*,.pdf,.txt,.doc,.docx", u.style.display = "none", c.addEventListener("click", () => u.click()), u.addEventListener("change", () => {
362
+ var m;
363
+ (m = u.files) != null && m[0] && this.h.onAttach && this.h.onAttach(u.files[0]), u.value = "";
339
364
  });
340
- const u = t("div", "ocw-input");
341
- u.append(v, r, this.input, w);
342
- const l = t("div", "ocw-footer");
343
- ((k = a.i18n) == null ? void 0 : k.poweredBy) !== void 0 ? l.textContent = a.i18n.poweredBy : l.innerHTML = 'Powered by <a href="https://relay.paramms.com" target="_blank" rel="noopener">Relay</a>', this.footer = l, e.append(c, this.chips, this.scroll, this.typing, this.quick, this.formHost, this.csatPanel, this.offlinePanel, this.preChatPanel, this.deflectPanel, u, this.footer), this.cobrowseCanvas = t("canvas", "ocw-cobrowse-canvas"), this.cobrowseToolbar = t("div", "ocw-cobrowse-toolbar");
344
- for (const x of ["#f5713c", "#1c1b1a", "#2563eb", "#16a34a", "#dc2626"]) {
345
- const S = t("button", "ocw-cobrowse-swatch");
346
- S.style.background = x, S.type = "button", x === this.cobrowseColor && S.classList.add("sel"), S.addEventListener("click", () => {
347
- this.cobrowseColor = x;
348
- for (const B of this.cobrowseToolbar.querySelectorAll(".ocw-cobrowse-swatch")) B.classList.remove("sel");
365
+ const l = o("div", "ocw-input");
366
+ l.append(c, u, this.input, h);
367
+ const E = o("div", "ocw-footer");
368
+ ((B = r.i18n) == null ? void 0 : B.poweredBy) !== void 0 ? E.textContent = r.i18n.poweredBy : E.innerHTML = 'Powered by <a href="https://relay.paramms.com" target="_blank" rel="noopener">Relay</a>', this.footer = E, e.append(a, this.chips, this.scroll, this.typing, this.quick, this.formHost, this.csatPanel, this.offlinePanel, this.preChatPanel, this.deflectPanel, l, this.footer), this.cobrowseCanvas = o("canvas", "ocw-cobrowse-canvas"), this.cobrowseToolbar = o("div", "ocw-cobrowse-toolbar");
369
+ for (const m of ["#f5713c", "#1c1b1a", "#2563eb", "#16a34a", "#dc2626"]) {
370
+ const S = o("button", "ocw-cobrowse-swatch");
371
+ S.style.background = m, S.type = "button", m === this.cobrowseColor && S.classList.add("sel"), S.addEventListener("click", () => {
372
+ this.cobrowseColor = m;
373
+ for (const q of this.cobrowseToolbar.querySelectorAll(".ocw-cobrowse-swatch")) q.classList.remove("sel");
349
374
  S.classList.add("sel");
350
375
  }), this.cobrowseToolbar.append(S);
351
376
  }
352
- const C = t("button", "ocw-cobrowse-clear", "Clear");
353
- C.addEventListener("click", () => {
354
- var x, S;
355
- return (S = (x = this.h).onAnnotateClear) == null ? void 0 : S.call(x);
356
- }), this.cobrowseToolbar.append(C), this.cobrowseHint = t("div", "ocw-cobrowse-hint", "🖍 Draw to point things out — visible to both sides"), e.append(this.cobrowseCanvas, this.cobrowseToolbar, this.cobrowseHint), this.bindCobrowsePointerEvents();
377
+ const g = o("button", "ocw-cobrowse-clear", "Clear");
378
+ g.addEventListener("click", () => {
379
+ var m, S;
380
+ return (S = (m = this.h).onAnnotateClear) == null ? void 0 : S.call(m);
381
+ }), this.cobrowseToolbar.append(g), this.cobrowseHint = o("div", "ocw-cobrowse-hint", "🖍 Draw to point things out — visible to both sides"), e.append(this.cobrowseCanvas, this.cobrowseToolbar, this.cobrowseHint), this.bindCobrowsePointerEvents();
382
+ const w = (m) => {
383
+ e.classList.toggle("ocw-compact", m > 0 && m < _e);
384
+ };
385
+ w(e.clientWidth), typeof ResizeObserver < "u" && (this.compactObserver = new ResizeObserver((m) => {
386
+ var q;
387
+ const S = ((q = m[0]) == null ? void 0 : q.contentRect.width) ?? e.clientWidth;
388
+ w(S);
389
+ }), this.compactObserver.observe(e));
357
390
  }
358
391
  /** Returns the scroll container so history.ts can attach scroll listeners. */
359
392
  getScrollEl() {
@@ -366,48 +399,51 @@ class Ee {
366
399
  }
367
400
  /** Call when the widget is unmounted. Disconnects scroll listeners and clears timers. */
368
401
  destroy() {
369
- var e;
370
- (e = this.scrollCleanup) == null || e.call(this), this.scrollCleanup = null, this.typingTimer && (clearTimeout(this.typingTimer), this.typingTimer = null);
402
+ var e, i;
403
+ (e = this.scrollCleanup) == null || e.call(this), this.scrollCleanup = null, this.typingTimer && (clearTimeout(this.typingTimer), this.typingTimer = null), (i = this.compactObserver) == null || i.disconnect(), this.compactObserver = null;
371
404
  }
372
- /** Render list rows, optionally filtered by search query. */
373
- /** Build a single WhatsApp-style conversation row. */
374
- /** Returns true when the chat screen is visible (not the list). */
375
- /** Navigate to the chat screen (slide list left, slide chat in from right). */
376
- /** Navigate back to the list screen. */
377
405
  flushSend() {
378
406
  this.hideDeflection();
379
407
  const e = this.input.value.trim();
380
- e && (this.input.value = "", this.h.onTyping(!1), this.h.onSend(e));
408
+ e && (this.input.value = "", this.sendBtn.disabled = !0, this.autoGrowInput(), this.h.onTyping(!1), this.h.onSend(e));
409
+ }
410
+ /** Grow the composer with its content (up to the CSS max-height), collapse
411
+ * when cleared. scrollHeight is 0 in non-layout environments (jsdom) —
412
+ * skip there so tests and SSR-ish mounts are unaffected. */
413
+ autoGrowInput() {
414
+ this.input.style.height = "auto";
415
+ const e = this.input.scrollHeight;
416
+ e > 0 ? this.input.style.height = `${Math.min(e, 120)}px` : this.input.style.removeProperty("height");
381
417
  }
382
418
  signalTyping() {
383
419
  const e = this.input.value.trim().slice(0, 100) || void 0;
384
420
  this.h.onTyping(!0, e), this.typingTimer && clearTimeout(this.typingTimer), this.typingTimer = setTimeout(() => this.h.onTyping(!1), 2e3);
385
421
  }
386
422
  render(e) {
387
- var r, u, l, C, m;
423
+ var c, u, l, E, g;
388
424
  if (this.storeRef = e, this.cobrowseBtn.classList.toggle("show", !!e.subject), !e.subject && this.cobrowseActive && (this.cobrowseActive = !1, this.updateCobrowseUI()), this.cobrowseActive && e.annotationVersion !== this.lastAnnotationVersion && (this.lastAnnotationVersion = e.annotationVersion, this.resizeCobrowseCanvas(), this.redrawCobrowse()), e.accent && this.root.style.setProperty("--ocw-accent", e.accent), this.e2eBadge.style.display = e.e2e ? "inline-flex" : "none", this.buildSubjectCard(e), e.subject) {
389
- const h = (r = this.cfg.subject) == null ? void 0 : r.ownerLabel;
390
- h && (this.headerName.textContent = h);
425
+ const w = (c = this.cfg.subject) == null ? void 0 : c.ownerLabel;
426
+ w && (this.headerName.textContent = w);
391
427
  }
392
428
  this.quick.style.display = e.messages().length === 0 ? "flex" : "none", this.chips.replaceChildren();
393
429
  const i = e.visibleActions();
394
430
  this.chips.style.display = i.length ? "flex" : "none";
395
- for (const h of i) this.chips.append(this.chipEl(h));
396
- const s = this.scroll.scrollHeight, a = this.scroll.scrollTop;
431
+ for (const w of i) this.chips.append(this.chipEl(w));
432
+ const s = this.scroll.scrollHeight, r = this.scroll.scrollTop;
397
433
  if (this.scroll.replaceChildren(), this.subjectCard.childNodes.length && this.scroll.append(this.subjectCard), e.hasMoreHistory) {
398
- const h = t("div", "ocw-load-more");
399
- h.textContent = "↑ Loading earlier messages…", h.style.pointerEvents = "none", this.scroll.append(h);
434
+ const w = o("div", "ocw-load-more");
435
+ w.textContent = "↑ Loading earlier messages…", w.style.pointerEvents = "none", this.scroll.append(w);
400
436
  }
401
- let c = 0;
402
- for (const h of e.messages())
403
- this.scroll.append(this.messageEl(h, e)), h.senderId !== this.me && h.seq > c && (c = h.seq);
404
- a > 20 ? this.scroll.scrollTop = this.scroll.scrollHeight - s + a : this.scroll.scrollTop = this.scroll.scrollHeight, c > 0 && this.h.onReadUpTo(c);
437
+ let a = 0;
438
+ for (const w of e.messages())
439
+ this.scroll.append(this.messageEl(w, e)), w.senderId !== this.me && w.seq > a && (a = w.seq);
440
+ r > 20 ? this.scroll.scrollTop = this.scroll.scrollHeight - s + r : this.scroll.scrollTop = this.scroll.scrollHeight, a > 0 && this.h.onReadUpTo(a);
405
441
  const f = [...e.typing];
406
442
  this.typing.classList.toggle("active", f.length > 0);
407
443
  const b = this.typing.querySelector(".ocw-typing-bubble");
408
444
  b && b.setAttribute("aria-label", f.length ? "typing" : ""), this.footer.style.display = e.whiteLabel ? "none" : "block";
409
- const d = e.messages().some((h) => h.senderRole === "guest");
410
- !!((u = e.preChat) != null && u.enabled) && !this.preChatDone && !d && (e.preChat.showWhen !== "offline" || e.offline) ? (this.preChatBuilt || this.buildPreChatPanel(e.preChat), this.preChatPanel.style.display = "block", this.offlinePanel.style.display = "none", (l = this.root.querySelector(".ocw-input")) == null || l.style.setProperty("display", "none")) : (this.preChatPanel.style.display = "none", e.offline ? (this.offlinePanel.style.display === "none" && this.buildOfflinePanel(e.offlineMessage), this.offlinePanel.style.display = "block", (C = this.root.querySelector(".ocw-input")) == null || C.style.setProperty("display", "none")) : (this.offlinePanel.style.display = "none", (m = this.root.querySelector(".ocw-input")) == null || m.style.removeProperty("display")));
445
+ const d = e.messages().some((w) => w.senderRole === "guest");
446
+ !!((u = e.preChat) != null && u.enabled) && !this.preChatDone && !d && (e.preChat.showWhen !== "offline" || e.offline) ? (this.preChatBuilt || this.buildPreChatPanel(e.preChat), this.preChatPanel.style.display = "block", this.offlinePanel.style.display = "none", (l = this.root.querySelector(".ocw-input")) == null || l.style.setProperty("display", "none")) : (this.preChatPanel.style.display = "none", e.offline ? (this.offlinePanel.style.display === "none" && this.buildOfflinePanel(e.offlineMessage), this.offlinePanel.style.display = "block", (E = this.root.querySelector(".ocw-input")) == null || E.style.setProperty("display", "none")) : (this.offlinePanel.style.display = "none", (g = this.root.querySelector(".ocw-input")) == null || g.style.removeProperty("display")));
411
447
  const v = ["resolved", "closed", "sold", "issued", "checked_out"];
412
448
  this.h.onCsat && !this.csatSubmitted && v.includes(e.state) && e.messages().length > 0 && (this.csatPanel.style.display === "none" && this.buildCsatPanel(), this.csatPanel.style.display = "block");
413
449
  }
@@ -422,67 +458,67 @@ class Ee {
422
458
  }
423
459
  buildOfflinePanel(e) {
424
460
  var b;
425
- this.offlinePanel.replaceChildren(), this.offlinePanel.append(t("div", "ocw-offline-icon", "🌙")), this.offlinePanel.append(t("div", "ocw-offline-title", ((b = this.cfg.i18n) == null ? void 0 : b.offline) ?? "We're offline right now")), this.offlinePanel.append(t("div", "ocw-offline-msg", e || "Leave your details and we'll get back to you soon."));
426
- const i = t("div", "ocw-offline-form"), s = t("input", "ocw-offline-input");
461
+ this.offlinePanel.replaceChildren(), this.offlinePanel.append(o("div", "ocw-offline-icon", "🌙")), this.offlinePanel.append(o("div", "ocw-offline-title", ((b = this.cfg.i18n) == null ? void 0 : b.offline) ?? "We're offline right now")), this.offlinePanel.append(o("div", "ocw-offline-msg", e || "Leave your details and we'll get back to you soon."));
462
+ const i = o("div", "ocw-offline-form"), s = o("input", "ocw-offline-input");
427
463
  s.placeholder = "Your name", s.type = "text";
428
- const a = t("input", "ocw-offline-input");
429
- a.placeholder = "Your email", a.type = "email";
430
- const c = t("textarea", "ocw-offline-input");
431
- c.placeholder = "Your message", c.rows = 3;
432
- const f = t("button", "ocw-offline-submit", "Send message");
433
- f.addEventListener("click", () => {
434
- !a.value.trim() || !c.value.trim() || (this.h.onSend(`[Offline form]
464
+ const r = o("input", "ocw-offline-input");
465
+ r.placeholder = "Your email", r.type = "email";
466
+ const a = o("textarea", "ocw-offline-input");
467
+ a.placeholder = "Your message", a.rows = 3;
468
+ const f = o("button", "ocw-offline-submit", "Send message");
469
+ f.type = "button", f.addEventListener("click", () => {
470
+ !r.value.trim() || !a.value.trim() || (f.disabled = !0, this.h.onSend(`[Offline form]
435
471
  Name: ${s.value || "Anonymous"}
436
- Email: ${a.value}
437
- Message: ${c.value}`), this.offlinePanel.replaceChildren(t("div", "ocw-offline-thanks", "✓ Message sent! We'll reply to your email.")));
438
- }), i.append(s, a, c, f), this.offlinePanel.append(i);
472
+ Email: ${r.value}
473
+ Message: ${a.value}`), this.offlinePanel.replaceChildren(o("div", "ocw-offline-thanks", "✓ Message sent! We'll reply to your email.")));
474
+ }), i.append(s, r, a, f), this.offlinePanel.append(i);
439
475
  }
440
476
  buildPreChatPanel(e) {
441
477
  var d;
442
- this.preChatBuilt = !0, this.preChatPanel.replaceChildren(), this.preChatPanel.append(t("div", "ocw-prechat-title", e.title ?? "Before we start…"));
443
- const i = t("div", "ocw-offline-form"), s = {};
444
- for (const w of e.fields ?? ["name", "email"]) {
445
- const v = t("input", "ocw-offline-input");
446
- v.type = w === "email" ? "email" : w === "phone" ? "tel" : "text", v.placeholder = w === "name" ? "Your name" : w === "email" ? "Your email" : "Your phone number", s[w] = v, i.append(v);
478
+ this.preChatBuilt = !0, this.preChatPanel.replaceChildren(), this.preChatPanel.append(o("div", "ocw-prechat-title", e.title ?? "Before we start…"));
479
+ const i = o("div", "ocw-offline-form"), s = {};
480
+ for (const h of e.fields ?? ["name", "email"]) {
481
+ const v = o("input", "ocw-offline-input");
482
+ v.type = h === "email" ? "email" : h === "phone" ? "tel" : "text", v.placeholder = h === "name" ? "Your name" : h === "email" ? "Your email" : "Your phone number", s[h] = v, i.append(v);
447
483
  }
448
- let a = null;
484
+ let r = null;
449
485
  if ((d = e.topics) != null && d.length) {
450
- a = t("select", void 0);
451
- const w = document.createElement("option");
452
- w.value = "", w.textContent = "What is this about?", a.append(w);
486
+ r = o("select", void 0);
487
+ const h = document.createElement("option");
488
+ h.value = "", h.textContent = "What is this about?", r.append(h);
453
489
  for (const v of e.topics) {
454
- const r = document.createElement("option");
455
- r.value = v, r.textContent = v, a.append(r);
490
+ const c = document.createElement("option");
491
+ c.value = v, c.textContent = v, r.append(c);
456
492
  }
457
- i.append(a);
493
+ i.append(r);
458
494
  }
459
- let c = null, f = null;
495
+ let a = null, f = null;
460
496
  if (e.callbackOption) {
461
- const w = t("label", "ocw-prechat-cb");
462
- c = document.createElement("input"), c.type = "checkbox", w.append(c, document.createTextNode("📞 Request a call back")), i.append(w), s.phone || (f = t("input", "ocw-offline-input"), f.type = "tel", f.placeholder = "Phone number for the call", f.style.display = "none", c.addEventListener("change", () => f.style.setProperty("display", c.checked ? "block" : "none")), i.append(f));
497
+ const h = o("label", "ocw-prechat-cb");
498
+ a = document.createElement("input"), a.type = "checkbox", h.append(a, document.createTextNode("📞 Request a call back")), i.append(h), s.phone || (f = o("input", "ocw-offline-input"), f.type = "tel", f.placeholder = "Phone number for the call", f.style.display = "none", a.addEventListener("change", () => f.style.setProperty("display", a.checked ? "block" : "none")), i.append(f));
463
499
  }
464
- const b = t("button", "ocw-offline-submit", "Start chat");
465
- b.addEventListener("click", () => {
466
- var u, l, C, m, h, I, T;
467
- const w = (u = s.email) == null ? void 0 : u.value.trim();
468
- if (s.email && (!w || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(w))) {
500
+ const b = o("button", "ocw-offline-submit", "Start chat");
501
+ b.type = "button", b.addEventListener("click", () => {
502
+ var u, l, E, g, w, k, I;
503
+ const h = (u = s.email) == null ? void 0 : u.value.trim();
504
+ if (s.email && (!h || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(h))) {
469
505
  s.email.focus();
470
506
  return;
471
507
  }
472
- const v = !!(c != null && c.checked), r = (((l = s.phone) == null ? void 0 : l.value) ?? (f == null ? void 0 : f.value) ?? "").trim();
473
- if (v && !r) {
474
- (C = s.phone ?? f) == null || C.focus();
508
+ const v = !!(a != null && a.checked), c = (((l = s.phone) == null ? void 0 : l.value) ?? (f == null ? void 0 : f.value) ?? "").trim();
509
+ if (v && !c) {
510
+ (E = s.phone ?? f) == null || E.focus();
475
511
  return;
476
512
  }
477
- if (a && ((m = e.topics) != null && m.length) && !a.value) {
478
- a.focus();
513
+ if (r && ((g = e.topics) != null && g.length) && !r.value) {
514
+ r.focus();
479
515
  return;
480
516
  }
481
- this.completePreChat(), (T = (I = this.h).onPreChat) == null || T.call(I, {
482
- ...(h = s.name) != null && h.value.trim() ? { name: s.name.value.trim() } : {},
483
- ...w ? { email: w } : {},
484
- ...r ? { phone: r } : {},
485
- ...a != null && a.value ? { topic: a.value } : {},
517
+ b.disabled = !0, this.completePreChat(), (I = (k = this.h).onPreChat) == null || I.call(k, {
518
+ ...(w = s.name) != null && w.value.trim() ? { name: s.name.value.trim() } : {},
519
+ ...h ? { email: h } : {},
520
+ ...c ? { phone: c } : {},
521
+ ...r != null && r.value ? { topic: r.value } : {},
486
522
  ...v ? { callback: !0 } : {}
487
523
  });
488
524
  }), i.append(b), this.preChatPanel.append(i);
@@ -494,10 +530,10 @@ Message: ${c.value}`), this.offlinePanel.replaceChildren(t("div", "ocw-offline-t
494
530
  /** KB deflection results ("was this your question?") above the composer. */
495
531
  showDeflection(e) {
496
532
  if (!e.length) return this.hideDeflection();
497
- this.deflectPanel.replaceChildren(), this.deflectPanel.append(t("div", "ocw-deflect-hint", "Instant answers — tap to expand"));
533
+ this.deflectPanel.replaceChildren(), this.deflectPanel.append(o("div", "ocw-deflect-hint", "Instant answers — tap to expand"));
498
534
  for (const i of e.slice(0, 3)) {
499
- const s = t("button", "ocw-deflect-card");
500
- s.append(t("div", "ocw-deflect-q", i.title), t("div", "ocw-deflect-a", i.answer)), s.addEventListener("click", () => s.classList.toggle("open")), this.deflectPanel.append(s);
535
+ const s = o("button", "ocw-deflect-card");
536
+ s.append(o("div", "ocw-deflect-q", i.title), o("div", "ocw-deflect-a", i.answer)), s.addEventListener("click", () => s.classList.toggle("open")), this.deflectPanel.append(s);
501
537
  }
502
538
  this.deflectPanel.style.display = "flex";
503
539
  }
@@ -505,19 +541,17 @@ Message: ${c.value}`), this.offlinePanel.replaceChildren(t("div", "ocw-offline-t
505
541
  this.deflectPanel.style.display = "none", this.deflectPanel.replaceChildren();
506
542
  }
507
543
  buildCsatPanel() {
508
- this.csatPanel.replaceChildren(), this.csatPanel.append(t("div", "ocw-csat-title", "How did we do?"));
509
- const e = t("div", "ocw-csat-stars"), i = [];
544
+ this.csatPanel.replaceChildren(), this.csatPanel.append(o("div", "ocw-csat-title", "How did we do?"));
545
+ const e = o("div", "ocw-csat-stars"), i = [];
510
546
  for (let s = 1; s <= 5; s++) {
511
- const a = t("button", "ocw-csat-star", "★");
512
- a.dataset.score = String(s), a.addEventListener("mouseenter", () => i.forEach((c, f) => c.classList.toggle("lit", f < s))), a.addEventListener("mouseleave", () => i.forEach((c) => c.classList.remove("lit"))), a.addEventListener("click", () => {
513
- var c, f;
514
- this.csatSubmitted = !0, this.csatPanel.replaceChildren(t("div", "ocw-csat-done", `Thanks for your ${s}★ rating!`)), (f = (c = this.h).onCsat) == null || f.call(c, s);
515
- }), i.push(a), e.append(a);
547
+ const r = o("button", "ocw-csat-star", "★");
548
+ r.dataset.score = String(s), r.addEventListener("mouseenter", () => i.forEach((a, f) => a.classList.toggle("lit", f < s))), r.addEventListener("mouseleave", () => i.forEach((a) => a.classList.remove("lit"))), r.addEventListener("click", () => {
549
+ var a, f;
550
+ this.csatSubmitted = !0, this.csatPanel.replaceChildren(o("div", "ocw-csat-done", `Thanks for your ${s}★ rating!`)), (f = (a = this.h).onCsat) == null || f.call(a, s);
551
+ }), i.push(r), e.append(r);
516
552
  }
517
553
  this.csatPanel.append(e);
518
554
  }
519
- /** Subject card from the server's Subject entity (per chatroom), with the
520
- * mount config as a fallback. Built once when data first arrives. */
521
555
  // ── Co-browsing (shared whiteboard) ──────────────────────────────────────
522
556
  updateCobrowseUI() {
523
557
  this.cobrowseCanvas.classList.toggle("active", this.cobrowseActive), this.cobrowseToolbar.classList.toggle("active", this.cobrowseActive), this.cobrowseHint.classList.toggle("active", this.cobrowseActive), this.cobrowseBtn.classList.toggle("on", this.cobrowseActive), this.cobrowseActive && (this.resizeCobrowseCanvas(), this.redrawCobrowse());
@@ -530,9 +564,9 @@ Message: ${c.value}`), this.offlinePanel.replaceChildren(t("div", "ocw-offline-t
530
564
  drawStroke(e) {
531
565
  const i = this.cobrowseCanvas.getContext("2d");
532
566
  if (!i || e.points.length < 2) return;
533
- const s = this.cobrowseCanvas.width, a = this.cobrowseCanvas.height;
534
- i.strokeStyle = e.color, i.lineWidth = Math.max(1, e.width * Math.min(s, a)), i.lineJoin = "round", i.lineCap = "round", i.beginPath(), i.moveTo(e.points[0].x * s, e.points[0].y * a);
535
- for (const c of e.points.slice(1)) i.lineTo(c.x * s, c.y * a);
567
+ const s = this.cobrowseCanvas.width, r = this.cobrowseCanvas.height;
568
+ i.strokeStyle = e.color, i.lineWidth = Math.max(1, e.width * Math.min(s, r)), i.lineJoin = "round", i.lineCap = "round", i.beginPath(), i.moveTo(e.points[0].x * s, e.points[0].y * r);
569
+ for (const a of e.points.slice(1)) i.lineTo(a.x * s, a.y * r);
536
570
  i.stroke();
537
571
  }
538
572
  redrawCobrowse() {
@@ -546,40 +580,40 @@ Message: ${c.value}`), this.offlinePanel.replaceChildren(t("div", "ocw-offline-t
546
580
  bindCobrowsePointerEvents() {
547
581
  let e = !1, i = [];
548
582
  const s = (f) => {
549
- const b = this.cobrowseCanvas.getBoundingClientRect(), d = b.width > 0 ? (f.clientX - b.left) / b.width : 0, w = b.height > 0 ? (f.clientY - b.top) / b.height : 0;
550
- return { x: Math.min(1, Math.max(0, d)), y: Math.min(1, Math.max(0, w)) };
551
- }, a = 6e-3;
583
+ const b = this.cobrowseCanvas.getBoundingClientRect(), d = b.width > 0 ? (f.clientX - b.left) / b.width : 0, h = b.height > 0 ? (f.clientY - b.top) / b.height : 0;
584
+ return { x: Math.min(1, Math.max(0, d)), y: Math.min(1, Math.max(0, h)) };
585
+ }, r = 6e-3;
552
586
  this.cobrowseCanvas.addEventListener("pointerdown", (f) => {
553
587
  this.cobrowseActive && (e = !0, i = [s(f)], this.cobrowseCanvas.setPointerCapture(f.pointerId));
554
588
  }), this.cobrowseCanvas.addEventListener("pointermove", (f) => {
555
- e && (i.push(s(f)), this.redrawCobrowse(), this.drawStroke({ points: i, color: this.cobrowseColor, width: a }));
589
+ e && (i.push(s(f)), this.redrawCobrowse(), this.drawStroke({ points: i, color: this.cobrowseColor, width: r }));
556
590
  });
557
- const c = (f) => {
591
+ const a = (f) => {
558
592
  var b, d;
559
593
  if (e) {
560
- e = !1, i.length > 1 && ((d = (b = this.h).onAnnotate) == null || d.call(b, { id: `an_${Date.now()}_${Math.random().toString(36).slice(2)}`, points: i, color: this.cobrowseColor, width: a })), i = [];
594
+ e = !1, i.length > 1 && ((d = (b = this.h).onAnnotate) == null || d.call(b, { id: `an_${Date.now()}_${Math.random().toString(36).slice(2)}`, points: i, color: this.cobrowseColor, width: r })), i = [];
561
595
  try {
562
596
  this.cobrowseCanvas.releasePointerCapture(f.pointerId);
563
597
  } catch {
564
598
  }
565
599
  }
566
600
  };
567
- this.cobrowseCanvas.addEventListener("pointerup", c), this.cobrowseCanvas.addEventListener("pointercancel", c);
601
+ this.cobrowseCanvas.addEventListener("pointerup", a), this.cobrowseCanvas.addEventListener("pointercancel", a);
568
602
  }
569
603
  buildSubjectCard(e) {
570
604
  if (this.subjectBuilt) return;
571
- const i = e.subject, s = this.cfg.subject, a = (i == null ? void 0 : i.title) ?? (s == null ? void 0 : s.title);
572
- if (!a) return;
573
- this.subjectBuilt = !0, this.subjectCard.replaceChildren(), this.subjectCard.append(t("div", "ocw-subject-title", a)), s != null && s.subtitle && this.subjectCard.append(t("div", "ocw-subject-sub", s.subtitle));
574
- const c = t("div", "ocw-tags");
575
- if (i) for (const [b, d] of Object.entries(i.fields)) c.append(t("span", "ocw-tag", `${b}: ${d}`));
576
- else for (const b of (s == null ? void 0 : s.tags) ?? []) c.append(t("span", "ocw-tag", b));
577
- c.childNodes.length && this.subjectCard.append(c);
605
+ const i = e.subject, s = this.cfg.subject, r = (i == null ? void 0 : i.title) ?? (s == null ? void 0 : s.title);
606
+ if (!r) return;
607
+ this.subjectBuilt = !0, this.subjectCard.replaceChildren(), this.subjectCard.append(o("div", "ocw-subject-title", r)), s != null && s.subtitle && this.subjectCard.append(o("div", "ocw-subject-sub", s.subtitle));
608
+ const a = o("div", "ocw-tags");
609
+ if (i) for (const [b, d] of Object.entries(i.fields)) a.append(o("span", "ocw-tag", `${b}: ${d}`));
610
+ else for (const b of (s == null ? void 0 : s.tags) ?? []) a.append(o("span", "ocw-tag", b));
611
+ a.childNodes.length && this.subjectCard.append(a);
578
612
  const f = (i == null ? void 0 : i.state) ?? (s == null ? void 0 : s.status);
579
613
  f && (this.statusBadge.textContent = f, this.statusBadge.style.display = "inline-flex");
580
614
  }
581
615
  chipEl(e) {
582
- const i = t("button", "ocw-chip", e.icon ? `${e.icon} ${e.label}` : e.label);
616
+ const i = o("button", "ocw-chip", e.icon ? `${e.icon} ${e.label}` : e.label);
583
617
  return i.dataset.actionId = e.id, i.addEventListener("click", async () => {
584
618
  var s;
585
619
  e.confirm && !await this.confirm(e.label) || ((s = e.input) != null && s.length ? this.openForm(e) : this.h.onInvoke(e.id));
@@ -588,14 +622,14 @@ Message: ${c.value}`), this.offlinePanel.replaceChildren(t("div", "ocw-offline-t
588
622
  /** In-widget confirmation modal (replaces window.confirm). */
589
623
  confirm(e) {
590
624
  return new Promise((i) => {
591
- const s = t("div", "ocw-modal"), a = t("div", "ocw-modal-card");
592
- a.append(t("div", "ocw-modal-title", e)), a.append(t("div", "ocw-modal-body", `Confirm “${e}”?`));
593
- const c = t("div", "ocw-modal-actions"), f = t("button", "ocw-modal-cancel", "Cancel"), b = t("button", "ocw-modal-ok", "Confirm"), d = (w) => {
594
- s.remove(), i(w);
625
+ const s = o("div", "ocw-modal"), r = o("div", "ocw-modal-card");
626
+ r.append(o("div", "ocw-modal-title", e)), r.append(o("div", "ocw-modal-body", `Confirm “${e}”?`));
627
+ const a = o("div", "ocw-modal-actions"), f = o("button", "ocw-modal-cancel", "Cancel"), b = o("button", "ocw-modal-ok", "Confirm"), d = (h) => {
628
+ s.remove(), i(h);
595
629
  };
596
- f.addEventListener("click", () => d(!1)), b.addEventListener("click", () => d(!0)), s.addEventListener("click", (w) => {
597
- w.target === s && d(!1);
598
- }), c.append(f, b), a.append(c), s.append(a), this.root.append(s), b.focus();
630
+ f.addEventListener("click", () => d(!1)), b.addEventListener("click", () => d(!0)), s.addEventListener("click", (h) => {
631
+ h.target === s && d(!1);
632
+ }), a.append(f, b), r.append(a), s.append(r), this.root.append(s), b.focus();
599
633
  });
600
634
  }
601
635
  /** Inline form for a form-effect action: typed inputs (date picker, number,
@@ -603,167 +637,169 @@ Message: ${c.value}`), this.offlinePanel.replaceChildren(t("div", "ocw-offline-t
603
637
  openForm(e) {
604
638
  var b, d;
605
639
  this.formHost.replaceChildren();
606
- const i = t("div", "ocw-form");
607
- i.append(t("div", "ocw-form-title", e.icon ? `${e.icon} ${e.label}` : e.label));
640
+ const i = o("div", "ocw-form");
641
+ i.append(o("div", "ocw-form-title", e.icon ? `${e.icon} ${e.label}` : e.label));
608
642
  const s = /* @__PURE__ */ new Map();
609
- for (const w of e.input ?? []) {
610
- const v = t("label", "ocw-form-row");
611
- if (v.append(t("span", "ocw-form-lbl", w.label)), w.type === "select" && ((b = w.options) != null && b.length)) {
612
- const r = t("select", "ocw-form-input");
613
- w.required || r.append(t("option", void 0, "— select —"));
614
- for (const u of w.options) {
615
- const l = t("option");
616
- l.value = u, l.textContent = u, r.append(l);
643
+ for (const h of e.input ?? []) {
644
+ const v = o("label", "ocw-form-row");
645
+ if (v.append(o("span", "ocw-form-lbl", h.label)), h.type === "select" && ((b = h.options) != null && b.length)) {
646
+ const c = o("select", "ocw-form-input");
647
+ h.required || c.append(o("option", void 0, "— select —"));
648
+ for (const u of h.options) {
649
+ const l = o("option");
650
+ l.value = u, l.textContent = u, c.append(l);
617
651
  }
618
- w.required && (r.required = !0), v.append(r), s.set(w.name, r);
652
+ h.required && (c.required = !0), v.append(c), s.set(h.name, c);
619
653
  } else {
620
- const r = t("input", "ocw-form-input");
621
- r.type = w.type === "number" ? "number" : w.type === "date" ? "datetime-local" : "text", w.required && (r.required = !0), v.append(r), s.set(w.name, r);
654
+ const c = o("input", "ocw-form-input");
655
+ c.type = h.type === "number" ? "number" : h.type === "date" ? "datetime-local" : "text", h.required && (c.required = !0), v.append(c), s.set(h.name, c);
622
656
  }
623
657
  i.append(v);
624
658
  }
625
- const a = t("div", "ocw-form-actions"), c = t("button", "ocw-form-cancel", "Cancel"), f = t("button", "ocw-form-submit", "Send");
626
- c.addEventListener("click", () => this.formHost.replaceChildren()), f.addEventListener("click", () => {
627
- const w = {};
628
- for (const [v, r] of s) {
629
- if (r.required && !r.value) {
630
- r.style.borderColor = "#e5484d";
659
+ const r = o("div", "ocw-form-actions"), a = o("button", "ocw-form-cancel", "Cancel"), f = o("button", "ocw-form-submit", "Send");
660
+ a.addEventListener("click", () => this.formHost.replaceChildren()), f.addEventListener("click", () => {
661
+ const h = {};
662
+ for (const [v, c] of s) {
663
+ if (c.required && !c.value) {
664
+ c.style.borderColor = "#e5484d";
631
665
  return;
632
666
  }
633
- w[v] = r.type === "number" ? Number(r.value) : r.value;
667
+ h[v] = c.type === "number" ? Number(c.value) : c.value;
634
668
  }
635
- this.formHost.replaceChildren(), this.h.onInvoke(e.id, w);
636
- }), a.append(c, f), i.append(a), this.formHost.append(i), (d = s.values().next().value) == null || d.focus();
669
+ this.formHost.replaceChildren(), this.h.onInvoke(e.id, h);
670
+ }), r.append(a, f), i.append(r), this.formHost.append(i), (d = s.values().next().value) == null || d.focus();
637
671
  }
638
672
  messageEl(e, i) {
639
- var r;
673
+ var c;
640
674
  if (e.senderRole === "system") {
641
- const u = t("div", "ocw-sys");
642
- return u.textContent = e.deletedAt ? "message deleted" : W(e.content), u;
675
+ const u = o("div", "ocw-sys");
676
+ return u.textContent = e.deletedAt ? "message deleted" : ie(e.content), u;
643
677
  }
644
- const s = e.senderId === this.me, a = !!e.internal, c = t("div", `ocw-row ${a ? "ocw-note mine" : s ? "mine" : "theirs"} ${e.senderRole === "bot" ? "ocw-bot" : ""}`);
645
- !s && !a && c.append(t("div", "ocw-dot", e.senderRole === "bot" ? "🤖" : "🧑"));
646
- const f = t("div"), b = t("div", "ocw-bubble-wrap");
678
+ const s = e.senderId === this.me, r = !!e.internal, a = o("div", `ocw-row ${r ? "ocw-note mine" : s ? "mine" : "theirs"} ${e.senderRole === "bot" ? "ocw-bot" : ""}`);
679
+ !s && !r && a.append(o("div", "ocw-dot", e.senderRole === "bot" ? "🤖" : "🧑"));
680
+ const f = o("div"), b = o("div", "ocw-bubble-wrap");
647
681
  if (e.replyToId) {
648
- const u = t("div", "ocw-reply-to", "↩ replying to a message");
682
+ const u = o("div", "ocw-reply-to", "↩ replying to a message");
649
683
  u.style.cssText = "font-size:11px;color:var(--ocw-mut);margin-bottom:2px;font-style:italic", f.append(u);
650
684
  }
651
- const d = t("div", "ocw-bubble");
652
- let w = null;
653
- if (e.deletedAt) d.append(t("span", "ocw-deleted", "message deleted"));
685
+ const d = o("div", "ocw-bubble");
686
+ let h = null;
687
+ if (e.deletedAt) d.append(o("span", "ocw-deleted", "message deleted"));
654
688
  else if (e.content.kind === "attachment") {
655
689
  const u = e.content;
656
- if ((r = u.mime) != null && r.startsWith("image/")) {
690
+ if ((c = u.mime) != null && c.startsWith("image/")) {
657
691
  const l = document.createElement("img");
658
692
  l.src = u.url, l.alt = u.name ?? "image", l.style.cssText = "max-width:220px;max-height:160px;border-radius:10px;display:block;cursor:pointer", l.addEventListener("click", () => window.open(u.url, "_blank")), d.append(l);
659
693
  } else {
660
694
  const l = document.createElement("a");
661
- l.href = u.url, l.target = "_blank", l.rel = "noopener", l.style.cssText = "display:flex;align-items:center;gap:8px;color:inherit;text-decoration:none", l.append(t("span", void 0, "📄"), t("span", void 0, u.name ?? "file")), d.append(l);
695
+ l.href = u.url, l.target = "_blank", l.rel = "noopener", l.style.cssText = "display:flex;align-items:center;gap:8px;color:inherit;text-decoration:none", l.append(o("span", void 0, "📄"), o("span", void 0, u.name ?? "file")), d.append(l);
662
696
  }
663
697
  } else if (e.content.kind === "appointment") {
664
- const u = e.content, l = t("div", "ocw-appt");
665
- l.append(t("div", "ocw-appt-title", `📅 ${u.title}`)), l.append(t("div", "ocw-appt-time", new Date(u.startIso).toLocaleString() + " – " + new Date(u.endIso).toLocaleTimeString())), u.location && l.append(t("div", "ocw-appt-loc", `📍 ${u.location}`)), u.description && l.append(t("div", "ocw-appt-desc", u.description));
666
- const C = t("div", "ocw-appt-links"), m = document.createElement("a");
667
- m.href = u.googleUrl, m.target = "_blank", m.rel = "noopener", m.className = "ocw-appt-btn", m.textContent = "📅 Add to Google Calendar";
668
- const h = document.createElement("a");
669
- h.href = u.icalUrl, h.download = `${u.title}.ics`, h.className = "ocw-appt-btn ocw-appt-btn-sec", h.textContent = "🍎 Apple / iCal", C.append(m, h), l.append(C), d.append(l);
698
+ const u = e.content, l = o("div", "ocw-appt");
699
+ l.append(o("div", "ocw-appt-title", `📅 ${u.title}`)), l.append(o("div", "ocw-appt-time", new Date(u.startIso).toLocaleString() + " – " + new Date(u.endIso).toLocaleTimeString())), u.location && l.append(o("div", "ocw-appt-loc", `📍 ${u.location}`)), u.description && l.append(o("div", "ocw-appt-desc", u.description));
700
+ const E = o("div", "ocw-appt-links"), g = document.createElement("a");
701
+ g.href = u.googleUrl, g.target = "_blank", g.rel = "noopener", g.className = "ocw-appt-btn", g.textContent = "📅 Add to Google Calendar";
702
+ const w = document.createElement("a");
703
+ w.href = u.icalUrl, w.download = `${u.title}.ics`, w.className = "ocw-appt-btn ocw-appt-btn-sec", w.textContent = "🍎 Apple / iCal", E.append(g, w), l.append(E), d.append(l);
670
704
  } else
671
- w = document.createTextNode(W(e.content)), d.append(w), e.editedAt && d.append(t("span", "ocw-edited", "(edited)"));
672
- if (b.append(d), !s && !a && this.h.onTranslate && e.content.kind === "text" && !e.deletedAt && e.seq > 0 && w) {
705
+ h = document.createTextNode(ie(e.content)), d.append(h), e.editedAt && d.append(o("span", "ocw-edited", "(edited)"));
706
+ if (b.append(d), !s && !r && this.h.onTranslate && e.content.kind === "text" && !e.deletedAt && e.seq > 0 && h) {
673
707
  const u = e.content.text;
674
708
  if (u.trim()) {
675
- const l = t("button", "ocw-translate-btn", "🌐");
676
- l.type = "button", l.title = "Translate", l.addEventListener("click", (C) => {
677
- if (C.stopPropagation(), this.showingTranslation.has(e.id)) {
678
- this.showingTranslation.delete(e.id), w.textContent = u, l.textContent = "🌐", l.title = "Translate";
709
+ const l = o("button", "ocw-translate-btn", "🌐");
710
+ l.type = "button", l.title = "Translate", l.addEventListener("click", (E) => {
711
+ if (E.stopPropagation(), this.showingTranslation.has(e.id)) {
712
+ this.showingTranslation.delete(e.id), h.textContent = u, l.textContent = "🌐", l.title = "Translate";
679
713
  return;
680
714
  }
681
- const m = this.translationCache.get(e.id);
682
- if (m !== void 0) {
683
- this.showingTranslation.add(e.id), w.textContent = m, l.textContent = "↩", l.title = "Show original";
715
+ const g = this.translationCache.get(e.id);
716
+ if (g !== void 0) {
717
+ this.showingTranslation.add(e.id), h.textContent = g, l.textContent = "↩", l.title = "Show original";
684
718
  return;
685
719
  }
686
- l.textContent = "⏳", this.h.onTranslate(u).then((h) => {
687
- if (h === null) {
720
+ l.textContent = "⏳", this.h.onTranslate(u).then((w) => {
721
+ if (w === null) {
688
722
  l.textContent = "⚠️", l.title = "Translation unavailable", setTimeout(() => {
689
723
  l.textContent = "🌐", l.title = "Translate";
690
724
  }, 1500);
691
725
  return;
692
726
  }
693
- this.translationCache.set(e.id, h), this.showingTranslation.add(e.id), w.textContent = h, l.textContent = "↩", l.title = "Show original";
727
+ this.translationCache.set(e.id, w), this.showingTranslation.add(e.id), h.textContent = w, l.textContent = "↩", l.title = "Show original";
694
728
  });
695
729
  }), b.append(l);
696
730
  }
697
731
  }
698
732
  if (s && !e.deletedAt && e.seq > 0 && (this.h.onEdit ?? this.h.onDelete)) {
699
- const u = t("div", "ocw-msg-menu");
733
+ const u = o("div", "ocw-msg-menu");
700
734
  if (this.h.onEdit) {
701
- const l = t("button", void 0, "✏️");
702
- l.title = "Edit", l.addEventListener("click", (C) => {
703
- C.stopPropagation();
704
- const m = W(e.content), h = document.createElement("textarea");
705
- h.value = m, h.rows = Math.min(4, Math.ceil(m.length / 40) + 1), h.style.cssText = "width:100%;resize:vertical;border:1px solid var(--ocw-accent);border-radius:8px;padding:6px 10px;font:inherit;font-size:14px;background:#fff;color:#1c1b1a;box-sizing:border-box";
706
- const I = t("button", "ocw-form-submit", "Save");
707
- I.style.cssText = "margin-top:6px;padding:5px 14px;font-size:13px";
708
- const T = t("button", "ocw-form-cancel", "Cancel");
709
- T.style.cssText = "margin-top:6px;padding:5px 10px;font-size:13px";
710
- const $ = t("div");
711
- $.style.cssText = "display:flex;gap:6px;justify-content:flex-end", $.append(T, I);
712
- const A = t("div");
713
- A.append(h, $), d.replaceChildren(A), h.focus(), h.select();
714
- const P = () => d.replaceChildren(w ?? document.createTextNode(m));
715
- T.addEventListener("click", P), I.addEventListener("click", () => {
716
- const z = h.value.trim();
717
- z && z !== m && this.h.onEdit(e.id, z), P();
718
- }), h.addEventListener("keydown", (z) => {
719
- z.key === "Enter" && !z.shiftKey && (z.preventDefault(), I.click()), z.key === "Escape" && P();
735
+ const l = o("button", void 0, "✏️");
736
+ l.title = "Edit", l.addEventListener("click", (E) => {
737
+ E.stopPropagation();
738
+ const g = ie(e.content), w = document.createElement("textarea");
739
+ w.value = g, w.rows = Math.min(4, Math.ceil(g.length / 40) + 1), w.style.cssText = "width:100%;resize:vertical;border:1px solid var(--ocw-accent);border-radius:8px;padding:6px 10px;font:inherit;font-size:14px;background:#fff;color:#1c1b1a;box-sizing:border-box";
740
+ const k = o("button", "ocw-form-submit", "Save");
741
+ k.style.cssText = "margin-top:6px;padding:5px 14px;font-size:13px";
742
+ const I = o("button", "ocw-form-cancel", "Cancel");
743
+ I.style.cssText = "margin-top:6px;padding:5px 10px;font-size:13px";
744
+ const j = o("div");
745
+ j.style.cssText = "display:flex;gap:6px;justify-content:flex-end", j.append(I, k);
746
+ const C = o("div");
747
+ C.append(w, j), d.replaceChildren(C), w.focus(), w.select();
748
+ const R = () => d.replaceChildren(h ?? document.createTextNode(g));
749
+ I.addEventListener("click", R), k.addEventListener("click", () => {
750
+ const A = w.value.trim();
751
+ A && A !== g && this.h.onEdit(e.id, A), R();
752
+ }), w.addEventListener("keydown", (A) => {
753
+ A.key === "Enter" && !A.shiftKey && (A.preventDefault(), k.click()), A.key === "Escape" && R();
720
754
  });
721
755
  }), u.append(l);
722
756
  }
723
757
  if (this.h.onDelete) {
724
- const l = t("button", "del", "🗑");
725
- l.title = "Delete", l.addEventListener("click", (C) => {
726
- C.stopPropagation(), this.h.onDelete(e.id);
758
+ const l = o("button", "del", "🗑");
759
+ l.title = "Delete", l.addEventListener("click", (E) => {
760
+ E.stopPropagation(), this.h.onDelete(e.id);
727
761
  }), u.append(l);
728
762
  }
729
763
  b.append(u);
730
764
  }
731
765
  if (f.append(b), this.h.onReact && !e.deletedAt && e.seq > 0) {
732
- const u = t("div", "ocw-react-wrap"), l = t("div", "ocw-react");
766
+ const u = o("div", "ocw-react-wrap"), l = o("div", "ocw-react");
733
767
  if (e.reactions && Object.keys(e.reactions).length)
734
- for (const [h, I] of Object.entries(e.reactions)) {
735
- const T = t("button", `ocw-react-pill${I.includes(this.me) ? " mine" : ""}`, `${h} ${I.length}`);
736
- T.addEventListener("click", () => {
737
- var $, A;
738
- return (A = ($ = this.h).onReact) == null ? void 0 : A.call($, e.id, h, I.includes(this.me));
739
- }), l.append(T);
768
+ for (const [w, k] of Object.entries(e.reactions)) {
769
+ const I = o("button", `ocw-react-pill${k.includes(this.me) ? " mine" : ""}`, `${w} ${k.length}`);
770
+ I.addEventListener("click", () => {
771
+ var j, C;
772
+ return (C = (j = this.h).onReact) == null ? void 0 : C.call(j, e.id, w, k.includes(this.me));
773
+ }), l.append(I);
740
774
  }
741
- const C = t("button", "ocw-react-btn", "+"), m = t("div", "ocw-react-picker");
742
- for (const h of Ce) {
743
- const I = t("button", void 0, h);
744
- I.addEventListener("click", (T) => {
745
- var A, P, z, k;
746
- T.stopPropagation();
747
- const $ = (P = (A = e.reactions) == null ? void 0 : A[h]) == null ? void 0 : P.includes(this.me);
748
- (k = (z = this.h).onReact) == null || k.call(z, e.id, h, !!$), m.style.display = "none";
749
- }), m.append(I);
775
+ const E = o("button", "ocw-react-btn", "+"), g = o("div", "ocw-react-picker");
776
+ for (const w of Ne) {
777
+ const k = o("button", void 0, w);
778
+ k.addEventListener("click", (I) => {
779
+ var C, R, A, O;
780
+ I.stopPropagation();
781
+ const j = (R = (C = e.reactions) == null ? void 0 : C[w]) == null ? void 0 : R.includes(this.me);
782
+ (O = (A = this.h).onReact) == null || O.call(A, e.id, w, !!j), g.style.display = "none";
783
+ }), g.append(k);
750
784
  }
751
- m.style.display = "none", C.addEventListener("click", (h) => {
752
- h.stopPropagation(), m.style.display = m.style.display === "none" ? "flex" : "none";
753
- }), document.addEventListener("click", () => {
754
- m.style.display = "none";
755
- }, { once: !0 }), l.append(C), u.append(l, m), f.append(u);
756
- } else e.reactions && Object.keys(e.reactions).length && f.append(t("div", "ocw-react", Object.entries(e.reactions).map(([u, l]) => `${u}${l.length}`).join(" ")));
757
- const v = t("div", "ocw-time ocw-meta", Se(e.ts));
785
+ g.style.display = "none", E.addEventListener("click", (w) => {
786
+ w.stopPropagation();
787
+ const k = g.style.display === "none";
788
+ g.style.display = k ? "flex" : "none", k && document.addEventListener("click", () => {
789
+ g.style.display = "none";
790
+ }, { once: !0 });
791
+ }), l.append(E), u.append(l, g), f.append(u);
792
+ } else e.reactions && Object.keys(e.reactions).length && f.append(o("div", "ocw-react", Object.entries(e.reactions).map(([u, l]) => `${u}${l.length}`).join(" ")));
793
+ const v = o("div", "ocw-time ocw-meta", Ve(e.ts));
758
794
  if (s && e.status) {
759
- const u = t("span", `ocw-tick${e.status === "read" ? " read" : e.status === "delivered" ? " delivered" : ""}`, ze(e.status));
795
+ const u = o("span", `ocw-tick${e.status === "read" ? " read" : e.status === "delivered" ? " delivered" : ""}`, Ke(e.status));
760
796
  v.append(u);
761
797
  }
762
- return s && e.seq > 0 && i.lastReadByOthers >= e.seq && v.append(t("span", "ocw-seen", " · Seen")), f.append(v), c.append(f), c;
798
+ return s && e.seq > 0 && i.lastReadByOthers >= e.seq && v.append(o("span", "ocw-seen", " · Seen")), f.append(v), a.append(f), a;
763
799
  }
764
800
  }
765
- function ze(o) {
766
- switch (o) {
801
+ function Ke(t) {
802
+ switch (t) {
767
803
  case "read":
768
804
  return "✓✓";
769
805
  case "delivered":
@@ -774,35 +810,44 @@ function ze(o) {
774
810
  return "🕓";
775
811
  }
776
812
  }
777
- const R = /* @__PURE__ */ new WeakMap(), U = /* @__PURE__ */ new Map();
778
- function ne(o) {
779
- return `relay-launcher::${o.profileId}::${o.subjectId ?? ""}`;
813
+ const H = /* @__PURE__ */ new WeakMap(), X = /* @__PURE__ */ new Map();
814
+ function ge(t) {
815
+ return `relay-launcher::${t.profileId}::${t.subjectId ?? ""}`;
816
+ }
817
+ let F = null;
818
+ function Ge() {
819
+ if (F && F.state !== "closed") return F;
820
+ try {
821
+ return F = new AudioContext(), F;
822
+ } catch {
823
+ return null;
824
+ }
780
825
  }
781
- function Ae(o) {
826
+ function tt(t) {
782
827
  var e;
783
- (e = R.get(o)) == null || e.close(), R.delete(o);
828
+ (e = H.get(t)) == null || e.close(), H.delete(t);
784
829
  }
785
- function Be(o) {
786
- var J, Q, G, Z, ee;
787
- R.has(o.el) && (R.get(o.el).close(), R.delete(o.el)), o.launcher && ((J = U.get(ne(o))) == null || J.close());
788
- const e = fe();
830
+ function ot(t) {
831
+ var le, de, pe, he, ue;
832
+ H.has(t.el) && (H.get(t.el).close(), H.delete(t.el)), t.launcher && ((le = X.get(ge(t))) == null || le.close());
833
+ const e = je();
789
834
  let i, s = !1;
790
- const a = o.token ?? o.userId ?? e, c = a !== e ? e : void 0, { wsUrl: f, httpBase: b } = ue(o.url, o.apiUrl);
791
- let d = new xe(a);
792
- const w = o.subjectId ? `${a}::${o.subjectId}` : a, v = new be(w);
793
- let r, u = !1, l = null, C = null;
794
- !o.launcher && !o.el.style.height && o.el.clientHeight === 0 && (o.el.style.width = o.el.style.width || "100%", o.el.style.height = "600px");
835
+ const r = t.token ?? t.userId ?? e, a = r !== e ? e : void 0, { wsUrl: f, httpBase: b } = Ae(t.url, t.apiUrl);
836
+ let d = new Me(r);
837
+ const h = t.subjectId ? `${r}::${t.subjectId}` : r, v = new Be(h);
838
+ let c, u = !1, l = null, E = null, g = null, w = null;
839
+ !t.launcher && !t.el.style.height && t.el.clientHeight === 0 && (t.el.style.width = t.el.style.width || "100%", t.el.style.height = "600px");
795
840
  for (const n of v.load()) d.addOptimistic(n.clientMsgId, n.content);
796
- let m = null, h = null, I = 0, T = !o.launcher;
797
- if (o.launcher) {
798
- const p = (o.position ?? "bottom-right").includes("right");
799
- m = document.createElement("div"), m.style.cssText = `position:fixed;${p ? "right:20px" : "left:20px"};bottom:20px;z-index:9999;display:flex;flex-direction:column;align-items:${p ? "flex-end" : "flex-start"};gap:12px`;
800
- const g = document.createElement("div"), E = typeof window < "u" ? window.matchMedia("(max-width: 479px)") : null, O = (q) => {
801
- g.style.cssText = q ? [
841
+ let k = null, I = null, j = 0, C = !t.launcher;
842
+ if (t.launcher) {
843
+ const p = (t.position ?? "bottom-right").includes("right");
844
+ k = document.createElement("div"), k.style.cssText = `position:fixed;${p ? "right:20px" : "left:20px"};bottom:20px;z-index:9999;display:flex;flex-direction:column;align-items:${p ? "flex-end" : "flex-start"};gap:12px`;
845
+ const x = document.createElement("div"), L = typeof window < "u" && typeof window.matchMedia == "function" ? window.matchMedia("(max-width: 479px)") : null, V = (P) => {
846
+ t.el.classList.toggle("ocw-fs", P), x.style.cssText = P ? [
802
847
  "position:fixed",
803
848
  "inset:0",
804
849
  "width:100%",
805
- "height:100%",
850
+ "height:100dvh",
806
851
  "border-radius:0",
807
852
  "overflow:hidden",
808
853
  "box-shadow:none",
@@ -813,8 +858,8 @@ function Be(o) {
813
858
  "opacity:0",
814
859
  "z-index:9998"
815
860
  ].join(";") : [
816
- "width:380px",
817
- "height:600px",
861
+ "width:min(380px, calc(100vw - 40px))",
862
+ "height:min(600px, calc(100dvh - 108px))",
818
863
  "border-radius:16px",
819
864
  "overflow:hidden",
820
865
  "box-shadow:0 8px 40px rgba(0,0,0,.18)",
@@ -827,117 +872,190 @@ function Be(o) {
827
872
  "transform:scale(.95)"
828
873
  ].join(";");
829
874
  };
830
- O((E == null ? void 0 : E.matches) ?? !1);
831
- const N = (q) => O(q.matches);
832
- E == null || E.addEventListener("change", N), l = E, C = N, o.el.style.cssText = "width:100%;height:100%;overflow:hidden", g.append(o.el);
833
- const L = document.createElement("button");
834
- L.style.cssText = [
875
+ V((L == null ? void 0 : L.matches) ?? !1);
876
+ const Y = (P) => V(P.matches);
877
+ L == null || L.addEventListener("change", Y), l = L, E = Y, t.el.style.cssText = "width:100%;height:100%;overflow:hidden", x.append(t.el);
878
+ const Q = '<svg viewBox="0 0 24 24" width="26" height="26" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>', Z = '<svg viewBox="0 0 24 24" width="22" height="22" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"/></svg>', M = document.createElement("button");
879
+ M.type = "button", M.style.cssText = [
835
880
  "width:56px;height:56px;border-radius:50%",
836
- `background:${o.accent ?? "#4F63F5"}`,
837
- "color:#fff;border:none;font-size:24px;cursor:pointer",
881
+ `background:${t.accent ?? "#4F63F5"}`,
882
+ "color:#fff;border:none;cursor:pointer",
838
883
  "box-shadow:0 4px 16px rgba(0,0,0,.25)",
839
884
  "position:relative;flex:none",
885
+ "display:flex;align-items:center;justify-content:center",
840
886
  "transition:transform .15s"
841
- ].join(";"), L.textContent = "💬", L.onmouseenter = () => {
842
- L.style.transform = "scale(1.08)";
843
- }, L.onmouseleave = () => {
844
- L.style.transform = "scale(1)";
845
- }, h = document.createElement("span"), h.style.cssText = "position:absolute;top:-4px;right:-4px;background:#ef4444;color:#fff;border-radius:50%;width:20px;height:20px;font-size:11px;font-weight:700;display:none;align-items:center;justify-content:center", L.append(h), m.append(g, L), document.body.append(m);
846
- const _ = (q) => {
847
- q ? (g.style.display = "flex", requestAnimationFrame(() => {
848
- g.style.opacity = "1", g.style.transform = "scale(1)";
849
- })) : (g.style.opacity = "0", g.style.transform = "scale(.95)", setTimeout(() => {
850
- T || (g.style.display = "none");
887
+ ].join(";"), M.onmouseenter = () => {
888
+ M.style.transform = "scale(1.08)";
889
+ }, M.onmouseleave = () => {
890
+ M.style.transform = "scale(1)";
891
+ }, I = document.createElement("span"), I.style.cssText = "position:absolute;top:-4px;right:-4px;background:#ef4444;color:#fff;border-radius:50%;width:20px;height:20px;font-size:11px;font-weight:700;display:none;align-items:center;justify-content:center";
892
+ const K = () => {
893
+ M.innerHTML = C ? Z : Q, M.setAttribute("aria-label", C ? "Close chat" : "Open chat"), M.setAttribute("aria-expanded", String(C)), M.append(I);
894
+ };
895
+ K(), k.append(x, M), document.body.append(k);
896
+ const ee = `ocw-teaser-dismissed::${t.profileId}`, G = typeof t.launcherMessage == "string" ? { title: t.launcherMessage } : t.launcherMessage;
897
+ let z = null;
898
+ const Se = () => {
899
+ try {
900
+ return sessionStorage.getItem(ee) === "1";
901
+ } catch {
902
+ return !1;
903
+ }
904
+ }, ze = '<svg viewBox="0 0 24 24" width="15" height="15" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>', fe = () => {
905
+ const P = G != null && G.title ? G : d.launcherMessage ?? void 0;
906
+ if (!(!!(P != null && P.title) && !C && !Se())) {
907
+ z && (z.remove(), z = null);
908
+ return;
909
+ }
910
+ if (z) return;
911
+ z = document.createElement("div"), z.setAttribute("role", "button"), z.setAttribute("tabindex", "0"), z.setAttribute("aria-label", P.title), z.style.cssText = [
912
+ "max-width:280px",
913
+ "background:#fff",
914
+ "border-radius:14px",
915
+ "box-shadow:0 6px 28px rgba(0,0,0,.16)",
916
+ "padding:14px 40px 14px 16px",
917
+ "position:relative",
918
+ "cursor:pointer",
919
+ "font-family:inherit",
920
+ `align-self:${p ? "flex-end" : "flex-start"}`,
921
+ "animation:ocw-teaser-in .22s ease-out"
922
+ ].join(";");
923
+ const oe = document.createElement("div");
924
+ if (oe.textContent = P.title, oe.style.cssText = "font-size:15px;font-weight:600;color:#111;line-height:1.35", z.append(oe), P.subtitle) {
925
+ const D = document.createElement("div");
926
+ D.style.cssText = "display:flex;align-items:center;gap:6px;margin-top:6px;font-size:13px;color:#6b7280";
927
+ const ne = document.createElement("span");
928
+ ne.innerHTML = ze, ne.style.cssText = `color:${t.accent ?? "#4F63F5"};display:inline-flex`;
929
+ const be = document.createElement("span");
930
+ be.textContent = P.subtitle, D.append(ne, be), z.append(D);
931
+ }
932
+ const N = document.createElement("button");
933
+ N.type = "button", N.setAttribute("aria-label", "Dismiss"), N.innerHTML = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M18 6 6 18M6 6l12 12" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"/></svg>', N.style.cssText = "position:absolute;top:8px;right:8px;width:24px;height:24px;border:none;border-radius:50%;background:#f3f4f6;color:#6b7280;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0", N.addEventListener("click", (D) => {
934
+ D.stopPropagation();
935
+ try {
936
+ sessionStorage.setItem(ee, "1");
937
+ } catch {
938
+ }
939
+ z && (z.remove(), z = null);
940
+ }), z.append(N);
941
+ const we = () => {
942
+ C || (C = !0, te(!0), K(), j = 0, I && (I.style.display = "none"), z && (z.remove(), z = null));
943
+ };
944
+ z.addEventListener("click", we), z.addEventListener("keydown", (D) => {
945
+ (D.key === "Enter" || D.key === " ") && (D.preventDefault(), we());
946
+ }), k.insertBefore(z, M);
947
+ };
948
+ if (!document.getElementById("ocw-teaser-style")) {
949
+ const P = document.createElement("style");
950
+ P.id = "ocw-teaser-style", P.textContent = "@keyframes ocw-teaser-in{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}", document.head.append(P);
951
+ }
952
+ w = fe, fe();
953
+ const te = (P) => {
954
+ P ? (x.style.display = "flex", requestAnimationFrame(() => {
955
+ x.style.opacity = "1", x.style.transform = "scale(1)";
956
+ })) : (x.style.opacity = "0", x.style.transform = "scale(.95)", setTimeout(() => {
957
+ C || (x.style.display = "none");
851
958
  }, 180));
852
959
  };
853
- L.addEventListener("click", () => {
854
- T = !T, _(T), L.textContent = T ? "✕" : "💬", L.append(h), T && (I = 0, h && (h.style.display = "none"));
855
- }), document.addEventListener("keydown", (q) => {
856
- q.key === "Escape" && T && (T = !1, _(!1), L.textContent = "💬", L.append(h));
857
- });
960
+ M.addEventListener("click", () => {
961
+ if (C = !C, te(C), K(), C) {
962
+ j = 0, I && (I.style.display = "none");
963
+ try {
964
+ sessionStorage.setItem(ee, "1");
965
+ } catch {
966
+ }
967
+ }
968
+ w == null || w();
969
+ }), g = (P) => {
970
+ P.key === "Escape" && C && (C = !1, te(!1), K());
971
+ }, document.addEventListener("keydown", g);
858
972
  }
859
- const $ = () => {
860
- T || (I++, h && (h.textContent = String(I), h.style.display = "flex"));
973
+ const R = () => {
974
+ C || (j++, I && (I.textContent = String(j), I.style.display = "flex"));
861
975
  }, A = () => {
862
976
  try {
863
- const n = new AudioContext(), p = n.createOscillator(), g = n.createGain();
864
- p.connect(g), g.connect(n.destination), p.frequency.setValueAtTime(880, n.currentTime), p.frequency.exponentialRampToValueAtTime(440, n.currentTime + 0.15), g.gain.setValueAtTime(0.3, n.currentTime), g.gain.exponentialRampToValueAtTime(1e-3, n.currentTime + 0.3), p.start(), p.stop(n.currentTime + 0.3);
977
+ const n = Ge();
978
+ if (!n) return;
979
+ n.state === "suspended" && n.resume().catch(() => {
980
+ });
981
+ const p = n.createOscillator(), x = n.createGain();
982
+ p.connect(x), x.connect(n.destination), p.frequency.setValueAtTime(880, n.currentTime), p.frequency.exponentialRampToValueAtTime(440, n.currentTime + 0.15), x.gain.setValueAtTime(0.3, n.currentTime), x.gain.exponentialRampToValueAtTime(1e-3, n.currentTime + 0.3), p.start(), p.stop(n.currentTime + 0.3);
865
983
  } catch {
866
984
  }
867
- }, P = [], z = (n) => {
868
- for (; P.length; ) {
869
- const p = P.shift();
870
- v.add({ clientMsgId: p.clientMsgId, content: p.content, ts: Date.now() }), k.send({ type: "send", conversationId: n, clientMsgId: p.clientMsgId, content: p.content });
985
+ }, O = [], U = (n) => {
986
+ for (; O.length; ) {
987
+ const p = O.shift();
988
+ v.add({ clientMsgId: p.clientMsgId, content: p.content, ts: Date.now() }), T.send({ type: "send", conversationId: n, clientMsgId: p.clientMsgId, content: p.content });
871
989
  }
872
990
  };
873
- let k;
874
- const x = new me(`ocw-e2e-${o.profileId}`);
875
- let S = !1;
876
- const B = [], D = [];
877
- let Y = !1;
878
- const F = (n, p) => {
879
- x.sealText(p).then((g) => {
880
- r && k.send({ type: "send", conversationId: r, clientMsgId: n, content: g });
991
+ let T;
992
+ const B = new qe(`ocw-e2e-${t.profileId}`);
993
+ let m = !1;
994
+ const S = [], q = [];
995
+ let se = !1;
996
+ const J = (n, p) => {
997
+ B.sealText(p).then((x) => {
998
+ c && T.send({ type: "send", conversationId: c, clientMsgId: n, content: x });
881
999
  });
882
- }, ie = (n, p, g) => {
883
- x.sealText(p, g).then((E) => {
884
- r && k.send({ type: "send", conversationId: r, clientMsgId: n, content: E });
1000
+ }, ye = (n, p, x) => {
1001
+ B.sealText(p, x).then((L) => {
1002
+ c && T.send({ type: "send", conversationId: c, clientMsgId: n, content: L });
885
1003
  });
886
- }, se = () => {
887
- for (; B.length; ) {
888
- const n = B.shift();
889
- F(n.clientMsgId, n.text);
1004
+ }, ve = () => {
1005
+ for (; S.length; ) {
1006
+ const n = S.shift();
1007
+ J(n.clientMsgId, n.text);
890
1008
  }
891
- for (; D.length; ) {
892
- const n = D.shift();
893
- F(n.clientMsgId, n.text);
1009
+ for (; q.length; ) {
1010
+ const n = q.shift();
1011
+ J(n.clientMsgId, n.text);
894
1012
  }
895
- }, ae = (n) => {
896
- k.send({ type: "fetchPrekey", targetUserId: n });
897
- }, re = o.i18n ?? {}, ce = ["ar", "he", "fa", "ur"], le = typeof navigator < "u" ? (navigator.language ?? "").slice(0, 2).toLowerCase() : "";
898
- ce.includes(le) && !o.el.dir && (o.el.dir = "rtl", o.el.style.fontFamily = o.el.style.fontFamily || "Tahoma,Arial,system-ui,sans-serif");
899
- const K = new ke(), V = `oc_prechat_${o.profileId}_${a.slice(-8)}`, j = new Ee(o.el, a, {
1013
+ }, ke = (n) => {
1014
+ T.send({ type: "fetchPrekey", targetUserId: n });
1015
+ }, Ce = t.i18n ?? {}, Te = ["ar", "he", "fa", "ur"], Ee = typeof navigator < "u" ? (navigator.language ?? "").slice(0, 2).toLowerCase() : "";
1016
+ Te.includes(Ee) && !t.el.dir && (t.el.dir = "rtl", t.el.style.fontFamily = t.el.style.fontFamily || "Tahoma,Arial,system-ui,sans-serif");
1017
+ const re = new He(), ae = `oc_prechat_${t.profileId}_${r.slice(-8)}`, $ = new Ye(t.el, r, {
900
1018
  onSend(n) {
901
- const p = `cm_${Math.random().toString(36).slice(2)}`, g = { kind: "text", text: n };
902
- d.addOptimistic(p, g), j.render(d), d.e2e ? x.ready ? F(p, n) : Y ? D.push({ clientMsgId: p, text: n }) : (B.push({ clientMsgId: p, text: n }), d.assignedAgentId && ae(d.assignedAgentId)) : r ? (v.add({ clientMsgId: p, content: g, ts: Date.now() }), k.send({ type: "send", conversationId: r, clientMsgId: p, content: g })) : P.push({ clientMsgId: p, content: g });
1019
+ const p = `cm_${Math.random().toString(36).slice(2)}`, x = { kind: "text", text: n };
1020
+ d.addOptimistic(p, x), $.render(d), d.e2e ? B.ready ? J(p, n) : se ? q.push({ clientMsgId: p, text: n }) : (S.push({ clientMsgId: p, text: n }), d.assignedAgentId && ke(d.assignedAgentId)) : c ? (v.add({ clientMsgId: p, content: x, ts: Date.now() }), T.send({ type: "send", conversationId: c, clientMsgId: p, content: x })) : O.push({ clientMsgId: p, content: x });
903
1021
  },
904
1022
  async onAttach(n) {
905
- if (!r) return;
906
- const p = `${b}/upload?name=${encodeURIComponent(n.name)}`, g = `cm_${Math.random().toString(36).slice(2)}`;
907
- d.addOptimistic(g, { kind: "text", text: `📎 Uploading ${n.name}…` }), j.render(d);
1023
+ if (!c) return;
1024
+ const p = `${b}/upload?name=${encodeURIComponent(n.name)}`, x = `cm_${Math.random().toString(36).slice(2)}`;
1025
+ d.addOptimistic(x, { kind: "text", text: `📎 Uploading ${n.name}…` }), $.render(d);
908
1026
  try {
909
- const E = await fetch(p, {
1027
+ const L = await fetch(p, {
910
1028
  method: "POST",
911
1029
  // Use the resolved connection token (a signed JWT, an explicit userId,
912
1030
  // or the anonymous persistent uid) — NOT just opts.token. The server
913
1031
  // requires an Authorization header, and the verifier accepts an
914
1032
  // anonymous id as a guest; keying off opts.token alone meant anonymous
915
1033
  // visitors (the common case) sent no auth and every upload 401'd.
916
- headers: { "content-type": n.type, authorization: `Bearer ${a}` },
1034
+ headers: { "content-type": n.type, authorization: `Bearer ${r}` },
917
1035
  body: n
918
1036
  });
919
- if (!E.ok) throw new Error(`Upload failed: ${E.status}`);
920
- const { url: O, name: N, mime: L, size: _ } = await E.json();
921
- k.send({ type: "send", conversationId: r, clientMsgId: g, content: { kind: "attachment", url: O, name: N, mime: L, size: _ } });
922
- } catch (E) {
923
- d.addOptimistic(g, { kind: "text", text: `⚠️ Upload failed: ${E.message}` }), j.render(d);
1037
+ if (!L.ok) throw new Error(`Upload failed: ${L.status}`);
1038
+ const { url: V, name: Y, mime: Q, size: Z } = await L.json();
1039
+ T.send({ type: "send", conversationId: c, clientMsgId: x, content: { kind: "attachment", url: V, name: Y, mime: Q, size: Z } });
1040
+ } catch (L) {
1041
+ d.addOptimistic(x, { kind: "text", text: `⚠️ Upload failed: ${L.message}` }), $.render(d);
924
1042
  }
925
1043
  },
926
1044
  onInvoke(n, p) {
927
- r && k.send({ type: "invoke", conversationId: r, actionId: n, clientInvokeId: `iv_${Math.random().toString(36).slice(2)}`, ...p ? { inputs: p } : {} });
1045
+ c && T.send({ type: "invoke", conversationId: c, actionId: n, clientInvokeId: `iv_${Math.random().toString(36).slice(2)}`, ...p ? { inputs: p } : {} });
928
1046
  },
929
1047
  onTyping(n, p) {
930
- r && k.send({ type: "typing", conversationId: r, isTyping: n, ...p ? { preview: p } : {} });
1048
+ c && T.send({ type: "typing", conversationId: c, isTyping: n, ...p ? { preview: p } : {} });
931
1049
  },
932
1050
  onPreChat(n) {
933
1051
  try {
934
- localStorage.setItem(V, "1");
1052
+ localStorage.setItem(ae, "1");
935
1053
  } catch {
936
1054
  }
937
- k.send({
1055
+ T.send({
938
1056
  type: "open",
939
- profileId: o.profileId,
940
- ...o.subjectId ? { subjectId: o.subjectId } : {},
1057
+ profileId: t.profileId,
1058
+ ...t.subjectId ? { subjectId: t.subjectId } : {},
941
1059
  userInfo: {
942
1060
  ...n.name ? { name: n.name } : {},
943
1061
  ...n.email ? { email: n.email } : {},
@@ -948,168 +1066,168 @@ function Be(o) {
948
1066
  }
949
1067
  });
950
1068
  const p = n.callback ? `📞 Call-back requested${n.phone ? `: ${n.phone}` : ""}${n.topic ? ` — ${n.topic}` : ""}` : n.topic ? `Topic: ${n.topic}` : "";
951
- p && r && !d.e2e && k.send({ type: "send", conversationId: r, clientMsgId: `pc_${Math.random().toString(36).slice(2, 12)}`, content: { kind: "text", text: p } });
1069
+ p && c && !d.e2e && T.send({ type: "send", conversationId: c, clientMsgId: `pc_${Math.random().toString(36).slice(2, 12)}`, content: { kind: "text", text: p } });
952
1070
  },
953
1071
  onDeflectQuery(n) {
954
1072
  if (s) return;
955
1073
  clearTimeout(i);
956
1074
  const p = n.trim();
957
1075
  if (p.length < 3) {
958
- j.hideDeflection();
1076
+ $.hideDeflection();
959
1077
  return;
960
1078
  }
961
1079
  i = setTimeout(() => {
962
- fetch(`${b}/kb/search?profileId=${encodeURIComponent(o.profileId)}&q=${encodeURIComponent(p.slice(0, 200))}`).then((g) => g.ok ? g.json() : { articles: [] }).then((g) => j.showDeflection(g.articles ?? [])).catch(() => j.hideDeflection());
1080
+ fetch(`${b}/kb/search?profileId=${encodeURIComponent(t.profileId)}&q=${encodeURIComponent(p.slice(0, 200))}`).then((x) => x.ok ? x.json() : { articles: [] }).then((x) => $.showDeflection(x.articles ?? [])).catch(() => $.hideDeflection());
963
1081
  }, 350);
964
1082
  },
965
1083
  onReadUpTo(n) {
966
- r && k.send({ type: "read", conversationId: r, seq: n });
1084
+ c && T.send({ type: "read", conversationId: c, seq: n });
967
1085
  },
968
1086
  onLoadMore() {
969
- if (!r || !d.e2e) return;
1087
+ if (!c || !d.e2e) return;
970
1088
  const n = d.messages()[0];
971
- n && k.send({ type: "history", conversationId: r, beforeSeq: n.seq, limit: 20 });
1089
+ n && T.send({ type: "history", conversationId: c, beforeSeq: n.seq, limit: 20 });
972
1090
  },
973
1091
  onEdit(n, p) {
974
- r && k.send({ type: "edit", conversationId: r, messageId: n, content: { kind: "text", text: p } });
1092
+ c && T.send({ type: "edit", conversationId: c, messageId: n, content: { kind: "text", text: p } });
975
1093
  },
976
1094
  onDelete(n) {
977
- r && k.send({ type: "delete", conversationId: r, messageId: n });
1095
+ c && T.send({ type: "delete", conversationId: c, messageId: n });
978
1096
  },
979
- onReact(n, p, g) {
980
- r && k.send({ type: "react", conversationId: r, messageId: n, emoji: p, remove: g });
1097
+ onReact(n, p, x) {
1098
+ c && T.send({ type: "react", conversationId: c, messageId: n, emoji: p, remove: x });
981
1099
  },
982
1100
  onCsat(n) {
983
- r && fetch(`${b}/conversations/${r}/csat`, {
1101
+ c && fetch(`${b}/conversations/${c}/csat`, {
984
1102
  method: "POST",
985
- headers: { "Content-Type": "application/json", Authorization: `Bearer ${a}` },
1103
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${r}` },
986
1104
  body: JSON.stringify({ score: n })
987
1105
  }).catch(() => {
988
1106
  });
989
1107
  },
990
1108
  onAnnotate(n) {
991
- r && k.send({ type: "annotate", conversationId: r, stroke: n });
1109
+ c && T.send({ type: "annotate", conversationId: c, stroke: n });
992
1110
  },
993
1111
  onAnnotateClear() {
994
- r && k.send({ type: "annotate_clear", conversationId: r });
1112
+ c && T.send({ type: "annotate_clear", conversationId: c });
995
1113
  },
996
- ...o.translateLang ? {
1114
+ ...t.translateLang ? {
997
1115
  async onTranslate(n) {
998
1116
  try {
999
1117
  const p = await fetch(`${b}/translate`, {
1000
1118
  method: "POST",
1001
- headers: { "content-type": "application/json", authorization: `Bearer ${a}` },
1002
- body: JSON.stringify({ text: n, targetLang: o.translateLang })
1119
+ headers: { "content-type": "application/json", authorization: `Bearer ${r}` },
1120
+ body: JSON.stringify({ text: n, targetLang: t.translateLang })
1003
1121
  });
1004
1122
  if (!p.ok) return null;
1005
- const { translated: g } = await p.json();
1006
- return g;
1123
+ const { translated: x } = await p.json();
1124
+ return x;
1007
1125
  } catch {
1008
1126
  return null;
1009
1127
  }
1010
1128
  }
1011
1129
  } : {},
1012
- ...o.onBack ? { onBack: o.onBack } : {}
1130
+ ...t.onBack ? { onBack: t.onBack } : {}
1013
1131
  }, {
1014
- ...o.subject ? { subject: o.subject } : {},
1015
- ...o.quickReplies ? { quickReplies: o.quickReplies } : {},
1016
- ...o.accent ? { accent: o.accent } : {},
1017
- ...(Q = o.user) != null && Q.name || (G = o.user) != null && G.avatar ? { userInfo: { ...o.user.name ? { name: o.user.name } : {}, ...o.user.avatar ? { avatar: o.user.avatar } : {} } } : {},
1018
- i18n: re
1132
+ ...t.subject ? { subject: t.subject } : {},
1133
+ ...t.quickReplies ? { quickReplies: t.quickReplies } : {},
1134
+ ...t.accent ? { accent: t.accent } : {},
1135
+ ...(de = t.user) != null && de.name || (pe = t.user) != null && pe.avatar ? { userInfo: { ...t.user.name ? { name: t.user.name } : {}, ...t.user.avatar ? { avatar: t.user.avatar } : {} } } : {},
1136
+ i18n: Ce
1019
1137
  });
1020
1138
  try {
1021
- localStorage.getItem(V) && j.completePreChat();
1139
+ localStorage.getItem(ae) && $.completePreChat();
1022
1140
  } catch {
1023
1141
  }
1024
- const X = o.user && (o.user.name || o.user.email || o.user.avatar || o.user.meta) ? {
1025
- ...o.user.name ? { name: o.user.name } : {},
1026
- ...o.user.email ? { email: o.user.email } : {},
1027
- ...o.user.avatar ? { avatar: o.user.avatar } : {},
1028
- ...o.user.meta ? { meta: o.user.meta } : {}
1029
- } : void 0, de = {
1142
+ const ce = t.user && (t.user.name || t.user.email || t.user.avatar || t.user.meta) ? {
1143
+ ...t.user.name ? { name: t.user.name } : {},
1144
+ ...t.user.email ? { email: t.user.email } : {},
1145
+ ...t.user.avatar ? { avatar: t.user.avatar } : {},
1146
+ ...t.user.meta ? { meta: t.user.meta } : {}
1147
+ } : void 0, Ie = {
1030
1148
  type: "open",
1031
- profileId: o.profileId,
1149
+ profileId: t.profileId,
1032
1150
  // Direct conversations use the kind/peerId pair; the dm:… subject key is
1033
1151
  // server-derived and owner-keyed, so passing it as subjectId from the
1034
1152
  // NON-owner side would find-or-create a junk duplicate thread.
1035
- ...o.kind === "direct" && o.peerId ? { kind: "direct", peerId: o.peerId } : o.subjectId ? { subjectId: o.subjectId } : {},
1036
- ...c ? { linkFrom: c } : {},
1037
- ...X ? { userInfo: X } : {},
1153
+ ...t.kind === "direct" && t.peerId ? { kind: "direct", peerId: t.peerId } : t.subjectId ? { subjectId: t.subjectId } : {},
1154
+ ...a ? { linkFrom: a } : {},
1155
+ ...ce ? { userInfo: ce } : {},
1038
1156
  ...typeof location < "u" ? { pageUrl: location.href } : {},
1039
1157
  ...typeof document < "u" && document.title ? { pageTitle: document.title } : {},
1040
1158
  // Pass subject display info so the server can persist it to the Subject record.
1041
1159
  // This is how listingTitle and listingMeta get saved without a separate API call.
1042
- ...(Z = o.subject) != null && Z.title ? { subjectTitle: o.subject.title } : {},
1043
- ...(ee = o.subject) != null && ee.subtitle ? { subjectMeta: o.subject.subtitle } : {}
1160
+ ...(he = t.subject) != null && he.title ? { subjectTitle: t.subject.title } : {},
1161
+ ...(ue = t.subject) != null && ue.subtitle ? { subjectMeta: t.subject.subtitle } : {}
1044
1162
  };
1045
- k = new ge({
1046
- ...o.refreshToken ? { refreshToken: o.refreshToken } : {},
1163
+ T = new Re({
1164
+ ...t.refreshToken ? { refreshToken: t.refreshToken } : {},
1047
1165
  url: f,
1048
- token: a,
1049
- open: de,
1166
+ token: r,
1167
+ open: Ie,
1050
1168
  getCursor: () => d.highestSeq(),
1051
- onStatusChange: (n, p) => j.setConnStatus(n, p),
1169
+ onStatusChange: (n, p) => $.setConnStatus(n, p),
1052
1170
  onFrame(n) {
1053
- if (K.apply(n), n.type === "opened") {
1054
- if (r = n.conversation.id, !u) {
1171
+ if (re.apply(n), n.type === "opened") {
1172
+ if (c = n.conversation.id, !u) {
1055
1173
  u = !0;
1056
1174
  for (const p of v.load())
1057
- k.send({ type: "send", conversationId: r, clientMsgId: p.clientMsgId, content: p.content });
1175
+ T.send({ type: "send", conversationId: c, clientMsgId: p.clientMsgId, content: p.content });
1058
1176
  }
1059
- we(f, a, r, d, j, b), P.length && z(r);
1177
+ $e(f, r, c, d, $, b), O.length && U(c);
1060
1178
  }
1061
1179
  if (n.type === "ack" && v.remove(n.clientMsgId), n.type === "prekeyBundle") {
1062
- n.bundle && (Y = !0, x.x3dhSendTo(n.bundle).then((p) => {
1063
- const g = [...B.splice(0), ...D.splice(0)];
1064
- for (const E of g) ie(E.clientMsgId, E.text, p);
1065
- j.render(d);
1180
+ n.bundle && (se = !0, B.x3dhSendTo(n.bundle).then((p) => {
1181
+ const x = [...S.splice(0), ...q.splice(0)];
1182
+ for (const L of x) ye(L.clientMsgId, L.text, p);
1183
+ $.render(d);
1066
1184
  }));
1067
1185
  return;
1068
1186
  }
1069
1187
  if (n.type === "message" && d.e2e) {
1070
- const p = ye(n.message.content);
1071
- if (p && !x.ready) {
1072
- x.x3dhReceiveFrom(p.x3dhIK, p.x3dhEK, p.x3dhSPK).then(async () => {
1073
- await x.openFrame(n), d.apply(n), j.render(d);
1188
+ const p = Oe(n.message.content);
1189
+ if (p && !B.ready) {
1190
+ B.x3dhReceiveFrom(p.x3dhIK, p.x3dhEK, p.x3dhSPK).then(async () => {
1191
+ await B.openFrame(n), d.apply(n), $.render(d);
1074
1192
  });
1075
1193
  return;
1076
1194
  }
1077
1195
  }
1078
1196
  if (n.type === "peerkey") {
1079
- x.onPeerKey(n.key).then(() => {
1080
- se(), j.render(d);
1197
+ B.onPeerKey(n.key).then(() => {
1198
+ ve(), $.render(d);
1081
1199
  });
1082
1200
  return;
1083
1201
  }
1084
1202
  (async () => {
1085
- if (d.e2e && await x.openFrame(n), d.apply(n), n.type === "message" && n.message.senderId !== a && !n.message.internal && ($(), A()), d.e2e && r && !S) {
1086
- S = !0;
1087
- const p = await x.initX3DH();
1088
- k.send({ type: "uploadPrekeys", ...p });
1089
- const g = await x.begin();
1090
- k.send({ type: "pubkey", conversationId: r, key: g });
1203
+ if (d.e2e && await B.openFrame(n), d.apply(n), n.type === "manifest" && (w == null || w()), n.type === "message" && n.message.senderId !== r && !n.message.internal && (R(), A()), d.e2e && c && !m) {
1204
+ m = !0;
1205
+ const p = await B.initX3DH();
1206
+ T.send({ type: "uploadPrekeys", ...p });
1207
+ const x = await B.begin();
1208
+ T.send({ type: "pubkey", conversationId: c, key: x });
1091
1209
  }
1092
- j.render(d);
1210
+ $.render(d);
1093
1211
  })();
1094
1212
  }
1095
- }), k.connect(), j.render(d);
1096
- const M = o.launcher ? ne(o) : null, H = { close: () => {
1097
- s = !0, clearTimeout(i), k.close(), m == null || m.remove(), j.destroy(), K.destroy(), l && C && l.removeEventListener("change", C), R.delete(o.el), M && U.get(M) === H && U.delete(M);
1213
+ }), T.connect(), $.render(d);
1214
+ const _ = t.launcher ? ge(t) : null, W = { close: () => {
1215
+ s = !0, clearTimeout(i), T.close(), k == null || k.remove(), $.destroy(), re.destroy(), l && E && l.removeEventListener("change", E), g && (document.removeEventListener("keydown", g), g = null), H.delete(t.el), _ && X.get(_) === W && X.delete(_);
1098
1216
  } };
1099
- return R.set(o.el, H), M && U.set(M, H), H;
1217
+ return H.set(t.el, W), _ && X.set(_, W), W;
1100
1218
  }
1101
1219
  export {
1102
- xe as ChatStore,
1103
- ge as ConnectionManager,
1104
- me as E2ESession,
1105
- be as PersistentOutbox,
1106
- Ee as Renderer,
1107
- He as asConversationId,
1108
- ye as extractX3DHInit,
1109
- Me as httpBaseFromWsUrl,
1110
- Be as mount,
1111
- ue as resolveRelayUrls,
1112
- we as restoreHistory,
1113
- Ae as unmount
1220
+ Me as ChatStore,
1221
+ Re as ConnectionManager,
1222
+ qe as E2ESession,
1223
+ Be as PersistentOutbox,
1224
+ Ye as Renderer,
1225
+ at as asConversationId,
1226
+ Oe as extractX3DHInit,
1227
+ st as httpBaseFromWsUrl,
1228
+ ot as mount,
1229
+ Ae as resolveRelayUrls,
1230
+ $e as restoreHistory,
1231
+ tt as unmount
1114
1232
  };
1115
1233
  //# sourceMappingURL=index.js.map