@paramms/chat-widget 1.6.3 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chatlist.d.ts +4 -0
- package/dist/chatlist2.js +407 -353
- package/dist/chatlist2.js.map +1 -1
- package/dist/core.js.map +1 -1
- package/dist/embed-lite.js +3 -3
- package/dist/embed-lite.js.map +1 -1
- package/dist/embed.d.ts +3 -0
- package/dist/embed.js +3 -3
- package/dist/embed.js.map +1 -1
- package/dist/i18n.d.ts +41 -0
- package/dist/index.d.ts +12 -16
- package/dist/index.js +551 -518
- package/dist/index.js.map +1 -1
- package/dist/outbox.js +5 -0
- package/dist/outbox.js.map +1 -1
- package/dist/protocol/entities.d.ts +1 -0
- package/dist/react.d.ts +7 -3
- package/dist/react.js +322 -312
- package/dist/react.js.map +1 -1
- package/dist/renderer.d.ts +3 -1
- package/dist/store.d.ts +3 -0
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/renderer.styles.ts","../src/renderer.ts","../src/index.ts"],"sourcesContent":["// Injected stylesheet for the chat widget (`.ocw`). Extracted from renderer.ts\n// to keep the renderer focused on behaviour. Tokens come from the single\n// source of truth in theme-tokens.ts.\nimport { lightTokens, darkTokens } from './theme-tokens.js'\n\nexport const CSS = `\n.ocw { ${lightTokens('ocw')}\n position:relative;\n display:flex; flex-direction:column; height:100%; min-height:320px; background:var(--ocw-bg);\n font-family:var(--ocw-fb); color:var(--ocw-ink); overflow:hidden; }\n@media (prefers-color-scheme: dark) { .ocw[data-theme=\"auto\"] { ${darkTokens('ocw')} } }\n.ocw[data-theme=\"dark\"] { ${darkTokens('ocw')} }\n/* ── Responsive sizing ──────────────────────────────────────────────────────\n * Sizing is driven by the WIDGET'S OWN width (ResizeObserver toggles\n * .ocw-compact below 400px), not the viewport — so a widget embedded in a\n * narrow desktop sidebar sizes the same as one on a phone, and a tablet in\n * landscape keeps comfortable desktop sizing. A viewport query alone can't\n * see the container. Fullscreen (launcher on mobile) additionally gets\n * .ocw-fs from the launcher, which is the only case that should remove the\n * corner radius — an INLINE embed on a phone must NOT take over the page\n * (the old blanket min-height:100dvh rule did exactly that). */\n.ocw.ocw-fs { border-radius:0 !important; }\n.ocw-compact .ocw-bubble { font-size:15px; }\n.ocw-compact.ocw .ocw-input textarea { font-size:16px; } /* ≥16px prevents iOS zoom on focus */\n.ocw-compact .ocw-chip { padding:9px 14px; font-size:14px; }\n.ocw-compact .ocw-modal-card { width:90%; }\n.ocw-compact .ocw-row { max-width:94%; }\n.ocw-compact .ocw-sendbtn { min-width:44px; height:44px; }\n.ocw-compact .ocw-back { width:34px; height:34px; }\n.ocw-compact .ocw-quick button { padding:9px 15px; font-size:14px; }\n/* RTL support: when the host element has dir=rtl, flip layout direction */\n[dir=\"rtl\"] .ocw-row.mine { flex-direction:row; }\n[dir=\"rtl\"] .ocw-row.theirs { flex-direction:row-reverse; }\n[dir=\"rtl\"] .mine .ocw-bubble { border-bottom-right-radius:18px; border-bottom-left-radius:4px; }\n[dir=\"rtl\"] .theirs .ocw-bubble { border-bottom-left-radius:18px; border-bottom-right-radius:4px; }\n[dir=\"rtl\"] .ocw-input { flex-direction:row-reverse; }\n[dir=\"rtl\"] .mine .ocw-meta { text-align:left; }\n.ocw-head { display:flex; align-items:center; gap:10px; padding:12px 14px; background:var(--ocw-card); border-bottom:1px solid var(--ocw-line); }\n.ocw-back { flex:none; width:30px; height:30px; margin:-2px -2px -2px -4px; border:none; background:none; color:var(--ocw-ink); font-size:26px; line-height:1; cursor:pointer; border-radius:50%; display:flex; align-items:center; justify-content:center; }\n.ocw-back:hover { background:var(--ocw-bg); }\n.ocw-avatar { width:34px; height:34px; border-radius:50%; background:var(--ocw-tint); color:var(--ocw-accent); font-family:var(--ocw-fh); font-weight:600; display:flex; align-items:center; justify-content:center; font-size:13px; flex:none; }\n.ocw-head-main { flex:1; min-width:0; }\n.ocw-head-name { font-family:var(--ocw-fh); font-weight:600; font-size:15px; }\n.ocw-head-meta { color:var(--ocw-mut); font-size:12px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n.ocw-head-status { font-size:10.5px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n.ocw-head-status.online { color:#5cb37a; }\n.ocw-head-status.away { color:#c98b2e; }\n.ocw-msg-sender { font-size:10px; color:var(--ocw-mut); margin-bottom:2px; }\n.ocw-resolved-note { text-align:center; font-size:10.5px; color:var(--ocw-mut); margin:6px 0 12px; }\n.ocw-reopen { display:none; width:calc(100% - 32px); margin:0 16px 14px; height:40px; border:1.5px solid var(--ocw-accent); color:var(--ocw-accent); background:none; border-radius:20px; font:inherit; font-size:13.5px; font-weight:600; cursor:pointer; }\n.ocw-reopen:hover { background:var(--ocw-tint); }\n.ocw-badge { font-size:11px; font-weight:600; color:#2f8a52; background:#eafaf0; border-radius:20px; padding:5px 12px; }\n.ocw-e2e { font-size:11px; font-weight:700; color:var(--ocw-accent); background:var(--ocw-tint); border-radius:20px; padding:5px 12px; align-items:center; }\n.ocw-menu { color:var(--ocw-mut); width:30px; height:30px; border-radius:50%; border:1px solid var(--ocw-line); background:var(--ocw-card); cursor:pointer; }\n.ocw-chiprow { display:flex; gap:8px; padding:10px 12px; background:var(--ocw-card); border-bottom:1px solid var(--ocw-line); overflow-x:auto; }\n.ocw-chip { flex:none; display:flex; align-items:center; gap:6px; border:none; background:var(--ocw-tint); color:var(--ocw-accent); border-radius:999px; padding:7px 13px; font-size:12px; font-weight:600; cursor:pointer; white-space:nowrap; }\n.ocw-chip:hover { border-color:var(--ocw-accent); color:var(--ocw-accent); }\n.ocw-scroll { flex:1; min-height:0; overflow-y:auto; padding:16px 14px; display:flex; flex-direction:column; gap:10px; }\n.ocw-subject { background:var(--ocw-tint); border:none; border-radius:16px; padding:10px 12px; }\n.ocw-subject-title { font-family:var(--ocw-fh); font-weight:600; font-size:13px; margin-bottom:2px; }\n.ocw-subject-sub { color:var(--ocw-mut); font-size:10.5px; margin-bottom:6px; }\n.ocw-tags { display:flex; flex-wrap:wrap; gap:6px; }\n.ocw-tag { font-size:12px; color:#5b554e; background:#efeae3; border-radius:8px; padding:4px 10px; }\n.ocw-row { display:flex; align-items:flex-end; gap:8px; max-width:86%; }\n.ocw-row.mine { align-self:flex-end; flex-direction:row-reverse; }\n.ocw-row.theirs { align-self:flex-start; }\n.ocw-bubble { padding:10px 14px; border-radius:18px; font-size:13.5px; line-height:1.45; word-wrap:break-word; max-width:100%; width:fit-content; }\n.theirs .ocw-bubble { background:var(--ocw-tint); border-bottom-left-radius:4px; }\n.mine .ocw-bubble { background:var(--ocw-accent2); color:#fff; border-bottom-right-radius:4px; }\n.ocw-sys { align-self:center; color:var(--ocw-mut); font-size:12.5px; font-style:italic; text-align:center; max-width:90%; }\n.ocw-bot .ocw-bubble { background:var(--ocw-tint); }\n.ocw-note .ocw-bubble { background:#fffbeb; border:1.5px dashed #f59e0b; color:#78350f; border-radius:12px !important; }\n.ocw-note .ocw-bubble::before { content:'🔒 Note — '; font-size:11px; font-weight:700; color:#b45309; display:block; margin-bottom:3px; letter-spacing:.3px; }\n.ocw-time { font-size:10.5px; color:var(--ocw-mut); margin-top:3px; }\n.mine .ocw-meta { text-align:right; }\n.ocw-tick { margin-left:4px; font-size:11px; color:var(--ocw-mut); }\n.ocw-tick.read { color:#3b82f6; }\n.ocw-tick.delivered { color:var(--ocw-mut); }\n.ocw-deleted { font-style:italic; color:var(--ocw-mut); }\n.ocw-edited { font-size:10px; color:var(--ocw-mut); margin-left:4px; }\n.ocw-react { font-size:12px; margin-top:3px; display:flex; flex-wrap:wrap; gap:3px; }\n.ocw-react-pill { display:inline-flex; align-items:center; gap:3px; border:1px solid var(--ocw-line); border-radius:999px; padding:2px 7px; background:var(--ocw-card); font-size:12px; cursor:pointer; }\n.ocw-react-pill:hover { border-color:var(--ocw-accent); }\n.ocw-react-pill.mine { border-color:var(--ocw-accent); background:#fff8f5; }\n.ocw-react-wrap { position:relative; }\n.ocw-react-wrap:not(:hover) .ocw-react-picker { display:none; }\n.ocw-react-picker { position:absolute; bottom:calc(100% + 4px); left:0; background:var(--ocw-card); border:1px solid var(--ocw-line); 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; }\n.ocw-react-picker button { background:none; border:none; font-size:16px; cursor:pointer; padding:2px; border-radius:6px; }\n.ocw-react-picker button:hover { background:var(--ocw-bg); }\n.ocw-react-btn { background:none; border:1px solid var(--ocw-line); border-radius:999px; padding:2px 7px; font-size:12px; cursor:pointer; color:var(--ocw-mut); }\n.ocw-react-btn:hover { border-color:var(--ocw-accent); color:var(--ocw-accent); }\n.ocw-msg-menu { position:absolute; top:0; right:0; display:none; gap:3px; }\n.ocw-row.mine:hover .ocw-msg-menu { display:flex; }\n.ocw-row.theirs:hover .ocw-msg-menu { display:flex; left:0; right:auto; }\n.ocw-msg-menu button { background:var(--ocw-card); border:1px solid var(--ocw-line); border-radius:6px; font-size:11px; padding:2px 6px; cursor:pointer; color:var(--ocw-mut); }\n.ocw-msg-menu button:hover { border-color:var(--ocw-accent); color:var(--ocw-accent); }\n.ocw-msg-menu button.del:hover { border-color:#e74c3c; color:#e74c3c; }\n.ocw-bubble-wrap { position:relative; }\n.ocw-seen { font-size:10.5px; color:var(--ocw-mut); }\n.ocw-appt { background:#f0f7ff; border:1px solid #c7deff; border-radius:12px; padding:12px 14px; max-width:260px; }\n.ocw-appt-title { font-weight:700; font-size:14px; margin-bottom:4px; }\n.ocw-appt-time { font-size:12px; color:#1d4ed8; margin-bottom:4px; }\n.ocw-appt-loc { font-size:12px; color:var(--ocw-mut); margin-bottom:4px; }\n.ocw-appt-desc { font-size:12px; color:var(--ocw-mut); margin-bottom:10px; white-space:pre-wrap; }\n.ocw-appt-links { display:flex; flex-direction:column; gap:6px; }\n.ocw-appt-btn { display:block; text-align:center; padding:8px 12px; border-radius:8px; font-size:13px; font-weight:600; text-decoration:none; background:var(--ocw-accent); color:#fff; }\n.ocw-appt-btn-sec { background:var(--ocw-card); color:var(--ocw-accent); border:1px solid var(--ocw-accent); }\n.ocw-conn-status { font-size:10px; color:var(--ocw-mut); margin-left:4px; }\n.ocw-conn-status.warn { color:#e67e22; }\n.ocw-conn-status.err { color:#c0392b; font-weight:600; }\n.ocw-load-more { display:block; width:100%; background:none; border:1px solid var(--ocw-line); border-radius:10px; padding:6px 0; font-size:12px; color:var(--ocw-mut); cursor:pointer; margin-bottom:8px; }\n.ocw-load-more:hover { border-color:var(--ocw-accent); color:var(--ocw-accent); }\n.ocw-away { margin:0 14px 8px; padding:9px 12px; background:#fff8e6; border:1px solid #f0e2bd; border-radius:10px; font-size:12px; color:#7a5c17; display:flex; gap:7px; align-items:flex-start; line-height:1.45; }\n.ocw button:focus-visible, .ocw textarea:focus-visible, .ocw a:focus-visible, .ocw [tabindex]:focus-visible { outline:2px solid var(--ocw-accent); outline-offset:2px; border-radius:8px; }\n.ocw-sendbtn:active { transform:scale(.92); }\n.ocw-quick button:active, .ocw-chip:active { transform:scale(.97); }\n@media (prefers-color-scheme: dark) { .ocw[data-theme=\"auto\"] .ocw-away { background:#3a3018; border-color:#5a4a1f; color:#e8d9a8; } .ocw[data-theme=\"auto\"] .ocw-note .ocw-bubble { background:#332b12; border-color:#7a5c17; color:#f0e2bd; } .ocw[data-theme=\"auto\"] .ocw-note .ocw-bubble::before { color:#e0c060; } }\n.ocw[data-theme=\"dark\"] .ocw-away { background:#3a3018; border-color:#5a4a1f; color:#e8d9a8; }\n.ocw[data-theme=\"dark\"] .ocw-note .ocw-bubble { background:#332b12; border-color:#7a5c17; color:#f0e2bd; }\n.ocw[data-theme=\"dark\"] .ocw-note .ocw-bubble::before { color:#e0c060; }\n.ocw-away-icon { flex:none; }\n/* Shared form styles (used by the pre-chat panel; named for the retired offline form). */\n.ocw-offline-form { display:flex; flex-direction:column; gap:8px; text-align:left; }\n.ocw-offline-input { border:none; background:var(--ocw-tint); border-radius:14px; padding:10px 12px; font-size:13px; font-family:inherit; color:var(--ocw-ink); }\n.ocw-offline-input:focus { outline:none; box-shadow:inset 0 0 0 1.5px var(--ocw-accent); }\n.ocw-offline-submit { background:var(--ocw-accent); color:var(--ocw-onaccent); border:none; border-radius:20px; padding:11px; font-family:var(--ocw-fh); font-size:13.5px; font-weight:600; cursor:pointer; box-shadow:var(--ocw-shadow); }\n.ocw-prechat { margin:16px; padding:0; background:none; border:none; }\n.ocw-prechat-title { font-family:var(--ocw-fh); font-weight:600; font-size:20px; margin-bottom:10px; }\n.ocw-prechat select { border:none; border-radius:14px; padding:10px 12px; font:inherit; font-size:13px; background:var(--ocw-tint); color:var(--ocw-ink); }\n.ocw-prechat-cb { display:flex; align-items:center; gap:8px; font-size:13px; color:var(--ocw-ink); }\n.ocw-deflect { margin:0 14px 8px; display:flex; flex-direction:column; gap:6px; }\n.ocw-deflect-card { text-align:left; background:var(--ocw-card); border:1px solid var(--ocw-line); border-radius:12px; padding:10px 12px; font:inherit; font-size:13px; cursor:pointer; }\n.ocw-deflect-card:hover { border-color:var(--ocw-accent); }\n.ocw-deflect-q { font-weight:600; margin-bottom:2px; }\n.ocw-deflect-a { color:var(--ocw-mut); font-size:12.5px; display:none; white-space:pre-wrap; }\n.ocw-deflect-card.open .ocw-deflect-a { display:block; }\n.ocw-deflect-hint { font-size:11.5px; color:var(--ocw-mut); text-align:center; }\n.ocw-csat-title { font-size:13px; font-weight:600; margin-bottom:8px; }\n.ocw-csat-stars { display:flex; gap:6px; }\n.ocw-csat-star { background:none; border:none; font-size:22px; cursor:pointer; padding:2px; opacity:.4; transition:opacity .15s; }\n.ocw-csat-star:hover, .ocw-csat-star.lit { opacity:1; }\n.ocw-csat-done { font-size:12px; color:var(--ocw-mut); margin-top:6px; }\n\n.ocw-typing { min-height:22px; padding:0 16px 4px; display:flex; align-items:center; }\n.ocw-typing-bubble { display:none; align-items:center; gap:3px; background:var(--ocw-card); border:1px solid var(--ocw-line); border-radius:14px; border-bottom-left-radius:4px; padding:7px 12px; }\n.ocw-typing.active .ocw-typing-bubble { display:flex; }\n.ocw-typing-dot { width:6px; height:6px; border-radius:50%; background:var(--ocw-mut); animation:ocw-bounce 1.2s infinite ease-in-out; }\n.ocw-typing-dot:nth-child(2) { animation-delay:.2s; }\n.ocw-typing-dot:nth-child(3) { animation-delay:.4s; }\n@keyframes ocw-bounce { 0%,60%,100%{transform:translateY(0)} 30%{transform:translateY(-5px)} }\n.ocw-quick { display:flex; gap:8px; padding:8px 12px 6px; overflow-x:auto; scrollbar-width:none; flex-shrink:0; }\n.ocw-quick::-webkit-scrollbar { display:none; }\n.ocw-quick button { flex:none; border:none; background:var(--ocw-tint); border-radius:999px; padding:7px 14px; font-size:12px; font-weight:600; cursor:pointer; color:var(--ocw-accent); white-space:nowrap; transition:background .12s,color .12s; }\n.ocw-quick button:hover { background:var(--ocw-accent); color:var(--ocw-onaccent); }\n.ocw-form-host:empty { display:none; }\n.ocw-form { margin:6px 12px 0; padding:12px; background:var(--ocw-card); border:1px solid var(--ocw-line); border-radius:14px; }\n.ocw-form-title { font-weight:700; font-size:14px; margin-bottom:8px; }\n.ocw-form-row { display:flex; flex-direction:column; gap:3px; margin-bottom:8px; }\n.ocw-form-lbl { font-size:12px; color:var(--ocw-mut); }\n.ocw-form-input { border:1px solid var(--ocw-line); border-radius:9px; padding:9px 11px; font:inherit; font-size:14px; outline:none; }\n.ocw-form-input:focus { border-color:var(--ocw-accent); }\n.ocw-form-actions { display:flex; justify-content:flex-end; gap:8px; margin-top:4px; }\n.ocw-form-cancel { background:none; border:none; color:var(--ocw-mut); font-size:13px; cursor:pointer; padding:8px 10px; }\n.ocw-form-submit { background:var(--ocw-accent); color:#fff; border:none; border-radius:999px; padding:8px 18px; font-size:13px; font-weight:600; cursor:pointer; }\n.ocw-modal { position:absolute; inset:0; background:rgba(20,18,16,.42); display:flex; align-items:center; justify-content:center; z-index:50; }\n.ocw-modal-card { background:var(--ocw-card); border-radius:16px; padding:20px; width:78%; max-width:300px; box-shadow:0 14px 44px rgba(0,0,0,.22); }\n.ocw-modal-title { font-weight:700; font-size:16px; margin-bottom:6px; }\n.ocw-modal-body { color:var(--ocw-mut); font-size:14px; margin-bottom:16px; }\n.ocw-modal-actions { display:flex; justify-content:flex-end; gap:8px; }\n.ocw-modal-cancel { background:none; border:none; color:var(--ocw-mut); font-size:14px; cursor:pointer; padding:9px 12px; }\n.ocw-modal-ok { background:var(--ocw-accent); color:#fff; border:none; border-radius:999px; padding:9px 20px; font-size:14px; font-weight:600; cursor:pointer; }\n.ocw-input { display:flex; align-items:center; gap:10px; padding:12px; }\n.ocw-footer { text-align:center; font-size:11px; color:var(--ocw-mut); padding:6px 0 8px; }\n.ocw-footer a { color:var(--ocw-mut); text-decoration:none; font-weight:600; }\n.ocw-footer a:hover { color:var(--ocw-accent); }\n.ocw-attach { background:none;border:none;cursor:pointer;font-size:18px;padding:4px 6px;opacity:.6;flex-none; }\n.ocw-attach:hover { opacity:1; }\n.ocw-input textarea { flex:1; min-width:0; resize:none; border:none; border-radius:20px; padding:11px 16px; font:inherit; font-size:14px; line-height:1.4; background:var(--ocw-tint); outline:none; max-height:120px; overflow-y:auto; }\n.ocw-input textarea:focus { box-shadow:inset 0 0 0 1.5px var(--ocw-accent); }\n/* Send button auto-sizes to its label: the default is a fixed circle around an\n * inline SVG; a TEXT label (i18n.send: \"Send\", \"보내기\", \"Enviar\"…) switches to\n * .ocw-sendbtn-label — a pill whose width follows the text. A fixed 42px\n * circle with 18px type overflowed the moment anyone localized the label. */\n.ocw-sendbtn { min-width:38px; height:38px; border-radius:999px; border:none; background:var(--ocw-accent2); 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; }\n.ocw-sendbtn-label { padding:0 16px; white-space:nowrap; }\n.ocw-sendbtn svg { width:19px; height:19px; display:block; }\n.ocw-sendbtn:not(:disabled):hover { transform:scale(1.05); }\n.ocw-sendbtn:not(:disabled):active { transform:scale(.96); }\n.ocw-sendbtn:disabled { opacity:.5; cursor:default; }\n.ocw-sendbtn:focus-visible, .ocw-back:focus-visible, .ocw-chip:focus-visible { outline:2px solid var(--ocw-accent); outline-offset:2px; }\n\n.ocw-translate-btn { position:absolute; bottom:2px; right:-26px; background:var(--ocw-card); border:1px solid var(--ocw-line); border-radius:50%; width:22px; height:22px; font-size:11px; cursor:pointer; color:var(--ocw-mut); display:flex; align-items:center; justify-content:center; opacity:0; transition:opacity .15s; padding:0; }\n.ocw-row.theirs .ocw-translate-btn { right:auto; left:-26px; }\n.ocw-bubble-wrap:hover .ocw-translate-btn { opacity:1; }\n.ocw-translation { margin-top:6px; padding-top:6px; border-top:1px solid var(--ocw-line); font-size:13px; line-height:1.45; }\n.ocw-translation::before { content:'🌐 '; opacity:.75; }\n`\n","import type { ManifestAction, MessageContent } from './protocol/index.js'\nimport type { ChatStore, RenderMessage } from './store.js'\nimport { CSS } from './renderer.styles.js'\n\nexport interface WidgetConfig {\n subject?: { title?: string; subtitle?: string; tags?: string[]; status?: string; ownerLabel?: string }\n quickReplies?: string[]\n accent?: string\n /** Secondary accent — the guest's OWN bubble + send button. Defaults to the\n * design blue; if omitted while `accent` is set, follows `accent` so a single\n * accent override re-themes cohesively. */\n accent2?: string\n /** Colour scheme. Default 'light'. 'auto' follows the OS (prefers-color-scheme);\n * 'dark'/'light' force it. Dark is never auto-applied unless the host opts in. */\n theme?: 'auto' | 'light' | 'dark'\n /** Load the brand webfonts (Baloo 2 + Nunito). Default true; set false for\n * strict-CSP / privacy-sensitive hosts (falls back to the system stack). */\n webfont?: boolean\n /** Identified user info — shown as the guest avatar/name in the widget header. */\n userInfo?: { name?: string; avatar?: string }\n /** i18n string overrides */\n i18n?: { placeholder?: string; send?: string; offline?: string; poweredBy?: string; online?: string; away?: string; aiAssistant?: string; resolved?: string; reopen?: string; sources?: string; helpful?: string; notHelpful?: string }\n\n}\n\nexport interface RendererHandlers {\n onSend(text: string): void\n onAttach?(file: File): void\n onInvoke(actionId: string, inputs?: Record<string, unknown>): void\n onTyping(isTyping: boolean, preview?: string): void\n onReadUpTo(seq: number): void\n onReact?(messageId: string, emoji: string, remove: boolean): void\n onCsat?(score: number): void\n onLoadMore?(): void\n onEdit?(messageId: string, newText: string): void\n onDelete?(messageId: string): void\n /** Pre-chat qualification submitted (values keyed by field; topic/callback included). */\n onPreChat?(values: { name?: string; email?: string; phone?: string; topic?: string; callback?: boolean }): void\n /** KB deflection: the guest is typing their FIRST message — look up articles. */\n onDeflectQuery?(q: string): void\n /** Translate a message's text for display. Return null if unavailable —\n * the renderer shows a brief \"unavailable\" hint and leaves the original. */\n onTranslate?(text: string): Promise<string | null>\n /** Stack navigation (chat-app surfaces): when set, the header shows a back\n * chevron on the left that calls this — tap a conversation → chatroom →\n * back → list, like a native messaging app. Omit for a standalone widget,\n * which has nothing to go \"back\" to. */\n onBack?(): void\n}\n\nconst STYLE_ID = 'objectchat-widget-styles'\nconst REACTION_EMOJIS = ['👍', '❤️', '😂', '😮', '😢', '🙏']\n/** Width below which the widget switches to compact (touch-friendly) sizing. */\nconst COMPACT_BREAKPOINT = 400\n// Inline, dependency-free send glyph — `currentColor` follows the button text\n// colour; no emoji/font dependency so it renders identically across platforms.\nconst SEND_ICON_SVG =\n '<svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">' +\n '<path d=\"M22 2 11 13M22 2l-7 20-4-9-9-4 20-7z\" stroke=\"currentColor\" stroke-width=\"2\" ' +\n 'stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>'\n// CSS lives in renderer.styles.ts (single source of truth for .ocw styling).\n\nfunction injectStyles(): void {\n if (typeof document === 'undefined' || document.getElementById(STYLE_ID)) return\n const s = document.createElement('style'); s.id = STYLE_ID; s.textContent = CSS; document.head.appendChild(s)\n}\n\n/** Load the brand webfonts (Baloo 2 + Nunito) once. The design's identity is its\n * rounded type — without this the widget falls back to system fonts and looks\n * generic. Injected as a <link> so a strict host CSP that blocks it degrades\n * gracefully to the system stack. Opt out with `webfont: false`. */\nconst FONT_ID = 'ocw-webfont'\nfunction injectFonts(): void {\n if (typeof document === 'undefined' || document.getElementById(FONT_ID)) return\n const l = document.createElement('link')\n l.id = FONT_ID; l.rel = 'stylesheet'\n l.href = 'https://fonts.googleapis.com/css2?family=Baloo+2:wght@500;600;700&family=Nunito:wght@400;500;600;700&display=swap'\n document.head.appendChild(l)\n}\n\nfunction el<K extends keyof HTMLElementTagNameMap>(tag: K, cls?: string, text?: string): HTMLElementTagNameMap[K] {\n const n = document.createElement(tag); if (cls) n.className = cls; if (text !== undefined) n.textContent = text; return n\n}\nfunction fmtTime(ts: number): string {\n try { return new Date(ts).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) } catch { return '' }\n}\nfunction contentText(c: MessageContent): string {\n switch (c.kind) {\n case 'text': return c.text\n case 'system': return typeof c.data?.['message'] === 'string' ? String(c.data['message']) : c.event\n case 'card': return [c.title, c.body].filter(Boolean).join(' — ')\n case 'attachment': return c.name ?? c.url\n case 'form': return c.prompt\n case 'appointment': return `📅 ${c.title} — ${new Date(c.startIso).toLocaleString()}`\n }\n}\n\n/** Renders a ChatStore into a host element in the Image-1 layout: header →\n * subject card → action chips → chat → quick replies → input. Self-injects its\n * stylesheet so it looks right wherever it mounts. Accent comes from the\n * dashboard-authored profile theme (via the manifest), falling back to config. */\nexport class Renderer {\n private readonly scroll: HTMLElement\n private readonly chips: HTMLElement\n private readonly typing: HTMLElement\n private readonly quick: HTMLElement\n private readonly formHost: HTMLElement\n private readonly csatPanel: HTMLElement\n private readonly awayNotice: HTMLElement\n private readonly headStatus: HTMLElement\n private readonly reopenBtn: HTMLButtonElement\n private readonly preChatPanel: HTMLElement\n private readonly deflectPanel: HTMLElement\n private preChatBuilt = false\n private preChatDone = false\n private readonly footer: HTMLElement\n private readonly input: HTMLTextAreaElement\n private readonly subjectCard: HTMLElement\n private readonly e2eBadge: HTMLElement\n private readonly statusBadge: HTMLElement\n private readonly headerName: HTMLElement\n private readonly connStatus: HTMLElement\n private typingTimer: ReturnType<typeof setTimeout> | null = null\n private csatSubmitted = false\n private readonly sendBtn: HTMLButtonElement\n /** Container-driven responsive sizing — toggles .ocw-compact (see CSS note). */\n private compactObserver: ResizeObserver | null = null\n private storeRef: ChatStore | null = null\n private readonly cfgAccent2: string | undefined\n private scrollCleanup: (() => void) | null = null\n\n /** Returns the scroll container so history.ts can attach scroll listeners. */\n getScrollEl(): HTMLElement | null { return this.scroll }\n\n /** Registers a cleanup fn removed on destroy() to prevent listener leaks. */\n setScrollCleanup(fn: () => void): void {\n this.scrollCleanup?.()\n this.scrollCleanup = fn\n }\n\n // ── Live translation ──────────────────────────────────────────────────-\n private readonly translationCache = new Map<string, string>()\n private readonly showingTranslation = new Set<string>()\n\n\n /** Last seq the guest has seen per conversationId — used to compute unread badges. */\n\n\n constructor(\n private readonly root: HTMLElement,\n private readonly me: string,\n private readonly h: RendererHandlers,\n private readonly cfg: WidgetConfig = {},\n ) {\n injectStyles()\n if (cfg.webfont !== false) injectFonts()\n // Clear any previous widget content on this element before building.\n // This is the last line of defence against double-mounts: even if mount()\n // is called twice on the same element (React StrictMode, HMR, caller bug),\n // the second Renderer wipes the first one's DOM so only one UI is visible.\n root.replaceChildren()\n root.classList.add('ocw')\n if (cfg.accent) root.style.setProperty('--ocw-accent', cfg.accent)\n // Two-accent model: explicit accent2 wins; else if only accent is set, accent2\n // follows it (single-token retheme); else the CSS defaults (indigo + blue) hold.\n const accent2 = cfg.accent2 ?? cfg.accent\n if (accent2) root.style.setProperty('--ocw-accent2', accent2)\n this.cfgAccent2 = cfg.accent2\n // Colour scheme is a deliberate choice, never auto-detected by default:\n // unset → 'light'. 'auto' is an explicit opt-in that follows the OS via the\n // prefers-color-scheme media query (which is scoped to [data-theme=\"auto\"]);\n // 'dark'/'light' force the scheme regardless of the OS.\n root.dataset.theme = cfg.theme ?? 'light'\n\n // Header\n const head = el('div', 'ocw-head')\n // Stack navigation: a back chevron returns to the conversation list. Only\n // shown when the host wired onBack (chat-app surfaces) — a standalone\n // support widget has no list to go back to.\n if (this.h.onBack) {\n const back = el('button', 'ocw-back', '‹') as HTMLButtonElement\n back.type = 'button'\n back.setAttribute('aria-label', 'Back')\n back.addEventListener('click', () => this.h.onBack!())\n head.append(back)\n }\n // Header identity is the party the guest is talking TO (the brand/room), so\n // the avatar mirrors the header name — a brand monogram (e.g. \"NM\"), never a\n // generic person glyph. Hidden entirely when there's no brand name to show.\n const brandName = cfg.subject?.ownerLabel ?? cfg.subject?.title ?? ''\n const avatarEl = el('div', 'ocw-avatar')\n if (brandName.trim()) {\n avatarEl.textContent = brandName.trim()[0]!.toUpperCase()\n head.append(avatarEl)\n }\n const hm = el('div', 'ocw-head-main')\n this.headerName = el('div', 'ocw-head-name', cfg.subject?.ownerLabel ?? cfg.subject?.title ?? '')\n hm.append(this.headerName)\n this.headStatus = el('div', 'ocw-head-status'); this.headStatus.style.display = 'none'\n hm.append(this.headStatus)\n if (cfg.subject?.subtitle) hm.append(el('div', 'ocw-head-meta', cfg.subject.subtitle))\n head.append(hm)\n this.statusBadge = el('span', 'ocw-badge', cfg.subject?.status ?? '')\n if (!cfg.subject?.status) this.statusBadge.style.display = 'none'\n head.append(this.statusBadge)\n this.e2eBadge = el('span', 'ocw-e2e', '🔒 E2E'); this.e2eBadge.style.display = 'none'; head.append(this.e2eBadge)\n this.connStatus = el('span', 'ocw-conn-status'); this.connStatus.style.display = 'none'; head.append(this.connStatus)\n\n head.append(el('button', 'ocw-menu', '⋯'))\n\n // Action chips (filled in render)\n this.chips = el('div', 'ocw-chiprow')\n\n // Scroll area with optional subject card + messages\n this.scroll = el('div', 'ocw-scroll')\n this.subjectCard = el('div', 'ocw-subject')\n this.typing = el('div', 'ocw-typing')\n const typingBubble = el('div', 'ocw-typing-bubble')\n typingBubble.append(el('div', 'ocw-typing-dot'), el('div', 'ocw-typing-dot'), el('div', 'ocw-typing-dot'))\n this.typing.append(typingBubble)\n this.quick = el('div', 'ocw-quick')\n for (const q of cfg.quickReplies ?? []) {\n const b = el('button', undefined, q)\n b.addEventListener('click', () => {\n this.h.onSend(q)\n // Hide quick replies immediately after one is tapped\n this.quick.style.display = 'none'\n })\n this.quick.append(b)\n }\n\n // Input\n this.formHost = el('div', 'ocw-form-host')\n this.csatPanel = el('div', 'ocw-csat'); this.csatPanel.style.display = 'none'\n // Away notice (outside office hours). INFORMATIONAL only — it never blocks\n // the composer: the message is delivered either way and an agent replies\n // when they're back. (It used to be a card that replaced the composer with\n // a name/email/message form; guests could not simply chat.)\n this.awayNotice = el('div', 'ocw-away'); this.awayNotice.style.display = 'none'\n this.preChatPanel = el('div', 'ocw-prechat'); this.preChatPanel.style.display = 'none'\n this.deflectPanel = el('div', 'ocw-deflect'); this.deflectPanel.style.display = 'none'\n this.input = el('textarea', undefined); this.input.rows = 1\n // i18n.placeholder was documented in MountOptions but never actually\n // applied — the composer always said \"Message…\" regardless.\n this.input.placeholder = cfg.i18n?.placeholder ?? 'Message…'\n const sendBtn = el('button', 'ocw-sendbtn') as HTMLButtonElement\n sendBtn.type = 'button'\n const sendLabel = cfg.i18n?.send\n if (sendLabel) {\n // Text label → auto-width pill (see the .ocw-sendbtn-label CSS note).\n sendBtn.textContent = sendLabel\n sendBtn.classList.add('ocw-sendbtn-label')\n sendBtn.setAttribute('aria-label', sendLabel)\n } else {\n // Default: inline SVG paper plane — renders identically on every\n // platform (the old '➤' text glyph varied per OS font).\n sendBtn.innerHTML = SEND_ICON_SVG\n sendBtn.setAttribute('aria-label', 'Send message')\n }\n // The :disabled style existed but nothing ever set the state — send is\n // inactive until there's something to send, like every mainstream chat UI.\n sendBtn.disabled = true\n this.sendBtn = sendBtn\n sendBtn.addEventListener('click', () => this.flushSend())\n this.input.addEventListener('input', () => {\n this.sendBtn.disabled = this.input.value.trim().length === 0\n this.autoGrowInput()\n // Deflection fires only for the FIRST message of an empty conversation —\n // once a thread exists, suggestions would just be noise.\n if (this.storeRef && !this.storeRef.messages().some(m => m.senderRole === 'guest')) this.h.onDeflectQuery?.(this.input.value)\n else this.hideDeflection()\n })\n this.input.addEventListener('keydown', (e) => {\n // Guard against IME composition (Korean/Japanese/Chinese input): while\n // the user is selecting a candidate from the IME's suggestion list,\n // pressing Enter to CONFIRM the candidate also fires a keydown with\n // key === 'Enter'. Without this check, that confirmation keystroke was\n // being treated as \"send the message\" — firing early with a partial\n // composition, and then firing again on the real Enter press with\n // whatever text was left, producing two bubbles for one message\n // (e.g. typing \"음식\" sends \"음식\" then \"식\").\n // e.isComposing covers most browsers; keyCode 229 is the long-standing\n // fallback for browsers/IMEs that don't set isComposing reliably.\n if (e.isComposing || e.keyCode === 229) return\n if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); this.flushSend() } else this.signalTyping()\n })\n\n const attachBtn = el('button', 'ocw-attach', '📎'); attachBtn.title = 'Attach image or file'\n const fileInput = document.createElement('input'); fileInput.type = 'file'\n fileInput.accept = 'image/*,.pdf,.txt,.doc,.docx'; fileInput.style.display = 'none'\n attachBtn.addEventListener('click', () => fileInput.click())\n fileInput.addEventListener('change', () => { if (fileInput.files?.[0] && this.h.onAttach) this.h.onAttach(fileInput.files[0]); fileInput.value = '' })\n\n const inputRow = el('div', 'ocw-input'); inputRow.append(attachBtn, fileInput, this.input, sendBtn)\n\n const footer = el('div', 'ocw-footer')\n // i18n.poweredBy: if the caller provides it, treat as plain text — no innerHTML.\n // Only our own hardcoded default renders the anchor as HTML.\n if (cfg.i18n?.poweredBy !== undefined) {\n footer.textContent = cfg.i18n.poweredBy\n } else {\n footer.innerHTML = 'Powered by <a href=\"https://relay.paramms.com\" target=\"_blank\" rel=\"noopener\">Relay</a>'\n }\n this.footer = footer\n\n this.reopenBtn = el('button', 'ocw-reopen', this.cfg.i18n?.reopen ?? 'Reopen conversation') as HTMLButtonElement\n this.reopenBtn.type = 'button'\n this.reopenBtn.addEventListener('click', () => {\n this.reopenBtn.style.display = 'none'\n const ta = this.root.querySelector('.ocw-input textarea') as HTMLTextAreaElement | null\n ta?.focus()\n })\n root.append(head, this.chips, this.scroll, this.typing, this.quick, this.formHost, this.csatPanel, this.preChatPanel, this.deflectPanel, this.awayNotice, inputRow, this.reopenBtn, this.footer)\n\n // Container-driven responsive sizing: compact below COMPACT_BREAKPOINT of\n // the widget's OWN width. Covers phones AND narrow desktop embeds — a\n // viewport media query can't see the container. Guarded: jsdom/tests and\n // very old runtimes have no ResizeObserver; they keep desktop sizing.\n const applyCompact = (w: number): void => { root.classList.toggle('ocw-compact', w > 0 && w < COMPACT_BREAKPOINT) }\n applyCompact(root.clientWidth)\n if (typeof ResizeObserver !== 'undefined') {\n this.compactObserver = new ResizeObserver((entries) => {\n const w = entries[0]?.contentRect.width ?? root.clientWidth\n applyCompact(w)\n })\n this.compactObserver.observe(root)\n }\n }\n\n /** Call when the widget is unmounted. Disconnects scroll listeners and clears timers. */\n destroy(): void {\n this.scrollCleanup?.()\n this.scrollCleanup = null\n if (this.typingTimer) { clearTimeout(this.typingTimer); this.typingTimer = null }\n this.compactObserver?.disconnect()\n this.compactObserver = null\n }\n\n\n private flushSend(): void {\n this.hideDeflection()\n const text = this.input.value.trim()\n if (!text) return\n this.input.value = ''\n this.sendBtn.disabled = true\n this.autoGrowInput() // collapse back to one row\n this.h.onTyping(false)\n this.h.onSend(text)\n }\n\n /** Grow the composer with its content (up to the CSS max-height), collapse\n * when cleared. scrollHeight is 0 in non-layout environments (jsdom) —\n * skip there so tests and SSR-ish mounts are unaffected. */\n private autoGrowInput(): void {\n this.input.style.height = 'auto'\n const sh = this.input.scrollHeight\n if (sh > 0) this.input.style.height = `${Math.min(sh, 120)}px`\n else this.input.style.removeProperty('height')\n }\n private signalTyping(): void {\n const preview = this.input.value.trim().slice(0, 100) || undefined\n this.h.onTyping(true, preview)\n if (this.typingTimer) clearTimeout(this.typingTimer)\n this.typingTimer = setTimeout(() => this.h.onTyping(false), 2000)\n }\n\n /** A3.3 — per-chatroom bot identity from the manifest; relabels bot bubbles. */\n setBotName(name: string): void {\n this.cfg.i18n = { ...(this.cfg.i18n ?? {}), aiAssistant: name }\n }\n\n render(store: ChatStore): void {\n this.storeRef = store\n if (store.accent) {\n this.root.style.setProperty('--ocw-accent', store.accent)\n this.root.style.setProperty('--ocw-accent2', this.cfgAccent2 ?? store.accent)\n }\n this.e2eBadge.style.display = store.e2e ? 'inline-flex' : 'none'\n this.buildSubjectCard(store)\n // Header: when a subject is attached, show ownerLabel (\"Seller\", \"Host\")\n // or nothing — the subject card below carries the identity.\n // Without a subject, the header is already set to cfg.subject?.ownerLabel\n // or \"Chat\" from the constructor — don't overwrite it with the domain name\n // which would duplicate the subject card title or clutter a plain chat.\n if (store.subject) {\n const ownerLabel = this.cfg.subject?.ownerLabel\n if (ownerLabel) this.headerName.textContent = ownerLabel\n // else leave constructor default (\"Chat\")\n }\n // Without a subject: leave header as-is (set once in constructor)\n\n // Quick replies are a first-touch affordance (\"Is this still available?\").\n // They belong only on an empty conversation — once there's any message,\n // hide them, and keep them hidden on every re-render (returning to the\n // widget, reload, back-nav). Without this they reappear each mount even\n // though the conversation is already underway.\n this.quick.style.display = store.messages().length === 0 ? 'flex' : 'none'\n\n // Action chips from the manifest (filtered by state in the store)\n this.chips.replaceChildren()\n const actions = store.visibleActions()\n this.chips.style.display = actions.length ? 'flex' : 'none'\n for (const a of actions) this.chips.append(this.chipEl(a))\n\n // Messages\n // Preserve scroll anchor when history is prepended: capture height before\n // replaceChildren so we can restore relative position after.\n const prevScrollHeight = this.scroll.scrollHeight\n const prevScrollTop = this.scroll.scrollTop\n\n this.scroll.replaceChildren()\n if (this.subjectCard.childNodes.length) this.scroll.append(this.subjectCard)\n if (store.hasMoreHistory) {\n // Sentinel at top — scroll to here triggers load-more via the scroll\n // listener set up by restoreHistory. Shows a subtle loading indicator\n // so the user knows older messages are available.\n const sentinel = el('div', 'ocw-load-more')\n sentinel.textContent = '↑ Loading earlier messages…'\n sentinel.style.pointerEvents = 'none'\n this.scroll.append(sentinel)\n }\n let maxOther = 0\n let prevSender: string | null = null\n const msgs = [...store.messages()]\n for (let i = 0; i < msgs.length; i++) {\n const m = msgs[i]!\n const showLabel = m.senderRole !== 'system' && m.senderId !== this.me && !m.internal && m.senderId !== prevSender\n this.scroll.append(this.messageEl(m, store, showLabel, i === msgs.length - 1))\n if (m.senderRole !== 'system') prevSender = m.senderId\n if (m.senderId !== this.me && m.seq > maxOther) maxOther = m.seq\n }\n // Auto-scroll to bottom only for new messages; restore anchor when history was prepended.\n if (prevScrollTop > 20) {\n this.scroll.scrollTop = this.scroll.scrollHeight - prevScrollHeight + prevScrollTop\n } else {\n this.scroll.scrollTop = this.scroll.scrollHeight\n }\n if (maxOther > 0) this.h.onReadUpTo(maxOther)\n\n const typingNames = [...store.typing]\n this.typing.classList.toggle('active', typingNames.length > 0)\n // Bubble is always present in DOM (hidden via CSS); just update label\n const bubble = this.typing.querySelector('.ocw-typing-bubble')\n if (bubble) bubble.setAttribute('aria-label', typingNames.length ? 'typing' : '')\n this.footer.style.display = store.whiteLabel ? 'none' : 'block'\n\n // Offline mode: show form instead of chat input\n // Pre-chat qualification (dashboard-configured, arrives in the manifest):\n // shown before the FIRST message when enabled — 'offline'-scoped configs\n // replace the default leave-a-message form; 'always' configs gate the\n // composer while the team is online too. Never re-shown once completed\n // or once the conversation has any history.\n // \"Before the first message\" means the GUEST hasn't spoken — a chatroom\n // welcomeMessage is a real stored system message, so counting ALL\n // messages suppressed pre-chat (and deflection) on exactly the chatrooms\n // most likely to configure them.\n const guestHasSpoken = store.messages().some(m => m.senderRole === 'guest')\n const preChatWanted = !!store.preChat?.enabled && !this.preChatDone && !guestHasSpoken &&\n (store.preChat!.showWhen !== 'offline' || store.offline)\n if (preChatWanted) {\n if (!this.preChatBuilt) this.buildPreChatPanel(store.preChat!)\n this.preChatPanel.style.display = 'block'\n this.awayNotice.style.display = 'none'\n ;(this.root.querySelector('.ocw-input') as HTMLElement | null)?.style.setProperty('display', 'none')\n } else {\n this.preChatPanel.style.display = 'none'\n // Being outside office hours only changes EXPECTATIONS, never capability:\n // say we're away, keep the composer, deliver the message, reply later.\n if (store.offline) {\n if (!this.awayNotice.firstChild) {\n this.awayNotice.append(el('span', 'ocw-away-icon', '🌙'), el('span', '', ''))\n }\n const copy = this.awayNotice.lastChild as HTMLElement\n copy.textContent = store.offlineMessage\n || this.cfg.i18n?.offline\n || \"We're away right now — send your message and we'll reply as soon as we're back.\"\n this.awayNotice.style.display = 'flex'\n } else {\n this.awayNotice.style.display = 'none'\n }\n ;(this.root.querySelector('.ocw-input') as HTMLElement | null)?.style.removeProperty('display')\n }\n\n // CSAT: show star-rating panel when conversation reaches a terminal state\n // and the user hasn't yet rated. Terminal states are heuristic: 'resolved',\n // 'closed', 'sold', 'issued', 'checked_out'. The panel self-dismisses on submit.\n const terminalStates = ['resolved', 'closed', 'sold', 'issued', 'checked_out']\n if (this.h.onCsat && !this.csatSubmitted && terminalStates.includes(store.state) && store.messages().length > 0) {\n if (this.csatPanel.style.display === 'none') this.buildCsatPanel()\n this.csatPanel.style.display = 'block'\n }\n\n // Header availability line: green when online, amber when the chatroom is\n // outside office hours. Server-provided offlineMessage (already localized)\n // is preferred for the away text; falls back to the i18n label.\n const isTerminal = terminalStates.includes(store.state)\n if (store.offline) {\n this.headStatus.textContent = `● ${store.offlineMessage || this.cfg.i18n?.away || 'Away'}`\n this.headStatus.className = 'ocw-head-status away'\n this.headStatus.style.display = ''\n } else if (store.conversationId) {\n this.headStatus.textContent = `● ${this.cfg.i18n?.online ?? 'Online'}`\n this.headStatus.className = 'ocw-head-status online'\n this.headStatus.style.display = ''\n } else {\n this.headStatus.style.display = 'none'\n }\n\n // Resolved: a legible divider in the thread + a one-tap reopen affordance.\n if (isTerminal && store.messages().length > 0) {\n const note = el('div', 'ocw-resolved-note', `— ${this.cfg.i18n?.resolved ?? 'Marked as resolved'} —`)\n this.scroll.append(note)\n this.reopenBtn.style.display = 'block'\n } else {\n this.reopenBtn.style.display = 'none'\n }\n }\n\n setConnStatus(status: 'connecting' | 'open' | 'reconnecting' | 'error', message?: string): void {\n if (status === 'open') { this.connStatus.style.display = 'none'; return }\n this.connStatus.style.display = ''\n // 'error' is a FATAL, non-transient state (bad token, closed chatroom, or the\n // relay is unreachable after repeated tries) — show a clear reason and don't\n // pretend we're still \"connecting…\". Anything else is transient.\n const fatal = status === 'error'\n this.connStatus.className = `ocw-conn-status${fatal ? ' err' : status === 'reconnecting' ? ' warn' : ''}`\n this.connStatus.textContent = fatal\n ? `⚠ ${message ?? 'Chat unavailable'}`\n : status === 'reconnecting' ? (message ?? '↻ reconnecting…') : '● connecting…'\n }\n\n private buildPreChatPanel(cfg: import('./protocol/frames.js').PreChatConfig): void {\n this.preChatBuilt = true\n this.preChatPanel.replaceChildren()\n this.preChatPanel.append(el('div', 'ocw-prechat-title', cfg.title ?? 'Before we start…'))\n const form = el('div', 'ocw-offline-form')\n const inputs: Partial<Record<'name' | 'email' | 'phone', HTMLInputElement>> = {}\n for (const f of cfg.fields ?? ['name', 'email']) {\n const inp = el('input', 'ocw-offline-input') as HTMLInputElement\n inp.type = f === 'email' ? 'email' : f === 'phone' ? 'tel' : 'text'\n inp.placeholder = f === 'name' ? 'Your name' : f === 'email' ? 'Your email' : 'Your phone number'\n inputs[f] = inp\n form.append(inp)\n }\n let topicSel: HTMLSelectElement | null = null\n if (cfg.topics?.length) {\n topicSel = el('select', undefined) as HTMLSelectElement\n const ph = document.createElement('option'); ph.value = ''; ph.textContent = 'What is this about?'; topicSel.append(ph)\n for (const t of cfg.topics) { const o = document.createElement('option'); o.value = t; o.textContent = t; topicSel.append(o) }\n form.append(topicSel)\n }\n let callbackCb: HTMLInputElement | null = null\n let phoneForCb: HTMLInputElement | null = null\n if (cfg.callbackOption) {\n const row = el('label', 'ocw-prechat-cb')\n callbackCb = document.createElement('input'); callbackCb.type = 'checkbox'\n row.append(callbackCb, document.createTextNode('📞 Request a call back'))\n form.append(row)\n if (!inputs.phone) {\n phoneForCb = el('input', 'ocw-offline-input') as HTMLInputElement\n phoneForCb.type = 'tel'; phoneForCb.placeholder = 'Phone number for the call'; phoneForCb.style.display = 'none'\n callbackCb.addEventListener('change', () => phoneForCb!.style.setProperty('display', callbackCb!.checked ? 'block' : 'none'))\n form.append(phoneForCb)\n }\n }\n const submit = el('button', 'ocw-offline-submit', 'Start chat') as HTMLButtonElement\n submit.type = 'button'\n submit.addEventListener('click', () => {\n const email = inputs.email?.value.trim()\n if (inputs.email && (!email || !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email))) { inputs.email.focus(); return }\n const callback = !!callbackCb?.checked\n const phone = (inputs.phone?.value ?? phoneForCb?.value ?? '').trim()\n if (callback && !phone) { (inputs.phone ?? phoneForCb)?.focus(); return }\n if (topicSel && cfg.topics?.length && !topicSel.value) { topicSel.focus(); return }\n submit.disabled = true // a double-click must not qualify twice\n this.completePreChat()\n this.h.onPreChat?.({\n ...(inputs.name?.value.trim() ? { name: inputs.name.value.trim() } : {}),\n ...(email ? { email } : {}),\n ...(phone ? { phone } : {}),\n ...(topicSel?.value ? { topic: topicSel.value } : {}),\n ...(callback ? { callback: true } : {}),\n })\n })\n form.append(submit)\n this.preChatPanel.append(form)\n }\n\n /** Mark pre-chat complete (submitted now or in a previous session). */\n completePreChat(): void {\n this.preChatDone = true\n this.preChatPanel.style.display = 'none'\n if (this.storeRef) this.render(this.storeRef)\n }\n\n /** KB deflection results (\"was this your question?\") above the composer. */\n showDeflection(articles: { id: string; title: string; answer: string }[]): void {\n if (!articles.length) return this.hideDeflection()\n this.deflectPanel.replaceChildren()\n this.deflectPanel.append(el('div', 'ocw-deflect-hint', 'Instant answers — tap to expand'))\n for (const a of articles.slice(0, 3)) {\n const card = el('button', 'ocw-deflect-card')\n card.append(el('div', 'ocw-deflect-q', a.title), el('div', 'ocw-deflect-a', a.answer))\n card.addEventListener('click', () => card.classList.toggle('open'))\n this.deflectPanel.append(card)\n }\n this.deflectPanel.style.display = 'flex'\n }\n\n hideDeflection(): void {\n this.deflectPanel.style.display = 'none'\n this.deflectPanel.replaceChildren()\n }\n\n private buildCsatPanel(): void {\n this.csatPanel.replaceChildren()\n this.csatPanel.append(el('div', 'ocw-csat-title', 'How did we do?'))\n const stars = el('div', 'ocw-csat-stars')\n const btns: HTMLButtonElement[] = []\n for (let i = 1; i <= 5; i++) {\n const b = el('button', 'ocw-csat-star', '★')\n b.dataset['score'] = String(i)\n b.addEventListener('mouseenter', () => btns.forEach((bb, idx) => bb.classList.toggle('lit', idx < i)))\n b.addEventListener('mouseleave', () => btns.forEach(bb => bb.classList.remove('lit')))\n b.addEventListener('click', () => {\n this.csatSubmitted = true\n this.csatPanel.replaceChildren(el('div', 'ocw-csat-done', `Thanks for your ${i}★ rating!`))\n this.h.onCsat?.(i)\n })\n btns.push(b); stars.append(b)\n }\n this.csatPanel.append(stars)\n }\n\n /** Whether the subject card (server Subject entity, mount config fallback)\n * has been built — built once when data first arrives. */\n private subjectBuilt = false\n\n private buildSubjectCard(store: ChatStore): void {\n if (this.subjectBuilt) return\n const s = store.subject\n const cfg = this.cfg.subject\n // Only show the subject card when there is actual subject data (from the\n // server) or an explicit subject config passed by the embedder (title,\n // tags, status). Never fall back to store.name — that's the domain/profile\n // name and is already shown in the header; rendering it here as well is\n // what caused the duplication seen in the Hotel front desk screenshot.\n const title = s?.title ?? cfg?.title\n if (!title) return\n this.subjectBuilt = true\n this.subjectCard.replaceChildren()\n this.subjectCard.append(el('div', 'ocw-subject-title', title))\n if (cfg?.subtitle) this.subjectCard.append(el('div', 'ocw-subject-sub', cfg.subtitle))\n const tags = el('div', 'ocw-tags')\n if (s) for (const [k, v] of Object.entries(s.fields)) tags.append(el('span', 'ocw-tag', `${k}: ${v}`))\n else for (const t of cfg?.tags ?? []) tags.append(el('span', 'ocw-tag', t))\n if (tags.childNodes.length) this.subjectCard.append(tags)\n const status = s?.state ?? cfg?.status\n if (status) { this.statusBadge.textContent = status; this.statusBadge.style.display = 'inline-flex' }\n }\n\n private chipEl(a: ManifestAction): HTMLButtonElement {\n const btn = el('button', 'ocw-chip', a.icon ? `${a.icon} ${a.label}` : a.label)\n btn.dataset['actionId'] = a.id\n btn.addEventListener('click', async () => {\n if (a.confirm && !(await this.confirm(a.label))) return\n if (a.input?.length) this.openForm(a)\n else this.h.onInvoke(a.id)\n })\n return btn\n }\n\n /** In-widget confirmation modal (replaces window.confirm). */\n private confirm(label: string): Promise<boolean> {\n return new Promise((resolve) => {\n const overlay = el('div', 'ocw-modal')\n const card = el('div', 'ocw-modal-card')\n card.append(el('div', 'ocw-modal-title', label))\n card.append(el('div', 'ocw-modal-body', `Confirm “${label}”?`))\n const row = el('div', 'ocw-modal-actions')\n const cancel = el('button', 'ocw-modal-cancel', 'Cancel')\n const ok = el('button', 'ocw-modal-ok', 'Confirm')\n const close = (v: boolean) => { overlay.remove(); resolve(v) }\n cancel.addEventListener('click', () => close(false))\n ok.addEventListener('click', () => close(true))\n overlay.addEventListener('click', (e) => { if (e.target === overlay) close(false) })\n row.append(cancel, ok); card.append(row); overlay.append(card)\n this.root.append(overlay)\n ok.focus()\n })\n }\n\n /** Inline form for a form-effect action: typed inputs (date picker, number,\n * text) rendered above the composer — no browser prompts. */\n private openForm(a: ManifestAction): void {\n this.formHost.replaceChildren()\n const panel = el('div', 'ocw-form')\n panel.append(el('div', 'ocw-form-title', a.icon ? `${a.icon} ${a.label}` : a.label))\n const inputs = new Map<string, HTMLInputElement>()\n for (const f of a.input ?? []) {\n const row = el('label', 'ocw-form-row'); row.append(el('span', 'ocw-form-lbl', f.label))\n if (f.type === 'select' && f.options?.length) {\n const sel = el('select', 'ocw-form-input')\n if (!f.required) sel.append(el('option', undefined, '— select —'))\n for (const opt of f.options) { const o = el('option'); o.value = opt; o.textContent = opt; sel.append(o) }\n if (f.required) sel.required = true\n row.append(sel)\n inputs.set(f.name, sel as unknown as HTMLInputElement)\n } else {\n const inp = el('input', 'ocw-form-input')\n inp.type = f.type === 'number' ? 'number' : f.type === 'date' ? 'datetime-local' : 'text'\n if (f.required) inp.required = true\n row.append(inp); inputs.set(f.name, inp)\n }\n panel.append(row)\n }\n const actions = el('div', 'ocw-form-actions')\n const cancel = el('button', 'ocw-form-cancel', 'Cancel')\n const submit = el('button', 'ocw-form-submit', 'Send')\n cancel.addEventListener('click', () => this.formHost.replaceChildren())\n submit.addEventListener('click', () => {\n const out: Record<string, unknown> = {}\n for (const [name, inp] of inputs) {\n if (inp.required && !inp.value) { inp.style.borderColor = '#e5484d'; return }\n out[name] = inp.type === 'number' ? Number(inp.value) : inp.value\n }\n this.formHost.replaceChildren()\n this.h.onInvoke(a.id, out)\n })\n actions.append(cancel, submit); panel.append(actions)\n this.formHost.append(panel)\n inputs.values().next().value?.focus()\n }\n\n private messageEl(m: RenderMessage, store: ChatStore, showLabel = false, isLast = false): HTMLElement {\n if (m.senderRole === 'system') {\n const sys = el('div', 'ocw-sys'); sys.textContent = m.deletedAt ? 'message deleted' : contentText(m.content); return sys\n }\n const mine = m.senderId === this.me\n const isNote = !!m.internal\n const row = el('div', `ocw-row ${isNote ? 'ocw-note mine' : mine ? 'mine' : 'theirs'} ${m.senderRole === 'bot' ? 'ocw-bot' : ''}`)\n const col = el('div')\n if (showLabel) {\n const who = m.senderRole === 'bot'\n ? (this.cfg.i18n?.aiAssistant ?? 'AI Assistant')\n : (this.cfg.subject?.ownerLabel ?? this.cfg.subject?.title ?? 'Support')\n col.append(el('div', 'ocw-msg-sender', who))\n }\n const bubbleWrap = el('div', 'ocw-bubble-wrap')\n // Reply-to context if present\n if (m.replyToId) {\n const replyCtx = el('div', 'ocw-reply-to', '↩ replying to a message')\n replyCtx.style.cssText = 'font-size:11px;color:var(--ocw-mut);margin-bottom:2px;font-style:italic'\n col.append(replyCtx)\n }\n const bubble = el('div', 'ocw-bubble')\n let textNode: Text | null = null\n if (m.deletedAt) bubble.append(el('span', 'ocw-deleted', 'message deleted'))\n else if (m.content.kind === 'attachment') {\n const c = m.content\n if (c.mime?.startsWith('image/')) {\n const img = document.createElement('img')\n img.src = c.url; img.alt = c.name ?? 'image'\n img.style.cssText = 'max-width:220px;max-height:160px;border-radius:10px;display:block;cursor:pointer'\n img.addEventListener('click', () => window.open(c.url, '_blank'))\n bubble.append(img)\n } else if (c.mime?.startsWith('video/')) {\n const video = document.createElement('video')\n video.src = c.url; video.controls = true\n video.style.cssText = 'max-width:240px;max-height:180px;border-radius:10px;display:block'\n bubble.append(video)\n } else {\n const a = document.createElement('a')\n a.href = c.url; a.target = '_blank'; a.rel = 'noopener'\n a.style.cssText = 'display:flex;align-items:center;gap:8px;color:inherit;text-decoration:none'\n a.append(el('span', undefined, '📄'), el('span', undefined, c.name ?? 'file'))\n bubble.append(a)\n }\n } else {\n if (m.content.kind === 'appointment') {\n const ap = m.content\n const card = el('div', 'ocw-appt')\n card.append(el('div', 'ocw-appt-title', `\\u{1F4C5} ${ap.title}`))\n card.append(el('div', 'ocw-appt-time', new Date(ap.startIso).toLocaleString() + ' \\u2013 ' + new Date(ap.endIso).toLocaleTimeString()))\n if (ap.location) card.append(el('div', 'ocw-appt-loc', `\\u{1F4CD} ${ap.location}`))\n if (ap.description) card.append(el('div', 'ocw-appt-desc', ap.description))\n const links = el('div', 'ocw-appt-links')\n const gLink = document.createElement('a'); gLink.href = ap.googleUrl; gLink.target = '_blank'; gLink.rel = 'noopener'; gLink.className = 'ocw-appt-btn'; gLink.textContent = '\\u{1F4C5} Add to Google Calendar'\n const iLink = document.createElement('a'); iLink.href = ap.icalUrl; iLink.download = `${ap.title}.ics`; iLink.className = 'ocw-appt-btn ocw-appt-btn-sec'; iLink.textContent = '\\u{1F34E} Apple / iCal'\n links.append(gLink, iLink); card.append(links); bubble.append(card)\n } else {\n textNode = document.createTextNode(contentText(m.content))\n bubble.append(textNode)\n if (m.editedAt) bubble.append(el('span', 'ocw-edited', '(edited)'))\n }\n }\n bubbleWrap.append(bubble)\n\n // Guided-flow choices: a prompt message carries tappable options. Only the\n // LATEST message's chips are live — older prompts (on reload / after the guest\n // has answered) must not re-fire, so gate on isLast. Tapping sends the label\n // as a guest turn (reusing onSend), which the server-side flow then consumes.\n if (isLast && !m.deletedAt && m.content.kind === 'text' && m.content.quickReplies?.length) {\n const chips = el('div', 'ocw-flow-choices')\n chips.style.cssText = 'display:flex;flex-wrap:wrap;gap:6px;margin-top:6px'\n for (const label of m.content.quickReplies) {\n const b = el('button', 'ocw-flow-choice', label)\n b.type = 'button'\n b.style.cssText = 'border:1px solid var(--ocw-bd,rgba(0,0,0,.12));background:var(--ocw-bg,#fff);color:inherit;border-radius:14px;padding:5px 12px;font:inherit;font-size:13px;cursor:pointer'\n b.addEventListener('click', () => {\n chips.querySelectorAll('button').forEach(x => ((x as HTMLButtonElement).disabled = true))\n chips.style.opacity = '0.5'\n this.h.onSend(label)\n })\n chips.append(b)\n }\n bubbleWrap.append(chips)\n }\n\n // Live translation: only for the other party's plain-text messages (not notes).\n // The translation renders as its own line directly AFTER the original text —\n // both stay visible, the message is never overwritten — toggled by a button.\n if (!mine && !isNote && this.h.onTranslate && m.content.kind === 'text' && !m.deletedAt && m.seq > 0 && textNode) {\n const original = m.content.text\n if (original.trim()) {\n const translateBtn = el('button', 'ocw-translate-btn', '🌐')\n translateBtn.type = 'button'\n translateBtn.title = 'Translate'\n const transLine = el('div', 'ocw-translation')\n transLine.style.display = 'none'\n bubble.append(transLine)\n\n const showLine = (text: string): void => {\n transLine.textContent = text\n transLine.style.display = ''\n this.showingTranslation.add(m.id)\n translateBtn.textContent = '↩'\n translateBtn.title = 'Show original'\n }\n const hideLine = (): void => {\n transLine.style.display = 'none'\n this.showingTranslation.delete(m.id)\n translateBtn.textContent = '🌐'\n translateBtn.title = 'Translate'\n }\n\n translateBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n if (this.showingTranslation.has(m.id)) { hideLine(); return }\n const cached = this.translationCache.get(m.id)\n if (cached !== undefined) { showLine(cached); return }\n translateBtn.textContent = '⏳'\n void this.h.onTranslate!(original).then((result) => {\n if (result === null) {\n translateBtn.textContent = '⚠️'\n translateBtn.title = 'Translation unavailable'\n setTimeout(() => { translateBtn.textContent = '🌐'; translateBtn.title = 'Translate' }, 1500)\n return\n }\n this.translationCache.set(m.id, result)\n showLine(result)\n })\n })\n bubbleWrap.append(translateBtn)\n\n // Keep a shown translation visible across re-renders.\n const cached = this.translationCache.get(m.id)\n if (this.showingTranslation.has(m.id) && cached !== undefined) showLine(cached)\n }\n }\n // Edit/delete context menu on own non-deleted messages\n if (mine && !m.deletedAt && m.seq > 0 && (this.h.onEdit ?? this.h.onDelete)) {\n const menu = el('div', 'ocw-msg-menu')\n if (this.h.onEdit) {\n const editBtn = el('button', undefined, '✏️')\n editBtn.title = 'Edit'\n editBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n // Inline edit: replace bubble text with a small textarea + save/cancel\n const original = contentText(m.content)\n const ta = document.createElement('textarea')\n ta.value = original\n ta.rows = Math.min(4, Math.ceil(original.length / 40) + 1)\n ta.style.cssText = 'width:100%;resize:vertical;border:1px solid var(--ocw-accent);border-radius:8px;padding:6px 10px;font:inherit;font-size:14px;background:var(--ocw-card);color:#1c1b1a;box-sizing:border-box'\n const saveBtn = el('button', 'ocw-form-submit', 'Save')\n saveBtn.style.cssText = 'margin-top:6px;padding:5px 14px;font-size:13px'\n const cancelBtn = el('button', 'ocw-form-cancel', 'Cancel')\n cancelBtn.style.cssText = 'margin-top:6px;padding:5px 10px;font-size:13px'\n const btnRow = el('div'); btnRow.style.cssText = 'display:flex;gap:6px;justify-content:flex-end'\n btnRow.append(cancelBtn, saveBtn)\n const editPanel = el('div'); editPanel.append(ta, btnRow)\n bubble.replaceChildren(editPanel)\n ta.focus(); ta.select()\n const restore = () => bubble.replaceChildren(textNode ?? document.createTextNode(original))\n cancelBtn.addEventListener('click', restore)\n saveBtn.addEventListener('click', () => {\n const newText = ta.value.trim()\n if (newText && newText !== original) { this.h.onEdit!(m.id, newText); restore() }\n else restore()\n })\n ta.addEventListener('keydown', (ke) => {\n if (ke.key === 'Enter' && !ke.shiftKey) { ke.preventDefault(); saveBtn.click() }\n if (ke.key === 'Escape') restore()\n })\n })\n menu.append(editBtn)\n }\n if (this.h.onDelete) {\n const delBtn = el('button', 'del', '🗑')\n delBtn.title = 'Delete'\n delBtn.addEventListener('click', (e) => { e.stopPropagation(); this.h.onDelete!(m.id) })\n menu.append(delBtn)\n }\n bubbleWrap.append(menu)\n }\n col.append(bubbleWrap)\n\n // A3.3 — bot answer affordances: KB source citations + 👍/👎 feedback. Only on\n // the bot's own answers (not human/system), non-deleted, persisted messages.\n if (m.senderRole === 'bot' && !m.deletedAt && m.seq > 0 && m.content.kind === 'text') {\n const cites = m.content.citations\n if (cites?.length) {\n const src = el('div', 'ocw-cites')\n src.style.cssText = 'margin-top:4px;font-size:11px;color:var(--ocw-mut,#667)'\n src.textContent = `${this.cfg.i18n?.sources ?? 'Sources'}: ${cites.map(c => c.source).join(', ')}`\n col.append(src)\n }\n if (this.h.onReact) {\n const fb = el('div', 'ocw-bot-feedback')\n fb.style.cssText = 'display:flex;gap:6px;margin-top:4px'\n const mk = (emoji: string, label: string) => {\n const b = el('button', 'ocw-fb-btn', emoji)\n b.type = 'button'; b.title = label\n b.style.cssText = 'border:1px solid var(--ocw-bd,rgba(0,0,0,.12));background:transparent;border-radius:12px;padding:1px 8px;font-size:12px;cursor:pointer;opacity:.7'\n const already = () => !!m.reactions?.[emoji]?.includes(this.me as never)\n if (already()) b.style.opacity = '1'\n b.addEventListener('click', () => { this.h.onReact?.(m.id, emoji, already()); b.style.opacity = '1' })\n return b\n }\n fb.append(mk('👍', this.cfg.i18n?.helpful ?? 'Helpful'), mk('👎', this.cfg.i18n?.notHelpful ?? 'Not helpful'))\n col.append(fb)\n }\n }\n\n if (this.h.onReact && !m.deletedAt && m.seq > 0) {\n const reactWrap = el('div', 'ocw-react-wrap')\n const reactRow = el('div', 'ocw-react')\n // Existing reaction pills\n if (m.reactions && Object.keys(m.reactions).length) {\n for (const [emoji, users] of Object.entries(m.reactions)) {\n const pill = el('button', `ocw-react-pill${(users as string[]).includes(this.me) ? ' mine' : ''}`, `${emoji} ${(users as string[]).length}`)\n pill.addEventListener('click', () => this.h.onReact?.(m.id, emoji, (users as string[]).includes(this.me)))\n reactRow.append(pill)\n }\n }\n // Add-reaction button + picker\n const addBtn = el('button', 'ocw-react-btn', '+')\n const picker = el('div', 'ocw-react-picker')\n for (const emoji of REACTION_EMOJIS) {\n const pb = el('button', undefined, emoji)\n pb.addEventListener('click', (e) => {\n e.stopPropagation()\n const alreadyReacted = m.reactions?.[emoji]?.includes(this.me as never)\n this.h.onReact?.(m.id, emoji, !!alreadyReacted)\n picker.style.display = 'none'\n })\n picker.append(pb)\n }\n picker.style.display = 'none'\n addBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n const opening = picker.style.display === 'none'\n picker.style.display = opening ? 'flex' : 'none'\n // Close on the next outside click. Registered only when OPENING —\n // the old code added one document listener per message on EVERY\n // render, so long conversations piled up hundreds of stale handlers.\n if (opening) document.addEventListener('click', () => { picker.style.display = 'none' }, { once: true })\n })\n reactRow.append(addBtn)\n reactWrap.append(reactRow, picker)\n col.append(reactWrap)\n } else if (m.reactions && Object.keys(m.reactions).length) {\n col.append(el('div', 'ocw-react', Object.entries(m.reactions).map(([e, u]) => `${e}${(u as string[]).length}`).join(' ')))\n }\n const meta = el('div', 'ocw-time ocw-meta', fmtTime(m.ts))\n if (mine && m.status) {\n const t = el('span', `ocw-tick${m.status === 'read' ? ' read' : m.status === 'delivered' ? ' delivered' : ''}`, tick(m.status))\n meta.append(t)\n }\n // \"Seen\" indicator when agent has read past this message\n if (mine && m.seq > 0 && store.lastReadByOthers >= m.seq) {\n meta.append(el('span', 'ocw-seen', ' · Seen'))\n }\n col.append(meta)\n row.append(col)\n return row\n }\n}\n\nfunction tick(s: NonNullable<RenderMessage['status']>): string {\n switch (s) {\n case 'read': return '✓✓' // blue double tick rendered via CSS colour\n case 'delivered': return '✓✓'\n case 'sent': return '✓'\n default: return '🕓'\n }\n}\n","import { persistentUid } from './uid.js'\nimport {\n asConversationId,\n type ClientFrame, type ConversationId,\n} from './protocol/index.js'\nimport { ChatStore } from './store.js'\nimport { ConnectionManager } from './connection.js'\nimport { PersistentOutbox } from './outbox.js'\nimport { E2ESession, extractX3DHInit } from './e2e.js'\nimport { Renderer, type WidgetConfig } from './renderer.js'\nimport { restoreHistory, resolveRelayUrls } from './history.js'\nimport { mountChatList, type ChatListEntry, type ChatListHandle } from './chatlist.js'\n\nexport interface UserInfo {\n /** Display name shown in the conversation (e.g. \"Sarah Chen\"). */\n name?: string\n /** Email address — passed as conversation metadata for agent context. */\n email?: string\n /** Avatar URL — shown as the guest's avatar in both widget and dashboard. */\n avatar?: string\n /** Any custom key/value metadata to attach to the conversation\n * (e.g. plan tier, account ID, page URL). Shown to agents in the sidebar. */\n meta?: Record<string, string>\n}\n\nexport interface MountOptions {\n el: HTMLElement\n /** Relay URL. Any scheme works — `https://api.example.com` is fine; the widget\n * derives the WebSocket URL (`wss://…/ws`) and REST base from it. */\n url: string\n /** HTTP(S) base for REST calls — only needed when the REST API is on a\n * DIFFERENT origin than the socket. Normally leave unset. */\n apiUrl?: string\n profileId: string\n subjectId?: string\n /** Open a user↔user direct conversation with `peerId` instead of a support\n * thread. Requires signed identity on the chatroom (both `kind: 'direct'`\n * and `peerId` together; `subjectId` is ignored — the server derives the\n * symmetric DM key so both sides land in the SAME conversation). */\n kind?: 'direct'\n peerId?: string\n /** IDENTITY (tiered — the host owns identity, the widget never has to persist it):\n * 1. `token` — a signed identity token. Either a capability token, or (recommended\n * for embedders) an ES256 JWT `{sub,iat,exp}` signed by your backend with the\n * private key whose public half is set as the chatroom's `guestPublicKey`.\n * The server cryptographically verifies it. Works in ANY language/environment,\n * no cookies or storage required. This is the production path.\n * 2. `userId` — a stable id you already have for the visitor (e.g. your logged-in\n * user id). Unauthenticated (\"you vouch for it\") but works everywhere. Used\n * only when `token` is absent.\n * 3. Neither — the widget falls back to best-effort local identity on the host\n * origin (first-party cookie + localStorage). A returning visitor on the same\n * browser keeps their history; if storage is blocked they get a fresh chat. */\n token?: string\n /** Called when a signed token is rejected (expired): return a fresh token\n * from your backend to renew the session without a reload. */\n refreshToken?: () => Promise<string | null>\n userId?: string\n subject?: WidgetConfig['subject']\n quickReplies?: string[]\n accent?: string\n /** Secondary accent (guest bubble + send button). Defaults to the design\n * blue; omit and it follows `accent` for a cohesive single-token retheme. */\n accent2?: string\n /** Colour scheme. Default 'light'. 'auto' follows the OS; 'dark'/'light' force it. */\n theme?: 'auto' | 'light' | 'dark'\n /** Load Baloo 2 + Nunito webfonts (default true). false = system fonts only. */\n webfont?: boolean\n /** Stack navigation: when set, the chatroom header shows a back chevron that\n * calls this. Used by `<ChatApp>` so tapping a conversation opens the room and\n * the back arrow returns to the list — native-app style. */\n onBack?: () => void\n /** Turn this into a self-contained inbox: the back chevron opens the user's\n * full thread list IN PLACE (no navigation), and picking a row opens that\n * thread with a back chevron returning to the list. */\n inbox?: boolean\n /** Inbox scope: 'tenant' (default) lists the user's threads across ALL your\n * chatrooms; 'profile' limits to this chatroom. */\n inboxScope?: 'tenant' | 'profile'\n /** If set, shows a 🌐 translate button on incoming messages that translates\n * them into this language (ISO code or language name) via the server's\n * /translate endpoint. Omit to disable the feature. */\n translateLang?: string\n /** If true, mount as a floating launcher button that opens/closes the chat */\n launcher?: boolean\n /** Position of the launcher button: default 'bottom-right' */\n position?: 'bottom-right' | 'bottom-left'\n /** Launcher teaser — the \"optional message\" card shown ABOVE the closed\n * launcher button to invite a chat (like Channel.io's greeting). Pass a\n * string for just a title, or `{ title, subtitle }`. If omitted, the\n * widget uses `defaults.launcherMessage` from the chatroom manifest when\n * present. Dismissible by the visitor (remembered for the browser session);\n * auto-hides once the chat is opened. Only applies in `launcher` mode. */\n launcherMessage?: string | { title: string; subtitle?: string }\n /** Optional user info for identified users. When provided, the name/email/\n * avatar are shown to agents in the dashboard instead of the anonymous ID.\n * The token still controls identity — this is display metadata only.\n * Anonymous users (no token, no user) remain fully anonymous. */\n user?: UserInfo\n /** Optional per-tenant feature switches. All default to ON (omit for current\n * behavior). Set a flag to `false` to disable the feature — the widget then\n * does no work for it (no reaction picker built per message, no CSAT panel,\n * no KB-deflection lookups). Gating is by not wiring the handler, so the\n * renderer skips the feature entirely. */\n features?: {\n reactions?: boolean // emoji reactions on messages (default true)\n csat?: boolean // post-resolution satisfaction survey (default true)\n deflection?: boolean // pre-first-message KB article suggestions (default true)\n }\n /** i18n: override UI strings. All keys are optional — omitted keys fall\n * back to English defaults. */\n i18n?: {\n placeholder?: string // input placeholder, default \"Message…\"\n send?: string // send button label, default \"➤\"\n offline?: string // offline panel title, default \"We're offline right now\"\n poweredBy?: string // footer text, default \"Powered by Relay\"\n online?: string // header status when available, default \"Online\"\n away?: string // header status when offline, default \"Away\"\n aiAssistant?: string // speaker label for bot messages, default \"AI Assistant\"\n resolved?: string // resolved divider text, default \"Marked as resolved\"\n reopen?: string // reopen button label, default \"Reopen conversation\"\n }\n}\n\nexport interface WidgetHandle { close(): void }\n\n\n// ── Mount registry ────────────────────────────────────────────────────────────\n// Tracks active widget instances per host element. Prevents double-mounting\n// when React strict mode, HMR, or caller code calls mount() twice on the same\n// element — the most common cause of two widgets appearing on one page.\nconst _registry = new WeakMap<Element, WidgetHandle>()\n// Launcher widgets attach to document.body (not the ref div), and in launcher\n// mode React may re-create the ref div on re-render — so the el-keyed registry\n// above can't catch a stale launcher. This slot-keyed registry guarantees at\n// most ONE launcher per (profileId, subjectId), so an identity flicker or\n// re-render can never leave two stacked bubbles/panels on the page.\nconst _launcherRegistry = new Map<string, WidgetHandle>()\nfunction launcherSlot(opts: MountOptions): string {\n return `relay-launcher::${opts.profileId}::${opts.subjectId ?? ''}`\n}\n\n// One AudioContext for ALL widget instances on the page (notification blips\n// are fire-and-forget; nothing about them is per-instance).\nlet _audioCtx: AudioContext | null = null\nfunction getAudioContext(): AudioContext | null {\n if (_audioCtx && _audioCtx.state !== 'closed') return _audioCtx\n try { _audioCtx = new AudioContext(); return _audioCtx } catch { return null }\n}\n\n/** Unmount any widget currently mounted on `el`. No-op if nothing is mounted. */\nexport function unmount(el: Element): void {\n _registry.get(el)?.close()\n _registry.delete(el)\n}\n\n// Vanilla in-widget inbox: a thread list ⇄ single thread, swapped in place inside\n// the host element. Reuses mountChatList + the single-thread mount() (same pieces\n// the React ChatApp composes) so the HTML embed gets a working inbox with no\n// page navigation — the back chevron in a thread returns to the list.\nfunction mountInbox(opts: MountOptions): WidgetHandle {\n const el = opts.el\n let room: WidgetHandle | null = null\n let list: ChatListHandle | null = null\n let closed = false\n\n const clear = () => { room?.close(); room = null; list?.close(); list = null; el.innerHTML = '' }\n\n function openList() {\n if (closed) return\n clear()\n list = mountChatList({\n el, url: opts.url,\n ...(opts.apiUrl ? { apiUrl: opts.apiUrl } : {}),\n ...(opts.profileId ? { profileId: opts.profileId } : {}),\n scope: opts.inboxScope ?? 'tenant',\n ...(opts.token ? { token: opts.token } : {}),\n ...(opts.userId ? { userId: opts.userId } : {}),\n ...(opts.accent ? { accent: opts.accent } : {}),\n ...(opts.theme ? { theme: opts.theme } : {}),\n ...(opts.webfont === false ? { webfont: false } : {}),\n onSelect: (entry: ChatListEntry) => openRoom(entry),\n })\n }\n\n function openRoom(entry: ChatListEntry) {\n if (closed) return\n clear()\n // Drop the inbox-level subject/kind so each opened thread carries only its own.\n const { subjectId: _s, kind: _k, peerId: _p, ...base } = opts\n room = mount({\n ...base,\n inbox: false,\n launcher: false,\n el,\n ...(entry.profileId ? { profileId: entry.profileId } : {}),\n ...(entry.subjectId ? { subjectId: entry.subjectId } : {}),\n ...(entry.kind === 'direct' && entry.peerId ? { kind: 'direct', peerId: entry.peerId } : {}),\n onBack: openList,\n })\n }\n\n openList()\n return { close() { closed = true; clear() } }\n}\n\nexport function mount(opts: MountOptions): WidgetHandle {\n // In-widget inbox: reuse the same chatlist + single-thread mount that the React\n // ChatApp composes, but in vanilla — so the HTML embed gets a real thread list\n // in place (back → list → pick → thread → back), not a host navigation.\n if (opts.inbox) return mountInbox(opts)\n\n // Auto-close any previous instance on this exact element before re-mounting.\n // Covers React double-invoke in StrictMode, HMR, and accidental duplicate calls.\n if (_registry.has(opts.el)) {\n _registry.get(opts.el)!.close()\n _registry.delete(opts.el)\n }\n // Launcher mode: also close any prior launcher for the same slot, even if it\n // was mounted on a now-detached div (React re-creates the ref div on\n // re-render). This is what prevents two stacked widgets after an identity\n // flicker (anonymous → logged-in).\n if (opts.launcher) _launcherRegistry.get(launcherSlot(opts))?.close()\n\n // Tiered identity (see MountOptions): a host-provided signed token wins, then a\n // host-vouched userId, then best-effort local persistence. The widget never\n // depends on its own storage when the host supplies identity — which is what\n // makes it safe to embed in any environment (iframes, webviews, SSR, etc.).\n // Always keep the stable per-browser anonymous id, even when the host\n // identifies the visitor — so on login we can tell the server to merge the\n // anonymous conversation into the user (Channel.io-style boot+identify).\n const anonId = persistentUid()\n let deflectTimer: ReturnType<typeof setTimeout> | undefined\n let destroyed = false\n const token = opts.token ?? opts.userId ?? anonId\n // If we're connecting as an identified user (token differs from the anon id),\n // pass the anon id as linkFrom so the server adopts any anonymous history.\n const linkFrom = token !== anonId ? anonId : undefined\n // Accept any scheme on `url` (https/http/wss/ws) and derive both the concrete\n // WebSocket URL and the REST base from it. `apiUrl` overrides the REST base\n // only when the API is on a different origin than the socket.\n const { wsUrl, httpBase } = resolveRelayUrls(opts.url, opts.apiUrl)\n let store = new ChatStore(token as never)\n // Key the outbox by token + subjectId so each listing has its own pending queue.\n // Without this, a pending message from listing A appears as a ghost on listing B.\n const outboxKey = opts.subjectId ? `${token}::${opts.subjectId}` : token\n const outbox = new PersistentOutbox(outboxKey)\n let cid: ConversationId | undefined\n let outboxRestored = false\n\n let _mql: MediaQueryList | null = null\n let _mqlHandler: ((e: MediaQueryListEvent) => void) | null = null\n let _escHandler: ((e: KeyboardEvent) => void) | null = null\n let _paintTeaser: (() => void) | null = null\n // (e.g. a bare `<div id=\"chat\"></div>` with no CSS). Without this the\n // widget's internal `height:100%` collapses to near-zero. Only applies\n // when the element truly has no height set — explicit CSS always wins.\n if (!opts.launcher && !opts.el.style.height && opts.el.clientHeight === 0) {\n opts.el.style.width = opts.el.style.width || '100%'\n opts.el.style.height = '600px'\n }\n\n // Restore pending outbox items for this specific listing/conversation.\n // The outbox is keyed by token+subjectId so ghost bubbles from other listings\n // never appear here.\n for (const item of outbox.load()) store.addOptimistic(item.clientMsgId, item.content)\n\n // ── Launcher mode ─────────────────────────────────────────────────────────\n let launcherEl: HTMLElement | null = null\n let badgeEl: HTMLElement | null = null\n let unread = 0\n let open = !opts.launcher // start open when not in launcher mode\n\n if (opts.launcher) {\n const pos = opts.position ?? 'bottom-right'\n const isRight = pos.includes('right')\n\n // Outer wrapper holds both the panel and the bubble button\n launcherEl = document.createElement('div')\n launcherEl.style.cssText = `position:fixed;${isRight ? 'right:20px' : 'left:20px'};bottom:20px;z-index:9999;display:flex;flex-direction:column;align-items:${isRight ? 'flex-end' : 'flex-start'};gap:12px`\n\n // ── Chat panel — sits above the bubble ─────────────────────────────────\n const panel = document.createElement('div')\n // Responsive panel: full-screen on mobile (<480px); on desktop 380×600\n // CLAMPED to the viewport (`min(…)`) so a short or narrow browser window\n // never clips the composer — same clamp the React launcher already uses.\n // A MediaQueryList keeps the layout live across rotation/resize.\n const mql = typeof window !== 'undefined' && typeof window.matchMedia === 'function'\n ? window.matchMedia('(max-width: 479px)') : null\n const applyPanelLayout = (mobile: boolean) => {\n // Fullscreen is the ONLY case that strips the widget's corner radius —\n // the renderer keys .ocw-fs, never a blanket viewport query, so inline\n // mobile embeds keep their normal in-page layout.\n opts.el.classList.toggle('ocw-fs', mobile)\n panel.style.cssText = mobile ? [\n 'position:fixed', 'inset:0', 'width:100%', 'height:100dvh',\n 'border-radius:0', 'overflow:hidden',\n 'box-shadow:none', 'display:none', 'flex-direction:column', 'background:#fff',\n 'transition:opacity .18s', 'opacity:0', 'z-index:9998',\n ].join(';') : [\n 'width:min(380px, calc(100vw - 40px))', 'height:min(600px, calc(100dvh - 108px))',\n 'border-radius:28px', 'overflow:hidden',\n 'box-shadow:0 12px 32px rgba(108,92,231,.14)',\n 'display:none', 'flex-direction:column', 'background:#fff',\n 'transform-origin:bottom ' + (isRight ? 'right' : 'left'),\n 'transition:opacity .18s,transform .18s', 'opacity:0', 'transform:scale(.95)',\n ].join(';')\n }\n applyPanelLayout(mql?.matches ?? false)\n const mqlHandler = (e: MediaQueryListEvent): void => applyPanelLayout(e.matches)\n mql?.addEventListener('change', mqlHandler)\n _mql = mql; _mqlHandler = mqlHandler\n\n // Move the mount target INSIDE the panel — not full-page\n opts.el.style.cssText = 'width:100%;height:100%;overflow:hidden'\n panel.append(opts.el)\n\n // ── Bubble button ─────────────────────────────────────────────────────\n // Inline SVGs, not emoji: '💬'/'✕' render differently on every OS (and as\n // colour emoji can clash with the accent); these are crisp everywhere.\n const CHAT_SVG =\n '<svg viewBox=\"0 0 24 24\" width=\"26\" height=\"26\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">' +\n '<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\" ' +\n 'fill=\"currentColor\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>'\n const CLOSE_SVG =\n '<svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">' +\n '<path d=\"M18 6 6 18M6 6l12 12\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"/></svg>'\n\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.style.cssText = [\n `width:56px;height:56px;border-radius:50%`,\n `background:${opts.accent ?? '#6c5ce7'}`,\n `color:#fff;border:none;cursor:pointer`,\n `box-shadow:0 12px 32px rgba(108,92,231,.14)`,\n `position:relative;flex:none`,\n `display:flex;align-items:center;justify-content:center`,\n `transition:transform .15s`,\n ].join(';')\n btn.onmouseenter = () => { btn.style.transform = 'scale(1.08)' }\n btn.onmouseleave = () => { btn.style.transform = 'scale(1)' }\n\n badgeEl = document.createElement('span')\n badgeEl.style.cssText = `position:absolute;top:-4px;right:-4px;background:#4c6fff;color:#fff;border-radius:50%;width:20px;height:20px;font-size:11px;font-weight:700;display:none;align-items:center;justify-content:center`\n\n // (Re)build the bubble's content for the current open state. innerHTML\n // wipes children, so the badge is re-appended each time.\n const paintBubble = () => {\n btn.innerHTML = open ? CLOSE_SVG : CHAT_SVG\n btn.setAttribute('aria-label', open ? 'Close chat' : 'Open chat')\n btn.setAttribute('aria-expanded', String(open))\n btn.append(badgeEl!)\n }\n paintBubble()\n\n launcherEl.append(panel, btn)\n document.body.append(launcherEl)\n\n // ── Launcher teaser (the \"optional message\" card) ─────────────────────\n // A small dismissible card above the button inviting a chat. Source of\n // truth: the explicit `launcherMessage` option (instant), else the\n // chatroom manifest's `defaults.launcherMessage` (arrives on connect).\n // Dismissal is per browser session so it doesn't nag across page nav.\n const teaserKey = `ocw-teaser-dismissed::${opts.profileId}`\n const optMsg = typeof opts.launcherMessage === 'string'\n ? { title: opts.launcherMessage }\n : opts.launcherMessage\n let teaserEl: HTMLElement | null = null\n const teaserDismissed = (): boolean => {\n try { return sessionStorage.getItem(teaserKey) === '1' } catch { return false }\n }\n const CHAT_MINI_SVG =\n '<svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">' +\n '<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\" ' +\n 'fill=\"currentColor\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>'\n const paintTeaser = (): void => {\n const msg = optMsg?.title ? optMsg : (store.launcherMessage ?? undefined)\n const shouldShow = !!msg?.title && !open && !teaserDismissed()\n if (!shouldShow) { if (teaserEl) { teaserEl.remove(); teaserEl = null } return }\n if (teaserEl) return // already shown; don't rebuild (avoids flicker)\n teaserEl = document.createElement('div')\n teaserEl.setAttribute('role', 'button')\n teaserEl.setAttribute('tabindex', '0')\n teaserEl.setAttribute('aria-label', msg!.title)\n teaserEl.style.cssText = [\n 'max-width:280px', 'background:#fff', 'border-radius:16px',\n 'box-shadow:0 12px 32px rgba(108,92,231,.14)', 'padding:14px 40px 14px 16px',\n 'position:relative', 'cursor:pointer', 'font-family:inherit',\n `align-self:${isRight ? 'flex-end' : 'flex-start'}`,\n 'animation:ocw-teaser-in .22s ease-out',\n ].join(';')\n const title = document.createElement('div')\n title.textContent = msg!.title\n title.style.cssText = 'font-size:15px;font-weight:600;color:#111;line-height:1.35'\n teaserEl.append(title)\n if (msg!.subtitle) {\n const sub = document.createElement('div')\n sub.style.cssText = 'display:flex;align-items:center;gap:6px;margin-top:6px;font-size:13px;color:#6b7280'\n const ic = document.createElement('span'); ic.innerHTML = CHAT_MINI_SVG; ic.style.cssText = `color:${opts.accent ?? '#6c5ce7'};display:inline-flex`\n const st = document.createElement('span'); st.textContent = msg!.subtitle\n sub.append(ic, st); teaserEl.append(sub)\n }\n // Close (×) — dismiss for the session without opening the chat.\n const x = document.createElement('button')\n x.type = 'button'\n x.setAttribute('aria-label', 'Dismiss')\n x.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 x.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 x.addEventListener('click', (e) => {\n e.stopPropagation()\n try { sessionStorage.setItem(teaserKey, '1') } catch { /* storage blocked — dismiss for this pageview only */ }\n if (teaserEl) { teaserEl.remove(); teaserEl = null }\n })\n teaserEl.append(x)\n const openFromTeaser = (): void => {\n if (open) return\n open = true; showPanel(true); paintBubble()\n unread = 0; if (badgeEl) badgeEl.style.display = 'none'\n if (teaserEl) { teaserEl.remove(); teaserEl = null }\n }\n teaserEl.addEventListener('click', openFromTeaser)\n teaserEl.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openFromTeaser() } })\n // Insert ABOVE the button (button is the last child of launcherEl).\n launcherEl!.insertBefore(teaserEl, btn)\n }\n // Keyframes for the gentle pop-in (injected once).\n if (!document.getElementById('ocw-teaser-style')) {\n const st = document.createElement('style'); st.id = 'ocw-teaser-style'\n st.textContent = '@keyframes ocw-teaser-in{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}'\n document.head.append(st)\n }\n _paintTeaser = paintTeaser\n paintTeaser() // shows immediately when the option is set; manifest repaints later\n\n const showPanel = (show: boolean) => {\n if (show) {\n panel.style.display = 'flex'\n requestAnimationFrame(() => { panel.style.opacity = '1'; panel.style.transform = 'scale(1)' })\n } else {\n panel.style.opacity = '0'; panel.style.transform = 'scale(.95)'\n setTimeout(() => { if (!open) panel.style.display = 'none' }, 180)\n }\n }\n\n btn.addEventListener('click', () => {\n open = !open\n showPanel(open)\n paintBubble()\n if (open) {\n unread = 0; if (badgeEl) badgeEl.style.display = 'none'\n // Opening satisfies the teaser's whole purpose — retire it for the session.\n try { sessionStorage.setItem(teaserKey, '1') } catch { /* ignore */ }\n }\n _paintTeaser?.()\n })\n\n // Close on Escape. Kept as a named handler so close() can REMOVE it —\n // the old anonymous listener outlived the widget (leaked on every\n // remount/identify, and a stale one could still flip `open`).\n _escHandler = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && open) { open = false; showPanel(false); paintBubble() }\n }\n document.addEventListener('keydown', _escHandler)\n }\n\n const addUnread = () => {\n if (open) return\n unread++\n if (badgeEl) { badgeEl.textContent = String(unread); badgeEl.style.display = 'flex' }\n }\n\n // ── Notification sound ────────────────────────────────────────────────────\n const playSound = () => {\n try {\n // ONE lazy shared context, resumed on each play. The old code created a\n // fresh AudioContext per message; browsers cap concurrent contexts (~6\n // in Chrome), after which construction throws and chat goes silent.\n const ctx = getAudioContext()\n if (!ctx) return\n if (ctx.state === 'suspended') void ctx.resume().catch(() => {})\n const osc = ctx.createOscillator(); const gain = ctx.createGain()\n osc.connect(gain); gain.connect(ctx.destination)\n osc.frequency.setValueAtTime(880, ctx.currentTime)\n osc.frequency.exponentialRampToValueAtTime(440, ctx.currentTime + 0.15)\n gain.gain.setValueAtTime(0.3, ctx.currentTime)\n gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.3)\n osc.start(); osc.stop(ctx.currentTime + 0.3)\n } catch { /* audio not available */ }\n }\n\n // Messages typed before the 'opened' frame arrives are queued here and\n // flushed once cid is known. This prevents silent message loss when the\n // user types immediately after the widget mounts (before WS handshake).\n const preSendQueue: { clientMsgId: string; content: import('./protocol/index.js').MessageContent }[] = []\n\n const flushPreSendQueue = (conversationId: ConversationId) => {\n while (preSendQueue.length) {\n const item = preSendQueue.shift()!\n outbox.add({ clientMsgId: item.clientMsgId, content: item.content, ts: Date.now() })\n conn.send({ type: 'send', conversationId, clientMsgId: item.clientMsgId, content: item.content })\n }\n }\n\n let conn: ConnectionManager\n // E2E is compiled out of the 'lite' build (__E2E__=false) via dead-code\n // elimination — non-encrypted chatrooms don't ship the crypto. In the lite\n // build e2e is null and encrypted rooms are refused (never sent as plaintext).\n const e2e = __E2E__ ? new E2ESession(`ocw-e2e-${opts.profileId}`) : null\n let e2eStarted = false\n // Live ECDH pending (peer not yet online)\n const pending: { clientMsgId: string; text: string }[] = []\n // X3DH async: pending send awaiting the peer's prekey bundle\n const x3dhPending: { clientMsgId: string; text: string }[] = []\n let x3dhBundleFetched = false\n\n const sendSealed = (clientMsgId: string, text: string): void => {\n void e2e!.sealText(text).then((content) => {\n if (cid) conn.send({ type: 'send', conversationId: cid, clientMsgId, content })\n })\n }\n\n const sendSealedX3DH = (clientMsgId: string, text: string, x3dhInit: { ephemeralKey: string; spkId: string; senderIK: string; usedOTP: boolean }): void => {\n void e2e!.sealText(text, x3dhInit).then((content) => {\n if (cid) conn.send({ type: 'send', conversationId: cid, clientMsgId, content })\n })\n }\n\n const flushPending = (): void => {\n while (pending.length) { const p = pending.shift()!; sendSealed(p.clientMsgId, p.text) }\n while (x3dhPending.length) { const p = x3dhPending.shift()!; sendSealed(p.clientMsgId, p.text) }\n }\n\n /** Fetch the peer's prekey bundle and perform X3DH sender init. */\n const fetchAndX3DH = (targetUserId: string): void => {\n conn.send({ type: 'fetchPrekey', targetUserId: targetUserId as never })\n }\n\n const i18n = opts.i18n ?? {}\n // Auto-detect RTL for Arabic/Hebrew/Persian/Urdu regardless of i18n strings\n const rtlLocales = ['ar', 'he', 'fa', 'ur']\n const browserLang = typeof navigator !== 'undefined' ? (navigator.language ?? '').slice(0, 2).toLowerCase() : ''\n if (rtlLocales.includes(browserLang) && !opts.el.dir) {\n opts.el.dir = 'rtl'\n opts.el.style.fontFamily = opts.el.style.fontFamily || 'Tahoma,Arial,system-ui,sans-serif'\n }\n // Pre-chat completion is per (chatroom, identity) — a returning visitor who\n // already qualified goes straight to the composer.\n const preChatKey = `oc_prechat_${opts.profileId}_${token.slice(-8)}`\n\n const renderer = new Renderer(opts.el, token, {\n onSend(text) {\n const clientMsgId = `cm_${Math.random().toString(36).slice(2)}`\n const content: import('./protocol/index.js').MessageContent = { kind: 'text', text }\n store.addOptimistic(clientMsgId, content)\n renderer.render(store)\n if (store.e2e) {\n if (!__E2E__) {\n console.error('[relay] this chat is end-to-end encrypted — use the full widget build')\n return\n }\n if (e2e!.ready) {\n sendSealed(clientMsgId, text)\n } else if (x3dhBundleFetched) {\n x3dhPending.push({ clientMsgId, text })\n } else {\n pending.push({ clientMsgId, text })\n if (store.assignedAgentId) fetchAndX3DH(store.assignedAgentId)\n }\n } else if (!cid) {\n // Connection not yet opened — queue the message; flushed on 'opened'\n preSendQueue.push({ clientMsgId, content })\n } else {\n outbox.add({ clientMsgId, content, ts: Date.now() })\n conn.send({ type: 'send', conversationId: cid, clientMsgId, content })\n }\n },\n async onAttach(file: File) {\n if (!cid) return\n const uploadUrl = `${httpBase}/upload?name=${encodeURIComponent(file.name)}`\n const clientMsgId = `cm_${Math.random().toString(36).slice(2)}`\n // Optimistic: show uploading state\n store.addOptimistic(clientMsgId, { kind: 'text', text: `📎 Uploading ${file.name}…` })\n renderer.render(store)\n try {\n const res = await fetch(uploadUrl, {\n method: 'POST',\n // Use the resolved connection token (a signed JWT, an explicit userId,\n // or the anonymous persistent uid) — NOT just opts.token. The server\n // requires an Authorization header, and the verifier accepts an\n // anonymous id as a guest; keying off opts.token alone meant anonymous\n // visitors (the common case) sent no auth and every upload 401'd.\n headers: { 'content-type': file.type, authorization: `Bearer ${token}` },\n body: file,\n })\n if (!res.ok) throw new Error(`Upload failed: ${res.status}`)\n const { url, name, mime, size } = await res.json() as { url: string; name: string; mime: string; size: number }\n // Swap the \"📎 Uploading…\" placeholder for the REAL attachment content,\n // keyed by the SAME clientMsgId, BEFORE we send. Without this the\n // optimistic entry stayed as placeholder text: the server's `ack`\n // carries no content (see frames.ts), so reconciliation did\n // `{...msg, status:'sent'}` and kept the \"Uploading…\" text forever. The\n // real attachment only appeared on the next history fetch — i.e. after\n // a refresh — which reads exactly as \"the upload is slow\" even though\n // it already succeeded. Re-seeding here means the ack confirms an\n // attachment, and it renders immediately in the live view.\n store.addOptimistic(clientMsgId, { kind: 'attachment', url, name, mime, size })\n renderer.render(store)\n conn.send({ type: 'send', conversationId: cid, clientMsgId, content: { kind: 'attachment', url, name, mime, size } })\n } catch (e) {\n store.addOptimistic(clientMsgId, { kind: 'text', text: `⚠️ Upload failed: ${(e as Error).message}` })\n renderer.render(store)\n }\n },\n onInvoke(actionId, inputs) {\n if (!cid) return\n conn.send({ type: 'invoke', conversationId: cid, actionId, clientInvokeId: `iv_${Math.random().toString(36).slice(2)}`, ...(inputs ? { inputs } : {}) })\n },\n onTyping(isTyping, preview) { if (cid) conn.send({ type: 'typing', conversationId: cid, isTyping, ...(preview ? { preview } : {}) }) },\n onPreChat(values) {\n // Persist \"done\" per (chatroom, browser identity) so reloads skip the form.\n try { localStorage.setItem(preChatKey, '1') } catch { /* private mode */ }\n // Identity fields flow through the SAME open+userInfo path the host's\n // `user` config uses — the engine sanitizes and stores them on the\n // conversation (guestName/guestEmail; phone lands in guest meta).\n conn.send({\n type: 'open', profileId: opts.profileId as never,\n ...(opts.subjectId ? { subjectId: opts.subjectId as never } : {}),\n userInfo: {\n ...(values.name ? { name: values.name } : {}),\n ...(values.email ? { email: values.email } : {}),\n ...(values.phone || values.topic ? { meta: {\n ...(values.phone ? { phone: values.phone } : {}),\n ...(values.topic ? { topic: values.topic } : {}),\n } } : {}),\n },\n } as never)\n // Topic / callback become the visible first line so agents see the\n // qualification without opening the CRM pane. A callback request is\n // explicit and carries the number.\n const first = values.callback\n ? `📞 Call-back requested${values.phone ? `: ${values.phone}` : ''}${values.topic ? ` — ${values.topic}` : ''}`\n : values.topic ? `Topic: ${values.topic}` : ''\n // E2E rooms: identity fields still flow (userInfo above), but the\n // qualification line must not be sent as plaintext into an encrypted\n // conversation — agents see topic/phone in the CRM pane instead.\n if (first && cid && !store.e2e) {\n conn.send({ type: 'send', conversationId: cid, clientMsgId: `pc_${Math.random().toString(36).slice(2, 12)}`, content: { kind: 'text', text: first } })\n }\n },\n ...(opts.features?.deflection !== false ? {\n onDeflectQuery(q: string) {\n if (destroyed) return\n // Debounced keyword lookup against the chatroom's KB — \"was this your\n // question?\" before the first message ever sends. Fails silent: a KB\n // hiccup must never affect typing.\n clearTimeout(deflectTimer)\n const query = q.trim()\n if (query.length < 3) { renderer.hideDeflection(); return }\n deflectTimer = setTimeout(() => {\n void fetch(`${httpBase}/kb/search?profileId=${encodeURIComponent(opts.profileId)}&q=${encodeURIComponent(query.slice(0, 200))}`)\n .then(r => (r.ok ? r.json() : { articles: [] }))\n .then((d: { articles?: { id: string; title: string; answer: string }[] }) => renderer.showDeflection(d.articles ?? []))\n .catch(() => renderer.hideDeflection())\n }, 350)\n },\n } : {}),\n onReadUpTo(seq) { if (cid) conn.send({ type: 'read', conversationId: cid, seq }) },\n onLoadMore() {\n // WS fallback for E2E rooms where REST history can't be decrypted.\n // Non-E2E rooms use scroll-triggered REST pagination from restoreHistory().\n if (!cid || !store.e2e) return\n const oldest = store.messages()[0]\n if (oldest) conn.send({ type: 'history', conversationId: cid, beforeSeq: oldest.seq, limit: 20 })\n },\n onEdit(messageId, newText) {\n if (cid) conn.send({ type: 'edit', conversationId: cid, messageId: messageId as never, content: { kind: 'text', text: newText } })\n },\n onDelete(messageId) {\n if (cid) conn.send({ type: 'delete', conversationId: cid, messageId: messageId as never })\n },\n ...(opts.features?.reactions !== false ? {\n onReact(messageId: string, emoji: string, remove: boolean) {\n if (!cid) return\n conn.send({ type: 'react', conversationId: cid, messageId: messageId as never, emoji, remove })\n },\n } : {}),\n ...(opts.features?.csat !== false ? {\n onCsat(score: number) {\n if (!cid) return\n fetch(`${httpBase}/conversations/${cid}/csat`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },\n body: JSON.stringify({ score }),\n }).catch(() => {})\n },\n } : {}),\n ...(opts.translateLang ? {\n async onTranslate(text: string) {\n try {\n const res = await fetch(`${httpBase}/translate`, {\n method: 'POST',\n headers: { 'content-type': 'application/json', authorization: `Bearer ${token}` },\n body: JSON.stringify({ text, targetLang: opts.translateLang }),\n })\n if (!res.ok) return null\n const { translated } = await res.json() as { translated: string | null }\n return translated\n } catch { return null }\n },\n } : {}),\n ...(opts.onBack ? { onBack: opts.onBack } : {}),\n }, {\n ...(opts.subject ? { subject: opts.subject } : {}),\n ...(opts.quickReplies ? { quickReplies: opts.quickReplies } : {}),\n ...(opts.accent ? { accent: opts.accent } : {}),\n ...(opts.accent2 ? { accent2: opts.accent2 } : {}),\n ...(opts.theme ? { theme: opts.theme } : {}),\n ...(opts.webfont === false ? { webfont: false } : {}),\n ...(opts.user?.name || opts.user?.avatar ? { userInfo: { ...(opts.user.name ? { name: opts.user.name } : {}), ...(opts.user.avatar ? { avatar: opts.user.avatar } : {}) } } : {}),\n i18n,\n\n })\n\n // Returning visitor who already completed pre-chat → straight to composer.\n try { if (localStorage.getItem(preChatKey)) renderer.completePreChat() } catch { /* private mode */ }\n\n // Identified-user display info rides on the open frame itself: the server\n // persists it onto the conversation (sanitized) so agents see who they're\n // talking to. The previous approach — sending a `note` frame after 'opened' —\n // never worked: `note` is agent-only, so the server answered FORBIDDEN and\n // the info was silently dropped. Carrying it on `open` also means it reaches\n // the dashboard for EXISTING conversations (e.g. a visitor who logs in after\n // chatting anonymously), not just brand-new empty ones.\n const userInfo = opts.user && (opts.user.name || opts.user.email || opts.user.avatar || opts.user.meta)\n ? {\n ...(opts.user.name ? { name: opts.user.name } : {}),\n ...(opts.user.email ? { email: opts.user.email } : {}),\n ...(opts.user.avatar ? { avatar: opts.user.avatar } : {}),\n ...(opts.user.meta ? { meta: opts.user.meta } : {}),\n }\n : undefined\n\n const openFrame: Extract<ClientFrame, { type: 'open' }> = {\n type: 'open', profileId: opts.profileId as never,\n // Direct conversations use the kind/peerId pair; the dm:… subject key is\n // server-derived and owner-keyed, so passing it as subjectId from the\n // NON-owner side would find-or-create a junk duplicate thread.\n ...(opts.kind === 'direct' && opts.peerId\n ? { kind: 'direct' as const, peerId: opts.peerId as never }\n : opts.subjectId ? { subjectId: opts.subjectId as never } : {}),\n ...(linkFrom ? { linkFrom: linkFrom as never } : {}),\n ...(userInfo ? { userInfo } : {}),\n ...(typeof location !== 'undefined' ? { pageUrl: location.href } : {}),\n ...(typeof document !== 'undefined' && document.title ? { pageTitle: document.title } : {}),\n // Pass subject display info so the server can persist it to the Subject record.\n // This is how listingTitle and listingMeta get saved without a separate API call.\n ...(opts.subject?.title ? { subjectTitle: opts.subject.title } : {}),\n ...(opts.subject?.subtitle ? { subjectMeta: opts.subject.subtitle } : {}),\n }\n\n conn = new ConnectionManager({\n ...(opts.refreshToken ? { refreshToken: opts.refreshToken } : {}),\n url: wsUrl, token, open: openFrame,\n getCursor: () => store.highestSeq(),\n onStatusChange: (s, msg) => renderer.setConnStatus(s, msg),\n onFrame(frame) {\n if (frame.type === 'opened') {\n cid = frame.conversation.id\n\n // Flush persistent outbox exactly once (idempotent: items are ack-removed).\n if (!outboxRestored) {\n outboxRestored = true\n for (const item of outbox.load()) {\n conn.send({ type: 'send', conversationId: cid, clientMsgId: item.clientMsgId, content: item.content })\n }\n }\n\n // Restore history on EVERY successful open (covers reconnects too).\n // On reconnect the store still has messages in memory so this is a\n // no-op if there's nothing newer — cheap REST call, correct behaviour.\n void restoreHistory(wsUrl, token, cid, store, renderer, httpBase)\n\n // Flush any messages typed while the connection was still opening.\n if (preSendQueue.length) flushPreSendQueue(cid)\n }\n\n if (frame.type === 'ack') outbox.remove(frame.clientMsgId)\n\n // X3DH: handle incoming prekey bundle response\n if (__E2E__ && frame.type === 'prekeyBundle') {\n if (frame.bundle) {\n x3dhBundleFetched = true\n void e2e!.x3dhSendTo(frame.bundle).then((x3dhInit) => {\n // Drain any X3DH-pending messages with the derived key.\n const toSend = [...pending.splice(0), ...x3dhPending.splice(0)]\n for (const p of toSend) sendSealedX3DH(p.clientMsgId, p.text, x3dhInit)\n renderer.render(store)\n })\n }\n // If no bundle, peer has no prekeys; fall back to live ECDH queue\n return\n }\n\n // X3DH recipient: detect init message in incoming encrypted messages\n if (__E2E__ && frame.type === 'message' && store.e2e) {\n const x3dh = extractX3DHInit(frame.message.content)\n if (x3dh && !e2e!.ready) {\n void e2e!.x3dhReceiveFrom(x3dh.x3dhIK, x3dh.x3dhEK, x3dh.x3dhSPK, x3dh.x3dhOTP).then(async () => {\n // Now decrypt the message that carried the init\n await e2e!.openFrame(frame)\n store.apply(frame)\n renderer.render(store)\n })\n return\n }\n }\n\n if (__E2E__ && frame.type === 'peerkey') {\n void e2e!.onPeerKey(frame.key).then(() => { flushPending(); renderer.render(store) })\n return\n }\n void (async () => {\n if (__E2E__ && store.e2e) await e2e!.openFrame(frame)\n store.apply(frame)\n // The manifest carries the launcher teaser (defaults.launcherMessage);\n // repaint so a manifest-configured message appears over the closed launcher.\n if (frame.type === 'manifest') {\n _paintTeaser?.()\n // A3.3 — per-chatroom bot identity: prefer the manifest's botName for\n // the bot bubble label over the generic \"AI Assistant\".\n const bn = (frame as { botName?: string }).botName\n if (bn) renderer.setBotName(bn)\n }\n // Trigger badge + sound for new messages from others.\n // In chatList mode, skip the badge if the user is already in the chat\n // screen for this conversation — they can see the message immediately.\n if (frame.type === 'message' && frame.message.senderId !== (token as never) && !frame.message.internal) {\n addUnread()\n playSound()\n }\n // Once we learn the room is E2E, run the key handshake exactly once.\n if (__E2E__ && store.e2e && cid && !e2eStarted) {\n e2eStarted = true\n // Upload our prekey bundle for async E2E support.\n const prekeyPayload = await e2e!.initX3DH()\n conn.send({ type: 'uploadPrekeys', ...prekeyPayload })\n // Also do live ECDH handshake in case peer is already online.\n const liveKey = await e2e!.begin()\n conn.send({ type: 'pubkey', conversationId: cid, key: liveKey })\n }\n renderer.render(store)\n // Keep seenSeq in sync so the chat list shows accurate unread counts\n })()\n },\n })\n\n\n conn.connect()\n // Show restored 'pending' bubbles (if any) immediately, before the socket opens.\n renderer.render(store)\n\n const slot = opts.launcher ? launcherSlot(opts) : null\n const handle: WidgetHandle = { close: () => {\n destroyed = true\n clearTimeout(deflectTimer)\n conn.close(); launcherEl?.remove(); renderer.destroy()\n if (_mql && _mqlHandler) _mql.removeEventListener('change', _mqlHandler)\n if (_escHandler) { document.removeEventListener('keydown', _escHandler); _escHandler = null }\n _registry.delete(opts.el)\n if (slot && _launcherRegistry.get(slot) === handle) _launcherRegistry.delete(slot)\n } }\n _registry.set(opts.el, handle)\n if (slot) _launcherRegistry.set(slot, handle)\n return handle\n}\n\nexport { ChatStore } from './store.js'\nexport { ConnectionManager } from './connection.js'\nexport { Renderer } from './renderer.js'\nexport { asConversationId }\nexport { E2ESession, extractX3DHInit, type X3DHBundle } from './e2e.js'\nexport { PersistentOutbox, type OutboxItem } from './outbox.js'\nexport { restoreHistory, httpBaseFromWsUrl, resolveRelayUrls } from './history.js'\n\n"],"names":["CSS","lightTokens","darkTokens","STYLE_ID","REACTION_EMOJIS","COMPACT_BREAKPOINT","SEND_ICON_SVG","injectStyles","s","FONT_ID","injectFonts","l","el","tag","cls","text","n","fmtTime","ts","contentText","c","_a","Renderer","root","me","h","cfg","__publicField","accent2","head","back","brandName","_b","avatarEl","hm","_c","_d","_e","_f","_g","typingBubble","q","b","_h","sendBtn","sendLabel","_i","m","e","attachBtn","fileInput","inputRow","footer","_j","_k","ta","applyCompact","w","entries","fn","sh","preview","name","store","ownerLabel","actions","prevScrollHeight","prevScrollTop","sentinel","maxOther","prevSender","msgs","i","showLabel","typingNames","bubble","guestHasSpoken","copy","terminalStates","isTerminal","note","status","message","fatal","form","inputs","f","inp","topicSel","ph","t","o","callbackCb","phoneForCb","row","submit","email","callback","phone","articles","a","card","stars","btns","bb","idx","title","tags","k","v","btn","label","resolve","overlay","cancel","ok","close","panel","sel","opt","out","isLast","sys","mine","isNote","col","who","bubbleWrap","replyCtx","textNode","img","video","ap","links","gLink","iLink","chips","x","original","translateBtn","transLine","showLine","hideLine","cached","result","menu","editBtn","saveBtn","cancelBtn","btnRow","editPanel","restore","newText","ke","delBtn","cites","src","fb","mk","emoji","already","reactWrap","reactRow","users","pill","addBtn","picker","pb","alreadyReacted","opening","u","meta","tick","_registry","_launcherRegistry","launcherSlot","opts","_audioCtx","getAudioContext","unmount","mountInbox","room","list","closed","clear","openList","mountChatList","entry","openRoom","_s","_p","base","mount","anonId","persistentUid","deflectTimer","destroyed","token","linkFrom","wsUrl","httpBase","resolveRelayUrls","ChatStore","outboxKey","outbox","PersistentOutbox","cid","outboxRestored","_mql","_mqlHandler","_escHandler","_paintTeaser","item","launcherEl","badgeEl","unread","open","isRight","mql","applyPanelLayout","mobile","mqlHandler","CHAT_SVG","CLOSE_SVG","paintBubble","teaserKey","optMsg","teaserEl","teaserDismissed","CHAT_MINI_SVG","paintTeaser","msg","sub","ic","st","openFromTeaser","showPanel","show","addUnread","playSound","ctx","osc","gain","preSendQueue","flushPreSendQueue","conversationId","conn","e2e","E2ESession","e2eStarted","pending","x3dhPending","x3dhBundleFetched","sendSealed","clientMsgId","content","sendSealedX3DH","x3dhInit","flushPending","p","fetchAndX3DH","targetUserId","i18n","rtlLocales","browserLang","preChatKey","renderer","file","uploadUrl","res","url","mime","size","actionId","isTyping","values","first","query","r","d","seq","oldest","messageId","remove","score","translated","userInfo","openFrame","ConnectionManager","frame","restoreHistory","toSend","x3dh","extractX3DHInit","bn","prekeyPayload","liveKey","slot","handle"],"mappings":";;;;;;;;AAKO,MAAMA,KAAM;AAAA,SACVC,GAAY,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA,kEAIuCC,GAAW,KAAK,CAAC;AAAA,4BACvDA,GAAW,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GCuCvCC,KAAW,4BACXC,KAAkB,CAAC,MAAM,MAAM,MAAM,MAAM,MAAM,IAAI,GAErDC,KAAqB,KAGrBC,KACJ;AAKF,SAASC,KAAqB;AAC5B,MAAI,OAAO,WAAa,OAAe,SAAS,eAAeJ,EAAQ,EAAG;AAC1E,QAAMK,IAAI,SAAS,cAAc,OAAO;AAAG,EAAAA,EAAE,KAAKL,IAAUK,EAAE,cAAcR,IAAK,SAAS,KAAK,YAAYQ,CAAC;AAC9G;AAMA,MAAMC,KAAU;AAChB,SAASC,KAAoB;AAC3B,MAAI,OAAO,WAAa,OAAe,SAAS,eAAeD,EAAO,EAAG;AACzE,QAAME,IAAI,SAAS,cAAc,MAAM;AACvC,EAAAA,EAAE,KAAKF,IAASE,EAAE,MAAM,cACxBA,EAAE,OAAO,qHACT,SAAS,KAAK,YAAYA,CAAC;AAC7B;AAEA,SAASC,EAA0CC,GAAQC,GAAcC,GAAyC;AAChH,QAAMC,IAAI,SAAS,cAAcH,CAAG;AAAG,SAAIC,QAAO,YAAYA,IAASC,MAAS,WAAWC,EAAE,cAAcD,IAAaC;AAC1H;AACA,SAASC,GAAQC,GAAoB;AACnC,MAAI;AAAE,WAAO,IAAI,KAAKA,CAAE,EAAE,mBAAmB,CAAA,GAAI,EAAE,MAAM,WAAW,QAAQ,WAAW;AAAA,EAAE,QAAQ;AAAE,WAAO;AAAA,EAAG;AAC/G;AACA,SAASC,GAAYC,GAA2B;;AAC9C,UAAQA,EAAE,MAAA;AAAA,IACR,KAAK;AAAe,aAAOA,EAAE;AAAA,IAC7B,KAAK;AAAe,aAAO,SAAOC,IAAAD,EAAE,SAAF,gBAAAC,EAAS,YAAe,WAAW,OAAOD,EAAE,KAAK,OAAU,IAAIA,EAAE;AAAA,IACnG,KAAK;AAAe,aAAO,CAACA,EAAE,OAAOA,EAAE,IAAI,EAAE,OAAO,OAAO,EAAE,KAAK,KAAK;AAAA,IACvE,KAAK;AAAe,aAAOA,EAAE,QAAQA,EAAE;AAAA,IACvC,KAAK;AAAe,aAAOA,EAAE;AAAA,IAC7B,KAAK;AAAe,aAAO,MAAMA,EAAE,KAAK,MAAM,IAAI,KAAKA,EAAE,QAAQ,EAAE,eAAA,CAAgB;AAAA,EAAA;AAEvF;AAMO,MAAME,GAAS;AAAA;AAAA,EA+CpB,YACmBC,GACAC,GACAC,GACAC,IAAoB,CAAA,GACrC;AAnDe,IAAAC,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACT,IAAAA,EAAA,sBAAe;AACf,IAAAA,EAAA,qBAAc;AACL,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACT,IAAAA,EAAA,qBAAoD;AACpD,IAAAA,EAAA,uBAAgB;AACP,IAAAA,EAAA;AAET;AAAA,IAAAA,EAAA,yBAAyC;AACzC,IAAAA,EAAA,kBAA6B;AACpB,IAAAA,EAAA;AACT,IAAAA,EAAA,uBAAqC;AAY5B;AAAA,IAAAA,EAAA,8CAAuB,IAAA;AACvB,IAAAA,EAAA,gDAAyB,IAAA;AA8elC;AAAA;AAAA,IAAAA,EAAA,sBAAe;;AAveJ,SAAA,OAAAJ,GACA,KAAA,KAAAC,GACA,KAAA,IAAAC,GACA,KAAA,MAAAC,GAEjBnB,GAAA,GACImB,EAAI,YAAY,MAAOhB,GAAA,GAK3Ba,EAAK,gBAAA,GACLA,EAAK,UAAU,IAAI,KAAK,GACpBG,EAAI,UAAQH,EAAK,MAAM,YAAY,gBAAgBG,EAAI,MAAM;AAGjE,UAAME,IAAUF,EAAI,WAAWA,EAAI;AACnC,IAAIE,KAASL,EAAK,MAAM,YAAY,iBAAiBK,CAAO,GAC5D,KAAK,aAAaF,EAAI,SAKtBH,EAAK,QAAQ,QAAQG,EAAI,SAAS;AAGlC,UAAMG,IAAOjB,EAAG,OAAO,UAAU;AAIjC,QAAI,KAAK,EAAE,QAAQ;AACjB,YAAMkB,IAAOlB,EAAG,UAAU,YAAY,GAAG;AACzC,MAAAkB,EAAK,OAAO,UACZA,EAAK,aAAa,cAAc,MAAM,GACtCA,EAAK,iBAAiB,SAAS,MAAM,KAAK,EAAE,QAAS,GACrDD,EAAK,OAAOC,CAAI;AAAA,IAClB;AAIA,UAAMC,MAAYV,IAAAK,EAAI,YAAJ,gBAAAL,EAAa,iBAAcW,IAAAN,EAAI,YAAJ,gBAAAM,EAAa,UAAS,IAC7DC,IAAWrB,EAAG,OAAO,YAAY;AACvC,IAAImB,EAAU,WACZE,EAAS,cAAcF,EAAU,KAAA,EAAO,CAAC,EAAG,YAAA,GAC5CF,EAAK,OAAOI,CAAQ;AAEtB,UAAMC,IAAKtB,EAAG,OAAO,eAAe;AACpC,SAAK,aAAaA,EAAG,OAAO,mBAAiBuB,IAAAT,EAAI,YAAJ,gBAAAS,EAAa,iBAAcC,IAAAV,EAAI,YAAJ,gBAAAU,EAAa,UAAS,EAAE,GAChGF,EAAG,OAAO,KAAK,UAAU,GACzB,KAAK,aAAatB,EAAG,OAAO,iBAAiB,GAAG,KAAK,WAAW,MAAM,UAAU,QAChFsB,EAAG,OAAO,KAAK,UAAU,IACrBG,IAAAX,EAAI,YAAJ,QAAAW,EAAa,YAAUH,EAAG,OAAOtB,EAAG,OAAO,iBAAiBc,EAAI,QAAQ,QAAQ,CAAC,GACrFG,EAAK,OAAOK,CAAE,GACd,KAAK,cAActB,EAAG,QAAQ,eAAa0B,IAAAZ,EAAI,YAAJ,gBAAAY,EAAa,WAAU,EAAE,IAC/DC,IAAAb,EAAI,YAAJ,QAAAa,EAAa,WAAQ,KAAK,YAAY,MAAM,UAAU,SAC3DV,EAAK,OAAO,KAAK,WAAW,GAC5B,KAAK,WAAWjB,EAAG,QAAQ,WAAW,QAAQ,GAAG,KAAK,SAAS,MAAM,UAAU,QAAQiB,EAAK,OAAO,KAAK,QAAQ,GAChH,KAAK,aAAajB,EAAG,QAAQ,iBAAiB,GAAG,KAAK,WAAW,MAAM,UAAU,QAAQiB,EAAK,OAAO,KAAK,UAAU,GAEpHA,EAAK,OAAOjB,EAAG,UAAU,YAAY,GAAG,CAAC,GAGzC,KAAK,QAAQA,EAAG,OAAO,aAAa,GAGpC,KAAK,SAASA,EAAG,OAAO,YAAY,GACpC,KAAK,cAAcA,EAAG,OAAO,aAAa,GAC1C,KAAK,SAASA,EAAG,OAAO,YAAY;AACpC,UAAM4B,IAAe5B,EAAG,OAAO,mBAAmB;AAClD,IAAA4B,EAAa,OAAO5B,EAAG,OAAO,gBAAgB,GAAGA,EAAG,OAAO,gBAAgB,GAAGA,EAAG,OAAO,gBAAgB,CAAC,GACzG,KAAK,OAAO,OAAO4B,CAAY,GAC/B,KAAK,QAAQ5B,EAAG,OAAO,WAAW;AAClC,eAAW6B,KAAKf,EAAI,gBAAgB,CAAA,GAAI;AACtC,YAAMgB,IAAI9B,EAAG,UAAU,QAAW6B,CAAC;AACnC,MAAAC,EAAE,iBAAiB,SAAS,MAAM;AAChC,aAAK,EAAE,OAAOD,CAAC,GAEf,KAAK,MAAM,MAAM,UAAU;AAAA,MAC7B,CAAC,GACD,KAAK,MAAM,OAAOC,CAAC;AAAA,IACrB;AAGA,SAAK,WAAW9B,EAAG,OAAO,eAAe,GACzC,KAAK,YAAYA,EAAG,OAAO,UAAU,GAAG,KAAK,UAAU,MAAM,UAAU,QAKvE,KAAK,aAAaA,EAAG,OAAO,UAAU,GAAG,KAAK,WAAW,MAAM,UAAU,QACzE,KAAK,eAAeA,EAAG,OAAO,aAAa,GAAG,KAAK,aAAa,MAAM,UAAU,QAChF,KAAK,eAAeA,EAAG,OAAO,aAAa,GAAG,KAAK,aAAa,MAAM,UAAU,QAChF,KAAK,QAAQA,EAAG,YAAY,MAAS,GAAG,KAAK,MAAM,OAAO,GAG1D,KAAK,MAAM,gBAAc+B,IAAAjB,EAAI,SAAJ,gBAAAiB,EAAU,gBAAe;AAClD,UAAMC,IAAUhC,EAAG,UAAU,aAAa;AAC1C,IAAAgC,EAAQ,OAAO;AACf,UAAMC,KAAYC,IAAApB,EAAI,SAAJ,gBAAAoB,EAAU;AAC5B,IAAID,KAEFD,EAAQ,cAAcC,GACtBD,EAAQ,UAAU,IAAI,mBAAmB,GACzCA,EAAQ,aAAa,cAAcC,CAAS,MAI5CD,EAAQ,YAAYtC,IACpBsC,EAAQ,aAAa,cAAc,cAAc,IAInDA,EAAQ,WAAW,IACnB,KAAK,UAAUA,GACfA,EAAQ,iBAAiB,SAAS,MAAM,KAAK,WAAW,GACxD,KAAK,MAAM,iBAAiB,SAAS,MAAM;;AACzC,WAAK,QAAQ,WAAW,KAAK,MAAM,MAAM,OAAO,WAAW,GAC3D,KAAK,cAAA,GAGD,KAAK,YAAY,CAAC,KAAK,SAAS,SAAA,EAAW,KAAK,CAAAG,MAAKA,EAAE,eAAe,OAAO,KAAGf,KAAAX,IAAA,KAAK,GAAE,mBAAP,QAAAW,EAAA,KAAAX,GAAwB,KAAK,MAAM,cAC7G,eAAA;AAAA,IACZ,CAAC,GACD,KAAK,MAAM,iBAAiB,WAAW,CAAC2B,MAAM;AAW5C,MAAIA,EAAE,eAAeA,EAAE,YAAY,QAC/BA,EAAE,QAAQ,WAAW,CAACA,EAAE,YAAYA,EAAE,eAAA,GAAkB,KAAK,UAAA,UAAwB,aAAA;AAAA,IAC3F,CAAC;AAED,UAAMC,IAAYrC,EAAG,UAAU,cAAc,IAAI;AAAG,IAAAqC,EAAU,QAAQ;AACtE,UAAMC,IAAY,SAAS,cAAc,OAAO;AAAG,IAAAA,EAAU,OAAO,QACpEA,EAAU,SAAS,gCAAgCA,EAAU,MAAM,UAAU,QAC7ED,EAAU,iBAAiB,SAAS,MAAMC,EAAU,OAAO,GAC3DA,EAAU,iBAAiB,UAAU,MAAM;;AAAE,OAAI7B,IAAA6B,EAAU,UAAV,QAAA7B,EAAkB,MAAM,KAAK,EAAE,YAAU,KAAK,EAAE,SAAS6B,EAAU,MAAM,CAAC,CAAC,GAAGA,EAAU,QAAQ;AAAA,IAAG,CAAC;AAErJ,UAAMC,IAAWvC,EAAG,OAAO,WAAW;AAAG,IAAAuC,EAAS,OAAOF,GAAWC,GAAW,KAAK,OAAON,CAAO;AAElG,UAAMQ,IAASxC,EAAG,OAAO,YAAY;AAGrC,MAAIyC,IAAA3B,EAAI,SAAJ,gBAAA2B,EAAU,eAAc,SAC1BD,EAAO,cAAc1B,EAAI,KAAK,YAE9B0B,EAAO,YAAY,2FAErB,KAAK,SAASA,GAEd,KAAK,YAAYxC,EAAG,UAAU,gBAAc0C,IAAA,KAAK,IAAI,SAAT,gBAAAA,EAAe,WAAU,qBAAqB,GAC1F,KAAK,UAAU,OAAO,UACtB,KAAK,UAAU,iBAAiB,SAAS,MAAM;AAC7C,WAAK,UAAU,MAAM,UAAU;AAC/B,YAAMC,IAAK,KAAK,KAAK,cAAc,qBAAqB;AACxD,MAAAA,KAAA,QAAAA,EAAI;AAAA,IACN,CAAC,GACDhC,EAAK,OAAOM,GAAM,KAAK,OAAO,KAAK,QAAQ,KAAK,QAAQ,KAAK,OAAO,KAAK,UAAU,KAAK,WAAW,KAAK,cAAc,KAAK,cAAc,KAAK,YAAYsB,GAAU,KAAK,WAAW,KAAK,MAAM;AAM/L,UAAMK,IAAe,CAACC,MAAoB;AAAE,MAAAlC,EAAK,UAAU,OAAO,eAAekC,IAAI,KAAKA,IAAIpD,EAAkB;AAAA,IAAE;AAClH,IAAAmD,EAAajC,EAAK,WAAW,GACzB,OAAO,iBAAmB,QAC5B,KAAK,kBAAkB,IAAI,eAAe,CAACmC,MAAY;;AACrD,YAAMD,MAAIpC,IAAAqC,EAAQ,CAAC,MAAT,gBAAArC,EAAY,YAAY,UAASE,EAAK;AAChD,MAAAiC,EAAaC,CAAC;AAAA,IAChB,CAAC,GACD,KAAK,gBAAgB,QAAQlC,CAAI;AAAA,EAErC;AAAA;AAAA,EAnMA,cAAkC;AAAE,WAAO,KAAK;AAAA,EAAO;AAAA;AAAA,EAGvD,iBAAiBoC,GAAsB;;AACrC,KAAAtC,IAAA,KAAK,kBAAL,QAAAA,EAAA,YACA,KAAK,gBAAgBsC;AAAA,EACvB;AAAA;AAAA,EAgMA,UAAgB;;AACd,KAAAtC,IAAA,KAAK,kBAAL,QAAAA,EAAA,YACA,KAAK,gBAAgB,MACjB,KAAK,gBAAe,aAAa,KAAK,WAAW,GAAG,KAAK,cAAc,QAC3EW,IAAA,KAAK,oBAAL,QAAAA,EAAsB,cACtB,KAAK,kBAAkB;AAAA,EACzB;AAAA,EAGQ,YAAkB;AACxB,SAAK,eAAA;AACL,UAAMjB,IAAO,KAAK,MAAM,MAAM,KAAA;AAC9B,IAAKA,MACL,KAAK,MAAM,QAAQ,IACnB,KAAK,QAAQ,WAAW,IACxB,KAAK,cAAA,GACL,KAAK,EAAE,SAAS,EAAK,GACrB,KAAK,EAAE,OAAOA,CAAI;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAsB;AAC5B,SAAK,MAAM,MAAM,SAAS;AAC1B,UAAM6C,IAAK,KAAK,MAAM;AACtB,IAAIA,IAAK,IAAG,KAAK,MAAM,MAAM,SAAS,GAAG,KAAK,IAAIA,GAAI,GAAG,CAAC,OACrD,KAAK,MAAM,MAAM,eAAe,QAAQ;AAAA,EAC/C;AAAA,EACQ,eAAqB;AAC3B,UAAMC,IAAU,KAAK,MAAM,MAAM,OAAO,MAAM,GAAG,GAAG,KAAK;AACzD,SAAK,EAAE,SAAS,IAAMA,CAAO,GACzB,KAAK,eAAa,aAAa,KAAK,WAAW,GACnD,KAAK,cAAc,WAAW,MAAM,KAAK,EAAE,SAAS,EAAK,GAAG,GAAI;AAAA,EAClE;AAAA;AAAA,EAGA,WAAWC,GAAoB;AAC7B,SAAK,IAAI,OAAO,EAAE,GAAI,KAAK,IAAI,QAAQ,CAAA,GAAK,aAAaA,EAAA;AAAA,EAC3D;AAAA,EAEA,OAAOC,GAAwB;;AAa7B,QAZA,KAAK,WAAWA,GACZA,EAAM,WACR,KAAK,KAAK,MAAM,YAAY,gBAAgBA,EAAM,MAAM,GACxD,KAAK,KAAK,MAAM,YAAY,iBAAiB,KAAK,cAAcA,EAAM,MAAM,IAE9E,KAAK,SAAS,MAAM,UAAUA,EAAM,MAAM,gBAAgB,QAC1D,KAAK,iBAAiBA,CAAK,GAMvBA,EAAM,SAAS;AACjB,YAAMC,KAAa3C,IAAA,KAAK,IAAI,YAAT,gBAAAA,EAAkB;AACrC,MAAI2C,MAAY,KAAK,WAAW,cAAcA;AAAA,IAEhD;AAQA,SAAK,MAAM,MAAM,UAAUD,EAAM,WAAW,WAAW,IAAI,SAAS,QAGpE,KAAK,MAAM,gBAAA;AACX,UAAME,IAAUF,EAAM,eAAA;AACtB,SAAK,MAAM,MAAM,UAAUE,EAAQ,SAAS,SAAS;AACrD,eAAW,KAAKA,EAAS,MAAK,MAAM,OAAO,KAAK,OAAO,CAAC,CAAC;AAKzD,UAAMC,IAAmB,KAAK,OAAO,cAC/BC,IAAmB,KAAK,OAAO;AAIrC,QAFA,KAAK,OAAO,gBAAA,GACR,KAAK,YAAY,WAAW,eAAa,OAAO,OAAO,KAAK,WAAW,GACvEJ,EAAM,gBAAgB;AAIxB,YAAMK,IAAWxD,EAAG,OAAO,eAAe;AAC1C,MAAAwD,EAAS,cAAc,+BACvBA,EAAS,MAAM,gBAAgB,QAC/B,KAAK,OAAO,OAAOA,CAAQ;AAAA,IAC7B;AACA,QAAIC,IAAW,GACXC,IAA4B;AAChC,UAAMC,IAAO,CAAC,GAAGR,EAAM,UAAU;AACjC,aAASS,IAAI,GAAGA,IAAID,EAAK,QAAQC,KAAK;AACpC,YAAMzB,IAAIwB,EAAKC,CAAC,GACVC,IAAY1B,EAAE,eAAe,YAAYA,EAAE,aAAa,KAAK,MAAM,CAACA,EAAE,YAAYA,EAAE,aAAauB;AACvG,WAAK,OAAO,OAAO,KAAK,UAAUvB,GAAGgB,GAAOU,GAAWD,MAAMD,EAAK,SAAS,CAAC,CAAC,GACzExB,EAAE,eAAe,aAAUuB,IAAavB,EAAE,WAC1CA,EAAE,aAAa,KAAK,MAAMA,EAAE,MAAMsB,UAAqBtB,EAAE;AAAA,IAC/D;AAEA,IAAIoB,IAAgB,KAClB,KAAK,OAAO,YAAY,KAAK,OAAO,eAAeD,IAAmBC,IAEtE,KAAK,OAAO,YAAY,KAAK,OAAO,cAElCE,IAAW,KAAG,KAAK,EAAE,WAAWA,CAAQ;AAE5C,UAAMK,IAAc,CAAC,GAAGX,EAAM,MAAM;AACpC,SAAK,OAAO,UAAU,OAAO,UAAUW,EAAY,SAAS,CAAC;AAE7D,UAAMC,IAAS,KAAK,OAAO,cAAc,oBAAoB;AAC7D,IAAIA,KAAQA,EAAO,aAAa,cAAcD,EAAY,SAAS,WAAW,EAAE,GAChF,KAAK,OAAO,MAAM,UAAUX,EAAM,aAAa,SAAS;AAYxD,UAAMa,IAAiBb,EAAM,WAAW,KAAK,CAAAhB,MAAKA,EAAE,eAAe,OAAO;AAG1E,QAFsB,CAAC,GAACf,IAAA+B,EAAM,YAAN,QAAA/B,EAAe,YAAW,CAAC,KAAK,eAAe,CAAC4C,MACrEb,EAAM,QAAS,aAAa,aAAaA,EAAM;AAEhD,MAAK,KAAK,gBAAc,KAAK,kBAAkBA,EAAM,OAAQ,GAC7D,KAAK,aAAa,MAAM,UAAU,SAClC,KAAK,WAAW,MAAM,UAAU,SAC9B5B,IAAA,KAAK,KAAK,cAAc,YAAY,MAApC,QAAAA,EAA8D,MAAM,YAAY,WAAW;AAAA,SACxF;AAIL,UAHA,KAAK,aAAa,MAAM,UAAU,QAG9B4B,EAAM,SAAS;AACjB,QAAK,KAAK,WAAW,cACnB,KAAK,WAAW,OAAOnD,EAAG,QAAQ,iBAAiB,IAAI,GAAGA,EAAG,QAAQ,IAAI,EAAE,CAAC;AAE9E,cAAMiE,IAAO,KAAK,WAAW;AAC7B,QAAAA,EAAK,cAAcd,EAAM,oBACpB3B,IAAA,KAAK,IAAI,SAAT,gBAAAA,EAAe,YACf,mFACL,KAAK,WAAW,MAAM,UAAU;AAAA,MAClC;AACE,aAAK,WAAW,MAAM,UAAU;AAEhC,OAAAC,IAAA,KAAK,KAAK,cAAc,YAAY,MAApC,QAAAA,EAA8D,MAAM,eAAe;AAAA,IACvF;AAKA,UAAMyC,IAAiB,CAAC,YAAY,UAAU,QAAQ,UAAU,aAAa;AAC7E,IAAI,KAAK,EAAE,UAAU,CAAC,KAAK,iBAAiBA,EAAe,SAASf,EAAM,KAAK,KAAKA,EAAM,SAAA,EAAW,SAAS,MACxG,KAAK,UAAU,MAAM,YAAY,eAAa,eAAA,GAClD,KAAK,UAAU,MAAM,UAAU;AAMjC,UAAMgB,IAAaD,EAAe,SAASf,EAAM,KAAK;AActD,QAbIA,EAAM,WACR,KAAK,WAAW,cAAc,KAAKA,EAAM,oBAAkBzB,IAAA,KAAK,IAAI,SAAT,gBAAAA,EAAe,SAAQ,MAAM,IACxF,KAAK,WAAW,YAAY,wBAC5B,KAAK,WAAW,MAAM,UAAU,MACvByB,EAAM,kBACf,KAAK,WAAW,cAAc,OAAKxB,IAAA,KAAK,IAAI,SAAT,gBAAAA,EAAe,WAAU,QAAQ,IACpE,KAAK,WAAW,YAAY,0BAC5B,KAAK,WAAW,MAAM,UAAU,MAEhC,KAAK,WAAW,MAAM,UAAU,QAI9BwC,KAAchB,EAAM,SAAA,EAAW,SAAS,GAAG;AAC7C,YAAMiB,IAAOpE,EAAG,OAAO,qBAAqB,OAAK+B,IAAA,KAAK,IAAI,SAAT,gBAAAA,EAAe,aAAY,oBAAoB,IAAI;AACpG,WAAK,OAAO,OAAOqC,CAAI,GACvB,KAAK,UAAU,MAAM,UAAU;AAAA,IACjC;AACE,WAAK,UAAU,MAAM,UAAU;AAAA,EAEnC;AAAA,EAEA,cAAcC,GAA0DC,GAAwB;AAC9F,QAAID,MAAW,QAAQ;AAAE,WAAK,WAAW,MAAM,UAAU;AAAQ;AAAA,IAAO;AACxE,SAAK,WAAW,MAAM,UAAU;AAIhC,UAAME,IAAQF,MAAW;AACzB,SAAK,WAAW,YAAY,kBAAkBE,IAAQ,SAASF,MAAW,iBAAiB,UAAU,EAAE,IACvG,KAAK,WAAW,cAAcE,IAC1B,KAAKD,KAAW,kBAAkB,KAClCD,MAAW,iBAAkBC,KAAW,oBAAqB;AAAA,EACnE;AAAA,EAEQ,kBAAkBxD,GAAyD;;AACjF,SAAK,eAAe,IACpB,KAAK,aAAa,gBAAA,GAClB,KAAK,aAAa,OAAOd,EAAG,OAAO,qBAAqBc,EAAI,SAAS,kBAAkB,CAAC;AACxF,UAAM0D,IAAOxE,EAAG,OAAO,kBAAkB,GACnCyE,IAAwE,CAAA;AAC9E,eAAWC,KAAK5D,EAAI,UAAU,CAAC,QAAQ,OAAO,GAAG;AAC/C,YAAM6D,IAAM3E,EAAG,SAAS,mBAAmB;AAC3C,MAAA2E,EAAI,OAAOD,MAAM,UAAU,UAAUA,MAAM,UAAU,QAAQ,QAC7DC,EAAI,cAAcD,MAAM,SAAS,cAAcA,MAAM,UAAU,eAAe,qBAC9ED,EAAOC,CAAC,IAAIC,GACZH,EAAK,OAAOG,CAAG;AAAA,IACjB;AACA,QAAIC,IAAqC;AACzC,SAAInE,IAAAK,EAAI,WAAJ,QAAAL,EAAY,QAAQ;AACtB,MAAAmE,IAAW5E,EAAG,UAAU,MAAS;AACjC,YAAM6E,IAAK,SAAS,cAAc,QAAQ;AAAG,MAAAA,EAAG,QAAQ,IAAIA,EAAG,cAAc,uBAAuBD,EAAS,OAAOC,CAAE;AACtH,iBAAWC,KAAKhE,EAAI,QAAQ;AAAE,cAAMiE,IAAI,SAAS,cAAc,QAAQ;AAAG,QAAAA,EAAE,QAAQD,GAAGC,EAAE,cAAcD,GAAGF,EAAS,OAAOG,CAAC;AAAA,MAAE;AAC7H,MAAAP,EAAK,OAAOI,CAAQ;AAAA,IACtB;AACA,QAAII,IAAsC,MACtCC,IAAsC;AAC1C,QAAInE,EAAI,gBAAgB;AACtB,YAAMoE,IAAMlF,EAAG,SAAS,gBAAgB;AACxC,MAAAgF,IAAa,SAAS,cAAc,OAAO,GAAGA,EAAW,OAAO,YAChEE,EAAI,OAAOF,GAAY,SAAS,eAAe,wBAAwB,CAAC,GACxER,EAAK,OAAOU,CAAG,GACVT,EAAO,UACVQ,IAAajF,EAAG,SAAS,mBAAmB,GAC5CiF,EAAW,OAAO,OAAOA,EAAW,cAAc,6BAA6BA,EAAW,MAAM,UAAU,QAC1GD,EAAW,iBAAiB,UAAU,MAAMC,EAAY,MAAM,YAAY,WAAWD,EAAY,UAAU,UAAU,MAAM,CAAC,GAC5HR,EAAK,OAAOS,CAAU;AAAA,IAE1B;AACA,UAAME,IAASnF,EAAG,UAAU,sBAAsB,YAAY;AAC9D,IAAAmF,EAAO,OAAO,UACdA,EAAO,iBAAiB,SAAS,MAAM;;AACrC,YAAMC,KAAQ3E,IAAAgE,EAAO,UAAP,gBAAAhE,EAAc,MAAM;AAClC,UAAIgE,EAAO,UAAU,CAACW,KAAS,CAAC,6BAA6B,KAAKA,CAAK,IAAI;AAAE,QAAAX,EAAO,MAAM,MAAA;AAAS;AAAA,MAAO;AAC1G,YAAMY,IAAW,CAAC,EAACL,KAAA,QAAAA,EAAY,UACzBM,OAASlE,IAAAqD,EAAO,UAAP,gBAAArD,EAAc,WAAS6D,KAAA,gBAAAA,EAAY,UAAS,IAAI,KAAA;AAC/D,UAAII,KAAY,CAACC,GAAO;AAAE,SAAC/D,IAAAkD,EAAO,SAASQ,MAAhB,QAAA1D,EAA6B;AAAS;AAAA,MAAO;AACxE,UAAIqD,OAAYpD,IAAAV,EAAI,WAAJ,QAAAU,EAAY,WAAU,CAACoD,EAAS,OAAO;AAAE,QAAAA,EAAS,MAAA;AAAS;AAAA,MAAO;AAClF,MAAAO,EAAO,WAAW,IAClB,KAAK,gBAAA,IACLxD,KAAAD,IAAA,KAAK,GAAE,cAAP,QAAAC,EAAA,KAAAD,GAAmB;AAAA,QACjB,IAAID,IAAAgD,EAAO,SAAP,QAAAhD,EAAa,MAAM,SAAS,EAAE,MAAMgD,EAAO,KAAK,MAAM,KAAA,EAAK,IAAM,CAAA;AAAA,QACrE,GAAIW,IAAQ,EAAE,OAAAA,EAAA,IAAU,CAAA;AAAA,QACxB,GAAIE,IAAQ,EAAE,OAAAA,EAAA,IAAU,CAAA;AAAA,QACxB,GAAIV,KAAA,QAAAA,EAAU,QAAQ,EAAE,OAAOA,EAAS,MAAA,IAAU,CAAA;AAAA,QAClD,GAAIS,IAAW,EAAE,UAAU,OAAS,CAAA;AAAA,MAAC;AAAA,IAEzC,CAAC,GACDb,EAAK,OAAOW,CAAM,GAClB,KAAK,aAAa,OAAOX,CAAI;AAAA,EAC/B;AAAA;AAAA,EAGA,kBAAwB;AACtB,SAAK,cAAc,IACnB,KAAK,aAAa,MAAM,UAAU,QAC9B,KAAK,YAAU,KAAK,OAAO,KAAK,QAAQ;AAAA,EAC9C;AAAA;AAAA,EAGA,eAAee,GAAiE;AAC9E,QAAI,CAACA,EAAS,OAAQ,QAAO,KAAK,eAAA;AAClC,SAAK,aAAa,gBAAA,GAClB,KAAK,aAAa,OAAOvF,EAAG,OAAO,oBAAoB,iCAAiC,CAAC;AACzF,eAAWwF,KAAKD,EAAS,MAAM,GAAG,CAAC,GAAG;AACpC,YAAME,IAAOzF,EAAG,UAAU,kBAAkB;AAC5C,MAAAyF,EAAK,OAAOzF,EAAG,OAAO,iBAAiBwF,EAAE,KAAK,GAAGxF,EAAG,OAAO,iBAAiBwF,EAAE,MAAM,CAAC,GACrFC,EAAK,iBAAiB,SAAS,MAAMA,EAAK,UAAU,OAAO,MAAM,CAAC,GAClE,KAAK,aAAa,OAAOA,CAAI;AAAA,IAC/B;AACA,SAAK,aAAa,MAAM,UAAU;AAAA,EACpC;AAAA,EAEA,iBAAuB;AACrB,SAAK,aAAa,MAAM,UAAU,QAClC,KAAK,aAAa,gBAAA;AAAA,EACpB;AAAA,EAEQ,iBAAuB;AAC7B,SAAK,UAAU,gBAAA,GACf,KAAK,UAAU,OAAOzF,EAAG,OAAO,kBAAkB,gBAAgB,CAAC;AACnE,UAAM0F,IAAQ1F,EAAG,OAAO,gBAAgB,GAClC2F,IAA4B,CAAA;AAClC,aAAS/B,IAAI,GAAGA,KAAK,GAAGA,KAAK;AAC3B,YAAM9B,IAAI9B,EAAG,UAAU,iBAAiB,GAAG;AAC3C,MAAA8B,EAAE,QAAQ,QAAW,OAAO8B,CAAC,GAC7B9B,EAAE,iBAAiB,cAAc,MAAM6D,EAAK,QAAQ,CAACC,GAAIC,MAAQD,EAAG,UAAU,OAAO,OAAOC,IAAMjC,CAAC,CAAC,CAAC,GACrG9B,EAAE,iBAAiB,cAAc,MAAM6D,EAAK,QAAQ,CAAAC,MAAMA,EAAG,UAAU,OAAO,KAAK,CAAC,CAAC,GACrF9D,EAAE,iBAAiB,SAAS,MAAM;;AAChC,aAAK,gBAAgB,IACrB,KAAK,UAAU,gBAAgB9B,EAAG,OAAO,iBAAiB,mBAAmB4D,CAAC,WAAW,CAAC,IAC1FxC,KAAAX,IAAA,KAAK,GAAE,WAAP,QAAAW,EAAA,KAAAX,GAAgBmD;AAAA,MAClB,CAAC,GACD+B,EAAK,KAAK7D,CAAC,GAAG4D,EAAM,OAAO5D,CAAC;AAAA,IAC9B;AACA,SAAK,UAAU,OAAO4D,CAAK;AAAA,EAC7B;AAAA,EAMQ,iBAAiBvC,GAAwB;AAC/C,QAAI,KAAK,aAAc;AACvB,UAAMvD,IAAIuD,EAAM,SACVrC,IAAM,KAAK,IAAI,SAMfgF,KAAQlG,KAAA,gBAAAA,EAAG,WAASkB,KAAA,gBAAAA,EAAK;AAC/B,QAAI,CAACgF,EAAO;AACZ,SAAK,eAAe,IACpB,KAAK,YAAY,gBAAA,GACjB,KAAK,YAAY,OAAO9F,EAAG,OAAO,qBAAqB8F,CAAK,CAAC,GACzDhF,KAAA,QAAAA,EAAK,YAAU,KAAK,YAAY,OAAOd,EAAG,OAAO,mBAAmBc,EAAI,QAAQ,CAAC;AACrF,UAAMiF,IAAO/F,EAAG,OAAO,UAAU;AACjC,QAAIJ,cAAc,CAACoG,GAAGC,CAAC,KAAK,OAAO,QAAQrG,EAAE,MAAM,EAAG,CAAAmG,EAAK,OAAO/F,EAAG,QAAQ,WAAW,GAAGgG,CAAC,KAAKC,CAAC,EAAE,CAAC;AAAA,QAChG,YAAWnB,MAAKhE,KAAA,gBAAAA,EAAK,SAAQ,CAAA,EAAI,CAAAiF,EAAK,OAAO/F,EAAG,QAAQ,WAAW8E,CAAC,CAAC;AAC1E,IAAIiB,EAAK,WAAW,UAAQ,KAAK,YAAY,OAAOA,CAAI;AACxD,UAAM1B,KAASzE,KAAA,gBAAAA,EAAG,WAASkB,KAAA,gBAAAA,EAAK;AAChC,IAAIuD,MAAU,KAAK,YAAY,cAAcA,GAAQ,KAAK,YAAY,MAAM,UAAU;AAAA,EACxF;AAAA,EAEQ,OAAOmB,GAAsC;AACnD,UAAMU,IAAMlG,EAAG,UAAU,YAAYwF,EAAE,OAAO,GAAGA,EAAE,IAAI,IAAIA,EAAE,KAAK,KAAKA,EAAE,KAAK;AAC9E,WAAAU,EAAI,QAAQ,WAAcV,EAAE,IAC5BU,EAAI,iBAAiB,SAAS,YAAY;;AACxC,MAAIV,EAAE,WAAW,CAAE,MAAM,KAAK,QAAQA,EAAE,KAAK,OACzC/E,IAAA+E,EAAE,UAAF,QAAA/E,EAAS,SAAQ,KAAK,SAAS+E,CAAC,IAC/B,KAAK,EAAE,SAASA,EAAE,EAAE;AAAA,IAC3B,CAAC,GACMU;AAAA,EACT;AAAA;AAAA,EAGQ,QAAQC,GAAiC;AAC/C,WAAO,IAAI,QAAQ,CAACC,MAAY;AAC9B,YAAMC,IAAUrG,EAAG,OAAO,WAAW,GAC/ByF,IAAOzF,EAAG,OAAO,gBAAgB;AACvC,MAAAyF,EAAK,OAAOzF,EAAG,OAAO,mBAAmBmG,CAAK,CAAC,GAC/CV,EAAK,OAAOzF,EAAG,OAAO,kBAAkB,YAAYmG,CAAK,IAAI,CAAC;AAC9D,YAAMjB,IAAMlF,EAAG,OAAO,mBAAmB,GACnCsG,IAAStG,EAAG,UAAU,oBAAoB,QAAQ,GAClDuG,IAAKvG,EAAG,UAAU,gBAAgB,SAAS,GAC3CwG,IAAQ,CAACP,MAAe;AAAE,QAAAI,EAAQ,OAAA,GAAUD,EAAQH,CAAC;AAAA,MAAE;AAC7D,MAAAK,EAAO,iBAAiB,SAAS,MAAME,EAAM,EAAK,CAAC,GACnDD,EAAG,iBAAiB,SAAS,MAAMC,EAAM,EAAI,CAAC,GAC9CH,EAAQ,iBAAiB,SAAS,CAACjE,MAAM;AAAE,QAAIA,EAAE,WAAWiE,KAASG,EAAM,EAAK;AAAA,MAAE,CAAC,GACnFtB,EAAI,OAAOoB,GAAQC,CAAE,GAAGd,EAAK,OAAOP,CAAG,GAAGmB,EAAQ,OAAOZ,CAAI,GAC7D,KAAK,KAAK,OAAOY,CAAO,GACxBE,EAAG,MAAA;AAAA,IACL,CAAC;AAAA,EACH;AAAA;AAAA;AAAA,EAIQ,SAASf,GAAyB;;AACxC,SAAK,SAAS,gBAAA;AACd,UAAMiB,IAAQzG,EAAG,OAAO,UAAU;AAClC,IAAAyG,EAAM,OAAOzG,EAAG,OAAO,kBAAkBwF,EAAE,OAAO,GAAGA,EAAE,IAAI,IAAIA,EAAE,KAAK,KAAKA,EAAE,KAAK,CAAC;AACnF,UAAMf,wBAAa,IAAA;AACnB,eAAWC,KAAKc,EAAE,SAAS,CAAA,GAAI;AAC7B,YAAMN,IAAMlF,EAAG,SAAS,cAAc;AACtC,UADyCkF,EAAI,OAAOlF,EAAG,QAAQ,gBAAgB0E,EAAE,KAAK,CAAC,GACnFA,EAAE,SAAS,cAAYjE,IAAAiE,EAAE,YAAF,QAAAjE,EAAW,SAAQ;AAC5C,cAAMiG,IAAM1G,EAAG,UAAU,gBAAgB;AACzC,QAAK0E,EAAE,YAAUgC,EAAI,OAAO1G,EAAG,UAAU,QAAW,YAAY,CAAC;AACjE,mBAAW2G,KAAOjC,EAAE,SAAS;AAAE,gBAAMK,IAAI/E,EAAG,QAAQ;AAAG,UAAA+E,EAAE,QAAQ4B,GAAK5B,EAAE,cAAc4B,GAAKD,EAAI,OAAO3B,CAAC;AAAA,QAAE;AACzG,QAAIL,EAAE,aAAUgC,EAAI,WAAW,KAC/BxB,EAAI,OAAOwB,CAAG,GACdjC,EAAO,IAAIC,EAAE,MAAMgC,CAAkC;AAAA,MACvD,OAAO;AACL,cAAM/B,IAAM3E,EAAG,SAAS,gBAAgB;AACxC,QAAA2E,EAAI,OAAOD,EAAE,SAAS,WAAW,WAAWA,EAAE,SAAS,SAAS,mBAAmB,QAC/EA,EAAE,aAAUC,EAAI,WAAW,KAC/BO,EAAI,OAAOP,CAAG,GAAGF,EAAO,IAAIC,EAAE,MAAMC,CAAG;AAAA,MACzC;AACA,MAAA8B,EAAM,OAAOvB,CAAG;AAAA,IAClB;AACA,UAAM7B,IAAUrD,EAAG,OAAO,kBAAkB,GACtCsG,IAAStG,EAAG,UAAU,mBAAmB,QAAQ,GACjDmF,IAASnF,EAAG,UAAU,mBAAmB,MAAM;AACrD,IAAAsG,EAAO,iBAAiB,SAAS,MAAM,KAAK,SAAS,iBAAiB,GACtEnB,EAAO,iBAAiB,SAAS,MAAM;AACrC,YAAMyB,IAA+B,CAAA;AACrC,iBAAW,CAAC1D,GAAMyB,CAAG,KAAKF,GAAQ;AAChC,YAAIE,EAAI,YAAY,CAACA,EAAI,OAAO;AAAE,UAAAA,EAAI,MAAM,cAAc;AAAW;AAAA,QAAO;AAC5E,QAAAiC,EAAI1D,CAAI,IAAIyB,EAAI,SAAS,WAAW,OAAOA,EAAI,KAAK,IAAIA,EAAI;AAAA,MAC9D;AACA,WAAK,SAAS,gBAAA,GACd,KAAK,EAAE,SAASa,EAAE,IAAIoB,CAAG;AAAA,IAC3B,CAAC,GACDvD,EAAQ,OAAOiD,GAAQnB,CAAM,GAAGsB,EAAM,OAAOpD,CAAO,GACpD,KAAK,SAAS,OAAOoD,CAAK,IAC1BrF,IAAAqD,EAAO,OAAA,EAAS,KAAA,EAAO,UAAvB,QAAArD,EAA8B;AAAA,EAChC;AAAA,EAEQ,UAAUe,GAAkBgB,GAAkBU,IAAY,IAAOgD,IAAS,IAAoB;;AACpG,QAAI1E,EAAE,eAAe,UAAU;AAC7B,YAAM2E,IAAM9G,EAAG,OAAO,SAAS;AAAG,aAAA8G,EAAI,cAAc3E,EAAE,YAAY,oBAAoB5B,GAAY4B,EAAE,OAAO,GAAU2E;AAAA,IACvH;AACA,UAAMC,IAAO5E,EAAE,aAAa,KAAK,IAC3B6E,IAAS,CAAC,CAAC7E,EAAE,UACb+C,IAAMlF,EAAG,OAAO,WAAWgH,IAAS,kBAAkBD,IAAO,SAAS,QAAQ,IAAI5E,EAAE,eAAe,QAAQ,YAAY,EAAE,EAAE,GAC3H8E,IAAMjH,EAAG,KAAK;AACpB,QAAI6D,GAAW;AACb,YAAMqD,IAAM/E,EAAE,eAAe,UACxB1B,IAAA,KAAK,IAAI,SAAT,gBAAAA,EAAe,gBAAe,mBAC9BW,IAAA,KAAK,IAAI,YAAT,gBAAAA,EAAkB,iBAAcG,IAAA,KAAK,IAAI,YAAT,gBAAAA,EAAkB,UAAS;AAChE,MAAA0F,EAAI,OAAOjH,EAAG,OAAO,kBAAkBkH,CAAG,CAAC;AAAA,IAC7C;AACA,UAAMC,IAAanH,EAAG,OAAO,iBAAiB;AAE9C,QAAImC,EAAE,WAAW;AACf,YAAMiF,IAAWpH,EAAG,OAAO,gBAAgB,yBAAyB;AACpE,MAAAoH,EAAS,MAAM,UAAU,2EACzBH,EAAI,OAAOG,CAAQ;AAAA,IACrB;AACA,UAAMrD,IAAS/D,EAAG,OAAO,YAAY;AACrC,QAAIqH,IAAwB;AAC5B,QAAIlF,EAAE,UAAW,CAAA4B,EAAO,OAAO/D,EAAG,QAAQ,eAAe,iBAAiB,CAAC;AAAA,aAClEmC,EAAE,QAAQ,SAAS,cAAc;AACxC,YAAM3B,IAAI2B,EAAE;AACZ,WAAIX,IAAAhB,EAAE,SAAF,QAAAgB,EAAQ,WAAW,WAAW;AAChC,cAAM8F,IAAM,SAAS,cAAc,KAAK;AACxC,QAAAA,EAAI,MAAM9G,EAAE,KAAK8G,EAAI,MAAM9G,EAAE,QAAQ,SACrC8G,EAAI,MAAM,UAAU,oFACpBA,EAAI,iBAAiB,SAAS,MAAM,OAAO,KAAK9G,EAAE,KAAK,QAAQ,CAAC,GAChEuD,EAAO,OAAOuD,CAAG;AAAA,MACnB,YAAW7F,IAAAjB,EAAE,SAAF,QAAAiB,EAAQ,WAAW,WAAW;AACvC,cAAM8F,IAAQ,SAAS,cAAc,OAAO;AAC5C,QAAAA,EAAM,MAAM/G,EAAE,KAAK+G,EAAM,WAAW,IACpCA,EAAM,MAAM,UAAU,qEACtBxD,EAAO,OAAOwD,CAAK;AAAA,MACrB,OAAO;AACL,cAAM/B,IAAI,SAAS,cAAc,GAAG;AACpC,QAAAA,EAAE,OAAOhF,EAAE,KAAKgF,EAAE,SAAS,UAAUA,EAAE,MAAM,YAC7CA,EAAE,MAAM,UAAU,8EAClBA,EAAE,OAAOxF,EAAG,QAAQ,QAAW,IAAI,GAAGA,EAAG,QAAQ,QAAWQ,EAAE,QAAQ,MAAM,CAAC,GAC7EuD,EAAO,OAAOyB,CAAC;AAAA,MACjB;AAAA,IACF,WACMrD,EAAE,QAAQ,SAAS,eAAe;AACpC,YAAMqF,IAAKrF,EAAE,SACPsD,IAAOzF,EAAG,OAAO,UAAU;AACjC,MAAAyF,EAAK,OAAOzF,EAAG,OAAO,kBAAkB,MAAawH,EAAG,KAAK,EAAE,CAAC,GAChE/B,EAAK,OAAOzF,EAAG,OAAO,iBAAiB,IAAI,KAAKwH,EAAG,QAAQ,EAAE,mBAAmB,QAAa,IAAI,KAAKA,EAAG,MAAM,EAAE,mBAAA,CAAoB,CAAC,GAClIA,EAAG,YAAU/B,EAAK,OAAOzF,EAAG,OAAO,gBAAgB,MAAawH,EAAG,QAAQ,EAAE,CAAC,GAC9EA,EAAG,eAAa/B,EAAK,OAAOzF,EAAG,OAAO,iBAAiBwH,EAAG,WAAW,CAAC;AAC1E,YAAMC,IAAQzH,EAAG,OAAO,gBAAgB,GAClC0H,IAAQ,SAAS,cAAc,GAAG;AAAG,MAAAA,EAAM,OAAOF,EAAG,WAAWE,EAAM,SAAS,UAAUA,EAAM,MAAM,YAAYA,EAAM,YAAY,gBAAgBA,EAAM,cAAc;AAC7K,YAAMC,IAAQ,SAAS,cAAc,GAAG;AAAG,MAAAA,EAAM,OAAOH,EAAG,SAASG,EAAM,WAAW,GAAGH,EAAG,KAAK,QAAQG,EAAM,YAAY,iCAAiCA,EAAM,cAAc,mBAC/KF,EAAM,OAAOC,GAAOC,CAAK,GAAGlC,EAAK,OAAOgC,CAAK,GAAG1D,EAAO,OAAO0B,CAAI;AAAA,IACpE;AACE,MAAA4B,IAAW,SAAS,eAAe9G,GAAY4B,EAAE,OAAO,CAAC,GACzD4B,EAAO,OAAOsD,CAAQ,GAClBlF,EAAE,YAAU4B,EAAO,OAAO/D,EAAG,QAAQ,cAAc,UAAU,CAAC;AAStE,QANAmH,EAAW,OAAOpD,CAAM,GAMpB8C,KAAU,CAAC1E,EAAE,aAAaA,EAAE,QAAQ,SAAS,YAAUT,IAAAS,EAAE,QAAQ,iBAAV,QAAAT,EAAwB,SAAQ;AACzF,YAAMkG,IAAQ5H,EAAG,OAAO,kBAAkB;AAC1C,MAAA4H,EAAM,MAAM,UAAU;AACtB,iBAAWzB,KAAShE,EAAE,QAAQ,cAAc;AAC1C,cAAML,IAAI9B,EAAG,UAAU,mBAAmBmG,CAAK;AAC/C,QAAArE,EAAE,OAAO,UACTA,EAAE,MAAM,UAAU,6KAClBA,EAAE,iBAAiB,SAAS,MAAM;AAChC,UAAA8F,EAAM,iBAAiB,QAAQ,EAAE,QAAQ,CAAAC,MAAOA,EAAwB,WAAW,EAAK,GACxFD,EAAM,MAAM,UAAU,OACtB,KAAK,EAAE,OAAOzB,CAAK;AAAA,QACrB,CAAC,GACDyB,EAAM,OAAO9F,CAAC;AAAA,MAChB;AACA,MAAAqF,EAAW,OAAOS,CAAK;AAAA,IACzB;AAKA,QAAI,CAACb,KAAQ,CAACC,KAAU,KAAK,EAAE,eAAe7E,EAAE,QAAQ,SAAS,UAAU,CAACA,EAAE,aAAaA,EAAE,MAAM,KAAKkF,GAAU;AAChH,YAAMS,IAAW3F,EAAE,QAAQ;AAC3B,UAAI2F,EAAS,QAAQ;AACnB,cAAMC,IAAe/H,EAAG,UAAU,qBAAqB,IAAI;AAC3D,QAAA+H,EAAa,OAAO,UACpBA,EAAa,QAAQ;AACrB,cAAMC,IAAYhI,EAAG,OAAO,iBAAiB;AAC7C,QAAAgI,EAAU,MAAM,UAAU,QAC1BjE,EAAO,OAAOiE,CAAS;AAEvB,cAAMC,IAAW,CAAC9H,MAAuB;AACvC,UAAA6H,EAAU,cAAc7H,GACxB6H,EAAU,MAAM,UAAU,IAC1B,KAAK,mBAAmB,IAAI7F,EAAE,EAAE,GAChC4F,EAAa,cAAc,KAC3BA,EAAa,QAAQ;AAAA,QACvB,GACMG,IAAW,MAAY;AAC3B,UAAAF,EAAU,MAAM,UAAU,QAC1B,KAAK,mBAAmB,OAAO7F,EAAE,EAAE,GACnC4F,EAAa,cAAc,MAC3BA,EAAa,QAAQ;AAAA,QACvB;AAEA,QAAAA,EAAa,iBAAiB,SAAS,CAAC3F,MAAM;AAE5C,cADAA,EAAE,gBAAA,GACE,KAAK,mBAAmB,IAAID,EAAE,EAAE,GAAG;AAAE,YAAA+F,EAAA;AAAY;AAAA,UAAO;AAC5D,gBAAMC,IAAS,KAAK,iBAAiB,IAAIhG,EAAE,EAAE;AAC7C,cAAIgG,MAAW,QAAW;AAAE,YAAAF,EAASE,CAAM;AAAG;AAAA,UAAO;AACrD,UAAAJ,EAAa,cAAc,KACtB,KAAK,EAAE,YAAaD,CAAQ,EAAE,KAAK,CAACM,MAAW;AAClD,gBAAIA,MAAW,MAAM;AACnB,cAAAL,EAAa,cAAc,MAC3BA,EAAa,QAAQ,2BACrB,WAAW,MAAM;AAAE,gBAAAA,EAAa,cAAc,MAAMA,EAAa,QAAQ;AAAA,cAAY,GAAG,IAAI;AAC5F;AAAA,YACF;AACA,iBAAK,iBAAiB,IAAI5F,EAAE,IAAIiG,CAAM,GACtCH,EAASG,CAAM;AAAA,UACjB,CAAC;AAAA,QACH,CAAC,GACDjB,EAAW,OAAOY,CAAY;AAG9B,cAAMI,IAAS,KAAK,iBAAiB,IAAIhG,EAAE,EAAE;AAC7C,QAAI,KAAK,mBAAmB,IAAIA,EAAE,EAAE,KAAKgG,MAAW,UAAWF,EAASE,CAAM;AAAA,MAChF;AAAA,IACF;AAEA,QAAIpB,KAAQ,CAAC5E,EAAE,aAAaA,EAAE,MAAM,MAAM,KAAK,EAAE,UAAU,KAAK,EAAE,WAAW;AAC3E,YAAMkG,IAAOrI,EAAG,OAAO,cAAc;AACrC,UAAI,KAAK,EAAE,QAAQ;AACjB,cAAMsI,IAAUtI,EAAG,UAAU,QAAW,IAAI;AAC5C,QAAAsI,EAAQ,QAAQ,QAChBA,EAAQ,iBAAiB,SAAS,CAAClG,MAAM;AACvC,UAAAA,EAAE,gBAAA;AAEF,gBAAM0F,IAAWvH,GAAY4B,EAAE,OAAO,GAChCQ,IAAK,SAAS,cAAc,UAAU;AAC5C,UAAAA,EAAG,QAAQmF,GACXnF,EAAG,OAAO,KAAK,IAAI,GAAG,KAAK,KAAKmF,EAAS,SAAS,EAAE,IAAI,CAAC,GACzDnF,EAAG,MAAM,UAAU;AACnB,gBAAM4F,IAAUvI,EAAG,UAAU,mBAAmB,MAAM;AACtD,UAAAuI,EAAQ,MAAM,UAAU;AACxB,gBAAMC,IAAYxI,EAAG,UAAU,mBAAmB,QAAQ;AAC1D,UAAAwI,EAAU,MAAM,UAAU;AAC1B,gBAAMC,IAASzI,EAAG,KAAK;AAAG,UAAAyI,EAAO,MAAM,UAAU,iDACjDA,EAAO,OAAOD,GAAWD,CAAO;AAChC,gBAAMG,IAAY1I,EAAG,KAAK;AAAG,UAAA0I,EAAU,OAAO/F,GAAI8F,CAAM,GACxD1E,EAAO,gBAAgB2E,CAAS,GAChC/F,EAAG,MAAA,GAASA,EAAG,OAAA;AACf,gBAAMgG,IAAU,MAAM5E,EAAO,gBAAgBsD,KAAY,SAAS,eAAeS,CAAQ,CAAC;AAC1F,UAAAU,EAAU,iBAAiB,SAASG,CAAO,GAC3CJ,EAAQ,iBAAiB,SAAS,MAAM;AACtC,kBAAMK,IAAUjG,EAAG,MAAM,KAAA;AACzB,YAAIiG,KAAWA,MAAYd,KAAY,KAAK,EAAE,OAAQ3F,EAAE,IAAIyG,CAAO,GAAGD,EAAA;AAAA,UAExE,CAAC,GACDhG,EAAG,iBAAiB,WAAW,CAACkG,MAAO;AACrC,YAAIA,EAAG,QAAQ,WAAW,CAACA,EAAG,aAAYA,EAAG,eAAA,GAAkBN,EAAQ,MAAA,IACnEM,EAAG,QAAQ,YAAUF,EAAA;AAAA,UAC3B,CAAC;AAAA,QACH,CAAC,GACDN,EAAK,OAAOC,CAAO;AAAA,MACrB;AACA,UAAI,KAAK,EAAE,UAAU;AACnB,cAAMQ,IAAS9I,EAAG,UAAU,OAAO,IAAI;AACvC,QAAA8I,EAAO,QAAQ,UACfA,EAAO,iBAAiB,SAAS,CAAC1G,MAAM;AAAE,UAAAA,EAAE,gBAAA,GAAmB,KAAK,EAAE,SAAUD,EAAE,EAAE;AAAA,QAAE,CAAC,GACvFkG,EAAK,OAAOS,CAAM;AAAA,MACpB;AACA,MAAA3B,EAAW,OAAOkB,CAAI;AAAA,IACxB;AAKA,QAJApB,EAAI,OAAOE,CAAU,GAIjBhF,EAAE,eAAe,SAAS,CAACA,EAAE,aAAaA,EAAE,MAAM,KAAKA,EAAE,QAAQ,SAAS,QAAQ;AACpF,YAAM4G,IAAQ5G,EAAE,QAAQ;AACxB,UAAI4G,KAAA,QAAAA,EAAO,QAAQ;AACjB,cAAMC,IAAMhJ,EAAG,OAAO,WAAW;AACjC,QAAAgJ,EAAI,MAAM,UAAU,2DACpBA,EAAI,cAAc,KAAGrH,IAAA,KAAK,IAAI,SAAT,gBAAAA,EAAe,YAAW,SAAS,KAAKoH,EAAM,IAAI,OAAKvI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC,IAChGyG,EAAI,OAAO+B,CAAG;AAAA,MAChB;AACA,UAAI,KAAK,EAAE,SAAS;AAClB,cAAMC,IAAKjJ,EAAG,OAAO,kBAAkB;AACvC,QAAAiJ,EAAG,MAAM,UAAU;AACnB,cAAMC,IAAK,CAACC,GAAehD,MAAkB;AAC3C,gBAAMrE,IAAI9B,EAAG,UAAU,cAAcmJ,CAAK;AAC1C,UAAArH,EAAE,OAAO,UAAUA,EAAE,QAAQqE,GAC7BrE,EAAE,MAAM,UAAU;AAClB,gBAAMsH,IAAU;;AAAM,oBAAC,GAAChI,KAAAX,IAAA0B,EAAE,cAAF,gBAAA1B,EAAc0I,OAAd,QAAA/H,EAAsB,SAAS,KAAK;AAAA;AAC5D,iBAAIgI,EAAA,MAAWtH,EAAE,MAAM,UAAU,MACjCA,EAAE,iBAAiB,SAAS,MAAM;;AAAE,aAAAV,KAAAX,IAAA,KAAK,GAAE,YAAP,QAAAW,EAAA,KAAAX,GAAiB0B,EAAE,IAAIgH,GAAOC,MAAYtH,EAAE,MAAM,UAAU;AAAA,UAAI,CAAC,GAC9FA;AAAA,QACT;AACA,QAAAmH,EAAG,OAAOC,EAAG,QAAMnH,IAAA,KAAK,IAAI,SAAT,gBAAAA,EAAe,YAAW,SAAS,GAAGmH,EAAG,QAAMhH,IAAA,KAAK,IAAI,SAAT,gBAAAA,EAAe,eAAc,aAAa,CAAC,GAC7G+E,EAAI,OAAOgC,CAAE;AAAA,MACf;AAAA,IACF;AAEA,QAAI,KAAK,EAAE,WAAW,CAAC9G,EAAE,aAAaA,EAAE,MAAM,GAAG;AAC/C,YAAMkH,IAAYrJ,EAAG,OAAO,gBAAgB,GACtCsJ,IAAWtJ,EAAG,OAAO,WAAW;AAEtC,UAAImC,EAAE,aAAa,OAAO,KAAKA,EAAE,SAAS,EAAE;AAC1C,mBAAW,CAACgH,GAAOI,CAAK,KAAK,OAAO,QAAQpH,EAAE,SAAS,GAAG;AACxD,gBAAMqH,IAAOxJ,EAAG,UAAU,iBAAkBuJ,EAAmB,SAAS,KAAK,EAAE,IAAI,UAAU,EAAE,IAAI,GAAGJ,CAAK,IAAKI,EAAmB,MAAM,EAAE;AAC3I,UAAAC,EAAK,iBAAiB,SAAS,MAAA;;AAAM,oBAAApI,KAAAX,IAAA,KAAK,GAAE,YAAP,gBAAAW,EAAA,KAAAX,GAAiB0B,EAAE,IAAIgH,GAAQI,EAAmB,SAAS,KAAK,EAAE;AAAA,WAAE,GACzGD,EAAS,OAAOE,CAAI;AAAA,QACtB;AAGF,YAAMC,IAASzJ,EAAG,UAAU,iBAAiB,GAAG,GAC1C0J,IAAS1J,EAAG,OAAO,kBAAkB;AAC3C,iBAAWmJ,KAAS3J,IAAiB;AACnC,cAAMmK,IAAK3J,EAAG,UAAU,QAAWmJ,CAAK;AACxC,QAAAQ,EAAG,iBAAiB,SAAS,CAACvH,MAAM;;AAClC,UAAAA,EAAE,gBAAA;AACF,gBAAMwH,KAAiBxI,KAAAX,IAAA0B,EAAE,cAAF,gBAAA1B,EAAc0I,OAAd,gBAAA/H,EAAsB,SAAS,KAAK;AAC3D,WAAAI,KAAAD,IAAA,KAAK,GAAE,YAAP,QAAAC,EAAA,KAAAD,GAAiBY,EAAE,IAAIgH,GAAO,CAAC,CAACS,IAChCF,EAAO,MAAM,UAAU;AAAA,QACzB,CAAC,GACDA,EAAO,OAAOC,CAAE;AAAA,MAClB;AACA,MAAAD,EAAO,MAAM,UAAU,QACvBD,EAAO,iBAAiB,SAAS,CAACrH,MAAM;AACtC,QAAAA,EAAE,gBAAA;AACF,cAAMyH,IAAUH,EAAO,MAAM,YAAY;AACzC,QAAAA,EAAO,MAAM,UAAUG,IAAU,SAAS,QAItCA,KAAS,SAAS,iBAAiB,SAAS,MAAM;AAAE,UAAAH,EAAO,MAAM,UAAU;AAAA,QAAO,GAAG,EAAE,MAAM,IAAM;AAAA,MACzG,CAAC,GACDJ,EAAS,OAAOG,CAAM,GACtBJ,EAAU,OAAOC,GAAUI,CAAM,GACjCzC,EAAI,OAAOoC,CAAS;AAAA,IACtB,MAAA,CAAWlH,EAAE,aAAa,OAAO,KAAKA,EAAE,SAAS,EAAE,UACjD8E,EAAI,OAAOjH,EAAG,OAAO,aAAa,OAAO,QAAQmC,EAAE,SAAS,EAAE,IAAI,CAAC,CAACC,GAAG0H,CAAC,MAAM,GAAG1H,CAAC,GAAI0H,EAAe,MAAM,EAAE,EAAE,KAAK,GAAG,CAAC,CAAC;AAE3H,UAAMC,IAAO/J,EAAG,OAAO,qBAAqBK,GAAQ8B,EAAE,EAAE,CAAC;AACzD,QAAI4E,KAAQ5E,EAAE,QAAQ;AACpB,YAAM2C,IAAI9E,EAAG,QAAQ,WAAWmC,EAAE,WAAW,SAAS,UAAUA,EAAE,WAAW,cAAc,eAAe,EAAE,IAAI6H,GAAK7H,EAAE,MAAM,CAAC;AAC9H,MAAA4H,EAAK,OAAOjF,CAAC;AAAA,IACf;AAEA,WAAIiC,KAAQ5E,EAAE,MAAM,KAAKgB,EAAM,oBAAoBhB,EAAE,OACnD4H,EAAK,OAAO/J,EAAG,QAAQ,YAAY,SAAS,CAAC,GAE/CiH,EAAI,OAAO8C,CAAI,GACf7E,EAAI,OAAO+B,CAAG,GACP/B;AAAA,EACT;AACF;AAEA,SAAS8E,GAAKpK,GAAiD;AAC7D,UAAQA,GAAA;AAAA,IACN,KAAK;AAAa,aAAO;AAAA,IACzB,KAAK;AAAa,aAAO;AAAA,IACzB,KAAK;AAAa,aAAO;AAAA,IACzB;AAAkB,aAAO;AAAA,EAAA;AAE7B;AC32BA,MAAMqK,wBAAgB,QAAA,GAMhBC,yBAAwB,IAAA;AAC9B,SAASC,GAAaC,GAA4B;AAChD,SAAO,mBAAmBA,EAAK,SAAS,KAAKA,EAAK,aAAa,EAAE;AACnE;AAIA,IAAIC,IAAiC;AACrC,SAASC,KAAuC;AAC9C,MAAID,KAAaA,EAAU,UAAU,SAAU,QAAOA;AACtD,MAAI;AAAE,WAAAA,IAAY,IAAI,aAAA,GAAuBA;AAAA,EAAU,QAAQ;AAAE,WAAO;AAAA,EAAK;AAC/E;AAGO,SAASE,GAAQvK,GAAmB;;AACzC,GAAAS,IAAAwJ,EAAU,IAAIjK,CAAE,MAAhB,QAAAS,EAAmB,SACnBwJ,EAAU,OAAOjK,CAAE;AACrB;AAMA,SAASwK,GAAWJ,GAAkC;AACpD,QAAMpK,IAAKoK,EAAK;AAChB,MAAIK,IAA4B,MAC5BC,IAA8B,MAC9BC,IAAS;AAEb,QAAMC,IAAQ,MAAM;AAAE,IAAAH,KAAA,QAAAA,EAAM,SAASA,IAAO,MAAMC,KAAA,QAAAA,EAAM,SAASA,IAAO,MAAM1K,EAAG,YAAY;AAAA,EAAG;AAEhG,WAAS6K,IAAW;AAClB,IAAIF,MACJC,EAAA,GACAF,IAAOI,GAAc;AAAA,MACnB,IAAA9K;AAAA,MAAI,KAAKoK,EAAK;AAAA,MACd,GAAIA,EAAK,SAAS,EAAE,QAAQA,EAAK,OAAA,IAAW,CAAA;AAAA,MAC5C,GAAIA,EAAK,YAAY,EAAE,WAAWA,EAAK,UAAA,IAAc,CAAA;AAAA,MACrD,OAAOA,EAAK,cAAc;AAAA,MAC1B,GAAIA,EAAK,QAAQ,EAAE,OAAOA,EAAK,MAAA,IAAU,CAAA;AAAA,MACzC,GAAIA,EAAK,SAAS,EAAE,QAAQA,EAAK,OAAA,IAAW,CAAA;AAAA,MAC5C,GAAIA,EAAK,SAAS,EAAE,QAAQA,EAAK,OAAA,IAAW,CAAA;AAAA,MAC5C,GAAIA,EAAK,QAAQ,EAAE,OAAOA,EAAK,MAAA,IAAU,CAAA;AAAA,MACzC,GAAIA,EAAK,YAAY,KAAQ,EAAE,SAAS,GAAA,IAAU,CAAA;AAAA,MAClD,UAAU,CAACW,MAAyBC,EAASD,CAAK;AAAA,IAAA,CACnD;AAAA,EACH;AAEA,WAASC,EAASD,GAAsB;AACtC,QAAIJ,EAAQ;AACZ,IAAAC,EAAA;AAEA,UAAM,EAAE,WAAWK,GAAI,MAAMvI,GAAI,QAAQwI,GAAI,GAAGC,EAAA,IAASf;AACzD,IAAAK,IAAOW,GAAM;AAAA,MACX,GAAGD;AAAA,MACH,OAAO;AAAA,MACP,UAAU;AAAA,MACV,IAAAnL;AAAA,MACA,GAAI+K,EAAM,YAAY,EAAE,WAAWA,EAAM,UAAA,IAAc,CAAA;AAAA,MACvD,GAAIA,EAAM,YAAY,EAAE,WAAWA,EAAM,UAAA,IAAc,CAAA;AAAA,MACvD,GAAIA,EAAM,SAAS,YAAYA,EAAM,SAAS,EAAE,MAAM,UAAU,QAAQA,EAAM,OAAA,IAAW,CAAA;AAAA,MACzF,QAAQF;AAAA,IAAA,CACT;AAAA,EACH;AAEA,SAAAA,EAAA,GACO,EAAE,QAAQ;AAAE,IAAAF,IAAS,IAAMC,EAAA;AAAA,EAAQ,EAAA;AAC5C;AAEO,SAASQ,GAAMhB,GAAkC;;AAItD,MAAIA,EAAK,MAAO,QAAOI,GAAWJ,CAAI;AAItC,EAAIH,EAAU,IAAIG,EAAK,EAAE,MACvBH,EAAU,IAAIG,EAAK,EAAE,EAAG,MAAA,GACxBH,EAAU,OAAOG,EAAK,EAAE,IAMtBA,EAAK,cAAU3J,KAAAyJ,GAAkB,IAAIC,GAAaC,CAAI,CAAC,MAAxC,QAAA3J,GAA2C;AAS9D,QAAM4K,IAASC,GAAA;AACf,MAAIC,GACAC,IAAY;AAChB,QAAMC,IAAQrB,EAAK,SAASA,EAAK,UAAUiB,GAGrCK,IAAWD,MAAUJ,IAASA,IAAS,QAIvC,EAAE,OAAAM,GAAO,UAAAC,MAAaC,GAAiBzB,EAAK,KAAKA,EAAK,MAAM;AAClE,MAAIjH,IAAQ,IAAI2I,GAAUL,CAAc;AAGxC,QAAMM,IAAY3B,EAAK,YAAY,GAAGqB,CAAK,KAAKrB,EAAK,SAAS,KAAKqB,GAC7DO,IAAS,IAAIC,GAAiBF,CAAS;AAC7C,MAAIG,GACAC,IAAiB,IAEjBC,IAA8B,MAC9BC,IAAyD,MACzDC,IAAmD,MACnDC,IAAoC;AAIxC,EAAI,CAACnC,EAAK,YAAY,CAACA,EAAK,GAAG,MAAM,UAAUA,EAAK,GAAG,iBAAiB,MACtEA,EAAK,GAAG,MAAM,QAAQA,EAAK,GAAG,MAAM,SAAS,QAC7CA,EAAK,GAAG,MAAM,SAAS;AAMzB,aAAWoC,KAAQR,EAAO,UAAc,cAAcQ,EAAK,aAAaA,EAAK,OAAO;AAGpF,MAAIC,IAAiC,MACjCC,IAAiC,MACjCC,IAAS,GACTC,IAAO,CAACxC,EAAK;AAEjB,MAAIA,EAAK,UAAU;AAEjB,UAAMyC,KADMzC,EAAK,YAAY,gBACT,SAAS,OAAO;AAGpC,IAAAqC,IAAa,SAAS,cAAc,KAAK,GACzCA,EAAW,MAAM,UAAU,kBAAkBI,IAAU,eAAe,WAAW,4EAA4EA,IAAU,aAAa,YAAY;AAGhM,UAAMpG,IAAQ,SAAS,cAAc,KAAK,GAKpCqG,IAAM,OAAO,SAAW,OAAe,OAAO,OAAO,cAAe,aACtE,OAAO,WAAW,oBAAoB,IAAI,MACxCC,IAAmB,CAACC,MAAoB;AAI5C,MAAA5C,EAAK,GAAG,UAAU,OAAO,UAAU4C,CAAM,GACzCvG,EAAM,MAAM,UAAUuG,IAAS;AAAA,QAC7B;AAAA,QAAkB;AAAA,QAAW;AAAA,QAAc;AAAA,QAC3C;AAAA,QAAmB;AAAA,QACnB;AAAA,QAAmB;AAAA,QAAgB;AAAA,QAAyB;AAAA,QAC5D;AAAA,QAA2B;AAAA,QAAa;AAAA,MAAA,EACxC,KAAK,GAAG,IAAI;AAAA,QACZ;AAAA,QAAwC;AAAA,QACxC;AAAA,QAAsB;AAAA,QACtB;AAAA,QACA;AAAA,QAAgB;AAAA,QAAyB;AAAA,QACzC,8BAA8BH,IAAU,UAAU;AAAA,QAClD;AAAA,QAA0C;AAAA,QAAa;AAAA,MAAA,EACvD,KAAK,GAAG;AAAA,IACZ;AACA,IAAAE,GAAiBD,KAAA,gBAAAA,EAAK,YAAW,EAAK;AACtC,UAAMG,IAAa,CAAC7K,MAAiC2K,EAAiB3K,EAAE,OAAO;AAC/E,IAAA0K,KAAA,QAAAA,EAAK,iBAAiB,UAAUG,IAChCb,IAAOU,GAAKT,IAAcY,GAG1B7C,EAAK,GAAG,MAAM,UAAU,0CACxB3D,EAAM,OAAO2D,EAAK,EAAE;AAKpB,UAAM8C,IACJ,0aAGIC,IACJ,4NAGIjH,IAAM,SAAS,cAAc,QAAQ;AAC3C,IAAAA,EAAI,OAAO,UACXA,EAAI,MAAM,UAAU;AAAA,MAClB;AAAA,MACA,cAAckE,EAAK,UAAU,SAAS;AAAA,MACtC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA,EACA,KAAK,GAAG,GACVlE,EAAI,eAAe,MAAM;AAAE,MAAAA,EAAI,MAAM,YAAY;AAAA,IAAc,GAC/DA,EAAI,eAAe,MAAM;AAAE,MAAAA,EAAI,MAAM,YAAY;AAAA,IAAW,GAE5DwG,IAAU,SAAS,cAAc,MAAM,GACvCA,EAAQ,MAAM,UAAU;AAIxB,UAAMU,KAAc,MAAM;AACxB,MAAAlH,EAAI,YAAY0G,IAAOO,IAAYD,GACnChH,EAAI,aAAa,cAAc0G,IAAO,eAAe,WAAW,GAChE1G,EAAI,aAAa,iBAAiB,OAAO0G,CAAI,CAAC,GAC9C1G,EAAI,OAAOwG,CAAQ;AAAA,IACrB;AACA,IAAAU,GAAA,GAEAX,EAAW,OAAOhG,GAAOP,CAAG,GAC5B,SAAS,KAAK,OAAOuG,CAAU;AAO/B,UAAMY,KAAY,yBAAyBjD,EAAK,SAAS,IACnDkD,KAAS,OAAOlD,EAAK,mBAAoB,WAC3C,EAAE,OAAOA,EAAK,oBACdA,EAAK;AACT,QAAImD,IAA+B;AACnC,UAAMC,KAAkB,MAAe;AACrC,UAAI;AAAE,eAAO,eAAe,QAAQH,EAAS,MAAM;AAAA,MAAI,QAAQ;AAAE,eAAO;AAAA,MAAM;AAAA,IAChF,GACMI,KACJ,0aAGIC,KAAc,MAAY;AAC9B,YAAMC,IAAML,MAAA,QAAAA,GAAQ,QAAQA,KAAUnK,EAAM,mBAAmB;AAE/D,UAAI,EADe,CAAC,EAACwK,KAAA,QAAAA,EAAK,UAAS,CAACf,KAAQ,CAACY,GAAA,IAC5B;AAAE,QAAID,MAAYA,EAAS,OAAA,GAAUA,IAAW;AAAO;AAAA,MAAO;AAC/E,UAAIA,EAAU;AACd,MAAAA,IAAW,SAAS,cAAc,KAAK,GACvCA,EAAS,aAAa,QAAQ,QAAQ,GACtCA,EAAS,aAAa,YAAY,GAAG,GACrCA,EAAS,aAAa,cAAcI,EAAK,KAAK,GAC9CJ,EAAS,MAAM,UAAU;AAAA,QACvB;AAAA,QAAmB;AAAA,QAAmB;AAAA,QACtC;AAAA,QAA+C;AAAA,QAC/C;AAAA,QAAqB;AAAA,QAAkB;AAAA,QACvC,cAAcV,IAAU,aAAa,YAAY;AAAA,QACjD;AAAA,MAAA,EACA,KAAK,GAAG;AACV,YAAM/G,KAAQ,SAAS,cAAc,KAAK;AAI1C,UAHAA,GAAM,cAAc6H,EAAK,OACzB7H,GAAM,MAAM,UAAU,8DACtByH,EAAS,OAAOzH,EAAK,GACjB6H,EAAK,UAAU;AACjB,cAAMC,IAAM,SAAS,cAAc,KAAK;AACxC,QAAAA,EAAI,MAAM,UAAU;AACpB,cAAMC,KAAK,SAAS,cAAc,MAAM;AAAG,QAAAA,GAAG,YAAYJ,IAAeI,GAAG,MAAM,UAAU,SAASzD,EAAK,UAAU,SAAS;AAC7H,cAAM0D,KAAK,SAAS,cAAc,MAAM;AAAG,QAAAA,GAAG,cAAcH,EAAK,UACjEC,EAAI,OAAOC,IAAIC,EAAE,GAAGP,EAAS,OAAOK,CAAG;AAAA,MACzC;AAEA,YAAM/F,IAAI,SAAS,cAAc,QAAQ;AACzC,MAAAA,EAAE,OAAO,UACTA,EAAE,aAAa,cAAc,SAAS,GACtCA,EAAE,YAAY,yMACdA,EAAE,MAAM,UAAU,6MAClBA,EAAE,iBAAiB,SAAS,CAACzF,MAAM;AACjC,QAAAA,EAAE,gBAAA;AACF,YAAI;AAAE,yBAAe,QAAQiL,IAAW,GAAG;AAAA,QAAE,QAAQ;AAAA,QAAyD;AAC9G,QAAIE,MAAYA,EAAS,OAAA,GAAUA,IAAW;AAAA,MAChD,CAAC,GACDA,EAAS,OAAO1F,CAAC;AACjB,YAAMkG,KAAiB,MAAY;AACjC,QAAInB,MACJA,IAAO,IAAMoB,GAAU,EAAI,GAAGZ,GAAA,GAC9BT,IAAS,GAAOD,MAASA,EAAQ,MAAM,UAAU,SAC7Ca,MAAYA,EAAS,OAAA,GAAUA,IAAW;AAAA,MAChD;AACA,MAAAA,EAAS,iBAAiB,SAASQ,EAAc,GACjDR,EAAS,iBAAiB,WAAW,CAACnL,MAAM;AAAE,SAAIA,EAAE,QAAQ,WAAWA,EAAE,QAAQ,SAAOA,EAAE,eAAA,GAAkB2L,GAAA;AAAA,MAAmB,CAAC,GAEhItB,EAAY,aAAac,GAAUrH,CAAG;AAAA,IACxC;AAEA,QAAI,CAAC,SAAS,eAAe,kBAAkB,GAAG;AAChD,YAAM4H,IAAK,SAAS,cAAc,OAAO;AAAG,MAAAA,EAAG,KAAK,oBACpDA,EAAG,cAAc,4GACjB,SAAS,KAAK,OAAOA,CAAE;AAAA,IACzB;AACA,IAAAvB,IAAemB,IACfA,GAAA;AAEA,UAAMM,KAAY,CAACC,MAAkB;AACnC,MAAIA,KACFxH,EAAM,MAAM,UAAU,QACtB,sBAAsB,MAAM;AAAE,QAAAA,EAAM,MAAM,UAAU,KAAKA,EAAM,MAAM,YAAY;AAAA,MAAW,CAAC,MAE7FA,EAAM,MAAM,UAAU,KAAKA,EAAM,MAAM,YAAY,cACnD,WAAW,MAAM;AAAE,QAAKmG,MAAMnG,EAAM,MAAM,UAAU;AAAA,MAAO,GAAG,GAAG;AAAA,IAErE;AAEA,IAAAP,EAAI,iBAAiB,SAAS,MAAM;AAIlC,UAHA0G,IAAO,CAACA,GACRoB,GAAUpB,CAAI,GACdQ,GAAA,GACIR,GAAM;AACR,QAAAD,IAAS,GAAOD,MAASA,EAAQ,MAAM,UAAU;AAEjD,YAAI;AAAE,yBAAe,QAAQW,IAAW,GAAG;AAAA,QAAE,QAAQ;AAAA,QAAe;AAAA,MACtE;AACA,MAAAd,KAAA,QAAAA;AAAA,IACF,CAAC,GAKDD,IAAc,CAAClK,MAAqB;AAClC,MAAIA,EAAE,QAAQ,YAAYwK,MAAQA,IAAO,IAAOoB,GAAU,EAAK,GAAGZ,GAAA;AAAA,IACpE,GACA,SAAS,iBAAiB,WAAWd,CAAW;AAAA,EAClD;AAEA,QAAM4B,IAAY,MAAM;AACtB,IAAItB,MACJD,KACID,MAAWA,EAAQ,cAAc,OAAOC,CAAM,GAAGD,EAAQ,MAAM,UAAU;AAAA,EAC/E,GAGMyB,IAAY,MAAM;AACtB,QAAI;AAIF,YAAMC,IAAM9D,GAAA;AACZ,UAAI,CAAC8D,EAAK;AACV,MAAIA,EAAI,UAAU,eAAkBA,EAAI,OAAA,EAAS,MAAM,MAAM;AAAA,MAAC,CAAC;AAC/D,YAAMC,IAAMD,EAAI,iBAAA,GAA0BE,IAAOF,EAAI,WAAA;AACrD,MAAAC,EAAI,QAAQC,CAAI,GAAGA,EAAK,QAAQF,EAAI,WAAW,GAC/CC,EAAI,UAAU,eAAe,KAAKD,EAAI,WAAW,GACjDC,EAAI,UAAU,6BAA6B,KAAKD,EAAI,cAAc,IAAI,GACtEE,EAAK,KAAK,eAAe,KAAKF,EAAI,WAAW,GAC7CE,EAAK,KAAK,6BAA6B,MAAOF,EAAI,cAAc,GAAG,GACnEC,EAAI,MAAA,GAASA,EAAI,KAAKD,EAAI,cAAc,GAAG;AAAA,IAC7C,QAAQ;AAAA,IAA4B;AAAA,EACtC,GAKMG,IAAiG,CAAA,GAEjGC,IAAoB,CAACC,MAAmC;AAC5D,WAAOF,EAAa,UAAQ;AAC1B,YAAM/B,IAAO+B,EAAa,MAAA;AAC1B,MAAAvC,EAAO,IAAI,EAAE,aAAaQ,EAAK,aAAa,SAASA,EAAK,SAAS,IAAI,KAAK,IAAA,EAAI,CAAG,GACnFkC,EAAK,KAAK,EAAE,MAAM,QAAQ,gBAAAD,GAAgB,aAAajC,EAAK,aAAa,SAASA,EAAK,QAAA,CAAS;AAAA,IAClG;AAAA,EACF;AAEA,MAAIkC;AAIJ,QAAMC,IAAgB,IAAIC,GAAW,WAAWxE,EAAK,SAAS,EAAE;AAChE,MAAIyE,IAAa;AAEjB,QAAMC,IAAmD,CAAA,GAEnDC,IAAuD,CAAA;AAC7D,MAAIC,IAAoB;AAExB,QAAMC,IAAa,CAACC,GAAqB/O,MAAuB;AAC9D,IAAKwO,EAAK,SAASxO,CAAI,EAAE,KAAK,CAACgP,MAAY;AACzC,MAAIjD,KAAKwC,EAAK,KAAK,EAAE,MAAM,QAAQ,gBAAgBxC,GAAK,aAAAgD,GAAa,SAAAC,GAAS;AAAA,IAChF,CAAC;AAAA,EACH,GAEMC,IAAiB,CAACF,GAAqB/O,GAAckP,MAAgG;AACzJ,IAAKV,EAAK,SAASxO,GAAMkP,CAAQ,EAAE,KAAK,CAACF,MAAY;AACnD,MAAIjD,KAAKwC,EAAK,KAAK,EAAE,MAAM,QAAQ,gBAAgBxC,GAAK,aAAAgD,GAAa,SAAAC,GAAS;AAAA,IAChF,CAAC;AAAA,EACH,GAEMG,IAAe,MAAY;AAC/B,WAAOR,EAAQ,UAAQ;AAAE,YAAMS,IAAIT,EAAQ,MAAA;AAAU,MAAAG,EAAWM,EAAE,aAAaA,EAAE,IAAI;AAAA,IAAE;AACvF,WAAOR,EAAY,UAAQ;AAAE,YAAMQ,IAAIR,EAAY,MAAA;AAAU,MAAAE,EAAWM,EAAE,aAAaA,EAAE,IAAI;AAAA,IAAE;AAAA,EACjG,GAGMC,KAAe,CAACC,MAA+B;AACnD,IAAAf,EAAK,KAAK,EAAE,MAAM,eAAe,cAAAe,GAAqC;AAAA,EACxE,GAEMC,KAAOtF,EAAK,QAAQ,CAAA,GAEpBuF,KAAa,CAAC,MAAM,MAAM,MAAM,IAAI,GACpCC,KAAc,OAAO,YAAc,OAAe,UAAU,YAAY,IAAI,MAAM,GAAG,CAAC,EAAE,YAAA,IAAgB;AAC9G,EAAID,GAAW,SAASC,EAAW,KAAK,CAACxF,EAAK,GAAG,QAC/CA,EAAK,GAAG,MAAM,OACdA,EAAK,GAAG,MAAM,aAAaA,EAAK,GAAG,MAAM,cAAc;AAIzD,QAAMyF,KAAa,cAAczF,EAAK,SAAS,IAAIqB,EAAM,MAAM,EAAE,CAAC,IAE5DqE,IAAW,IAAIpP,GAAS0J,EAAK,IAAIqB,GAAO;AAAA,IAC5C,OAAOtL,GAAM;AACX,YAAM+O,IAAc,MAAM,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC,IACvDC,IAAwD,EAAE,MAAM,QAAQ,MAAAhP,EAAA;AAC9E,MAAAgD,EAAM,cAAc+L,GAAaC,CAAO,GACxCW,EAAS,OAAO3M,CAAK,GACjBA,EAAM,MAKJwL,EAAK,QACPM,EAAWC,GAAa/O,CAAI,IACnB6O,IACTD,EAAY,KAAK,EAAE,aAAAG,GAAa,MAAA/O,EAAA,CAAM,KAEtC2O,EAAQ,KAAK,EAAE,aAAAI,GAAa,MAAA/O,EAAA,CAAM,GAC9BgD,EAAM,mBAAiBqM,GAAarM,EAAM,eAAe,KAErD+I,KAIVF,EAAO,IAAI,EAAE,aAAAkD,GAAa,SAAAC,GAAS,IAAI,KAAK,IAAA,GAAO,GACnDT,EAAK,KAAK,EAAE,MAAM,QAAQ,gBAAgBxC,GAAK,aAAAgD,GAAa,SAAAC,GAAS,KAHrEZ,EAAa,KAAK,EAAE,aAAAW,GAAa,SAAAC,EAAA,CAAS;AAAA,IAK9C;AAAA,IACA,MAAM,SAASY,GAAY;AACzB,UAAI,CAAC7D,EAAK;AACV,YAAM8D,IAAY,GAAGpE,CAAQ,gBAAgB,mBAAmBmE,EAAK,IAAI,CAAC,IACpEb,IAAc,MAAM,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AAE7D,MAAA/L,EAAM,cAAc+L,GAAa,EAAE,MAAM,QAAQ,MAAM,gBAAgBa,EAAK,IAAI,IAAA,CAAK,GACrFD,EAAS,OAAO3M,CAAK;AACrB,UAAI;AACF,cAAM8M,IAAM,MAAM,MAAMD,GAAW;AAAA,UACjC,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAMR,SAAS,EAAE,gBAAgBD,EAAK,MAAM,eAAe,UAAUtE,CAAK,GAAA;AAAA,UACpE,MAAMsE;AAAA,QAAA,CACP;AACD,YAAI,CAACE,EAAI,GAAI,OAAM,IAAI,MAAM,kBAAkBA,EAAI,MAAM,EAAE;AAC3D,cAAM,EAAE,KAAAC,GAAK,MAAAhN,GAAM,MAAAiN,GAAM,MAAAC,MAAS,MAAMH,EAAI,KAAA;AAU5C,QAAA9M,EAAM,cAAc+L,GAAa,EAAE,MAAM,cAAc,KAAAgB,GAAK,MAAAhN,GAAM,MAAAiN,GAAM,MAAAC,GAAM,GAC9EN,EAAS,OAAO3M,CAAK,GACrBuL,EAAK,KAAK,EAAE,MAAM,QAAQ,gBAAgBxC,GAAK,aAAAgD,GAAa,SAAS,EAAE,MAAM,cAAc,KAAAgB,GAAK,MAAAhN,GAAM,MAAAiN,GAAM,MAAAC,EAAA,GAAQ;AAAA,MACtH,SAAShO,GAAG;AACV,QAAAe,EAAM,cAAc+L,GAAa,EAAE,MAAM,QAAQ,MAAM,qBAAsB9M,EAAY,OAAO,GAAA,CAAI,GACpG0N,EAAS,OAAO3M,CAAK;AAAA,MACvB;AAAA,IACF;AAAA,IACA,SAASkN,GAAU5L,GAAQ;AACzB,MAAKyH,KACLwC,EAAK,KAAK,EAAE,MAAM,UAAU,gBAAgBxC,GAAK,UAAAmE,GAAU,gBAAgB,MAAM,KAAK,SAAS,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC,IAAI,GAAI5L,IAAS,EAAE,QAAAA,EAAA,IAAW,CAAA,EAAC,CAAI;AAAA,IACzJ;AAAA,IACA,SAAS6L,GAAUrN,GAAS;AAAE,MAAIiJ,KAAKwC,EAAK,KAAK,EAAE,MAAM,UAAU,gBAAgBxC,GAAK,UAAAoE,GAAU,GAAIrN,IAAU,EAAE,SAAAA,EAAA,IAAY,CAAA,GAAK;AAAA,IAAE;AAAA,IACrI,UAAUsN,GAAQ;AAEhB,UAAI;AAAE,qBAAa,QAAQV,IAAY,GAAG;AAAA,MAAE,QAAQ;AAAA,MAAqB;AAIzE,MAAAnB,EAAK,KAAK;AAAA,QACR,MAAM;AAAA,QAAQ,WAAWtE,EAAK;AAAA,QAC9B,GAAIA,EAAK,YAAY,EAAE,WAAWA,EAAK,UAAA,IAAuB,CAAA;AAAA,QAC9D,UAAU;AAAA,UACR,GAAImG,EAAO,OAAQ,EAAE,MAAOA,EAAO,KAAA,IAAU,CAAA;AAAA,UAC7C,GAAIA,EAAO,QAAQ,EAAE,OAAOA,EAAO,MAAA,IAAU,CAAA;AAAA,UAC7C,GAAIA,EAAO,SAASA,EAAO,QAAQ,EAAE,MAAM;AAAA,YACzC,GAAIA,EAAO,QAAQ,EAAE,OAAOA,EAAO,MAAA,IAAU,CAAA;AAAA,YAC7C,GAAIA,EAAO,QAAQ,EAAE,OAAOA,EAAO,MAAA,IAAU,CAAA;AAAA,UAAC,MAC1C,CAAA;AAAA,QAAC;AAAA,MACT,CACQ;AAIV,YAAMC,IAAQD,EAAO,WACjB,yBAAyBA,EAAO,QAAQ,KAAKA,EAAO,KAAK,KAAK,EAAE,GAAGA,EAAO,QAAQ,MAAMA,EAAO,KAAK,KAAK,EAAE,KAC3GA,EAAO,QAAQ,UAAUA,EAAO,KAAK,KAAK;AAI9C,MAAIC,KAAStE,KAAO,CAAC/I,EAAM,OACzBuL,EAAK,KAAK,EAAE,MAAM,QAAQ,gBAAgBxC,GAAK,aAAa,MAAM,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC,IAAI,SAAS,EAAE,MAAM,QAAQ,MAAMsE,EAAA,EAAM,CAAG;AAAA,IAEzJ;AAAA,IACA,KAAIpP,KAAAgJ,EAAK,aAAL,gBAAAhJ,GAAe,gBAAe,KAAQ;AAAA,MAC1C,eAAeS,GAAW;AACxB,YAAI2J,EAAW;AAIf,qBAAaD,CAAY;AACzB,cAAMkF,IAAQ5O,EAAE,KAAA;AAChB,YAAI4O,EAAM,SAAS,GAAG;AAAE,UAAAX,EAAS,eAAA;AAAkB;AAAA,QAAO;AAC1D,QAAAvE,IAAe,WAAW,MAAM;AAC9B,UAAK,MAAM,GAAGK,CAAQ,wBAAwB,mBAAmBxB,EAAK,SAAS,CAAC,MAAM,mBAAmBqG,EAAM,MAAM,GAAG,GAAG,CAAC,CAAC,EAAE,EAC5H,KAAK,CAAAC,MAAMA,EAAE,KAAKA,EAAE,KAAA,IAAS,EAAE,UAAU,CAAA,GAAK,EAC9C,KAAK,CAACC,MAAsEb,EAAS,eAAea,EAAE,YAAY,CAAA,CAAE,CAAC,EACrH,MAAM,MAAMb,EAAS,gBAAgB;AAAA,QAC1C,GAAG,GAAG;AAAA,MACR;AAAA,IAAA,IACI,CAAA;AAAA,IACJ,WAAWc,GAAO;AAAE,MAAI1E,OAAU,KAAK,EAAE,MAAM,QAAQ,gBAAgBA,GAAK,KAAA0E,GAAK;AAAA,IAAE;AAAA,IACnF,aAAa;AAGX,UAAI,CAAC1E,KAAO,CAAC/I,EAAM,IAAK;AACxB,YAAM0N,IAAS1N,EAAM,SAAA,EAAW,CAAC;AACjC,MAAI0N,KAAQnC,EAAK,KAAK,EAAE,MAAM,WAAW,gBAAgBxC,GAAK,WAAW2E,EAAO,KAAK,OAAO,IAAI;AAAA,IAClG;AAAA,IACA,OAAOC,GAAWlI,GAAS;AACzB,MAAIsD,KAAKwC,EAAK,KAAK,EAAE,MAAM,QAAQ,gBAAgBxC,GAAK,WAAA4E,GAA+B,SAAS,EAAE,MAAM,QAAQ,MAAMlI,EAAA,GAAW;AAAA,IACnI;AAAA,IACA,SAASkI,GAAW;AAClB,MAAI5E,OAAU,KAAK,EAAE,MAAM,UAAU,gBAAgBA,GAAK,WAAA4E,GAA+B;AAAA,IAC3F;AAAA,IACA,KAAIvP,KAAA6I,EAAK,aAAL,gBAAA7I,GAAe,eAAc,KAAQ;AAAA,MACzC,QAAQuP,GAAmB3H,GAAe4H,GAAiB;AACzD,QAAK7E,KACLwC,EAAK,KAAK,EAAE,MAAM,SAAS,gBAAgBxC,GAAK,WAAA4E,GAA+B,OAAA3H,GAAO,QAAA4H,GAAQ;AAAA,MAChG;AAAA,IAAA,IACI,CAAA;AAAA,IACJ,KAAIvP,KAAA4I,EAAK,aAAL,gBAAA5I,GAAe,UAAS,KAAQ;AAAA,MACpC,OAAOwP,GAAe;AACpB,QAAK9E,KACL,MAAM,GAAGN,CAAQ,kBAAkBM,CAAG,SAAS;AAAA,UAC7C,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,oBAAoB,eAAe,UAAUT,CAAK,GAAA;AAAA,UAC7E,MAAM,KAAK,UAAU,EAAE,OAAAuF,GAAO;AAAA,QAAA,CAC/B,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MACnB;AAAA,IAAA,IACI,CAAA;AAAA,IACJ,GAAI5G,EAAK,gBAAgB;AAAA,MACvB,MAAM,YAAYjK,GAAc;AAC9B,YAAI;AACF,gBAAM8P,IAAM,MAAM,MAAM,GAAGrE,CAAQ,cAAc;AAAA,YAC/C,QAAQ;AAAA,YACR,SAAS,EAAE,gBAAgB,oBAAoB,eAAe,UAAUH,CAAK,GAAA;AAAA,YAC7E,MAAM,KAAK,UAAU,EAAE,MAAAtL,GAAM,YAAYiK,EAAK,eAAe;AAAA,UAAA,CAC9D;AACD,cAAI,CAAC6F,EAAI,GAAI,QAAO;AACpB,gBAAM,EAAE,YAAAgB,EAAA,IAAe,MAAMhB,EAAI,KAAA;AACjC,iBAAOgB;AAAA,QACT,QAAQ;AAAE,iBAAO;AAAA,QAAK;AAAA,MACxB;AAAA,IAAA,IACE,CAAA;AAAA,IACJ,GAAI7G,EAAK,SAAS,EAAE,QAAQA,EAAK,OAAA,IAAW,CAAA;AAAA,EAAC,GAC5C;AAAA,IACD,GAAIA,EAAK,UAAU,EAAE,SAASA,EAAK,QAAA,IAAY,CAAA;AAAA,IAC/C,GAAIA,EAAK,eAAe,EAAE,cAAcA,EAAK,aAAA,IAAiB,CAAA;AAAA,IAC9D,GAAIA,EAAK,SAAS,EAAE,QAAQA,EAAK,OAAA,IAAW,CAAA;AAAA,IAC5C,GAAIA,EAAK,UAAU,EAAE,SAASA,EAAK,QAAA,IAAY,CAAA;AAAA,IAC/C,GAAIA,EAAK,QAAQ,EAAE,OAAOA,EAAK,MAAA,IAAU,CAAA;AAAA,IACzC,GAAIA,EAAK,YAAY,KAAQ,EAAE,SAAS,GAAA,IAAU,CAAA;AAAA,IAClD,IAAI3I,KAAA2I,EAAK,SAAL,QAAA3I,GAAW,SAAQC,KAAA0I,EAAK,SAAL,QAAA1I,GAAW,SAAS,EAAE,UAAU,EAAE,GAAI0I,EAAK,KAAK,OAAO,EAAE,MAAMA,EAAK,KAAK,KAAA,IAAS,CAAA,GAAK,GAAIA,EAAK,KAAK,SAAS,EAAE,QAAQA,EAAK,KAAK,WAAW,CAAA,EAAC,EAAG,IAAM,CAAA;AAAA,IAC9K,MAAAsF;AAAA,EAAA,CAED;AAGD,MAAI;AAAE,IAAI,aAAa,QAAQG,EAAU,OAAY,gBAAA;AAAA,EAAkB,QAAQ;AAAA,EAAqB;AASpG,QAAMqB,KAAW9G,EAAK,SAASA,EAAK,KAAK,QAAQA,EAAK,KAAK,SAASA,EAAK,KAAK,UAAUA,EAAK,KAAK,QAC9F;AAAA,IACE,GAAIA,EAAK,KAAK,OAAS,EAAE,MAAQA,EAAK,KAAK,KAAA,IAAW,CAAA;AAAA,IACtD,GAAIA,EAAK,KAAK,QAAS,EAAE,OAAQA,EAAK,KAAK,MAAA,IAAW,CAAA;AAAA,IACtD,GAAIA,EAAK,KAAK,SAAS,EAAE,QAAQA,EAAK,KAAK,OAAA,IAAW,CAAA;AAAA,IACtD,GAAIA,EAAK,KAAK,OAAS,EAAE,MAAQA,EAAK,KAAK,SAAW,CAAA;AAAA,EAAC,IAEzD,QAEE+G,KAAoD;AAAA,IACxD,MAAM;AAAA,IAAQ,WAAW/G,EAAK;AAAA;AAAA;AAAA;AAAA,IAI9B,GAAIA,EAAK,SAAS,YAAYA,EAAK,SAC/B,EAAE,MAAM,UAAmB,QAAQA,EAAK,OAAA,IACxCA,EAAK,YAAY,EAAE,WAAWA,EAAK,UAAA,IAAuB,CAAA;AAAA,IAC9D,GAAIsB,IAAW,EAAE,UAAAA,EAAA,IAAgC,CAAA;AAAA,IACjD,GAAIwF,KAAW,EAAE,UAAAA,GAAA,IAAa,CAAA;AAAA,IAC9B,GAAI,OAAO,WAAa,MAAc,EAAE,SAAS,SAAS,KAAA,IAAS,CAAA;AAAA,IACnE,GAAI,OAAO,WAAa,OAAe,SAAS,QAAQ,EAAE,WAAW,SAAS,MAAA,IAAU,CAAA;AAAA;AAAA;AAAA,IAGxF,IAAIvP,KAAAyI,EAAK,YAAL,QAAAzI,GAAc,QAAS,EAAE,cAAcyI,EAAK,QAAQ,MAAA,IAAsB,CAAA;AAAA,IAC9E,IAAIrI,KAAAqI,EAAK,YAAL,QAAArI,GAAc,WAAW,EAAE,aAAaqI,EAAK,QAAQ,aAAqB,CAAA;AAAA,EAAC;AAGjF,EAAAsE,IAAO,IAAI0C,GAAkB;AAAA,IAC3B,GAAIhH,EAAK,eAAe,EAAE,cAAcA,EAAK,aAAA,IAAiB,CAAA;AAAA,IAC9D,KAAKuB;AAAA,IAAO,OAAAF;AAAA,IAAO,MAAM0F;AAAA,IACzB,WAAW,MAAMhO,EAAM,WAAA;AAAA,IACvB,gBAAgB,CAACvD,GAAG+N,MAAQmC,EAAS,cAAclQ,GAAG+N,CAAG;AAAA,IACzD,QAAQ0D,GAAO;AACb,UAAIA,EAAM,SAAS,UAAU;AAI3B,YAHAnF,IAAMmF,EAAM,aAAa,IAGrB,CAAClF,GAAgB;AACnB,UAAAA,IAAiB;AACjB,qBAAWK,KAAQR,EAAO;AACxB,YAAA0C,EAAK,KAAK,EAAE,MAAM,QAAQ,gBAAgBxC,GAAK,aAAaM,EAAK,aAAa,SAASA,EAAK,QAAA,CAAS;AAAA,QAEzG;AAKA,QAAK8E,GAAe3F,GAAOF,GAAOS,GAAK/I,GAAO2M,GAAUlE,CAAQ,GAG5D2C,EAAa,UAAQC,EAAkBtC,CAAG;AAAA,MAChD;AAKA,UAHImF,EAAM,SAAS,SAAOrF,EAAO,OAAOqF,EAAM,WAAW,GAG1CA,EAAM,SAAS,gBAAgB;AAC5C,QAAIA,EAAM,WACRrC,IAAoB,IACfL,EAAK,WAAW0C,EAAM,MAAM,EAAE,KAAK,CAAChC,MAAa;AAEpD,gBAAMkC,IAAS,CAAC,GAAGzC,EAAQ,OAAO,CAAC,GAAG,GAAGC,EAAY,OAAO,CAAC,CAAC;AAC9D,qBAAWQ,KAAKgC,EAAQ,CAAAnC,EAAeG,EAAE,aAAaA,EAAE,MAAMF,CAAQ;AACtE,UAAAS,EAAS,OAAO3M,CAAK;AAAA,QACvB,CAAC;AAGH;AAAA,MACF;AAGA,UAAekO,EAAM,SAAS,aAAalO,EAAM,KAAK;AACpD,cAAMqO,IAAOC,GAAgBJ,EAAM,QAAQ,OAAO;AAClD,YAAIG,KAAQ,CAAC7C,EAAK,OAAO;AACvB,UAAKA,EAAK,gBAAgB6C,EAAK,QAAQA,EAAK,QAAQA,EAAK,SAASA,EAAK,OAAO,EAAE,KAAK,YAAY;AAE/F,kBAAM7C,EAAK,UAAU0C,CAAK,GAC1BlO,EAAM,MAAMkO,CAAK,GACjBvB,EAAS,OAAO3M,CAAK;AAAA,UACvB,CAAC;AACD;AAAA,QACF;AAAA,MACF;AAEA,UAAekO,EAAM,SAAS,WAAW;AACvC,QAAK1C,EAAK,UAAU0C,EAAM,GAAG,EAAE,KAAK,MAAM;AAAE,UAAA/B,EAAA,GAAgBQ,EAAS,OAAO3M,CAAK;AAAA,QAAE,CAAC;AACpF;AAAA,MACF;AACA,OAAM,YAAY;AAKhB,YAJeA,EAAM,OAAK,MAAMwL,EAAK,UAAU0C,CAAK,GACpDlO,EAAM,MAAMkO,CAAK,GAGbA,EAAM,SAAS,YAAY;AAC7B,UAAA9E,KAAA,QAAAA;AAGA,gBAAMmF,IAAML,EAA+B;AAC3C,UAAIK,KAAI5B,EAAS,WAAW4B,CAAE;AAAA,QAChC;AASA,YALIL,EAAM,SAAS,aAAaA,EAAM,QAAQ,aAAc5F,KAAmB,CAAC4F,EAAM,QAAQ,aAC5FnD,EAAA,GACAC,EAAA,IAGahL,EAAM,OAAO+I,KAAO,CAAC2C,GAAY;AAC9C,UAAAA,IAAa;AAEb,gBAAM8C,IAAgB,MAAMhD,EAAK,SAAA;AACjC,UAAAD,EAAK,KAAK,EAAE,MAAM,iBAAiB,GAAGiD,GAAe;AAErD,gBAAMC,IAAU,MAAMjD,EAAK,MAAA;AAC3B,UAAAD,EAAK,KAAK,EAAE,MAAM,UAAU,gBAAgBxC,GAAK,KAAK0F,GAAS;AAAA,QACjE;AACA,QAAA9B,EAAS,OAAO3M,CAAK;AAAA,MAEvB,GAAA;AAAA,IACF;AAAA,EAAA,CACD,GAGDuL,EAAK,QAAA,GAELoB,EAAS,OAAO3M,CAAK;AAErB,QAAM0O,IAAOzH,EAAK,WAAWD,GAAaC,CAAI,IAAI,MAC5C0H,IAAuB,EAAE,OAAO,MAAM;AAC1C,IAAAtG,IAAY,IACZ,aAAaD,CAAY,GACzBmD,EAAK,MAAA,GAASjC,KAAA,QAAAA,EAAY,UAAUqD,EAAS,QAAA,GACzC1D,KAAQC,KAAaD,EAAK,oBAAoB,UAAUC,CAAW,GACnEC,MAAe,SAAS,oBAAoB,WAAWA,CAAW,GAAGA,IAAc,OACvFrC,EAAU,OAAOG,EAAK,EAAE,GACpByH,KAAQ3H,GAAkB,IAAI2H,CAAI,MAAMC,KAAQ5H,GAAkB,OAAO2H,CAAI;AAAA,EACnF,EAAA;AACA,SAAA5H,EAAU,IAAIG,EAAK,IAAI0H,CAAM,GACzBD,KAAM3H,GAAkB,IAAI2H,GAAMC,CAAM,GACrCA;AACT;"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/renderer.styles.ts","../src/renderer.ts","../src/index.ts"],"sourcesContent":["// Injected stylesheet for the chat widget (`.ocw`). Extracted from renderer.ts\n// to keep the renderer focused on behaviour. Tokens come from the single\n// source of truth in theme-tokens.ts.\nimport { lightTokens, darkTokens } from './theme-tokens.js'\n\nexport const CSS = `\n.ocw { ${lightTokens('ocw')}\n position:relative;\n display:flex; flex-direction:column; height:100%; min-height:320px; background:var(--ocw-bg);\n font-family:var(--ocw-fb); color:var(--ocw-ink); overflow:hidden; }\n@media (prefers-color-scheme: dark) { .ocw[data-theme=\"auto\"] { ${darkTokens('ocw')} } }\n.ocw[data-theme=\"dark\"] { ${darkTokens('ocw')} }\n/* ── Responsive sizing ──────────────────────────────────────────────────────\n * Sizing is driven by the WIDGET'S OWN width (ResizeObserver toggles\n * .ocw-compact below 400px), not the viewport — so a widget embedded in a\n * narrow desktop sidebar sizes the same as one on a phone, and a tablet in\n * landscape keeps comfortable desktop sizing. A viewport query alone can't\n * see the container. Fullscreen (launcher on mobile) additionally gets\n * .ocw-fs from the launcher, which is the only case that should remove the\n * corner radius — an INLINE embed on a phone must NOT take over the page\n * (the old blanket min-height:100dvh rule did exactly that). */\n.ocw.ocw-fs { border-radius:0 !important; }\n.ocw-compact .ocw-bubble { font-size:15px; }\n.ocw-compact.ocw .ocw-input textarea { font-size:16px; } /* ≥16px prevents iOS zoom on focus */\n.ocw-compact .ocw-chip { padding:9px 14px; font-size:14px; }\n.ocw-compact .ocw-modal-card { width:90%; }\n.ocw-compact .ocw-row { max-width:94%; }\n.ocw-compact .ocw-sendbtn { min-width:44px; height:44px; }\n.ocw-compact .ocw-back { width:34px; height:34px; }\n.ocw-compact .ocw-quick button { padding:9px 15px; font-size:14px; }\n/* RTL support: when the host element has dir=rtl, flip layout direction */\n[dir=\"rtl\"] .ocw-row.mine { flex-direction:row; }\n[dir=\"rtl\"] .ocw-row.theirs { flex-direction:row-reverse; }\n[dir=\"rtl\"] .mine .ocw-bubble { border-bottom-right-radius:18px; border-bottom-left-radius:4px; }\n[dir=\"rtl\"] .theirs .ocw-bubble { border-bottom-left-radius:18px; border-bottom-right-radius:4px; }\n[dir=\"rtl\"] .ocw-input { flex-direction:row-reverse; }\n[dir=\"rtl\"] .mine .ocw-meta { text-align:left; }\n.ocw-head { display:flex; align-items:center; gap:10px; padding:12px 14px; background:var(--ocw-card); border-bottom:1px solid var(--ocw-line); }\n.ocw-back { flex:none; width:30px; height:30px; margin:-2px -2px -2px -4px; border:none; background:none; color:var(--ocw-ink); font-size:26px; line-height:1; cursor:pointer; border-radius:50%; display:flex; align-items:center; justify-content:center; }\n.ocw-back:hover { background:var(--ocw-bg); }\n.ocw-avatar { width:34px; height:34px; border-radius:50%; background:var(--ocw-tint); color:var(--ocw-accent); font-family:var(--ocw-fh); font-weight:600; display:flex; align-items:center; justify-content:center; font-size:13px; flex:none; }\n.ocw-head-main { flex:1; min-width:0; }\n.ocw-head-name { font-family:var(--ocw-fh); font-weight:600; font-size:15px; }\n.ocw-head-meta { color:var(--ocw-mut); font-size:12px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n.ocw-head-status { font-size:10.5px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n.ocw-head-status.online { color:#5cb37a; }\n.ocw-head-status.away { color:#c98b2e; }\n.ocw-msg-sender { font-size:10px; color:var(--ocw-mut); margin-bottom:2px; }\n.ocw-resolved-note { text-align:center; font-size:10.5px; color:var(--ocw-mut); margin:6px 0 12px; }\n.ocw-reopen { display:none; width:calc(100% - 32px); margin:0 16px 14px; height:40px; border:1.5px solid var(--ocw-accent); color:var(--ocw-accent); background:none; border-radius:20px; font:inherit; font-size:13.5px; font-weight:600; cursor:pointer; }\n.ocw-reopen:hover { background:var(--ocw-tint); }\n.ocw-badge { font-size:11px; font-weight:600; color:#2f8a52; background:#eafaf0; border-radius:20px; padding:5px 12px; }\n.ocw-e2e { font-size:11px; font-weight:700; color:var(--ocw-accent); background:var(--ocw-tint); border-radius:20px; padding:5px 12px; align-items:center; }\n.ocw-menu { color:var(--ocw-mut); width:30px; height:30px; border-radius:50%; border:1px solid var(--ocw-line); background:var(--ocw-card); cursor:pointer; }\n.ocw-chiprow { display:flex; gap:8px; padding:10px 12px; background:var(--ocw-card); border-bottom:1px solid var(--ocw-line); overflow-x:auto; }\n.ocw-chip { flex:none; display:flex; align-items:center; gap:6px; border:none; background:var(--ocw-tint); color:var(--ocw-accent); border-radius:999px; padding:7px 13px; font-size:12px; font-weight:600; cursor:pointer; white-space:nowrap; }\n.ocw-chip:hover { border-color:var(--ocw-accent); color:var(--ocw-accent); }\n.ocw-scroll { flex:1; min-height:0; overflow-y:auto; padding:16px 14px; display:flex; flex-direction:column; gap:10px; }\n.ocw-subject { background:var(--ocw-tint); border:none; border-radius:16px; padding:10px 12px; }\n.ocw-subject-title { font-family:var(--ocw-fh); font-weight:600; font-size:13px; margin-bottom:2px; }\n.ocw-subject-sub { color:var(--ocw-mut); font-size:10.5px; margin-bottom:6px; }\n.ocw-tags { display:flex; flex-wrap:wrap; gap:6px; }\n.ocw-tag { font-size:12px; color:#5b554e; background:#efeae3; border-radius:8px; padding:4px 10px; }\n.ocw-row { display:flex; align-items:flex-end; gap:8px; max-width:86%; }\n.ocw-row.mine { align-self:flex-end; flex-direction:row-reverse; }\n.ocw-row.theirs { align-self:flex-start; }\n.ocw-bubble { padding:10px 14px; border-radius:18px; font-size:13.5px; line-height:1.45; word-wrap:break-word; max-width:100%; width:fit-content; }\n.theirs .ocw-bubble { background:var(--ocw-tint); border-bottom-left-radius:4px; }\n.mine .ocw-bubble { background:var(--ocw-accent2); color:#fff; border-bottom-right-radius:4px; }\n.ocw-sys { align-self:center; color:var(--ocw-mut); font-size:12.5px; font-style:italic; text-align:center; max-width:90%; }\n.ocw-bot .ocw-bubble { background:var(--ocw-tint); }\n.ocw-note .ocw-bubble { background:#fffbeb; border:1.5px dashed #f59e0b; color:#78350f; border-radius:12px !important; }\n.ocw-note .ocw-bubble::before { content:'🔒 Note — '; font-size:11px; font-weight:700; color:#b45309; display:block; margin-bottom:3px; letter-spacing:.3px; }\n.ocw-time { font-size:10.5px; color:var(--ocw-mut); margin-top:3px; }\n.mine .ocw-meta { text-align:right; }\n.ocw-tick { margin-left:4px; font-size:11px; color:var(--ocw-mut); }\n.ocw-tick.read { color:#3b82f6; }\n.ocw-tick.delivered { color:var(--ocw-mut); }\n.ocw-deleted { font-style:italic; color:var(--ocw-mut); }\n.ocw-edited { font-size:10px; color:var(--ocw-mut); margin-left:4px; }\n.ocw-react { font-size:12px; margin-top:3px; display:flex; flex-wrap:wrap; gap:3px; }\n.ocw-react-pill { display:inline-flex; align-items:center; gap:3px; border:1px solid var(--ocw-line); border-radius:999px; padding:2px 7px; background:var(--ocw-card); font-size:12px; cursor:pointer; }\n.ocw-react-pill:hover { border-color:var(--ocw-accent); }\n.ocw-react-pill.mine { border-color:var(--ocw-accent); background:#fff8f5; }\n.ocw-react-wrap { position:relative; }\n.ocw-react-wrap:not(:hover) .ocw-react-picker { display:none; }\n.ocw-react-picker { position:absolute; bottom:calc(100% + 4px); left:0; background:var(--ocw-card); border:1px solid var(--ocw-line); 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; }\n.ocw-react-picker button { background:none; border:none; font-size:16px; cursor:pointer; padding:2px; border-radius:6px; }\n.ocw-react-picker button:hover { background:var(--ocw-bg); }\n.ocw-react-btn { background:none; border:1px solid var(--ocw-line); border-radius:999px; padding:2px 7px; font-size:12px; cursor:pointer; color:var(--ocw-mut); }\n.ocw-react-btn:hover { border-color:var(--ocw-accent); color:var(--ocw-accent); }\n.ocw-msg-menu { position:absolute; top:0; right:0; display:none; gap:3px; }\n.ocw-row.mine:hover .ocw-msg-menu { display:flex; }\n.ocw-row.theirs:hover .ocw-msg-menu { display:flex; left:0; right:auto; }\n.ocw-msg-menu button { background:var(--ocw-card); border:1px solid var(--ocw-line); border-radius:6px; font-size:11px; padding:2px 6px; cursor:pointer; color:var(--ocw-mut); }\n.ocw-msg-menu button:hover { border-color:var(--ocw-accent); color:var(--ocw-accent); }\n.ocw-msg-menu button.del:hover { border-color:#e74c3c; color:#e74c3c; }\n.ocw-bubble-wrap { position:relative; }\n.ocw-seen { font-size:10.5px; color:var(--ocw-mut); }\n.ocw-appt { background:#f0f7ff; border:1px solid #c7deff; border-radius:12px; padding:12px 14px; max-width:260px; }\n.ocw-appt-title { font-weight:700; font-size:14px; margin-bottom:4px; }\n.ocw-appt-time { font-size:12px; color:#1d4ed8; margin-bottom:4px; }\n.ocw-appt-loc { font-size:12px; color:var(--ocw-mut); margin-bottom:4px; }\n.ocw-appt-desc { font-size:12px; color:var(--ocw-mut); margin-bottom:10px; white-space:pre-wrap; }\n.ocw-appt-links { display:flex; flex-direction:column; gap:6px; }\n.ocw-appt-btn { display:block; text-align:center; padding:8px 12px; border-radius:8px; font-size:13px; font-weight:600; text-decoration:none; background:var(--ocw-accent); color:#fff; }\n.ocw-appt-btn-sec { background:var(--ocw-card); color:var(--ocw-accent); border:1px solid var(--ocw-accent); }\n.ocw-conn-status { font-size:10px; color:var(--ocw-mut); margin-left:4px; }\n.ocw-conn-status.warn { color:#e67e22; }\n.ocw-conn-status.err { color:#c0392b; font-weight:600; }\n.ocw-load-more { display:block; width:100%; background:none; border:1px solid var(--ocw-line); border-radius:10px; padding:6px 0; font-size:12px; color:var(--ocw-mut); cursor:pointer; margin-bottom:8px; }\n.ocw-load-more:hover { border-color:var(--ocw-accent); color:var(--ocw-accent); }\n.ocw-away { margin:0 14px 8px; padding:9px 12px; background:#fff8e6; border:1px solid #f0e2bd; border-radius:10px; font-size:12px; color:#7a5c17; display:flex; gap:7px; align-items:flex-start; line-height:1.45; }\n.ocw button:focus-visible, .ocw textarea:focus-visible, .ocw a:focus-visible, .ocw [tabindex]:focus-visible { outline:2px solid var(--ocw-accent); outline-offset:2px; border-radius:8px; }\n.ocw-sendbtn:active { transform:scale(.92); }\n.ocw-quick button:active, .ocw-chip:active { transform:scale(.97); }\n@media (prefers-color-scheme: dark) { .ocw[data-theme=\"auto\"] .ocw-away { background:#3a3018; border-color:#5a4a1f; color:#e8d9a8; } .ocw[data-theme=\"auto\"] .ocw-note .ocw-bubble { background:#332b12; border-color:#7a5c17; color:#f0e2bd; } .ocw[data-theme=\"auto\"] .ocw-note .ocw-bubble::before { color:#e0c060; } }\n.ocw[data-theme=\"dark\"] .ocw-away { background:#3a3018; border-color:#5a4a1f; color:#e8d9a8; }\n.ocw[data-theme=\"dark\"] .ocw-note .ocw-bubble { background:#332b12; border-color:#7a5c17; color:#f0e2bd; }\n.ocw[data-theme=\"dark\"] .ocw-note .ocw-bubble::before { color:#e0c060; }\n.ocw-away-icon { flex:none; }\n/* Shared form styles (used by the pre-chat panel; named for the retired offline form). */\n.ocw-offline-form { display:flex; flex-direction:column; gap:8px; text-align:left; }\n.ocw-offline-input { border:none; background:var(--ocw-tint); border-radius:14px; padding:10px 12px; font-size:13px; font-family:inherit; color:var(--ocw-ink); }\n.ocw-offline-input:focus { outline:none; box-shadow:inset 0 0 0 1.5px var(--ocw-accent); }\n.ocw-offline-submit { background:var(--ocw-accent); color:var(--ocw-onaccent); border:none; border-radius:20px; padding:11px; font-family:var(--ocw-fh); font-size:13.5px; font-weight:600; cursor:pointer; box-shadow:var(--ocw-shadow); }\n.ocw-prechat { margin:16px; padding:0; background:none; border:none; }\n.ocw-prechat-title { font-family:var(--ocw-fh); font-weight:600; font-size:20px; margin-bottom:10px; }\n.ocw-prechat select { border:none; border-radius:14px; padding:10px 12px; font:inherit; font-size:13px; background:var(--ocw-tint); color:var(--ocw-ink); }\n.ocw-prechat-cb { display:flex; align-items:center; gap:8px; font-size:13px; color:var(--ocw-ink); }\n.ocw-deflect { margin:0 14px 8px; display:flex; flex-direction:column; gap:6px; }\n.ocw-deflect-card { text-align:left; background:var(--ocw-card); border:1px solid var(--ocw-line); border-radius:12px; padding:10px 12px; font:inherit; font-size:13px; cursor:pointer; }\n.ocw-deflect-card:hover { border-color:var(--ocw-accent); }\n.ocw-deflect-q { font-weight:600; margin-bottom:2px; }\n.ocw-deflect-a { color:var(--ocw-mut); font-size:12.5px; display:none; white-space:pre-wrap; }\n.ocw-deflect-card.open .ocw-deflect-a { display:block; }\n.ocw-deflect-hint { font-size:11.5px; color:var(--ocw-mut); text-align:center; }\n.ocw-csat-title { font-size:13px; font-weight:600; margin-bottom:8px; }\n.ocw-csat-stars { display:flex; gap:6px; }\n.ocw-csat-star { background:none; border:none; font-size:22px; cursor:pointer; padding:2px; opacity:.4; transition:opacity .15s; }\n.ocw-csat-star:hover, .ocw-csat-star.lit { opacity:1; }\n.ocw-csat-done { font-size:12px; color:var(--ocw-mut); margin-top:6px; }\n\n.ocw-typing { min-height:22px; padding:0 16px 4px; display:flex; align-items:center; }\n.ocw-typing-bubble { display:none; align-items:center; gap:3px; background:var(--ocw-card); border:1px solid var(--ocw-line); border-radius:14px; border-bottom-left-radius:4px; padding:7px 12px; }\n.ocw-typing.active .ocw-typing-bubble { display:flex; }\n.ocw-typing-dot { width:6px; height:6px; border-radius:50%; background:var(--ocw-mut); animation:ocw-bounce 1.2s infinite ease-in-out; }\n.ocw-typing-dot:nth-child(2) { animation-delay:.2s; }\n.ocw-typing-dot:nth-child(3) { animation-delay:.4s; }\n@keyframes ocw-bounce { 0%,60%,100%{transform:translateY(0)} 30%{transform:translateY(-5px)} }\n.ocw-quick { display:flex; gap:8px; padding:8px 12px 6px; overflow-x:auto; scrollbar-width:none; flex-shrink:0; }\n.ocw-quick::-webkit-scrollbar { display:none; }\n.ocw-quick button { flex:none; border:none; background:var(--ocw-tint); border-radius:999px; padding:7px 14px; font-size:12px; font-weight:600; cursor:pointer; color:var(--ocw-accent); white-space:nowrap; transition:background .12s,color .12s; }\n.ocw-quick button:hover { background:var(--ocw-accent); color:var(--ocw-onaccent); }\n.ocw-form-host:empty { display:none; }\n.ocw-form { margin:6px 12px 0; padding:12px; background:var(--ocw-card); border:1px solid var(--ocw-line); border-radius:14px; }\n.ocw-form-title { font-weight:700; font-size:14px; margin-bottom:8px; }\n.ocw-form-row { display:flex; flex-direction:column; gap:3px; margin-bottom:8px; }\n.ocw-form-lbl { font-size:12px; color:var(--ocw-mut); }\n.ocw-form-input { border:1px solid var(--ocw-line); border-radius:9px; padding:9px 11px; font:inherit; font-size:14px; outline:none; }\n.ocw-form-input:focus { border-color:var(--ocw-accent); }\n.ocw-form-actions { display:flex; justify-content:flex-end; gap:8px; margin-top:4px; }\n.ocw-form-cancel { background:none; border:none; color:var(--ocw-mut); font-size:13px; cursor:pointer; padding:8px 10px; }\n.ocw-form-submit { background:var(--ocw-accent); color:#fff; border:none; border-radius:999px; padding:8px 18px; font-size:13px; font-weight:600; cursor:pointer; }\n.ocw-modal { position:absolute; inset:0; background:rgba(20,18,16,.42); display:flex; align-items:center; justify-content:center; z-index:50; }\n.ocw-modal-card { background:var(--ocw-card); border-radius:16px; padding:20px; width:78%; max-width:300px; box-shadow:0 14px 44px rgba(0,0,0,.22); }\n.ocw-modal-title { font-weight:700; font-size:16px; margin-bottom:6px; }\n.ocw-modal-body { color:var(--ocw-mut); font-size:14px; margin-bottom:16px; }\n.ocw-modal-actions { display:flex; justify-content:flex-end; gap:8px; }\n.ocw-modal-cancel { background:none; border:none; color:var(--ocw-mut); font-size:14px; cursor:pointer; padding:9px 12px; }\n.ocw-modal-ok { background:var(--ocw-accent); color:#fff; border:none; border-radius:999px; padding:9px 20px; font-size:14px; font-weight:600; cursor:pointer; }\n.ocw-input { display:flex; align-items:center; gap:10px; padding:12px; }\n.ocw-footer { text-align:center; font-size:11px; color:var(--ocw-mut); padding:6px 0 8px; }\n.ocw-footer a { color:var(--ocw-mut); text-decoration:none; font-weight:600; }\n.ocw-footer a:hover { color:var(--ocw-accent); }\n.ocw-attach { background:none;border:none;cursor:pointer;font-size:18px;padding:4px 6px;opacity:.6;flex-none; }\n.ocw-attach:hover { opacity:1; }\n.ocw-input textarea { flex:1; min-width:0; resize:none; border:none; border-radius:20px; padding:11px 16px; font:inherit; font-size:14px; line-height:1.4; background:var(--ocw-tint); outline:none; max-height:120px; overflow-y:auto; }\n.ocw-input textarea:focus { box-shadow:inset 0 0 0 1.5px var(--ocw-accent); }\n/* Send button auto-sizes to its label: the default is a fixed circle around an\n * inline SVG; a TEXT label (i18n.send: \"Send\", \"보내기\", \"Enviar\"…) switches to\n * .ocw-sendbtn-label — a pill whose width follows the text. A fixed 42px\n * circle with 18px type overflowed the moment anyone localized the label. */\n.ocw-sendbtn { min-width:38px; height:38px; border-radius:999px; border:none; background:var(--ocw-accent2); 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; }\n.ocw-sendbtn-label { padding:0 16px; white-space:nowrap; }\n.ocw-sendbtn svg { width:19px; height:19px; display:block; }\n.ocw-sendbtn:not(:disabled):hover { transform:scale(1.05); }\n.ocw-sendbtn:not(:disabled):active { transform:scale(.96); }\n.ocw-sendbtn:disabled { opacity:.5; cursor:default; }\n.ocw-sendbtn:focus-visible, .ocw-back:focus-visible, .ocw-chip:focus-visible { outline:2px solid var(--ocw-accent); outline-offset:2px; }\n\n.ocw-translate-btn { position:absolute; bottom:2px; right:-26px; background:var(--ocw-card); border:1px solid var(--ocw-line); border-radius:50%; width:22px; height:22px; font-size:11px; cursor:pointer; color:var(--ocw-mut); display:flex; align-items:center; justify-content:center; opacity:0; transition:opacity .15s; padding:0; }\n.ocw-row.theirs .ocw-translate-btn { right:auto; left:-26px; }\n.ocw-bubble-wrap:hover .ocw-translate-btn { opacity:1; }\n.ocw-translation { margin-top:6px; padding-top:6px; border-top:1px solid var(--ocw-line); font-size:13px; line-height:1.45; }\n.ocw-translation::before { content:'🌐 '; opacity:.75; }\n`\n","import type { ManifestAction, MessageContent } from './protocol/index.js'\nimport type { ChatStore, RenderMessage } from './store.js'\nimport { CSS } from './renderer.styles.js'\n\nexport interface WidgetConfig {\n subject?: { title?: string; subtitle?: string; tags?: string[]; status?: string; ownerLabel?: string }\n quickReplies?: string[]\n accent?: string\n /** Secondary accent — the guest's OWN bubble + send button. Defaults to the\n * design blue; if omitted while `accent` is set, follows `accent` so a single\n * accent override re-themes cohesively. */\n accent2?: string\n /** Colour scheme. Default 'light'. 'auto' follows the OS (prefers-color-scheme);\n * 'dark'/'light' force it. Dark is never auto-applied unless the host opts in. */\n theme?: 'auto' | 'light' | 'dark'\n /** Load the brand webfonts (Baloo 2 + Nunito). Default true; set false for\n * strict-CSP / privacy-sensitive hosts (falls back to the system stack). */\n webfont?: boolean\n /** Identified user info — shown as the guest avatar/name in the widget header. */\n userInfo?: { name?: string; avatar?: string }\n /** i18n string overrides */\n i18n?: { placeholder?: string; send?: string; offline?: string; poweredBy?: string; online?: string; away?: string; aiAssistant?: string; resolved?: string; reopen?: string; sources?: string; helpful?: string; notHelpful?: string }\n\n}\n\nexport interface RendererHandlers {\n onSend(text: string): void\n onAttach?(file: File): void\n onInvoke(actionId: string, inputs?: Record<string, unknown>): void\n onTyping(isTyping: boolean, preview?: string): void\n onReadUpTo(seq: number): void\n onReact?(messageId: string, emoji: string, remove: boolean): void\n onCsat?(score: number): void\n onLoadMore?(): void\n onEdit?(messageId: string, newText: string): void\n onDelete?(messageId: string): void\n /** Pre-chat qualification submitted (values keyed by field; topic/callback included). */\n onPreChat?(values: { name?: string; email?: string; phone?: string; topic?: string; callback?: boolean }): void\n /** KB deflection: the guest is typing their FIRST message — look up articles. */\n onDeflectQuery?(q: string): void\n /** Translate a message's text for display. Return null if unavailable —\n * the renderer shows a brief \"unavailable\" hint and leaves the original. */\n onTranslate?(text: string): Promise<string | null>\n /** Stack navigation (chat-app surfaces): when set, the header shows a back\n * chevron on the left that calls this — tap a conversation → chatroom →\n * back → list, like a native messaging app. Omit for a standalone widget,\n * which has nothing to go \"back\" to. */\n onBack?(): void\n}\n\nconst STYLE_ID = 'objectchat-widget-styles'\nconst REACTION_EMOJIS = ['👍', '❤️', '😂', '😮', '😢', '🙏']\n/** Width below which the widget switches to compact (touch-friendly) sizing. */\nconst COMPACT_BREAKPOINT = 400\n// Inline, dependency-free send glyph — `currentColor` follows the button text\n// colour; no emoji/font dependency so it renders identically across platforms.\nconst SEND_ICON_SVG =\n '<svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">' +\n '<path d=\"M22 2 11 13M22 2l-7 20-4-9-9-4 20-7z\" stroke=\"currentColor\" stroke-width=\"2\" ' +\n 'stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>'\n// CSS lives in renderer.styles.ts (single source of truth for .ocw styling).\n\nfunction injectStyles(): void {\n if (typeof document === 'undefined' || document.getElementById(STYLE_ID)) return\n const s = document.createElement('style'); s.id = STYLE_ID; s.textContent = CSS; document.head.appendChild(s)\n}\n\n/** Load the brand webfonts (Baloo 2 + Nunito) once. The design's identity is its\n * rounded type — without this the widget falls back to system fonts and looks\n * generic. Injected as a <link> so a strict host CSP that blocks it degrades\n * gracefully to the system stack. Opt out with `webfont: false`. */\nconst FONT_ID = 'ocw-webfont'\nfunction injectFonts(): void {\n if (typeof document === 'undefined' || document.getElementById(FONT_ID)) return\n const l = document.createElement('link')\n l.id = FONT_ID; l.rel = 'stylesheet'\n l.href = 'https://fonts.googleapis.com/css2?family=Baloo+2:wght@500;600;700&family=Nunito:wght@400;500;600;700&display=swap'\n document.head.appendChild(l)\n}\n\nfunction el<K extends keyof HTMLElementTagNameMap>(tag: K, cls?: string, text?: string): HTMLElementTagNameMap[K] {\n const n = document.createElement(tag); if (cls) n.className = cls; if (text !== undefined) n.textContent = text; return n\n}\n\n// Append plain text with any http(s) URLs turned into clickable links (safe:\n// text goes through textContent / createTextNode — no innerHTML). Returns a Text\n// node so callers that guard on \"has text\" keep working.\nfunction linkifyInto(parent: HTMLElement, text: string): Text {\n const urlRe = /(https?:\\/\\/[^\\s]+)/g\n let last = 0\n let first: Text | null = null\n let m: RegExpExecArray | null\n while ((m = urlRe.exec(text)) !== null) {\n if (m.index > last) { const tn = document.createTextNode(text.slice(last, m.index)); parent.append(tn); first ??= tn }\n let href = m[0]; let trail = ''\n // Don't swallow trailing sentence punctuation into the URL.\n const mt = /[).,!?;:]+$/.exec(href); if (mt) { trail = mt[0]; href = href.slice(0, -trail.length) }\n const a = document.createElement('a')\n a.href = href; a.target = '_blank'; a.rel = 'noopener noreferrer'; a.textContent = href\n a.style.cssText = 'color:inherit;text-decoration:underline;word-break:break-all'\n parent.append(a)\n if (trail) parent.append(document.createTextNode(trail))\n last = m.index + m[0].length\n }\n const tail = document.createTextNode(text.slice(last)); parent.append(tail); first ??= tail\n return first\n}\nfunction fmtTime(ts: number): string {\n try { return new Date(ts).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) } catch { return '' }\n}\nfunction contentText(c: MessageContent): string {\n switch (c.kind) {\n case 'text': return c.text\n case 'system': return typeof c.data?.['message'] === 'string' ? String(c.data['message']) : c.event\n case 'card': return [c.title, c.body].filter(Boolean).join(' — ')\n case 'attachment': return c.name ?? c.url\n case 'form': return c.prompt\n case 'appointment': return `📅 ${c.title} — ${new Date(c.startIso).toLocaleString()}`\n }\n}\n\n/** Renders a ChatStore into a host element in the Image-1 layout: header →\n * subject card → action chips → chat → quick replies → input. Self-injects its\n * stylesheet so it looks right wherever it mounts. Accent comes from the\n * dashboard-authored profile theme (via the manifest), falling back to config. */\nexport class Renderer {\n private readonly scroll: HTMLElement\n private readonly chips: HTMLElement\n private readonly typing: HTMLElement\n private readonly quick: HTMLElement\n private readonly formHost: HTMLElement\n private readonly csatPanel: HTMLElement\n private readonly awayNotice: HTMLElement\n private readonly headStatus: HTMLElement\n private readonly reopenBtn: HTMLButtonElement\n private readonly preChatPanel: HTMLElement\n private readonly deflectPanel: HTMLElement\n private preChatBuilt = false\n private preChatDone = false\n private readonly footer: HTMLElement\n private readonly input: HTMLTextAreaElement\n private readonly subjectCard: HTMLElement\n private readonly e2eBadge: HTMLElement\n private readonly statusBadge: HTMLElement\n private readonly headerName: HTMLElement\n private readonly connStatus: HTMLElement\n private typingTimer: ReturnType<typeof setTimeout> | null = null\n private csatSubmitted = false\n private readonly sendBtn: HTMLButtonElement\n /** Container-driven responsive sizing — toggles .ocw-compact (see CSS note). */\n private compactObserver: ResizeObserver | null = null\n private storeRef: ChatStore | null = null\n private readonly cfgAccent2: string | undefined\n private scrollCleanup: (() => void) | null = null\n\n /** Returns the scroll container so history.ts can attach scroll listeners. */\n getScrollEl(): HTMLElement | null { return this.scroll }\n\n /** Registers a cleanup fn removed on destroy() to prevent listener leaks. */\n setScrollCleanup(fn: () => void): void {\n this.scrollCleanup?.()\n this.scrollCleanup = fn\n }\n\n // ── Live translation ──────────────────────────────────────────────────-\n private readonly translationCache = new Map<string, string>()\n private readonly showingTranslation = new Set<string>()\n\n\n /** Last seq the guest has seen per conversationId — used to compute unread badges. */\n\n\n constructor(\n private readonly root: HTMLElement,\n private readonly me: string,\n private readonly h: RendererHandlers,\n private readonly cfg: WidgetConfig = {},\n ) {\n injectStyles()\n if (cfg.webfont !== false) injectFonts()\n // Clear any previous widget content on this element before building.\n // This is the last line of defence against double-mounts: even if mount()\n // is called twice on the same element (React StrictMode, HMR, caller bug),\n // the second Renderer wipes the first one's DOM so only one UI is visible.\n root.replaceChildren()\n root.classList.add('ocw')\n if (cfg.accent) root.style.setProperty('--ocw-accent', cfg.accent)\n // Two-accent model: explicit accent2 wins; else if only accent is set, accent2\n // follows it (single-token retheme); else the CSS defaults (indigo + blue) hold.\n const accent2 = cfg.accent2 ?? cfg.accent\n if (accent2) root.style.setProperty('--ocw-accent2', accent2)\n this.cfgAccent2 = cfg.accent2\n // Colour scheme is a deliberate choice, never auto-detected by default:\n // unset → 'light'. 'auto' is an explicit opt-in that follows the OS via the\n // prefers-color-scheme media query (which is scoped to [data-theme=\"auto\"]);\n // 'dark'/'light' force the scheme regardless of the OS.\n root.dataset.theme = cfg.theme ?? 'light'\n\n // Header\n const head = el('div', 'ocw-head')\n // Stack navigation: a back chevron returns to the conversation list. Only\n // shown when the host wired onBack (chat-app surfaces) — a standalone\n // support widget has no list to go back to.\n if (this.h.onBack) {\n const back = el('button', 'ocw-back', '‹') as HTMLButtonElement\n back.type = 'button'\n back.setAttribute('aria-label', 'Back')\n back.addEventListener('click', () => this.h.onBack!())\n head.append(back)\n }\n // Header identity is the party the guest is talking TO (the brand/room), so\n // the avatar mirrors the header name — a brand monogram (e.g. \"NM\"), never a\n // generic person glyph. Hidden entirely when there's no brand name to show.\n const brandName = cfg.subject?.ownerLabel ?? cfg.subject?.title ?? ''\n const avatarEl = el('div', 'ocw-avatar')\n if (brandName.trim()) {\n avatarEl.textContent = brandName.trim()[0]!.toUpperCase()\n head.append(avatarEl)\n }\n const hm = el('div', 'ocw-head-main')\n this.headerName = el('div', 'ocw-head-name', cfg.subject?.ownerLabel ?? cfg.subject?.title ?? '')\n hm.append(this.headerName)\n this.headStatus = el('div', 'ocw-head-status'); this.headStatus.style.display = 'none'\n hm.append(this.headStatus)\n if (cfg.subject?.subtitle) hm.append(el('div', 'ocw-head-meta', cfg.subject.subtitle))\n head.append(hm)\n this.statusBadge = el('span', 'ocw-badge', cfg.subject?.status ?? '')\n if (!cfg.subject?.status) this.statusBadge.style.display = 'none'\n head.append(this.statusBadge)\n this.e2eBadge = el('span', 'ocw-e2e', '🔒 E2E'); this.e2eBadge.style.display = 'none'; head.append(this.e2eBadge)\n this.connStatus = el('span', 'ocw-conn-status'); this.connStatus.style.display = 'none'; head.append(this.connStatus)\n\n head.append(el('button', 'ocw-menu', '⋯'))\n\n // Action chips (filled in render)\n this.chips = el('div', 'ocw-chiprow')\n\n // Scroll area with optional subject card + messages\n this.scroll = el('div', 'ocw-scroll')\n this.subjectCard = el('div', 'ocw-subject')\n this.typing = el('div', 'ocw-typing')\n const typingBubble = el('div', 'ocw-typing-bubble')\n typingBubble.append(el('div', 'ocw-typing-dot'), el('div', 'ocw-typing-dot'), el('div', 'ocw-typing-dot'))\n this.typing.append(typingBubble)\n this.quick = el('div', 'ocw-quick')\n for (const q of cfg.quickReplies ?? []) {\n const b = el('button', undefined, q)\n b.addEventListener('click', () => {\n this.h.onSend(q)\n // Hide quick replies immediately after one is tapped\n this.quick.style.display = 'none'\n })\n this.quick.append(b)\n }\n\n // Input\n this.formHost = el('div', 'ocw-form-host')\n this.csatPanel = el('div', 'ocw-csat'); this.csatPanel.style.display = 'none'\n // Away notice (outside office hours). INFORMATIONAL only — it never blocks\n // the composer: the message is delivered either way and an agent replies\n // when they're back. (It used to be a card that replaced the composer with\n // a name/email/message form; guests could not simply chat.)\n this.awayNotice = el('div', 'ocw-away'); this.awayNotice.style.display = 'none'\n this.preChatPanel = el('div', 'ocw-prechat'); this.preChatPanel.style.display = 'none'\n this.deflectPanel = el('div', 'ocw-deflect'); this.deflectPanel.style.display = 'none'\n this.input = el('textarea', undefined); this.input.rows = 1\n // i18n.placeholder was documented in MountOptions but never actually\n // applied — the composer always said \"Message…\" regardless.\n this.input.placeholder = cfg.i18n?.placeholder ?? 'Message…'\n const sendBtn = el('button', 'ocw-sendbtn') as HTMLButtonElement\n sendBtn.type = 'button'\n const sendLabel = cfg.i18n?.send\n if (sendLabel) {\n // Text label → auto-width pill (see the .ocw-sendbtn-label CSS note).\n sendBtn.textContent = sendLabel\n sendBtn.classList.add('ocw-sendbtn-label')\n sendBtn.setAttribute('aria-label', sendLabel)\n } else {\n // Default: inline SVG paper plane — renders identically on every\n // platform (the old '➤' text glyph varied per OS font).\n sendBtn.innerHTML = SEND_ICON_SVG\n sendBtn.setAttribute('aria-label', 'Send message')\n }\n // The :disabled style existed but nothing ever set the state — send is\n // inactive until there's something to send, like every mainstream chat UI.\n sendBtn.disabled = true\n this.sendBtn = sendBtn\n sendBtn.addEventListener('click', () => this.flushSend())\n this.input.addEventListener('input', () => {\n this.sendBtn.disabled = this.input.value.trim().length === 0\n this.autoGrowInput()\n // Deflection fires only for the FIRST message of an empty conversation —\n // once a thread exists, suggestions would just be noise.\n if (this.storeRef && !this.storeRef.messages().some(m => m.senderRole === 'guest')) this.h.onDeflectQuery?.(this.input.value)\n else this.hideDeflection()\n })\n this.input.addEventListener('keydown', (e) => {\n // Guard against IME composition (Korean/Japanese/Chinese input): while\n // the user is selecting a candidate from the IME's suggestion list,\n // pressing Enter to CONFIRM the candidate also fires a keydown with\n // key === 'Enter'. Without this check, that confirmation keystroke was\n // being treated as \"send the message\" — firing early with a partial\n // composition, and then firing again on the real Enter press with\n // whatever text was left, producing two bubbles for one message\n // (e.g. typing \"음식\" sends \"음식\" then \"식\").\n // e.isComposing covers most browsers; keyCode 229 is the long-standing\n // fallback for browsers/IMEs that don't set isComposing reliably.\n if (e.isComposing || e.keyCode === 229) return\n if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); this.flushSend() } else this.signalTyping()\n })\n\n const attachBtn = el('button', 'ocw-attach', '📎'); attachBtn.title = 'Attach image or file'\n const fileInput = document.createElement('input'); fileInput.type = 'file'\n fileInput.accept = 'image/*,.pdf,.txt,.doc,.docx'; fileInput.style.display = 'none'\n attachBtn.addEventListener('click', () => fileInput.click())\n fileInput.addEventListener('change', () => { if (fileInput.files?.[0] && this.h.onAttach) this.h.onAttach(fileInput.files[0]); fileInput.value = '' })\n\n const inputRow = el('div', 'ocw-input'); inputRow.append(attachBtn, fileInput, this.input, sendBtn)\n\n const footer = el('div', 'ocw-footer')\n // i18n.poweredBy: if the caller provides it, treat as plain text — no innerHTML.\n // Only our own hardcoded default renders the anchor as HTML.\n if (cfg.i18n?.poweredBy !== undefined) {\n footer.textContent = cfg.i18n.poweredBy\n } else {\n footer.innerHTML = 'Powered by <a href=\"https://relay.paramms.com\" target=\"_blank\" rel=\"noopener\">Relay</a>'\n }\n this.footer = footer\n\n this.reopenBtn = el('button', 'ocw-reopen', this.cfg.i18n?.reopen ?? 'Reopen conversation') as HTMLButtonElement\n this.reopenBtn.type = 'button'\n this.reopenBtn.addEventListener('click', () => {\n this.reopenBtn.style.display = 'none'\n const ta = this.root.querySelector('.ocw-input textarea') as HTMLTextAreaElement | null\n ta?.focus()\n })\n root.append(head, this.chips, this.scroll, this.typing, this.quick, this.formHost, this.csatPanel, this.preChatPanel, this.deflectPanel, this.awayNotice, inputRow, this.reopenBtn, this.footer)\n\n // Container-driven responsive sizing: compact below COMPACT_BREAKPOINT of\n // the widget's OWN width. Covers phones AND narrow desktop embeds — a\n // viewport media query can't see the container. Guarded: jsdom/tests and\n // very old runtimes have no ResizeObserver; they keep desktop sizing.\n const applyCompact = (w: number): void => { root.classList.toggle('ocw-compact', w > 0 && w < COMPACT_BREAKPOINT) }\n applyCompact(root.clientWidth)\n if (typeof ResizeObserver !== 'undefined') {\n this.compactObserver = new ResizeObserver((entries) => {\n const w = entries[0]?.contentRect.width ?? root.clientWidth\n applyCompact(w)\n })\n this.compactObserver.observe(root)\n }\n }\n\n /** Call when the widget is unmounted. Disconnects scroll listeners and clears timers. */\n destroy(): void {\n this.scrollCleanup?.()\n this.scrollCleanup = null\n if (this.typingTimer) { clearTimeout(this.typingTimer); this.typingTimer = null }\n this.compactObserver?.disconnect()\n this.compactObserver = null\n }\n\n\n private flushSend(): void {\n this.hideDeflection()\n const text = this.input.value.trim()\n if (!text) return\n this.input.value = ''\n this.sendBtn.disabled = true\n this.autoGrowInput() // collapse back to one row\n this.h.onTyping(false)\n this.h.onSend(text)\n }\n\n /** Grow the composer with its content (up to the CSS max-height), collapse\n * when cleared. scrollHeight is 0 in non-layout environments (jsdom) —\n * skip there so tests and SSR-ish mounts are unaffected. */\n private autoGrowInput(): void {\n this.input.style.height = 'auto'\n const sh = this.input.scrollHeight\n if (sh > 0) this.input.style.height = `${Math.min(sh, 120)}px`\n else this.input.style.removeProperty('height')\n }\n private signalTyping(): void {\n const preview = this.input.value.trim().slice(0, 100) || undefined\n this.h.onTyping(true, preview)\n if (this.typingTimer) clearTimeout(this.typingTimer)\n this.typingTimer = setTimeout(() => this.h.onTyping(false), 2000)\n }\n\n /** A3.3 — per-chatroom bot identity from the manifest; relabels bot bubbles. */\n setBotName(name: string): void {\n this.cfg.i18n = { ...(this.cfg.i18n ?? {}), aiAssistant: name }\n }\n\n render(store: ChatStore): void {\n this.storeRef = store\n if (store.accent) {\n this.root.style.setProperty('--ocw-accent', store.accent)\n this.root.style.setProperty('--ocw-accent2', this.cfgAccent2 ?? store.accent)\n }\n this.e2eBadge.style.display = store.e2e ? 'inline-flex' : 'none'\n this.buildSubjectCard(store)\n // Header: when a subject is attached, show ownerLabel (\"Seller\", \"Host\")\n // or nothing — the subject card below carries the identity.\n // Without a subject, the header is already set to cfg.subject?.ownerLabel\n // or \"Chat\" from the constructor — don't overwrite it with the domain name\n // which would duplicate the subject card title or clutter a plain chat.\n if (store.subject) {\n const ownerLabel = this.cfg.subject?.ownerLabel\n if (ownerLabel) this.headerName.textContent = ownerLabel\n // else leave constructor default (\"Chat\")\n }\n // Without a subject: leave header as-is (set once in constructor)\n\n // Quick replies are a first-touch affordance (\"Is this still available?\").\n // They belong only on an empty conversation — once there's any message,\n // hide them, and keep them hidden on every re-render (returning to the\n // widget, reload, back-nav). Without this they reappear each mount even\n // though the conversation is already underway.\n this.quick.style.display = store.messages().length === 0 ? 'flex' : 'none'\n\n // Action chips from the manifest (filtered by state in the store)\n this.chips.replaceChildren()\n const actions = store.visibleActions()\n this.chips.style.display = actions.length ? 'flex' : 'none'\n for (const a of actions) this.chips.append(this.chipEl(a))\n\n // Messages\n // Preserve scroll anchor when history is prepended: capture height before\n // replaceChildren so we can restore relative position after.\n const prevScrollHeight = this.scroll.scrollHeight\n const prevScrollTop = this.scroll.scrollTop\n\n this.scroll.replaceChildren()\n if (this.subjectCard.childNodes.length) this.scroll.append(this.subjectCard)\n if (store.hasMoreHistory) {\n // Sentinel at top — scroll to here triggers load-more via the scroll\n // listener set up by restoreHistory. Shows a subtle loading indicator\n // so the user knows older messages are available.\n const sentinel = el('div', 'ocw-load-more')\n sentinel.textContent = '↑ Loading earlier messages…'\n sentinel.style.pointerEvents = 'none'\n this.scroll.append(sentinel)\n }\n let maxOther = 0\n let prevSender: string | null = null\n const msgs = [...store.messages()]\n for (let i = 0; i < msgs.length; i++) {\n const m = msgs[i]!\n const showLabel = m.senderRole !== 'system' && m.senderId !== this.me && !m.internal && m.senderId !== prevSender\n this.scroll.append(this.messageEl(m, store, showLabel, i === msgs.length - 1))\n if (m.senderRole !== 'system') prevSender = m.senderId\n if (m.senderId !== this.me && m.seq > maxOther) maxOther = m.seq\n }\n // Auto-scroll to bottom only for new messages; restore anchor when history was prepended.\n if (prevScrollTop > 20) {\n this.scroll.scrollTop = this.scroll.scrollHeight - prevScrollHeight + prevScrollTop\n } else {\n this.scroll.scrollTop = this.scroll.scrollHeight\n }\n if (maxOther > 0) this.h.onReadUpTo(maxOther)\n\n const typingNames = [...store.typing]\n this.typing.classList.toggle('active', typingNames.length > 0)\n // Bubble is always present in DOM (hidden via CSS); just update label\n const bubble = this.typing.querySelector('.ocw-typing-bubble')\n if (bubble) bubble.setAttribute('aria-label', typingNames.length ? 'typing' : '')\n this.footer.style.display = store.whiteLabel ? 'none' : 'block'\n\n // Offline mode: show form instead of chat input\n // Pre-chat qualification (dashboard-configured, arrives in the manifest):\n // shown before the FIRST message when enabled — 'offline'-scoped configs\n // replace the default leave-a-message form; 'always' configs gate the\n // composer while the team is online too. Never re-shown once completed\n // or once the conversation has any history.\n // \"Before the first message\" means the GUEST hasn't spoken — a chatroom\n // welcomeMessage is a real stored system message, so counting ALL\n // messages suppressed pre-chat (and deflection) on exactly the chatrooms\n // most likely to configure them.\n const guestHasSpoken = store.messages().some(m => m.senderRole === 'guest')\n const preChatWanted = !!store.preChat?.enabled && !this.preChatDone && !guestHasSpoken &&\n (store.preChat!.showWhen !== 'offline' || store.offline)\n if (preChatWanted) {\n if (!this.preChatBuilt) this.buildPreChatPanel(store.preChat!)\n this.preChatPanel.style.display = 'block'\n this.awayNotice.style.display = 'none'\n ;(this.root.querySelector('.ocw-input') as HTMLElement | null)?.style.setProperty('display', 'none')\n } else {\n this.preChatPanel.style.display = 'none'\n // Being outside office hours only changes EXPECTATIONS, never capability:\n // say we're away, keep the composer, deliver the message, reply later.\n if (store.offline) {\n if (!this.awayNotice.firstChild) {\n this.awayNotice.append(el('span', 'ocw-away-icon', '🌙'), el('span', '', ''))\n }\n const copy = this.awayNotice.lastChild as HTMLElement\n copy.textContent = store.offlineMessage\n || this.cfg.i18n?.offline\n || \"We're away right now — send your message and we'll reply as soon as we're back.\"\n this.awayNotice.style.display = 'flex'\n } else {\n this.awayNotice.style.display = 'none'\n }\n ;(this.root.querySelector('.ocw-input') as HTMLElement | null)?.style.removeProperty('display')\n }\n\n // CSAT: show star-rating panel when conversation reaches a terminal state\n // and the user hasn't yet rated. Terminal states are heuristic: 'resolved',\n // 'closed', 'sold', 'issued', 'checked_out'. The panel self-dismisses on submit.\n const terminalStates = ['resolved', 'closed', 'sold', 'issued', 'checked_out']\n if (this.h.onCsat && !this.csatSubmitted && terminalStates.includes(store.state) && store.messages().length > 0) {\n if (this.csatPanel.style.display === 'none') this.buildCsatPanel()\n this.csatPanel.style.display = 'block'\n }\n\n // Header availability line: green when online, amber when the chatroom is\n // outside office hours. Server-provided offlineMessage (already localized)\n // is preferred for the away text; falls back to the i18n label.\n const isTerminal = terminalStates.includes(store.state)\n if (store.offline) {\n this.headStatus.textContent = `● ${store.offlineMessage || this.cfg.i18n?.away || 'Away'}`\n this.headStatus.className = 'ocw-head-status away'\n this.headStatus.style.display = ''\n } else if (store.conversationId) {\n this.headStatus.textContent = `● ${this.cfg.i18n?.online ?? 'Online'}`\n this.headStatus.className = 'ocw-head-status online'\n this.headStatus.style.display = ''\n } else {\n this.headStatus.style.display = 'none'\n }\n\n // Resolved: a legible divider in the thread + a one-tap reopen affordance.\n if (isTerminal && store.messages().length > 0) {\n const note = el('div', 'ocw-resolved-note', `— ${this.cfg.i18n?.resolved ?? 'Marked as resolved'} —`)\n this.scroll.append(note)\n this.reopenBtn.style.display = 'block'\n } else {\n this.reopenBtn.style.display = 'none'\n }\n }\n\n setConnStatus(status: 'connecting' | 'open' | 'reconnecting' | 'error', message?: string): void {\n if (status === 'open') { this.connStatus.style.display = 'none'; return }\n this.connStatus.style.display = ''\n // 'error' is a FATAL, non-transient state (bad token, closed chatroom, or the\n // relay is unreachable after repeated tries) — show a clear reason and don't\n // pretend we're still \"connecting…\". Anything else is transient.\n const fatal = status === 'error'\n this.connStatus.className = `ocw-conn-status${fatal ? ' err' : status === 'reconnecting' ? ' warn' : ''}`\n this.connStatus.textContent = fatal\n ? `⚠ ${message ?? 'Chat unavailable'}`\n : status === 'reconnecting' ? (message ?? '↻ reconnecting…') : '● connecting…'\n }\n\n private buildPreChatPanel(cfg: import('./protocol/frames.js').PreChatConfig): void {\n this.preChatBuilt = true\n this.preChatPanel.replaceChildren()\n this.preChatPanel.append(el('div', 'ocw-prechat-title', cfg.title ?? 'Before we start…'))\n const form = el('div', 'ocw-offline-form')\n const inputs: Partial<Record<'name' | 'email' | 'phone', HTMLInputElement>> = {}\n for (const f of cfg.fields ?? ['name', 'email']) {\n const inp = el('input', 'ocw-offline-input') as HTMLInputElement\n inp.type = f === 'email' ? 'email' : f === 'phone' ? 'tel' : 'text'\n inp.placeholder = f === 'name' ? 'Your name' : f === 'email' ? 'Your email' : 'Your phone number'\n inputs[f] = inp\n form.append(inp)\n }\n let topicSel: HTMLSelectElement | null = null\n if (cfg.topics?.length) {\n topicSel = el('select', undefined) as HTMLSelectElement\n const ph = document.createElement('option'); ph.value = ''; ph.textContent = 'What is this about?'; topicSel.append(ph)\n for (const t of cfg.topics) { const o = document.createElement('option'); o.value = t; o.textContent = t; topicSel.append(o) }\n form.append(topicSel)\n }\n let callbackCb: HTMLInputElement | null = null\n let phoneForCb: HTMLInputElement | null = null\n if (cfg.callbackOption) {\n const row = el('label', 'ocw-prechat-cb')\n callbackCb = document.createElement('input'); callbackCb.type = 'checkbox'\n row.append(callbackCb, document.createTextNode('📞 Request a call back'))\n form.append(row)\n if (!inputs.phone) {\n phoneForCb = el('input', 'ocw-offline-input') as HTMLInputElement\n phoneForCb.type = 'tel'; phoneForCb.placeholder = 'Phone number for the call'; phoneForCb.style.display = 'none'\n callbackCb.addEventListener('change', () => phoneForCb!.style.setProperty('display', callbackCb!.checked ? 'block' : 'none'))\n form.append(phoneForCb)\n }\n }\n const submit = el('button', 'ocw-offline-submit', 'Start chat') as HTMLButtonElement\n submit.type = 'button'\n submit.addEventListener('click', () => {\n const email = inputs.email?.value.trim()\n if (inputs.email && (!email || !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email))) { inputs.email.focus(); return }\n const callback = !!callbackCb?.checked\n const phone = (inputs.phone?.value ?? phoneForCb?.value ?? '').trim()\n if (callback && !phone) { (inputs.phone ?? phoneForCb)?.focus(); return }\n if (topicSel && cfg.topics?.length && !topicSel.value) { topicSel.focus(); return }\n submit.disabled = true // a double-click must not qualify twice\n this.completePreChat()\n this.h.onPreChat?.({\n ...(inputs.name?.value.trim() ? { name: inputs.name.value.trim() } : {}),\n ...(email ? { email } : {}),\n ...(phone ? { phone } : {}),\n ...(topicSel?.value ? { topic: topicSel.value } : {}),\n ...(callback ? { callback: true } : {}),\n })\n })\n form.append(submit)\n this.preChatPanel.append(form)\n }\n\n /** Mark pre-chat complete (submitted now or in a previous session). */\n completePreChat(): void {\n this.preChatDone = true\n this.preChatPanel.style.display = 'none'\n if (this.storeRef) this.render(this.storeRef)\n }\n\n /** KB deflection results (\"was this your question?\") above the composer. */\n showDeflection(articles: { id: string; title: string; answer: string }[]): void {\n if (!articles.length) return this.hideDeflection()\n this.deflectPanel.replaceChildren()\n this.deflectPanel.append(el('div', 'ocw-deflect-hint', 'Instant answers — tap to expand'))\n for (const a of articles.slice(0, 3)) {\n const card = el('button', 'ocw-deflect-card')\n card.append(el('div', 'ocw-deflect-q', a.title), el('div', 'ocw-deflect-a', a.answer))\n card.addEventListener('click', () => card.classList.toggle('open'))\n this.deflectPanel.append(card)\n }\n this.deflectPanel.style.display = 'flex'\n }\n\n hideDeflection(): void {\n this.deflectPanel.style.display = 'none'\n this.deflectPanel.replaceChildren()\n }\n\n private buildCsatPanel(): void {\n this.csatPanel.replaceChildren()\n this.csatPanel.append(el('div', 'ocw-csat-title', 'How did we do?'))\n const stars = el('div', 'ocw-csat-stars')\n const btns: HTMLButtonElement[] = []\n for (let i = 1; i <= 5; i++) {\n const b = el('button', 'ocw-csat-star', '★')\n b.dataset['score'] = String(i)\n b.addEventListener('mouseenter', () => btns.forEach((bb, idx) => bb.classList.toggle('lit', idx < i)))\n b.addEventListener('mouseleave', () => btns.forEach(bb => bb.classList.remove('lit')))\n b.addEventListener('click', () => {\n this.csatSubmitted = true\n this.csatPanel.replaceChildren(el('div', 'ocw-csat-done', `Thanks for your ${i}★ rating!`))\n this.h.onCsat?.(i)\n })\n btns.push(b); stars.append(b)\n }\n this.csatPanel.append(stars)\n }\n\n /** Whether the subject card (server Subject entity, mount config fallback)\n * has been built — built once when data first arrives. */\n private subjectBuilt = false\n\n private buildSubjectCard(store: ChatStore): void {\n if (this.subjectBuilt) return\n const s = store.subject\n const cfg = this.cfg.subject\n // Only show the subject card when there is actual subject data (from the\n // server) or an explicit subject config passed by the embedder (title,\n // tags, status). Never fall back to store.name — that's the domain/profile\n // name and is already shown in the header; rendering it here as well is\n // what caused the duplication seen in the Hotel front desk screenshot.\n const title = s?.title ?? cfg?.title\n if (!title) return\n this.subjectBuilt = true\n this.subjectCard.replaceChildren()\n this.subjectCard.append(el('div', 'ocw-subject-title', title))\n if (cfg?.subtitle) this.subjectCard.append(el('div', 'ocw-subject-sub', cfg.subtitle))\n const tags = el('div', 'ocw-tags')\n if (s) for (const [k, v] of Object.entries(s.fields)) tags.append(el('span', 'ocw-tag', `${k}: ${v}`))\n else for (const t of cfg?.tags ?? []) tags.append(el('span', 'ocw-tag', t))\n if (tags.childNodes.length) this.subjectCard.append(tags)\n const status = s?.state ?? cfg?.status\n if (status) { this.statusBadge.textContent = status; this.statusBadge.style.display = 'inline-flex' }\n }\n\n private chipEl(a: ManifestAction): HTMLButtonElement {\n const btn = el('button', 'ocw-chip', a.icon ? `${a.icon} ${a.label}` : a.label)\n btn.dataset['actionId'] = a.id\n btn.addEventListener('click', async () => {\n if (a.confirm && !(await this.confirm(a.label))) return\n if (a.input?.length) this.openForm(a)\n else this.h.onInvoke(a.id)\n })\n return btn\n }\n\n /** In-widget confirmation modal (replaces window.confirm). */\n private confirm(label: string): Promise<boolean> {\n return new Promise((resolve) => {\n const overlay = el('div', 'ocw-modal')\n const card = el('div', 'ocw-modal-card')\n card.append(el('div', 'ocw-modal-title', label))\n card.append(el('div', 'ocw-modal-body', `Confirm “${label}”?`))\n const row = el('div', 'ocw-modal-actions')\n const cancel = el('button', 'ocw-modal-cancel', 'Cancel')\n const ok = el('button', 'ocw-modal-ok', 'Confirm')\n const close = (v: boolean) => { overlay.remove(); resolve(v) }\n cancel.addEventListener('click', () => close(false))\n ok.addEventListener('click', () => close(true))\n overlay.addEventListener('click', (e) => { if (e.target === overlay) close(false) })\n row.append(cancel, ok); card.append(row); overlay.append(card)\n this.root.append(overlay)\n ok.focus()\n })\n }\n\n /** Inline form for a form-effect action: typed inputs (date picker, number,\n * text) rendered above the composer — no browser prompts. `prefill` seeds\n * field values (search pre-fill) — deterministic parse, correctable before\n * submit. */\n private openForm(a: ManifestAction, prefill?: Record<string, string>): void {\n this.formHost.replaceChildren()\n const panel = el('div', 'ocw-form')\n panel.append(el('div', 'ocw-form-title', a.icon ? `${a.icon} ${a.label}` : a.label))\n const inputs = new Map<string, HTMLInputElement>()\n for (const f of a.input ?? []) {\n const pre = prefill?.[f.name]\n const row = el('label', 'ocw-form-row'); row.append(el('span', 'ocw-form-lbl', f.label))\n if (f.type === 'select' && f.options?.length) {\n const sel = el('select', 'ocw-form-input')\n if (!f.required) sel.append(el('option', undefined, '— select —'))\n for (const opt of f.options) { const o = el('option'); o.value = opt; o.textContent = opt; sel.append(o) }\n if (f.required) sel.required = true\n if (pre != null && f.options.includes(pre)) sel.value = pre\n row.append(sel)\n inputs.set(f.name, sel as unknown as HTMLInputElement)\n } else {\n const inp = el('input', 'ocw-form-input')\n inp.type = f.type === 'number' ? 'number' : f.type === 'date' ? 'datetime-local' : 'text'\n if (f.required) inp.required = true\n if (pre != null && pre !== '') inp.value = pre\n row.append(inp); inputs.set(f.name, inp)\n }\n panel.append(row)\n }\n const actions = el('div', 'ocw-form-actions')\n const cancel = el('button', 'ocw-form-cancel', 'Cancel')\n const submit = el('button', 'ocw-form-submit', 'Send')\n cancel.addEventListener('click', () => this.formHost.replaceChildren())\n submit.addEventListener('click', () => {\n const out: Record<string, unknown> = {}\n for (const [name, inp] of inputs) {\n if (inp.required && !inp.value) { inp.style.borderColor = '#e5484d'; return }\n out[name] = inp.type === 'number' ? Number(inp.value) : inp.value\n }\n this.formHost.replaceChildren()\n this.h.onInvoke(a.id, out)\n })\n actions.append(cancel, submit); panel.append(actions)\n this.formHost.append(panel)\n inputs.values().next().value?.focus()\n }\n\n private messageEl(m: RenderMessage, store: ChatStore, showLabel = false, isLast = false): HTMLElement {\n if (m.senderRole === 'system') {\n const sys = el('div', 'ocw-sys'); sys.textContent = m.deletedAt ? 'message deleted' : contentText(m.content); return sys\n }\n const mine = m.senderId === this.me\n const isNote = !!m.internal\n const row = el('div', `ocw-row ${isNote ? 'ocw-note mine' : mine ? 'mine' : 'theirs'} ${m.senderRole === 'bot' ? 'ocw-bot' : ''}`)\n const col = el('div')\n if (showLabel) {\n const who = m.senderRole === 'bot'\n ? (this.cfg.i18n?.aiAssistant ?? 'AI Assistant')\n : (this.cfg.subject?.ownerLabel ?? this.cfg.subject?.title ?? 'Support')\n col.append(el('div', 'ocw-msg-sender', who))\n }\n const bubbleWrap = el('div', 'ocw-bubble-wrap')\n // Reply-to context if present\n if (m.replyToId) {\n const replyCtx = el('div', 'ocw-reply-to', '↩ replying to a message')\n replyCtx.style.cssText = 'font-size:11px;color:var(--ocw-mut);margin-bottom:2px;font-style:italic'\n col.append(replyCtx)\n }\n const bubble = el('div', 'ocw-bubble')\n let textNode: Text | null = null\n if (m.deletedAt) bubble.append(el('span', 'ocw-deleted', 'message deleted'))\n else if (m.content.kind === 'attachment') {\n const c = m.content\n if (c.mime?.startsWith('image/')) {\n const img = document.createElement('img')\n img.src = c.url; img.alt = c.name ?? 'image'\n img.style.cssText = 'max-width:220px;max-height:160px;border-radius:10px;display:block;cursor:pointer'\n img.addEventListener('click', () => window.open(c.url, '_blank'))\n bubble.append(img)\n } else if (c.mime?.startsWith('video/')) {\n const video = document.createElement('video')\n video.src = c.url; video.controls = true\n video.style.cssText = 'max-width:240px;max-height:180px;border-radius:10px;display:block'\n bubble.append(video)\n } else {\n const a = document.createElement('a')\n a.href = c.url; a.target = '_blank'; a.rel = 'noopener'\n a.style.cssText = 'display:flex;align-items:center;gap:8px;color:inherit;text-decoration:none'\n a.append(el('span', undefined, '📄'), el('span', undefined, c.name ?? 'file'))\n bubble.append(a)\n }\n } else {\n if (m.content.kind === 'appointment') {\n const ap = m.content\n const card = el('div', 'ocw-appt')\n card.append(el('div', 'ocw-appt-title', `\\u{1F4C5} ${ap.title}`))\n card.append(el('div', 'ocw-appt-time', new Date(ap.startIso).toLocaleString() + ' \\u2013 ' + new Date(ap.endIso).toLocaleTimeString()))\n if (ap.location) card.append(el('div', 'ocw-appt-loc', `\\u{1F4CD} ${ap.location}`))\n if (ap.description) card.append(el('div', 'ocw-appt-desc', ap.description))\n const links = el('div', 'ocw-appt-links')\n const gLink = document.createElement('a'); gLink.href = ap.googleUrl; gLink.target = '_blank'; gLink.rel = 'noopener'; gLink.className = 'ocw-appt-btn'; gLink.textContent = '\\u{1F4C5} Add to Google Calendar'\n const iLink = document.createElement('a'); iLink.href = ap.icalUrl; iLink.download = `${ap.title}.ics`; iLink.className = 'ocw-appt-btn ocw-appt-btn-sec'; iLink.textContent = '\\u{1F34E} Apple / iCal'\n links.append(gLink, iLink); card.append(links); bubble.append(card)\n } else {\n textNode = linkifyInto(bubble, contentText(m.content))\n if (m.editedAt) bubble.append(el('span', 'ocw-edited', '(edited)'))\n }\n }\n bubbleWrap.append(bubble)\n\n // Guided-flow choices: a prompt message carries tappable options. Only the\n // LATEST message's chips are live — older prompts (on reload / after the guest\n // has answered) must not re-fire, so gate on isLast. Tapping sends the label\n // as a guest turn (reusing onSend), which the server-side flow then consumes.\n if (isLast && !m.deletedAt && m.content.kind === 'text' && m.content.quickReplies?.length) {\n const chips = el('div', 'ocw-flow-choices')\n chips.style.cssText = 'display:flex;flex-wrap:wrap;gap:6px;margin-top:6px'\n for (const label of m.content.quickReplies) {\n const b = el('button', 'ocw-flow-choice', label)\n b.type = 'button'\n b.style.cssText = 'border:1px solid var(--ocw-bd,rgba(0,0,0,.12));background:var(--ocw-bg,#fff);color:inherit;border-radius:14px;padding:5px 12px;font:inherit;font-size:13px;cursor:pointer'\n b.addEventListener('click', () => {\n chips.querySelectorAll('button').forEach(x => ((x as HTMLButtonElement).disabled = true))\n chips.style.opacity = '0.5'\n this.h.onSend(label)\n })\n chips.append(b)\n }\n bubbleWrap.append(chips)\n }\n\n // Search pre-fill (Search Phase 2/3): a `form`-kind message points at an\n // action + optional parsed `prefill`. Render a button that opens that\n // action's form seeded with the parsed values — tap to review, not auto-run.\n // Gated on isLast so an old suggestion on reload doesn't re-offer stale\n // parses; the action chip still opens the form blank at any time.\n if (isLast && !m.deletedAt && m.content.kind === 'form') {\n const c = m.content\n const a = store.actionById(c.actionId)\n if (a?.input?.length) {\n const cta = el('button', 'ocw-chip', a.icon ? `${a.icon} ${a.label}` : a.label)\n cta.type = 'button'\n cta.style.cssText = 'margin-top:6px'\n cta.addEventListener('click', async () => {\n if (a.confirm && !(await this.confirm(a.label))) return\n this.openForm(a, c.prefill)\n })\n bubbleWrap.append(cta)\n }\n }\n\n // Live translation: only for the other party's plain-text messages (not notes).\n // The translation renders as its own line directly AFTER the original text —\n // both stay visible, the message is never overwritten — toggled by a button.\n if (!mine && !isNote && this.h.onTranslate && m.content.kind === 'text' && !m.deletedAt && m.seq > 0 && textNode) {\n const original = m.content.text\n if (original.trim()) {\n const translateBtn = el('button', 'ocw-translate-btn', '🌐')\n translateBtn.type = 'button'\n translateBtn.title = 'Translate'\n const transLine = el('div', 'ocw-translation')\n transLine.style.display = 'none'\n bubble.append(transLine)\n\n const showLine = (text: string): void => {\n transLine.textContent = text\n transLine.style.display = ''\n this.showingTranslation.add(m.id)\n translateBtn.textContent = '↩'\n translateBtn.title = 'Show original'\n }\n const hideLine = (): void => {\n transLine.style.display = 'none'\n this.showingTranslation.delete(m.id)\n translateBtn.textContent = '🌐'\n translateBtn.title = 'Translate'\n }\n\n translateBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n if (this.showingTranslation.has(m.id)) { hideLine(); return }\n const cached = this.translationCache.get(m.id)\n if (cached !== undefined) { showLine(cached); return }\n translateBtn.textContent = '⏳'\n void this.h.onTranslate!(original).then((result) => {\n if (result === null) {\n translateBtn.textContent = '⚠️'\n translateBtn.title = 'Translation unavailable'\n setTimeout(() => { translateBtn.textContent = '🌐'; translateBtn.title = 'Translate' }, 1500)\n return\n }\n this.translationCache.set(m.id, result)\n showLine(result)\n })\n })\n bubbleWrap.append(translateBtn)\n\n // Keep a shown translation visible across re-renders.\n const cached = this.translationCache.get(m.id)\n if (this.showingTranslation.has(m.id) && cached !== undefined) showLine(cached)\n }\n }\n // Edit/delete context menu on own non-deleted messages\n if (mine && !m.deletedAt && m.seq > 0 && (this.h.onEdit ?? this.h.onDelete)) {\n const menu = el('div', 'ocw-msg-menu')\n if (this.h.onEdit) {\n const editBtn = el('button', undefined, '✏️')\n editBtn.title = 'Edit'\n editBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n // Inline edit: replace bubble text with a small textarea + save/cancel\n const original = contentText(m.content)\n const ta = document.createElement('textarea')\n ta.value = original\n ta.rows = Math.min(4, Math.ceil(original.length / 40) + 1)\n ta.style.cssText = 'width:100%;resize:vertical;border:1px solid var(--ocw-accent);border-radius:8px;padding:6px 10px;font:inherit;font-size:14px;background:var(--ocw-card);color:#1c1b1a;box-sizing:border-box'\n const saveBtn = el('button', 'ocw-form-submit', 'Save')\n saveBtn.style.cssText = 'margin-top:6px;padding:5px 14px;font-size:13px'\n const cancelBtn = el('button', 'ocw-form-cancel', 'Cancel')\n cancelBtn.style.cssText = 'margin-top:6px;padding:5px 10px;font-size:13px'\n const btnRow = el('div'); btnRow.style.cssText = 'display:flex;gap:6px;justify-content:flex-end'\n btnRow.append(cancelBtn, saveBtn)\n const editPanel = el('div'); editPanel.append(ta, btnRow)\n bubble.replaceChildren(editPanel)\n ta.focus(); ta.select()\n const restore = () => bubble.replaceChildren(textNode ?? document.createTextNode(original))\n cancelBtn.addEventListener('click', restore)\n saveBtn.addEventListener('click', () => {\n const newText = ta.value.trim()\n if (newText && newText !== original) { this.h.onEdit!(m.id, newText); restore() }\n else restore()\n })\n ta.addEventListener('keydown', (ke) => {\n if (ke.key === 'Enter' && !ke.shiftKey) { ke.preventDefault(); saveBtn.click() }\n if (ke.key === 'Escape') restore()\n })\n })\n menu.append(editBtn)\n }\n if (this.h.onDelete) {\n const delBtn = el('button', 'del', '🗑')\n delBtn.title = 'Delete'\n delBtn.addEventListener('click', (e) => { e.stopPropagation(); this.h.onDelete!(m.id) })\n menu.append(delBtn)\n }\n bubbleWrap.append(menu)\n }\n col.append(bubbleWrap)\n\n // A3.3 — bot answer affordances: KB source citations + 👍/👎 feedback. Only on\n // the bot's own answers (not human/system), non-deleted, persisted messages.\n if (m.senderRole === 'bot' && !m.deletedAt && m.seq > 0 && m.content.kind === 'text') {\n const cites = m.content.citations\n if (cites?.length) {\n const src = el('div', 'ocw-cites')\n src.style.cssText = 'margin-top:4px;font-size:11px;color:var(--ocw-mut,#667)'\n src.textContent = `${this.cfg.i18n?.sources ?? 'Sources'}: ${cites.map(c => c.source).join(', ')}`\n col.append(src)\n }\n if (this.h.onReact) {\n const fb = el('div', 'ocw-bot-feedback')\n fb.style.cssText = 'display:flex;gap:6px;margin-top:4px'\n const mk = (emoji: string, label: string) => {\n const b = el('button', 'ocw-fb-btn', emoji)\n b.type = 'button'; b.title = label\n b.style.cssText = 'border:1px solid var(--ocw-bd,rgba(0,0,0,.12));background:transparent;border-radius:12px;padding:1px 8px;font-size:12px;cursor:pointer;opacity:.7'\n const already = () => !!m.reactions?.[emoji]?.includes(this.me as never)\n if (already()) b.style.opacity = '1'\n b.addEventListener('click', () => { this.h.onReact?.(m.id, emoji, already()); b.style.opacity = '1' })\n return b\n }\n fb.append(mk('👍', this.cfg.i18n?.helpful ?? 'Helpful'), mk('👎', this.cfg.i18n?.notHelpful ?? 'Not helpful'))\n col.append(fb)\n }\n }\n\n if (this.h.onReact && !m.deletedAt && m.seq > 0) {\n const reactWrap = el('div', 'ocw-react-wrap')\n const reactRow = el('div', 'ocw-react')\n // Existing reaction pills\n if (m.reactions && Object.keys(m.reactions).length) {\n for (const [emoji, users] of Object.entries(m.reactions)) {\n const pill = el('button', `ocw-react-pill${(users as string[]).includes(this.me) ? ' mine' : ''}`, `${emoji} ${(users as string[]).length}`)\n pill.addEventListener('click', () => this.h.onReact?.(m.id, emoji, (users as string[]).includes(this.me)))\n reactRow.append(pill)\n }\n }\n // Add-reaction button + picker\n const addBtn = el('button', 'ocw-react-btn', '+')\n const picker = el('div', 'ocw-react-picker')\n for (const emoji of REACTION_EMOJIS) {\n const pb = el('button', undefined, emoji)\n pb.addEventListener('click', (e) => {\n e.stopPropagation()\n const alreadyReacted = m.reactions?.[emoji]?.includes(this.me as never)\n this.h.onReact?.(m.id, emoji, !!alreadyReacted)\n picker.style.display = 'none'\n })\n picker.append(pb)\n }\n picker.style.display = 'none'\n addBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n const opening = picker.style.display === 'none'\n picker.style.display = opening ? 'flex' : 'none'\n // Close on the next outside click. Registered only when OPENING —\n // the old code added one document listener per message on EVERY\n // render, so long conversations piled up hundreds of stale handlers.\n if (opening) document.addEventListener('click', () => { picker.style.display = 'none' }, { once: true })\n })\n reactRow.append(addBtn)\n reactWrap.append(reactRow, picker)\n col.append(reactWrap)\n } else if (m.reactions && Object.keys(m.reactions).length) {\n col.append(el('div', 'ocw-react', Object.entries(m.reactions).map(([e, u]) => `${e}${(u as string[]).length}`).join(' ')))\n }\n const meta = el('div', 'ocw-time ocw-meta', fmtTime(m.ts))\n if (mine && m.status) {\n const t = el('span', `ocw-tick${m.status === 'read' ? ' read' : m.status === 'delivered' ? ' delivered' : ''}`, tick(m.status))\n meta.append(t)\n }\n // \"Seen\" indicator when agent has read past this message\n if (mine && m.seq > 0 && store.lastReadByOthers >= m.seq) {\n meta.append(el('span', 'ocw-seen', ' · Seen'))\n }\n col.append(meta)\n row.append(col)\n return row\n }\n}\n\nfunction tick(s: NonNullable<RenderMessage['status']>): string {\n switch (s) {\n case 'read': return '✓✓' // blue double tick rendered via CSS colour\n case 'delivered': return '✓✓'\n case 'sent': return '✓'\n default: return '🕓'\n }\n}\n","import { persistentUid } from './uid.js'\nimport {\n asConversationId,\n type ClientFrame, type ConversationId,\n} from './protocol/index.js'\nimport { ChatStore } from './store.js'\nimport { ConnectionManager } from './connection.js'\nimport { PersistentOutbox } from './outbox.js'\nimport { E2ESession, extractX3DHInit } from './e2e.js'\nimport { Renderer, type WidgetConfig } from './renderer.js'\nimport { restoreHistory, resolveRelayUrls } from './history.js'\nimport { mountChatList, type ChatListEntry, type ChatListHandle } from './chatlist.js'\nimport { resolveStrings, resolveLocale, type WidgetI18nOverrides } from './i18n.js'\n\nexport interface UserInfo {\n /** Display name shown in the conversation (e.g. \"Sarah Chen\"). */\n name?: string\n /** Email address — passed as conversation metadata for agent context. */\n email?: string\n /** Avatar URL — shown as the guest's avatar in both widget and dashboard. */\n avatar?: string\n /** Any custom key/value metadata to attach to the conversation\n * (e.g. plan tier, account ID, page URL). Shown to agents in the sidebar. */\n meta?: Record<string, string>\n}\n\nexport interface MountOptions {\n el: HTMLElement\n /** Relay URL. Any scheme works — `https://api.example.com` is fine; the widget\n * derives the WebSocket URL (`wss://…/ws`) and REST base from it. */\n url: string\n /** HTTP(S) base for REST calls — only needed when the REST API is on a\n * DIFFERENT origin than the socket. Normally leave unset. */\n apiUrl?: string\n profileId: string\n subjectId?: string\n /** Open a user↔user direct conversation with `peerId` instead of a support\n * thread. Requires signed identity on the chatroom (both `kind: 'direct'`\n * and `peerId` together; `subjectId` is ignored — the server derives the\n * symmetric DM key so both sides land in the SAME conversation). */\n kind?: 'direct'\n peerId?: string\n /** IDENTITY (tiered — the host owns identity, the widget never has to persist it):\n * 1. `token` — a signed identity token. Either a capability token, or (recommended\n * for embedders) an ES256 JWT `{sub,iat,exp}` signed by your backend with the\n * private key whose public half is set as the chatroom's `guestPublicKey`.\n * The server cryptographically verifies it. Works in ANY language/environment,\n * no cookies or storage required. This is the production path.\n * 2. `userId` — a stable id you already have for the visitor (e.g. your logged-in\n * user id). Unauthenticated (\"you vouch for it\") but works everywhere. Used\n * only when `token` is absent.\n * 3. Neither — the widget falls back to best-effort local identity on the host\n * origin (first-party cookie + localStorage). A returning visitor on the same\n * browser keeps their history; if storage is blocked they get a fresh chat. */\n token?: string\n /** Called when a signed token is rejected (expired): return a fresh token\n * from your backend to renew the session without a reload. */\n refreshToken?: () => Promise<string | null>\n userId?: string\n subject?: WidgetConfig['subject']\n quickReplies?: string[]\n accent?: string\n /** Secondary accent (guest bubble + send button). Defaults to the design\n * blue; omit and it follows `accent` for a cohesive single-token retheme. */\n accent2?: string\n /** Colour scheme. Default 'light'. 'auto' follows the OS; 'dark'/'light' force it. */\n theme?: 'auto' | 'light' | 'dark'\n /** Load Baloo 2 + Nunito webfonts (default true). false = system fonts only. */\n webfont?: boolean\n /** Stack navigation: when set, the chatroom header shows a back chevron that\n * calls this. Used by `<ChatApp>` so tapping a conversation opens the room and\n * the back arrow returns to the list — native-app style. */\n onBack?: () => void\n /** Turn this into a self-contained inbox: the back chevron opens the user's\n * full thread list IN PLACE (no navigation), and picking a row opens that\n * thread with a back chevron returning to the list. */\n inbox?: boolean\n /** Inbox scope: 'tenant' (default) lists the user's threads across ALL your\n * chatrooms; 'profile' limits to this chatroom. */\n inboxScope?: 'tenant' | 'profile'\n /** Target language for the 🌐 translate button shown under every incoming\n * message (via the server's /translate endpoint). Defaults to the guest's\n * own language (from `locale`/navigator.language) — translation is ON by\n * default. Set a specific ISO code to force it, or '' to disable. */\n translateLang?: string\n /** If true, mount as a floating launcher button that opens/closes the chat */\n launcher?: boolean\n /** Position of the launcher button: default 'bottom-right' */\n position?: 'bottom-right' | 'bottom-left'\n /** Launcher teaser — the \"optional message\" card shown ABOVE the closed\n * launcher button to invite a chat (like Channel.io's greeting). Pass a\n * string for just a title, or `{ title, subtitle }`. If omitted, the\n * widget uses `defaults.launcherMessage` from the chatroom manifest when\n * present. Dismissible by the visitor (remembered for the browser session);\n * auto-hides once the chat is opened. Only applies in `launcher` mode. */\n launcherMessage?: string | { title: string; subtitle?: string }\n /** Optional user info for identified users. When provided, the name/email/\n * avatar are shown to agents in the dashboard instead of the anonymous ID.\n * The token still controls identity — this is display metadata only.\n * Anonymous users (no token, no user) remain fully anonymous. */\n user?: UserInfo\n /** Optional per-tenant feature switches. All default to ON (omit for current\n * behavior). Set a flag to `false` to disable the feature — the widget then\n * does no work for it (no reaction picker built per message, no CSAT panel,\n * no KB-deflection lookups). Gating is by not wiring the handler, so the\n * renderer skips the feature entirely. */\n features?: {\n reactions?: boolean // emoji reactions on messages (default true)\n csat?: boolean // post-resolution satisfaction survey (default true)\n deflection?: boolean // pre-first-message KB article suggestions (default true)\n }\n /** UI language for built-in strings. A BCP-47 tag ('ko', 'ko-KR'), bare code,\n * or name. Omit to auto-detect from the visitor's browser (navigator.language).\n * Currently Korean ⇄ English; anything non-Korean resolves to English. */\n locale?: string\n /** i18n: override UI strings. All keys are optional — omitted keys use the\n * built-in dictionary for the resolved `locale` (English by default). */\n i18n?: WidgetI18nOverrides\n}\n\nexport interface WidgetHandle { close(): void }\n\n\n// ── Mount registry ────────────────────────────────────────────────────────────\n// Tracks active widget instances per host element. Prevents double-mounting\n// when React strict mode, HMR, or caller code calls mount() twice on the same\n// element — the most common cause of two widgets appearing on one page.\nconst _registry = new WeakMap<Element, WidgetHandle>()\n// Launcher widgets attach to document.body (not the ref div), and in launcher\n// mode React may re-create the ref div on re-render — so the el-keyed registry\n// above can't catch a stale launcher. This slot-keyed registry guarantees at\n// most ONE launcher per (profileId, subjectId), so an identity flicker or\n// re-render can never leave two stacked bubbles/panels on the page.\nconst _launcherRegistry = new Map<string, WidgetHandle>()\nfunction launcherSlot(opts: MountOptions): string {\n return `relay-launcher::${opts.profileId}::${opts.subjectId ?? ''}`\n}\n\n// One AudioContext for ALL widget instances on the page (notification blips\n// are fire-and-forget; nothing about them is per-instance).\nlet _audioCtx: AudioContext | null = null\nfunction getAudioContext(): AudioContext | null {\n if (_audioCtx && _audioCtx.state !== 'closed') return _audioCtx\n try { _audioCtx = new AudioContext(); return _audioCtx } catch { return null }\n}\n\n/** Unmount any widget currently mounted on `el`. No-op if nothing is mounted. */\nexport function unmount(el: Element): void {\n _registry.get(el)?.close()\n _registry.delete(el)\n}\n\n// Vanilla in-widget inbox: a thread list ⇄ single thread, swapped in place inside\n// the host element. Reuses mountChatList + the single-thread mount() (same pieces\n// the React ChatApp composes) so the HTML embed gets a working inbox with no\n// page navigation — the back chevron in a thread returns to the list.\nfunction mountInbox(opts: MountOptions): WidgetHandle {\n const el = opts.el\n let room: WidgetHandle | null = null\n let list: ChatListHandle | null = null\n let closed = false\n\n const clear = () => { room?.close(); room = null; list?.close(); list = null; el.innerHTML = '' }\n\n function openList() {\n if (closed) return\n clear()\n list = mountChatList({\n el, url: opts.url,\n ...(opts.apiUrl ? { apiUrl: opts.apiUrl } : {}),\n ...(opts.profileId ? { profileId: opts.profileId } : {}),\n scope: opts.inboxScope ?? 'tenant',\n ...(opts.token ? { token: opts.token } : {}),\n ...(opts.userId ? { userId: opts.userId } : {}),\n ...(opts.accent ? { accent: opts.accent } : {}),\n ...(opts.theme ? { theme: opts.theme } : {}),\n ...(opts.webfont === false ? { webfont: false } : {}),\n ...(opts.locale ? { locale: opts.locale } : {}),\n ...(opts.i18n ? { i18n: opts.i18n } : {}),\n onSelect: (entry: ChatListEntry) => openRoom(entry),\n })\n }\n\n function openRoom(entry: ChatListEntry) {\n if (closed) return\n clear()\n // Drop the inbox-level subject/kind so each opened thread carries only its own.\n const { subjectId: _s, kind: _k, peerId: _p, ...base } = opts\n room = mount({\n ...base,\n inbox: false,\n launcher: false,\n el,\n ...(entry.profileId ? { profileId: entry.profileId } : {}),\n ...(entry.subjectId ? { subjectId: entry.subjectId } : {}),\n ...(entry.kind === 'direct' && entry.peerId ? { kind: 'direct', peerId: entry.peerId } : {}),\n onBack: openList,\n })\n }\n\n openList()\n return { close() { closed = true; clear() } }\n}\n\nexport function mount(opts: MountOptions): WidgetHandle {\n // In-widget inbox: reuse the same chatlist + single-thread mount that the React\n // ChatApp composes, but in vanilla — so the HTML embed gets a real thread list\n // in place (back → list → pick → thread → back), not a host navigation.\n if (opts.inbox) return mountInbox(opts)\n\n // Auto-close any previous instance on this exact element before re-mounting.\n // Covers React double-invoke in StrictMode, HMR, and accidental duplicate calls.\n if (_registry.has(opts.el)) {\n _registry.get(opts.el)!.close()\n _registry.delete(opts.el)\n }\n // Launcher mode: also close any prior launcher for the same slot, even if it\n // was mounted on a now-detached div (React re-creates the ref div on\n // re-render). This is what prevents two stacked widgets after an identity\n // flicker (anonymous → logged-in).\n if (opts.launcher) _launcherRegistry.get(launcherSlot(opts))?.close()\n\n // Tiered identity (see MountOptions): a host-provided signed token wins, then a\n // host-vouched userId, then best-effort local persistence. The widget never\n // depends on its own storage when the host supplies identity — which is what\n // makes it safe to embed in any environment (iframes, webviews, SSR, etc.).\n // Always keep the stable per-browser anonymous id, even when the host\n // identifies the visitor — so on login we can tell the server to merge the\n // anonymous conversation into the user (Channel.io-style boot+identify).\n const anonId = persistentUid()\n let deflectTimer: ReturnType<typeof setTimeout> | undefined\n let destroyed = false\n const token = opts.token ?? opts.userId ?? anonId\n // If we're connecting as an identified user (token differs from the anon id),\n // pass the anon id as linkFrom so the server adopts any anonymous history.\n const linkFrom = token !== anonId ? anonId : undefined\n // Accept any scheme on `url` (https/http/wss/ws) and derive both the concrete\n // WebSocket URL and the REST base from it. `apiUrl` overrides the REST base\n // only when the API is on a different origin than the socket.\n const { wsUrl, httpBase } = resolveRelayUrls(opts.url, opts.apiUrl)\n let store = new ChatStore(token as never)\n // Key the outbox by token + subjectId so each listing has its own pending queue.\n // Without this, a pending message from listing A appears as a ghost on listing B.\n const outboxKey = opts.subjectId ? `${token}::${opts.subjectId}` : token\n const outbox = new PersistentOutbox(outboxKey)\n let cid: ConversationId | undefined\n let outboxRestored = false\n\n let _mql: MediaQueryList | null = null\n let _mqlHandler: ((e: MediaQueryListEvent) => void) | null = null\n let _escHandler: ((e: KeyboardEvent) => void) | null = null\n let _paintTeaser: (() => void) | null = null\n // (e.g. a bare `<div id=\"chat\"></div>` with no CSS). Without this the\n // widget's internal `height:100%` collapses to near-zero. Only applies\n // when the element truly has no height set — explicit CSS always wins.\n if (!opts.launcher && !opts.el.style.height && opts.el.clientHeight === 0) {\n opts.el.style.width = opts.el.style.width || '100%'\n opts.el.style.height = '600px'\n }\n\n // Restore pending outbox items for this specific listing/conversation.\n // The outbox is keyed by token+subjectId so ghost bubbles from other listings\n // never appear here.\n for (const item of outbox.load()) store.addOptimistic(item.clientMsgId, item.content)\n\n // ── Launcher mode ─────────────────────────────────────────────────────────\n let launcherEl: HTMLElement | null = null\n let badgeEl: HTMLElement | null = null\n let unread = 0\n let open = !opts.launcher // start open when not in launcher mode\n\n if (opts.launcher) {\n const pos = opts.position ?? 'bottom-right'\n const isRight = pos.includes('right')\n\n // Outer wrapper holds both the panel and the bubble button\n launcherEl = document.createElement('div')\n launcherEl.style.cssText = `position:fixed;${isRight ? 'right:20px' : 'left:20px'};bottom:20px;z-index:9999;display:flex;flex-direction:column;align-items:${isRight ? 'flex-end' : 'flex-start'};gap:12px`\n\n // ── Chat panel — sits above the bubble ─────────────────────────────────\n const panel = document.createElement('div')\n // Responsive panel: full-screen on mobile (<480px); on desktop 380×600\n // CLAMPED to the viewport (`min(…)`) so a short or narrow browser window\n // never clips the composer — same clamp the React launcher already uses.\n // A MediaQueryList keeps the layout live across rotation/resize.\n const mql = typeof window !== 'undefined' && typeof window.matchMedia === 'function'\n ? window.matchMedia('(max-width: 479px)') : null\n const applyPanelLayout = (mobile: boolean) => {\n // Fullscreen is the ONLY case that strips the widget's corner radius —\n // the renderer keys .ocw-fs, never a blanket viewport query, so inline\n // mobile embeds keep their normal in-page layout.\n opts.el.classList.toggle('ocw-fs', mobile)\n panel.style.cssText = mobile ? [\n 'position:fixed', 'inset:0', 'width:100%', 'height:100dvh',\n 'border-radius:0', 'overflow:hidden',\n 'box-shadow:none', 'display:none', 'flex-direction:column', 'background:#fff',\n 'transition:opacity .18s', 'opacity:0', 'z-index:9998',\n ].join(';') : [\n 'width:min(380px, calc(100vw - 40px))', 'height:min(600px, calc(100dvh - 108px))',\n 'border-radius:28px', 'overflow:hidden',\n 'box-shadow:0 12px 32px rgba(108,92,231,.14)',\n 'display:none', 'flex-direction:column', 'background:#fff',\n 'transform-origin:bottom ' + (isRight ? 'right' : 'left'),\n 'transition:opacity .18s,transform .18s', 'opacity:0', 'transform:scale(.95)',\n ].join(';')\n }\n applyPanelLayout(mql?.matches ?? false)\n const mqlHandler = (e: MediaQueryListEvent): void => applyPanelLayout(e.matches)\n mql?.addEventListener('change', mqlHandler)\n _mql = mql; _mqlHandler = mqlHandler\n\n // Move the mount target INSIDE the panel — not full-page\n opts.el.style.cssText = 'width:100%;height:100%;overflow:hidden'\n panel.append(opts.el)\n\n // ── Bubble button ─────────────────────────────────────────────────────\n // Inline SVGs, not emoji: '💬'/'✕' render differently on every OS (and as\n // colour emoji can clash with the accent); these are crisp everywhere.\n const CHAT_SVG =\n '<svg viewBox=\"0 0 24 24\" width=\"26\" height=\"26\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">' +\n '<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\" ' +\n 'fill=\"currentColor\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>'\n const CLOSE_SVG =\n '<svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">' +\n '<path d=\"M18 6 6 18M6 6l12 12\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"/></svg>'\n\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.style.cssText = [\n `width:56px;height:56px;border-radius:50%`,\n `background:${opts.accent ?? '#6c5ce7'}`,\n `color:#fff;border:none;cursor:pointer`,\n `box-shadow:0 12px 32px rgba(108,92,231,.14)`,\n `position:relative;flex:none`,\n `display:flex;align-items:center;justify-content:center`,\n `transition:transform .15s`,\n ].join(';')\n btn.onmouseenter = () => { btn.style.transform = 'scale(1.08)' }\n btn.onmouseleave = () => { btn.style.transform = 'scale(1)' }\n\n badgeEl = document.createElement('span')\n badgeEl.style.cssText = `position:absolute;top:-4px;right:-4px;background:#4c6fff;color:#fff;border-radius:50%;width:20px;height:20px;font-size:11px;font-weight:700;display:none;align-items:center;justify-content:center`\n\n // (Re)build the bubble's content for the current open state. innerHTML\n // wipes children, so the badge is re-appended each time.\n const paintBubble = () => {\n btn.innerHTML = open ? CLOSE_SVG : CHAT_SVG\n btn.setAttribute('aria-label', open ? 'Close chat' : 'Open chat')\n btn.setAttribute('aria-expanded', String(open))\n btn.append(badgeEl!)\n }\n paintBubble()\n\n launcherEl.append(panel, btn)\n document.body.append(launcherEl)\n\n // ── Launcher teaser (the \"optional message\" card) ─────────────────────\n // A small dismissible card above the button inviting a chat. Source of\n // truth: the explicit `launcherMessage` option (instant), else the\n // chatroom manifest's `defaults.launcherMessage` (arrives on connect).\n // Dismissal is per browser session so it doesn't nag across page nav.\n const teaserKey = `ocw-teaser-dismissed::${opts.profileId}`\n const optMsg = typeof opts.launcherMessage === 'string'\n ? { title: opts.launcherMessage }\n : opts.launcherMessage\n let teaserEl: HTMLElement | null = null\n const teaserDismissed = (): boolean => {\n try { return sessionStorage.getItem(teaserKey) === '1' } catch { return false }\n }\n const CHAT_MINI_SVG =\n '<svg viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">' +\n '<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\" ' +\n 'fill=\"currentColor\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>'\n const paintTeaser = (): void => {\n const msg = optMsg?.title ? optMsg : (store.launcherMessage ?? undefined)\n const shouldShow = !!msg?.title && !open && !teaserDismissed()\n if (!shouldShow) { if (teaserEl) { teaserEl.remove(); teaserEl = null } return }\n if (teaserEl) return // already shown; don't rebuild (avoids flicker)\n teaserEl = document.createElement('div')\n teaserEl.setAttribute('role', 'button')\n teaserEl.setAttribute('tabindex', '0')\n teaserEl.setAttribute('aria-label', msg!.title)\n teaserEl.style.cssText = [\n 'max-width:280px', 'background:#fff', 'border-radius:16px',\n 'box-shadow:0 12px 32px rgba(108,92,231,.14)', 'padding:14px 40px 14px 16px',\n 'position:relative', 'cursor:pointer', 'font-family:inherit',\n `align-self:${isRight ? 'flex-end' : 'flex-start'}`,\n 'animation:ocw-teaser-in .22s ease-out',\n ].join(';')\n const title = document.createElement('div')\n title.textContent = msg!.title\n title.style.cssText = 'font-size:15px;font-weight:600;color:#111;line-height:1.35'\n teaserEl.append(title)\n if (msg!.subtitle) {\n const sub = document.createElement('div')\n sub.style.cssText = 'display:flex;align-items:center;gap:6px;margin-top:6px;font-size:13px;color:#6b7280'\n const ic = document.createElement('span'); ic.innerHTML = CHAT_MINI_SVG; ic.style.cssText = `color:${opts.accent ?? '#6c5ce7'};display:inline-flex`\n const st = document.createElement('span'); st.textContent = msg!.subtitle\n sub.append(ic, st); teaserEl.append(sub)\n }\n // Close (×) — dismiss for the session without opening the chat.\n const x = document.createElement('button')\n x.type = 'button'\n x.setAttribute('aria-label', 'Dismiss')\n x.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 x.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 x.addEventListener('click', (e) => {\n e.stopPropagation()\n try { sessionStorage.setItem(teaserKey, '1') } catch { /* storage blocked — dismiss for this pageview only */ }\n if (teaserEl) { teaserEl.remove(); teaserEl = null }\n })\n teaserEl.append(x)\n const openFromTeaser = (): void => {\n if (open) return\n open = true; showPanel(true); paintBubble()\n unread = 0; if (badgeEl) badgeEl.style.display = 'none'\n if (teaserEl) { teaserEl.remove(); teaserEl = null }\n }\n teaserEl.addEventListener('click', openFromTeaser)\n teaserEl.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openFromTeaser() } })\n // Insert ABOVE the button (button is the last child of launcherEl).\n launcherEl!.insertBefore(teaserEl, btn)\n }\n // Keyframes for the gentle pop-in (injected once).\n if (!document.getElementById('ocw-teaser-style')) {\n const st = document.createElement('style'); st.id = 'ocw-teaser-style'\n st.textContent = '@keyframes ocw-teaser-in{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}'\n document.head.append(st)\n }\n _paintTeaser = paintTeaser\n paintTeaser() // shows immediately when the option is set; manifest repaints later\n\n const showPanel = (show: boolean) => {\n if (show) {\n panel.style.display = 'flex'\n requestAnimationFrame(() => { panel.style.opacity = '1'; panel.style.transform = 'scale(1)' })\n } else {\n panel.style.opacity = '0'; panel.style.transform = 'scale(.95)'\n setTimeout(() => { if (!open) panel.style.display = 'none' }, 180)\n }\n }\n\n btn.addEventListener('click', () => {\n open = !open\n showPanel(open)\n paintBubble()\n if (open) {\n unread = 0; if (badgeEl) badgeEl.style.display = 'none'\n // Opening satisfies the teaser's whole purpose — retire it for the session.\n try { sessionStorage.setItem(teaserKey, '1') } catch { /* ignore */ }\n }\n _paintTeaser?.()\n })\n\n // Close on Escape. Kept as a named handler so close() can REMOVE it —\n // the old anonymous listener outlived the widget (leaked on every\n // remount/identify, and a stale one could still flip `open`).\n _escHandler = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && open) { open = false; showPanel(false); paintBubble() }\n }\n document.addEventListener('keydown', _escHandler)\n }\n\n const addUnread = () => {\n if (open) return\n unread++\n if (badgeEl) { badgeEl.textContent = String(unread); badgeEl.style.display = 'flex' }\n }\n\n // ── Notification sound ────────────────────────────────────────────────────\n const playSound = () => {\n try {\n // ONE lazy shared context, resumed on each play. The old code created a\n // fresh AudioContext per message; browsers cap concurrent contexts (~6\n // in Chrome), after which construction throws and chat goes silent.\n const ctx = getAudioContext()\n if (!ctx) return\n if (ctx.state === 'suspended') void ctx.resume().catch(() => {})\n const osc = ctx.createOscillator(); const gain = ctx.createGain()\n osc.connect(gain); gain.connect(ctx.destination)\n osc.frequency.setValueAtTime(880, ctx.currentTime)\n osc.frequency.exponentialRampToValueAtTime(440, ctx.currentTime + 0.15)\n gain.gain.setValueAtTime(0.3, ctx.currentTime)\n gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.3)\n osc.start(); osc.stop(ctx.currentTime + 0.3)\n } catch { /* audio not available */ }\n }\n\n // Messages typed before the 'opened' frame arrives are queued here and\n // flushed once cid is known. This prevents silent message loss when the\n // user types immediately after the widget mounts (before WS handshake).\n const preSendQueue: { clientMsgId: string; content: import('./protocol/index.js').MessageContent }[] = []\n\n const flushPreSendQueue = (conversationId: ConversationId) => {\n while (preSendQueue.length) {\n const item = preSendQueue.shift()!\n outbox.add({ clientMsgId: item.clientMsgId, content: item.content, ts: Date.now() })\n conn.send({ type: 'send', conversationId, clientMsgId: item.clientMsgId, content: item.content })\n }\n }\n\n let conn: ConnectionManager\n // E2E is compiled out of the 'lite' build (__E2E__=false) via dead-code\n // elimination — non-encrypted chatrooms don't ship the crypto. In the lite\n // build e2e is null and encrypted rooms are refused (never sent as plaintext).\n const e2e = __E2E__ ? new E2ESession(`ocw-e2e-${opts.profileId}`) : null\n let e2eStarted = false\n // Live ECDH pending (peer not yet online)\n const pending: { clientMsgId: string; text: string }[] = []\n // X3DH async: pending send awaiting the peer's prekey bundle\n const x3dhPending: { clientMsgId: string; text: string }[] = []\n let x3dhBundleFetched = false\n\n const sendSealed = (clientMsgId: string, text: string): void => {\n void e2e!.sealText(text).then((content) => {\n if (cid) conn.send({ type: 'send', conversationId: cid, clientMsgId, content })\n })\n }\n\n const sendSealedX3DH = (clientMsgId: string, text: string, x3dhInit: { ephemeralKey: string; spkId: string; senderIK: string; usedOTP: boolean }): void => {\n void e2e!.sealText(text, x3dhInit).then((content) => {\n if (cid) conn.send({ type: 'send', conversationId: cid, clientMsgId, content })\n })\n }\n\n const flushPending = (): void => {\n while (pending.length) { const p = pending.shift()!; sendSealed(p.clientMsgId, p.text) }\n while (x3dhPending.length) { const p = x3dhPending.shift()!; sendSealed(p.clientMsgId, p.text) }\n }\n\n /** Fetch the peer's prekey bundle and perform X3DH sender init. */\n const fetchAndX3DH = (targetUserId: string): void => {\n conn.send({ type: 'fetchPrekey', targetUserId: targetUserId as never })\n }\n\n // Auto-localize: pick the visitor's language (explicit opts.locale → else the\n // browser), then let any host per-key i18n overrides win. Downstream code that\n // reads `i18n.<key> ?? 'English'` now receives the resolved (e.g. Korean) value.\n const i18n = resolveStrings(opts.locale, opts.i18n)\n // Auto-detect RTL for Arabic/Hebrew/Persian/Urdu regardless of i18n strings\n const rtlLocales = ['ar', 'he', 'fa', 'ur']\n const browserLang = typeof navigator !== 'undefined' ? (navigator.language ?? '').slice(0, 2).toLowerCase() : ''\n if (rtlLocales.includes(browserLang) && !opts.el.dir) {\n opts.el.dir = 'rtl'\n opts.el.style.fontFamily = opts.el.style.fontFamily || 'Tahoma,Arial,system-ui,sans-serif'\n }\n // Pre-chat completion is per (chatroom, identity) — a returning visitor who\n // already qualified goes straight to the composer.\n const preChatKey = `oc_prechat_${opts.profileId}_${token.slice(-8)}`\n\n const renderer = new Renderer(opts.el, token, {\n onSend(text) {\n const clientMsgId = `cm_${Math.random().toString(36).slice(2)}`\n const content: import('./protocol/index.js').MessageContent = { kind: 'text', text }\n store.addOptimistic(clientMsgId, content)\n renderer.render(store)\n if (store.e2e) {\n if (!__E2E__) {\n console.error('[relay] this chat is end-to-end encrypted — use the full widget build')\n return\n }\n if (e2e!.ready) {\n sendSealed(clientMsgId, text)\n } else if (x3dhBundleFetched) {\n x3dhPending.push({ clientMsgId, text })\n } else {\n pending.push({ clientMsgId, text })\n if (store.assignedAgentId) fetchAndX3DH(store.assignedAgentId)\n }\n } else if (!cid) {\n // Connection not yet opened — queue the message; flushed on 'opened'\n preSendQueue.push({ clientMsgId, content })\n } else {\n outbox.add({ clientMsgId, content, ts: Date.now() })\n conn.send({ type: 'send', conversationId: cid, clientMsgId, content })\n }\n },\n async onAttach(file: File) {\n if (!cid) return\n const uploadUrl = `${httpBase}/upload?name=${encodeURIComponent(file.name)}`\n const clientMsgId = `cm_${Math.random().toString(36).slice(2)}`\n // Optimistic: show uploading state\n store.addOptimistic(clientMsgId, { kind: 'text', text: `📎 Uploading ${file.name}…` })\n renderer.render(store)\n try {\n const res = await fetch(uploadUrl, {\n method: 'POST',\n // Use the resolved connection token (a signed JWT, an explicit userId,\n // or the anonymous persistent uid) — NOT just opts.token. The server\n // requires an Authorization header, and the verifier accepts an\n // anonymous id as a guest; keying off opts.token alone meant anonymous\n // visitors (the common case) sent no auth and every upload 401'd.\n headers: { 'content-type': file.type, authorization: `Bearer ${token}` },\n body: file,\n })\n if (!res.ok) throw new Error(`Upload failed: ${res.status}`)\n const { url, name, mime, size } = await res.json() as { url: string; name: string; mime: string; size: number }\n // Swap the \"📎 Uploading…\" placeholder for the REAL attachment content,\n // keyed by the SAME clientMsgId, BEFORE we send. Without this the\n // optimistic entry stayed as placeholder text: the server's `ack`\n // carries no content (see frames.ts), so reconciliation did\n // `{...msg, status:'sent'}` and kept the \"Uploading…\" text forever. The\n // real attachment only appeared on the next history fetch — i.e. after\n // a refresh — which reads exactly as \"the upload is slow\" even though\n // it already succeeded. Re-seeding here means the ack confirms an\n // attachment, and it renders immediately in the live view.\n store.addOptimistic(clientMsgId, { kind: 'attachment', url, name, mime, size })\n renderer.render(store)\n conn.send({ type: 'send', conversationId: cid, clientMsgId, content: { kind: 'attachment', url, name, mime, size } })\n } catch (e) {\n store.addOptimistic(clientMsgId, { kind: 'text', text: `⚠️ Upload failed: ${(e as Error).message}` })\n renderer.render(store)\n }\n },\n onInvoke(actionId, inputs) {\n if (!cid) return\n conn.send({ type: 'invoke', conversationId: cid, actionId, clientInvokeId: `iv_${Math.random().toString(36).slice(2)}`, ...(inputs ? { inputs } : {}) })\n },\n onTyping(isTyping, preview) { if (cid) conn.send({ type: 'typing', conversationId: cid, isTyping, ...(preview ? { preview } : {}) }) },\n onPreChat(values) {\n // Persist \"done\" per (chatroom, browser identity) so reloads skip the form.\n try { localStorage.setItem(preChatKey, '1') } catch { /* private mode */ }\n // Identity fields flow through the SAME open+userInfo path the host's\n // `user` config uses — the engine sanitizes and stores them on the\n // conversation (guestName/guestEmail; phone lands in guest meta).\n conn.send({\n type: 'open', profileId: opts.profileId as never,\n ...(opts.subjectId ? { subjectId: opts.subjectId as never } : {}),\n userInfo: {\n ...(values.name ? { name: values.name } : {}),\n ...(values.email ? { email: values.email } : {}),\n ...(values.phone || values.topic ? { meta: {\n ...(values.phone ? { phone: values.phone } : {}),\n ...(values.topic ? { topic: values.topic } : {}),\n } } : {}),\n },\n } as never)\n // Topic / callback become the visible first line so agents see the\n // qualification without opening the CRM pane. A callback request is\n // explicit and carries the number.\n const first = values.callback\n ? `📞 Call-back requested${values.phone ? `: ${values.phone}` : ''}${values.topic ? ` — ${values.topic}` : ''}`\n : values.topic ? `Topic: ${values.topic}` : ''\n // E2E rooms: identity fields still flow (userInfo above), but the\n // qualification line must not be sent as plaintext into an encrypted\n // conversation — agents see topic/phone in the CRM pane instead.\n if (first && cid && !store.e2e) {\n conn.send({ type: 'send', conversationId: cid, clientMsgId: `pc_${Math.random().toString(36).slice(2, 12)}`, content: { kind: 'text', text: first } })\n }\n },\n ...(opts.features?.deflection !== false ? {\n onDeflectQuery(q: string) {\n if (destroyed) return\n // Debounced keyword lookup against the chatroom's KB — \"was this your\n // question?\" before the first message ever sends. Fails silent: a KB\n // hiccup must never affect typing.\n clearTimeout(deflectTimer)\n const query = q.trim()\n if (query.length < 3) { renderer.hideDeflection(); return }\n deflectTimer = setTimeout(() => {\n void fetch(`${httpBase}/kb/search?profileId=${encodeURIComponent(opts.profileId)}&q=${encodeURIComponent(query.slice(0, 200))}`)\n .then(r => (r.ok ? r.json() : { articles: [] }))\n .then((d: { articles?: { id: string; title: string; answer: string }[] }) => renderer.showDeflection(d.articles ?? []))\n .catch(() => renderer.hideDeflection())\n }, 350)\n },\n } : {}),\n onReadUpTo(seq) { if (cid) conn.send({ type: 'read', conversationId: cid, seq }) },\n onLoadMore() {\n // WS fallback for E2E rooms where REST history can't be decrypted.\n // Non-E2E rooms use scroll-triggered REST pagination from restoreHistory().\n if (!cid || !store.e2e) return\n const oldest = store.messages()[0]\n if (oldest) conn.send({ type: 'history', conversationId: cid, beforeSeq: oldest.seq, limit: 20 })\n },\n onEdit(messageId, newText) {\n if (cid) conn.send({ type: 'edit', conversationId: cid, messageId: messageId as never, content: { kind: 'text', text: newText } })\n },\n onDelete(messageId) {\n if (cid) conn.send({ type: 'delete', conversationId: cid, messageId: messageId as never })\n },\n ...(opts.features?.reactions !== false ? {\n onReact(messageId: string, emoji: string, remove: boolean) {\n if (!cid) return\n conn.send({ type: 'react', conversationId: cid, messageId: messageId as never, emoji, remove })\n },\n } : {}),\n ...(opts.features?.csat !== false ? {\n onCsat(score: number) {\n if (!cid) return\n fetch(`${httpBase}/conversations/${cid}/csat`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },\n body: JSON.stringify({ score }),\n }).catch(() => {})\n },\n } : {}),\n ...((() => {\n // Translation is ON BY DEFAULT: a 🌐 button under every incoming message\n // translates it into the GUEST'S OWN language. Target = explicit\n // `translateLang`, else the guest's real locale (raw navigator.language /\n // `locale` — e.g. \"es\", \"ja\", not just the en/ko UI locale), so a guest\n // whose language the UI doesn't cover still gets translations. Server\n // returns {translated:null} when no provider is configured → the button\n // degrades gracefully. Pass translateLang:'' to opt OUT.\n const rawLocale = opts.locale ?? (typeof navigator !== 'undefined' ? navigator.language : '')\n const target = opts.translateLang !== undefined ? opts.translateLang : (rawLocale ? rawLocale.split('-')[0]! : resolveLocale())\n if (!target) return {}\n return {\n async onTranslate(text: string) {\n try {\n const res = await fetch(`${httpBase}/translate`, {\n method: 'POST',\n headers: { 'content-type': 'application/json', authorization: `Bearer ${token}` },\n body: JSON.stringify({ text, targetLang: target }),\n })\n if (!res.ok) return null\n const { translated } = await res.json() as { translated: string | null }\n return translated\n } catch { return null }\n },\n }\n })()),\n ...(opts.onBack ? { onBack: opts.onBack } : {}),\n }, {\n ...(opts.subject ? { subject: opts.subject } : {}),\n ...(opts.quickReplies ? { quickReplies: opts.quickReplies } : {}),\n ...(opts.accent ? { accent: opts.accent } : {}),\n ...(opts.accent2 ? { accent2: opts.accent2 } : {}),\n ...(opts.theme ? { theme: opts.theme } : {}),\n ...(opts.webfont === false ? { webfont: false } : {}),\n ...(opts.user?.name || opts.user?.avatar ? { userInfo: { ...(opts.user.name ? { name: opts.user.name } : {}), ...(opts.user.avatar ? { avatar: opts.user.avatar } : {}) } } : {}),\n i18n,\n\n })\n\n // Returning visitor who already completed pre-chat → straight to composer.\n try { if (localStorage.getItem(preChatKey)) renderer.completePreChat() } catch { /* private mode */ }\n\n // Identified-user display info rides on the open frame itself: the server\n // persists it onto the conversation (sanitized) so agents see who they're\n // talking to. The previous approach — sending a `note` frame after 'opened' —\n // never worked: `note` is agent-only, so the server answered FORBIDDEN and\n // the info was silently dropped. Carrying it on `open` also means it reaches\n // the dashboard for EXISTING conversations (e.g. a visitor who logs in after\n // chatting anonymously), not just brand-new empty ones.\n const userInfo = opts.user && (opts.user.name || opts.user.email || opts.user.avatar || opts.user.meta)\n ? {\n ...(opts.user.name ? { name: opts.user.name } : {}),\n ...(opts.user.email ? { email: opts.user.email } : {}),\n ...(opts.user.avatar ? { avatar: opts.user.avatar } : {}),\n ...(opts.user.meta ? { meta: opts.user.meta } : {}),\n }\n : undefined\n\n const openFrame: Extract<ClientFrame, { type: 'open' }> = {\n type: 'open', profileId: opts.profileId as never,\n // Direct conversations use the kind/peerId pair; the dm:… subject key is\n // server-derived and owner-keyed, so passing it as subjectId from the\n // NON-owner side would find-or-create a junk duplicate thread.\n ...(opts.kind === 'direct' && opts.peerId\n ? { kind: 'direct' as const, peerId: opts.peerId as never }\n : opts.subjectId ? { subjectId: opts.subjectId as never } : {}),\n ...(linkFrom ? { linkFrom: linkFrom as never } : {}),\n ...(userInfo ? { userInfo } : {}),\n ...(typeof location !== 'undefined' ? { pageUrl: location.href } : {}),\n ...(typeof document !== 'undefined' && document.title ? { pageTitle: document.title } : {}),\n // Pass subject display info so the server can persist it to the Subject record.\n // This is how listingTitle and listingMeta get saved without a separate API call.\n ...(opts.subject?.title ? { subjectTitle: opts.subject.title } : {}),\n ...(opts.subject?.subtitle ? { subjectMeta: opts.subject.subtitle } : {}),\n }\n\n conn = new ConnectionManager({\n ...(opts.refreshToken ? { refreshToken: opts.refreshToken } : {}),\n url: wsUrl, token, open: openFrame,\n getCursor: () => store.highestSeq(),\n onStatusChange: (s, msg) => renderer.setConnStatus(s, msg),\n onFrame(frame) {\n if (frame.type === 'opened') {\n cid = frame.conversation.id\n\n // Flush persistent outbox exactly once (idempotent: items are ack-removed).\n if (!outboxRestored) {\n outboxRestored = true\n for (const item of outbox.load()) {\n conn.send({ type: 'send', conversationId: cid, clientMsgId: item.clientMsgId, content: item.content })\n }\n }\n\n // Restore history on EVERY successful open (covers reconnects too).\n // On reconnect the store still has messages in memory so this is a\n // no-op if there's nothing newer — cheap REST call, correct behaviour.\n void restoreHistory(wsUrl, token, cid, store, renderer, httpBase)\n\n // Flush any messages typed while the connection was still opening.\n if (preSendQueue.length) flushPreSendQueue(cid)\n }\n\n if (frame.type === 'ack') outbox.remove(frame.clientMsgId)\n\n // X3DH: handle incoming prekey bundle response\n if (__E2E__ && frame.type === 'prekeyBundle') {\n if (frame.bundle) {\n x3dhBundleFetched = true\n void e2e!.x3dhSendTo(frame.bundle).then((x3dhInit) => {\n // Drain any X3DH-pending messages with the derived key.\n const toSend = [...pending.splice(0), ...x3dhPending.splice(0)]\n for (const p of toSend) sendSealedX3DH(p.clientMsgId, p.text, x3dhInit)\n renderer.render(store)\n })\n }\n // If no bundle, peer has no prekeys; fall back to live ECDH queue\n return\n }\n\n // X3DH recipient: detect init message in incoming encrypted messages\n if (__E2E__ && frame.type === 'message' && store.e2e) {\n const x3dh = extractX3DHInit(frame.message.content)\n if (x3dh && !e2e!.ready) {\n void e2e!.x3dhReceiveFrom(x3dh.x3dhIK, x3dh.x3dhEK, x3dh.x3dhSPK, x3dh.x3dhOTP).then(async () => {\n // Now decrypt the message that carried the init\n await e2e!.openFrame(frame)\n store.apply(frame)\n renderer.render(store)\n })\n return\n }\n }\n\n if (__E2E__ && frame.type === 'peerkey') {\n void e2e!.onPeerKey(frame.key).then(() => { flushPending(); renderer.render(store) })\n return\n }\n void (async () => {\n if (__E2E__ && store.e2e) await e2e!.openFrame(frame)\n store.apply(frame)\n // The manifest carries the launcher teaser (defaults.launcherMessage);\n // repaint so a manifest-configured message appears over the closed launcher.\n if (frame.type === 'manifest') {\n _paintTeaser?.()\n // A3.3 — per-chatroom bot identity: prefer the manifest's botName for\n // the bot bubble label over the generic \"AI Assistant\".\n const bn = (frame as { botName?: string }).botName\n if (bn) renderer.setBotName(bn)\n }\n // Trigger badge + sound for new messages from others.\n // In chatList mode, skip the badge if the user is already in the chat\n // screen for this conversation — they can see the message immediately.\n if (frame.type === 'message' && frame.message.senderId !== (token as never) && !frame.message.internal) {\n addUnread()\n playSound()\n }\n // Once we learn the room is E2E, run the key handshake exactly once.\n if (__E2E__ && store.e2e && cid && !e2eStarted) {\n e2eStarted = true\n // Upload our prekey bundle for async E2E support.\n const prekeyPayload = await e2e!.initX3DH()\n conn.send({ type: 'uploadPrekeys', ...prekeyPayload })\n // Also do live ECDH handshake in case peer is already online.\n const liveKey = await e2e!.begin()\n conn.send({ type: 'pubkey', conversationId: cid, key: liveKey })\n }\n renderer.render(store)\n // Keep seenSeq in sync so the chat list shows accurate unread counts\n })()\n },\n })\n\n\n conn.connect()\n // Show restored 'pending' bubbles (if any) immediately, before the socket opens.\n renderer.render(store)\n\n const slot = opts.launcher ? launcherSlot(opts) : null\n const handle: WidgetHandle = { close: () => {\n destroyed = true\n clearTimeout(deflectTimer)\n conn.close(); launcherEl?.remove(); renderer.destroy()\n if (_mql && _mqlHandler) _mql.removeEventListener('change', _mqlHandler)\n if (_escHandler) { document.removeEventListener('keydown', _escHandler); _escHandler = null }\n _registry.delete(opts.el)\n if (slot && _launcherRegistry.get(slot) === handle) _launcherRegistry.delete(slot)\n } }\n _registry.set(opts.el, handle)\n if (slot) _launcherRegistry.set(slot, handle)\n return handle\n}\n\nexport { ChatStore } from './store.js'\nexport { ConnectionManager } from './connection.js'\nexport { Renderer } from './renderer.js'\nexport { asConversationId }\nexport { E2ESession, extractX3DHInit, type X3DHBundle } from './e2e.js'\nexport { PersistentOutbox, type OutboxItem } from './outbox.js'\nexport { restoreHistory, httpBaseFromWsUrl, resolveRelayUrls } from './history.js'\n\n"],"names":["CSS","lightTokens","darkTokens","STYLE_ID","REACTION_EMOJIS","COMPACT_BREAKPOINT","SEND_ICON_SVG","injectStyles","s","FONT_ID","injectFonts","l","el","tag","cls","text","n","linkifyInto","parent","urlRe","last","first","m","tn","href","trail","mt","a","tail","fmtTime","ts","contentText","c","_a","Renderer","root","me","h","cfg","__publicField","accent2","head","back","brandName","_b","avatarEl","hm","_c","_d","_e","_f","_g","typingBubble","q","b","_h","sendBtn","sendLabel","_i","e","attachBtn","fileInput","inputRow","footer","_j","_k","ta","applyCompact","entries","w","fn","sh","preview","name","store","ownerLabel","actions","prevScrollHeight","prevScrollTop","sentinel","maxOther","prevSender","msgs","i","showLabel","typingNames","bubble","guestHasSpoken","copy","terminalStates","isTerminal","note","status","message","fatal","form","inputs","f","inp","topicSel","ph","t","o","callbackCb","phoneForCb","row","submit","email","callback","phone","articles","card","stars","btns","bb","idx","title","tags","k","v","btn","label","resolve","overlay","cancel","ok","close","prefill","panel","pre","sel","opt","out","isLast","sys","mine","isNote","col","who","bubbleWrap","replyCtx","textNode","img","video","ap","links","gLink","iLink","chips","x","cta","original","translateBtn","transLine","showLine","hideLine","cached","result","menu","editBtn","saveBtn","cancelBtn","btnRow","editPanel","restore","newText","ke","delBtn","cites","src","fb","mk","emoji","already","reactWrap","reactRow","users","pill","addBtn","picker","pb","alreadyReacted","opening","u","meta","tick","_registry","_launcherRegistry","launcherSlot","opts","_audioCtx","getAudioContext","unmount","mountInbox","room","list","closed","clear","openList","mountChatList","entry","openRoom","_s","_p","base","mount","anonId","persistentUid","deflectTimer","destroyed","token","linkFrom","wsUrl","httpBase","resolveRelayUrls","ChatStore","outboxKey","outbox","PersistentOutbox","cid","outboxRestored","_mql","_mqlHandler","_escHandler","_paintTeaser","item","launcherEl","badgeEl","unread","open","isRight","mql","applyPanelLayout","mobile","mqlHandler","CHAT_SVG","CLOSE_SVG","paintBubble","teaserKey","optMsg","teaserEl","teaserDismissed","CHAT_MINI_SVG","paintTeaser","msg","sub","ic","st","openFromTeaser","showPanel","show","addUnread","playSound","ctx","osc","gain","preSendQueue","flushPreSendQueue","conversationId","conn","e2e","E2ESession","e2eStarted","pending","x3dhPending","x3dhBundleFetched","sendSealed","clientMsgId","content","sendSealedX3DH","x3dhInit","flushPending","p","fetchAndX3DH","targetUserId","i18n","resolveStrings","rtlLocales","browserLang","preChatKey","renderer","file","uploadUrl","res","url","mime","size","actionId","isTyping","values","query","r","d","seq","oldest","messageId","remove","score","rawLocale","target","resolveLocale","translated","userInfo","openFrame","ConnectionManager","frame","restoreHistory","toSend","x3dh","extractX3DHInit","bn","prekeyPayload","liveKey","slot","handle"],"mappings":"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"}
|