@paramms/chat-widget 1.0.35 → 1.0.36
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/README.md +1 -1
- package/dist/chatlist.js +27 -27
- package/dist/chatlist.js.map +1 -1
- package/dist/embed.js +4 -2
- package/dist/embed.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +103 -95
- package/dist/index.js.map +1 -1
- package/dist/react.js +218 -230
- package/dist/react.js.map +1 -1
- package/dist/renderer.d.ts +5 -0
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/annotations.ts","../src/renderer.ts","../src/index.ts"],"sourcesContent":["// ── Co-browsing annotation overlay ────────────────────────────────────────────\n// Renders strokes an AGENT draws (from the dashboard's Draw mode) on top of\n// the guest's page: a fixed, full-viewport, click-through SVG. Stroke points\n// are NORMALIZED (0..1) relative to the sender's viewport, scaled to ours —\n// so \"circle the checkout button\" lands in roughly the same place on any\n// screen. Strokes fade out after a few seconds so the page never stays\n// scribbled on; `annotation_clear` wipes immediately.\nimport type { AnnotationStroke, ServerFrame } from './protocol/index.js'\n\nconst NS = 'http://www.w3.org/2000/svg'\nconst FADE_MS = 8_000\n\nexport class AnnotationOverlay {\n private svg: SVGSVGElement | null = null\n private readonly timers = new Set<ReturnType<typeof setTimeout>>()\n\n /** Feed every server frame; the overlay reacts to annotation frames only. */\n apply(frame: ServerFrame): void {\n if (frame.type === 'annotation') this.draw(frame.stroke)\n else if (frame.type === 'annotation_clear') this.clear()\n else if (frame.type === 'opened' && frame.annotations?.length) {\n for (const s of frame.annotations) this.draw(s)\n }\n }\n\n private ensureSvg(): SVGSVGElement {\n if (this.svg?.isConnected) return this.svg\n const svg = document.createElementNS(NS, 'svg')\n svg.setAttribute('data-relay-annotations', '')\n svg.setAttribute('aria-hidden', 'true')\n // Click-through and above everything except the widget itself.\n svg.setAttribute('style',\n 'position:fixed;inset:0;width:100vw;height:100vh;pointer-events:none;z-index:2147483000;')\n document.body.append(svg)\n this.svg = svg\n return svg\n }\n\n private draw(stroke: AnnotationStroke): void {\n if (typeof document === 'undefined' || !stroke.points?.length) return\n const svg = this.ensureSvg()\n const w = window.innerWidth, h = window.innerHeight\n const line = document.createElementNS(NS, 'polyline')\n line.setAttribute('points', stroke.points.map(p => `${(p.x * w).toFixed(1)},${(p.y * h).toFixed(1)}`).join(' '))\n line.setAttribute('fill', 'none')\n line.setAttribute('stroke', stroke.color || '#ff3b30')\n line.setAttribute('stroke-width', String(stroke.width || 3))\n line.setAttribute('stroke-linecap', 'round')\n line.setAttribute('stroke-linejoin', 'round')\n line.setAttribute('data-stroke-id', stroke.id)\n svg.append(line)\n // Fade + remove so annotations are ephemeral by default.\n const t = setTimeout(() => {\n line.style.transition = 'opacity 600ms'\n line.style.opacity = '0'\n const t2 = setTimeout(() => { line.remove(); this.timers.delete(t2) }, 650)\n this.timers.add(t2)\n this.timers.delete(t)\n }, FADE_MS)\n this.timers.add(t)\n }\n\n clear(): void {\n this.svg?.replaceChildren()\n }\n\n destroy(): void {\n for (const t of this.timers) clearTimeout(t)\n this.timers.clear()\n this.svg?.remove()\n this.svg = null\n }\n}\n","import type { ManifestAction, MessageContent, AnnotationStroke } from './protocol/index.js'\nimport type { ChatStore, RenderMessage } from './store.js'\n\nexport interface WidgetConfig {\n subject?: { title?: string; subtitle?: string; tags?: string[]; status?: string; ownerLabel?: string }\n quickReplies?: string[]\n accent?: string\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 }\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 /** Co-browsing: a freehand stroke was completed on the shared whiteboard. */\n onAnnotate?(stroke: Omit<AnnotationStroke, 'by'>): 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 /** Co-browsing: clear the shared whiteboard for everyone. */\n onAnnotateClear?(): 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 /** Multi-subject chat list (e.g. a marketplace with one thread per item):\n * fetch the guest's other conversations. Omit to hide the list button\n * entirely — single-conversation embeds don't need this. */\n /** Switch the active conversation to a different one from the list —\n * effectively a re-open with a different subjectId. */\n}\n\nconst STYLE_ID = 'objectchat-widget-styles'\nconst REACTION_EMOJIS = ['👍', '❤️', '😂', '😮', '😢', '🙏']\nconst CSS = `\n.ocw { --ocw-accent:#4F63F5; --ocw-bg:#f3efe9; --ocw-card:#fff; --ocw-line:#ececec; --ocw-ink:#1c1b1a; --ocw-mut:#9b9690;\n position:relative;\n display:flex; flex-direction:column; height:100%; min-height:320px; background:var(--ocw-bg);\n font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif; color:var(--ocw-ink); overflow:hidden; }\n@media (max-width:480px) {\n .ocw { min-height:100dvh; border-radius:0 !important; }\n .ocw-bubble { font-size:15px; }\n .ocw-input textarea { font-size:16px; } /* prevent iOS zoom on focus */\n .ocw-chip { padding:9px 14px; font-size:14px; }\n .ocw-modal-card { width:90%; }\n .ocw-row { max-width:94%; }\n}\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:6px; }\n[dir=\"rtl\"] .theirs .ocw-bubble { border-bottom-left-radius:18px; border-bottom-right-radius:6px; }\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-avatar { width:34px; height:34px; border-radius:50%; background:#ffe9d6; display:flex; align-items:center; justify-content:center; font-size:17px; flex:none; }\n.ocw-head-main { flex:1; min-width:0; }\n.ocw-head-name { font-weight:700; font-size:15px; }\n.ocw-head-meta { color:var(--ocw-mut); font-size:12px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n.ocw-badge { font-size:12px; font-weight:600; color:#15803d; background:#e8f6ec; border-radius:999px; padding:3px 10px; }\n.ocw-e2e { font-size:11px; font-weight:700; color:#3730a3; background:#eef2ff; border-radius:999px; padding:3px 9px; align-items:center; }\n.ocw-menu { color:var(--ocw-mut); width:30px; height:30px; border-radius:50%; border:1px solid var(--ocw-line); background:#fff; 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:1px solid #e3ded7; background:#fff; border-radius:999px; padding:7px 13px; font-size:13px; 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-card); border:1px solid var(--ocw-line); border-radius:16px; padding:14px 16px; }\n.ocw-subject-title { font-weight:700; font-size:16px; margin-bottom:2px; }\n.ocw-subject-sub { color:var(--ocw-mut); font-size:13px; margin-bottom:10px; }\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-dot { width:26px; height:26px; border-radius:50%; background:var(--ocw-accent); color:#fff; font-size:12px; font-weight:700; display:flex; align-items:center; justify-content:center; flex:none; }\n.ocw-bubble { padding:10px 14px; border-radius:18px; font-size:14.5px; line-height:1.4; word-wrap:break-word; }\n.theirs .ocw-bubble { background:#fff; border:1px solid var(--ocw-line); border-bottom-left-radius:6px; }\n.mine .ocw-bubble { background:var(--ocw-accent); color:#fff; border-bottom-right-radius:6px; }\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:#f0fdf4; border-color:#cdebd6; }\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 #e3ded7; border-radius:999px; padding:2px 7px; background:#fff; 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-add { display:none; position:absolute; bottom:100%; left:0; margin-bottom:4px; background:#fff; border:1px solid #e3ded7; border-radius:14px; padding:6px 8px; box-shadow:0 4px 16px rgba(0,0,0,.12); display:flex; gap:4px; z-index:10; }\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:#fff; border:1px solid #e3ded7; border-radius:14px; padding:6px 8px; box-shadow:0 4px 16px rgba(0,0,0,.12); display:flex; gap:4px; z-index:10; white-space:nowrap; }\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:#f3efe9; }\n.ocw-react-btn { background:none; border:1px solid #e3ded7; 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:#fff; border:1px solid #e3ded7; 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:#fff; 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 #e3ded7; 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-offline { margin:20px 14px; padding:20px; background:#fff; border:1px solid #e3ded7; border-radius:16px; text-align:center; }\n.ocw-offline-icon { font-size:32px; margin-bottom:8px; }\n.ocw-offline-title { font-weight:700; font-size:16px; margin-bottom:6px; }\n.ocw-offline-msg { font-size:13px; color:var(--ocw-mut); margin-bottom:16px; }\n.ocw-offline-form { display:flex; flex-direction:column; gap:8px; text-align:left; }\n.ocw-offline-input { border:1px solid #e3ded7; border-radius:10px; padding:10px 12px; font-size:13px; font-family:inherit; }\n.ocw-offline-input:focus { outline:none; border-color:var(--ocw-accent); }\n.ocw-offline-submit { background:var(--ocw-accent); color:#fff; border:none; border-radius:10px; padding:11px; font-size:14px; font-weight:600; cursor:pointer; }\n.ocw-offline-thanks { font-size:14px; color:#15803d; font-weight:600; }\n.ocw-prechat { margin:20px 14px; padding:20px; background:#fff; border:1px solid #e3ded7; border-radius:16px; }\n.ocw-prechat-title { font-weight:700; font-size:15px; margin-bottom:12px; }\n.ocw-prechat select { border:1px solid #e3ded7; border-radius:10px; padding:10px 12px; font:inherit; font-size:13px; background:#fff; }\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:#fff; border:1px solid #e3ded7; 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:#fff; 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:1px solid #e3ded7; background:#fff; border-radius:999px; padding:7px 14px; font-size:13px; cursor:pointer; color:#444; white-space:nowrap; transition:border-color .12s,color .12s; }\n.ocw-quick button:hover { border-color:var(--ocw-accent); color:var(--ocw-accent); }\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 #e3ded7; 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:#fff; 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; resize:none; border:1px solid #e3ded7; border-radius:22px; padding:11px 16px; font:inherit; font-size:14px; background:#fff; outline:none; max-height:96px; }\n.ocw-input textarea:focus { border-color:var(--ocw-accent); }\n.ocw-sendbtn { width:42px; height:42px; border-radius:50%; border:none; background:var(--ocw-accent); color:#fff; font-size:18px; cursor:pointer; flex:none; display:flex; align-items:center; justify-content:center; }\n.ocw-sendbtn:disabled { opacity:.5; cursor:default; }\n.ocw-cobrowse-btn { display:none; }\n.ocw-cobrowse-btn.show { display:inline-flex; }\n.ocw-cobrowse-btn.on { color:var(--ocw-accent); border-color:var(--ocw-accent); }\n.ocw-cobrowse-canvas { position:absolute; inset:0; z-index:40; touch-action:none; display:none; }\n.ocw-cobrowse-canvas.active { display:block; cursor:crosshair; }\n.ocw-cobrowse-toolbar { position:absolute; top:8px; right:8px; z-index:41; display:none; gap:6px; background:rgba(255,255,255,.92); border-radius:999px; padding:5px 8px; box-shadow:0 2px 10px rgba(0,0,0,.12); }\n.ocw-cobrowse-toolbar.active { display:flex; align-items:center; }\n.ocw-cobrowse-swatch { width:18px; height:18px; border-radius:50%; border:2px solid transparent; cursor:pointer; padding:0; }\n.ocw-cobrowse-swatch.sel { border-color:#1c1b1a; }\n.ocw-cobrowse-clear { background:none; border:1px solid #e3ded7; border-radius:999px; font-size:11px; padding:3px 8px; cursor:pointer; color:var(--ocw-mut); }\n.ocw-cobrowse-clear:hover { border-color:#e74c3c; color:#e74c3c; }\n.ocw-cobrowse-hint { position:absolute; bottom:8px; left:8px; z-index:41; font-size:11px; color:var(--ocw-mut); background:rgba(255,255,255,.9); border-radius:8px; padding:3px 8px; display:none; }\n.ocw-cobrowse-hint.active { display:block; }\n\n.ocw-translate-btn { position:absolute; bottom:2px; right:-26px; background:#fff; border:1px solid #e3ded7; 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-translated-tag { font-size:10px; color:var(--ocw-mut); margin-top:2px; }\n`\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\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 offlinePanel: HTMLElement\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\n // ── Co-browsing (shared whiteboard) ─────────────────────────────────────\n private readonly cobrowseBtn: HTMLButtonElement\n private readonly cobrowseCanvas: HTMLCanvasElement\n private readonly cobrowseToolbar: HTMLElement\n private readonly cobrowseHint: HTMLElement\n private cobrowseActive = false\n private cobrowseColor = '#f5713c'\n private lastAnnotationVersion = -1\n private storeRef: ChatStore | null = null\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 // 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\n // Header\n const head = el('div', 'ocw-head')\n const avatarEl = el('div', 'ocw-avatar')\n if (cfg.userInfo?.avatar) {\n const img = document.createElement('img')\n img.src = cfg.userInfo.avatar; img.alt = cfg.userInfo.name ?? 'You'\n img.style.cssText = 'width:100%;height:100%;border-radius:50%;object-fit:cover'\n avatarEl.append(img)\n } else {\n avatarEl.textContent = cfg.userInfo?.name ? cfg.userInfo.name[0]!.toUpperCase() : '🧑'\n }\n head.append(avatarEl)\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 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 this.cobrowseBtn = el('button', 'ocw-menu ocw-cobrowse-btn', '🖍') as HTMLButtonElement\n this.cobrowseBtn.title = 'Shared whiteboard — draw to point things out together'\n this.cobrowseBtn.addEventListener('click', () => { this.cobrowseActive = !this.cobrowseActive; this.updateCobrowseUI() })\n head.append(this.cobrowseBtn)\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(el('div', 'ocw-dot', '🧑'), 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 this.offlinePanel = el('div', 'ocw-offline'); this.offlinePanel.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; this.input.placeholder = 'Message…'\n const sendBtn = el('button', 'ocw-sendbtn', cfg.i18n?.send ?? '➤')\n sendBtn.addEventListener('click', () => this.flushSend())\n this.input.addEventListener('input', () => {\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 root.append(head, this.chips, this.scroll, this.typing, this.quick, this.formHost, this.csatPanel, this.offlinePanel, this.preChatPanel, this.deflectPanel, inputRow, this.footer)\n\n // Co-browsing overlay: a freehand canvas layered over the whole widget.\n this.cobrowseCanvas = el('canvas', 'ocw-cobrowse-canvas') as HTMLCanvasElement\n this.cobrowseToolbar = el('div', 'ocw-cobrowse-toolbar')\n for (const c of ['#f5713c', '#1c1b1a', '#2563eb', '#16a34a', '#dc2626']) {\n const sw = el('button', 'ocw-cobrowse-swatch') as HTMLButtonElement\n sw.style.background = c\n sw.type = 'button'\n if (c === this.cobrowseColor) sw.classList.add('sel')\n sw.addEventListener('click', () => {\n this.cobrowseColor = c\n for (const n of this.cobrowseToolbar.querySelectorAll('.ocw-cobrowse-swatch')) n.classList.remove('sel')\n sw.classList.add('sel')\n })\n this.cobrowseToolbar.append(sw)\n }\n const cobrowseClear = el('button', 'ocw-cobrowse-clear', 'Clear')\n cobrowseClear.addEventListener('click', () => this.h.onAnnotateClear?.())\n this.cobrowseToolbar.append(cobrowseClear)\n this.cobrowseHint = el('div', 'ocw-cobrowse-hint', '🖍 Draw to point things out — visible to both sides')\n root.append(this.cobrowseCanvas, this.cobrowseToolbar, this.cobrowseHint)\n this.bindCobrowsePointerEvents()\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 }\n\n\n /** Render list rows, optionally filtered by search query. */\n\n\n /** Build a single WhatsApp-style conversation row. */\n\n /** Returns true when the chat screen is visible (not the list). */\n\n /** Navigate to the chat screen (slide list left, slide chat in from right). */\n\n /** Navigate back to the list screen. */\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.h.onTyping(false)\n this.h.onSend(text)\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 render(store: ChatStore): void {\n this.storeRef = store\n // Co-browsing toggle is only meaningful for subject-anchored conversations.\n this.cobrowseBtn.classList.toggle('show', !!store.subject)\n if (!store.subject && this.cobrowseActive) { this.cobrowseActive = false; this.updateCobrowseUI() }\n if (this.cobrowseActive && store.annotationVersion !== this.lastAnnotationVersion) {\n this.lastAnnotationVersion = store.annotationVersion\n this.resizeCobrowseCanvas()\n this.redrawCobrowse()\n }\n if (store.accent) this.root.style.setProperty('--ocw-accent', store.accent)\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 for (const m of store.messages()) {\n this.scroll.append(this.messageEl(m, store))\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.offlinePanel.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 if (store.offline) {\n if (this.offlinePanel.style.display === 'none') this.buildOfflinePanel(store.offlineMessage)\n this.offlinePanel.style.display = 'block'\n ;(this.root.querySelector('.ocw-input') as HTMLElement | null)?.style.setProperty('display', 'none')\n } else {\n this.offlinePanel.style.display = 'none'\n ;(this.root.querySelector('.ocw-input') as HTMLElement | null)?.style.removeProperty('display')\n }\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\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 buildOfflinePanel(offlineMessage?: string): void {\n this.offlinePanel.replaceChildren()\n this.offlinePanel.append(el('div', 'ocw-offline-icon', '🌙'))\n this.offlinePanel.append(el('div', 'ocw-offline-title', this.cfg.i18n?.offline ?? \"We're offline right now\"))\n this.offlinePanel.append(el('div', 'ocw-offline-msg', offlineMessage || \"Leave your details and we'll get back to you soon.\"))\n const form = el('div', 'ocw-offline-form')\n const nameIn = el('input', 'ocw-offline-input') as HTMLInputElement; nameIn.placeholder = 'Your name'; nameIn.type = 'text'\n const emailIn = el('input', 'ocw-offline-input') as HTMLInputElement; emailIn.placeholder = 'Your email'; emailIn.type = 'email'\n const msgIn = el('textarea', 'ocw-offline-input') as HTMLTextAreaElement; msgIn.placeholder = 'Your message'; msgIn.rows = 3\n const submit = el('button', 'ocw-offline-submit', 'Send message')\n submit.addEventListener('click', () => {\n if (!emailIn.value.trim() || !msgIn.value.trim()) return\n // Post as a regular message (offline form is stored as a conversation once submitted)\n this.h.onSend(`[Offline form]\\nName: ${nameIn.value || 'Anonymous'}\\nEmail: ${emailIn.value}\\nMessage: ${msgIn.value}`)\n this.offlinePanel.replaceChildren(el('div', 'ocw-offline-thanks', '✓ Message sent! We\\'ll reply to your email.'))\n })\n form.append(nameIn, emailIn, msgIn, submit)\n this.offlinePanel.append(form)\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')\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 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 private subjectBuilt = false\n /** Subject card from the server's Subject entity (per chatroom), with the\n * mount config as a fallback. Built once when data first arrives. */\n // ── Co-browsing (shared whiteboard) ──────────────────────────────────────\n private updateCobrowseUI(): void {\n this.cobrowseCanvas.classList.toggle('active', this.cobrowseActive)\n this.cobrowseToolbar.classList.toggle('active', this.cobrowseActive)\n this.cobrowseHint.classList.toggle('active', this.cobrowseActive)\n this.cobrowseBtn.classList.toggle('on', this.cobrowseActive)\n if (this.cobrowseActive) {\n this.resizeCobrowseCanvas()\n this.redrawCobrowse()\n }\n }\n\n private resizeCobrowseCanvas(): void {\n this.cobrowseCanvas.width = this.root.clientWidth || 1\n this.cobrowseCanvas.height = this.root.clientHeight || 1\n }\n\n /** Draw a stroke whose points are normalized to 0..1, scaled to the current\n * canvas size — so strokes line up across different viewport sizes. */\n private drawStroke(stroke: { points: { x: number; y: number }[]; color: string; width: number }): void {\n const ctx = this.cobrowseCanvas.getContext('2d')\n if (!ctx || stroke.points.length < 2) return\n const w = this.cobrowseCanvas.width, h = this.cobrowseCanvas.height\n ctx.strokeStyle = stroke.color\n ctx.lineWidth = Math.max(1, stroke.width * Math.min(w, h))\n ctx.lineJoin = 'round'\n ctx.lineCap = 'round'\n ctx.beginPath()\n ctx.moveTo(stroke.points[0]!.x * w, stroke.points[0]!.y * h)\n for (const p of stroke.points.slice(1)) ctx.lineTo(p.x * w, p.y * h)\n ctx.stroke()\n }\n\n private redrawCobrowse(): void {\n const ctx = this.cobrowseCanvas.getContext('2d')\n if (!ctx) return\n ctx.clearRect(0, 0, this.cobrowseCanvas.width, this.cobrowseCanvas.height)\n for (const s of this.storeRef?.annotations ?? []) this.drawStroke(s)\n }\n\n private bindCobrowsePointerEvents(): void {\n let drawing = false\n let current: { x: number; y: number }[] = []\n const posFromEvent = (e: PointerEvent): { x: number; y: number } => {\n const rect = this.cobrowseCanvas.getBoundingClientRect()\n const x = rect.width > 0 ? (e.clientX - rect.left) / rect.width : 0\n const y = rect.height > 0 ? (e.clientY - rect.top) / rect.height : 0\n return { x: Math.min(1, Math.max(0, x)), y: Math.min(1, Math.max(0, y)) }\n }\n const STROKE_WIDTH = 0.006 // normalized — ~6px on a 1000px-wide canvas\n this.cobrowseCanvas.addEventListener('pointerdown', (e) => {\n if (!this.cobrowseActive) return\n drawing = true\n current = [posFromEvent(e)]\n this.cobrowseCanvas.setPointerCapture(e.pointerId)\n })\n this.cobrowseCanvas.addEventListener('pointermove', (e) => {\n if (!drawing) return\n current.push(posFromEvent(e))\n this.redrawCobrowse()\n this.drawStroke({ points: current, color: this.cobrowseColor, width: STROKE_WIDTH })\n })\n const finish = (e: PointerEvent): void => {\n if (!drawing) return\n drawing = false\n if (current.length > 1) {\n this.h.onAnnotate?.({ id: `an_${Date.now()}_${Math.random().toString(36).slice(2)}`, points: current, color: this.cobrowseColor, width: STROKE_WIDTH })\n }\n current = []\n try { this.cobrowseCanvas.releasePointerCapture(e.pointerId) } catch { /* not captured */ }\n }\n this.cobrowseCanvas.addEventListener('pointerup', finish)\n this.cobrowseCanvas.addEventListener('pointercancel', finish)\n }\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): 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 if (!mine && !isNote) row.append(el('div', 'ocw-dot', m.senderRole === 'bot' ? '🤖' : '🧑'))\n const col = el('div')\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 {\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 // Live translation: only for the other party's plain-text messages (not notes).\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 translateBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n if (this.showingTranslation.has(m.id)) {\n this.showingTranslation.delete(m.id)\n textNode!.textContent = original\n translateBtn.textContent = '🌐'\n translateBtn.title = 'Translate'\n return\n }\n const cached = this.translationCache.get(m.id)\n if (cached !== undefined) {\n this.showingTranslation.add(m.id)\n textNode!.textContent = cached\n translateBtn.textContent = '↩'\n translateBtn.title = 'Show original'\n return\n }\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 this.showingTranslation.add(m.id)\n textNode!.textContent = result\n translateBtn.textContent = '↩'\n translateBtn.title = 'Show original'\n })\n })\n bubbleWrap.append(translateBtn)\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:#fff;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 // Reactions: existing pills + add-reaction picker (hover-revealed)\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 picker.style.display = picker.style.display === 'none' ? 'flex' : 'none'\n })\n document.addEventListener('click', () => { picker.style.display = 'none' }, { once: true })\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 void store // suppress unused warning — store available for future use\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 { AnnotationOverlay } from './annotations.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'\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 /** 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 /** 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 /** 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 }\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/** 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\nexport function mount(opts: MountOptions): WidgetHandle {\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 // (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 — fixed 380×600, sits above the bubble ─────────────────\n const panel = document.createElement('div')\n // Responsive panel: full-screen on mobile (<480px), 380×600 on desktop.\n // Use a MediaQueryList so the layout updates if the user rotates their phone\n // or resizes the browser window — not just the state at mount time.\n const mql = typeof window !== 'undefined' ? window.matchMedia('(max-width: 479px)') : null\n const applyPanelLayout = (mobile: boolean) => {\n panel.style.cssText = mobile ? [\n 'position:fixed', 'inset:0', 'width:100%', 'height:100%',\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:380px', 'height:600px', 'border-radius:16px', 'overflow:hidden',\n 'box-shadow:0 8px 40px rgba(0,0,0,.18)',\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 const btn = document.createElement('button')\n btn.style.cssText = [\n `width:56px;height:56px;border-radius:50%`,\n `background:${opts.accent ?? '#4F63F5'}`,\n `color:#fff;border:none;font-size:24px;cursor:pointer`,\n `box-shadow:0 4px 16px rgba(0,0,0,.25)`,\n `position:relative;flex:none`,\n `transition:transform .15s`,\n ].join(';')\n btn.textContent = '💬'\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:#ef4444;color:#fff;border-radius:50%;width:20px;height:20px;font-size:11px;font-weight:700;display:none;align-items:center;justify-content:center`\n btn.append(badgeEl)\n\n launcherEl.append(panel, btn)\n document.body.append(launcherEl)\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 btn.textContent = open ? '✕' : '💬'\n btn.append(badgeEl!)\n if (open) { unread = 0; if (badgeEl) badgeEl.style.display = 'none' }\n })\n\n // Close on Escape\n document.addEventListener('keydown', (e) => {\n if (e.key === 'Escape' && open) { open = false; showPanel(false); btn.textContent = '💬'; btn.append(badgeEl!) }\n })\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 const ctx = new AudioContext()\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 const e2e = new E2ESession(`ocw-e2e-${opts.profileId}`)\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 }): 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 const annotations = new AnnotationOverlay()\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.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 headers: { 'content-type': file.type, ...(opts.token ? { authorization: `Bearer ${opts.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 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 onDeflectQuery(q) {\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 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 onReact(messageId, emoji, remove) {\n if (!cid) return\n conn.send({ type: 'react', conversationId: cid, messageId: messageId as never, emoji, remove })\n },\n onCsat(score) {\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 onAnnotate(stroke) {\n if (cid) conn.send({ type: 'annotate', conversationId: cid, stroke })\n },\n onAnnotateClear() {\n if (cid) conn.send({ type: 'annotate_clear', conversationId: cid })\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 }, {\n ...(opts.subject ? { subject: opts.subject } : {}),\n ...(opts.quickReplies ? { quickReplies: opts.quickReplies } : {}),\n ...(opts.accent ? { accent: opts.accent } : {}),\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 annotations.apply(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 (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 (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).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 (frame.type === 'peerkey') {\n void e2e.onPeerKey(frame.key).then(() => { flushPending(); renderer.render(store) })\n return\n }\n void (async () => {\n if (store.e2e) await e2e.openFrame(frame)\n store.apply(frame)\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 (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(); annotations.destroy()\n if (_mql && _mqlHandler) _mql.removeEventListener('change', _mqlHandler)\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":["NS","FADE_MS","AnnotationOverlay","__publicField","frame","_a","svg","stroke","w","h","line","p","t","t2","STYLE_ID","REACTION_EMOJIS","CSS","injectStyles","s","el","tag","cls","text","n","fmtTime","ts","contentText","c","Renderer","root","me","cfg","head","avatarEl","img","_b","hm","_c","_d","_e","_f","_g","typingBubble","q","b","sendBtn","_h","m","e","attachBtn","fileInput","inputRow","footer","_i","sw","cobrowseClear","fn","preview","store","ownerLabel","actions","a","prevScrollHeight","prevScrollTop","sentinel","maxOther","typingNames","bubble","guestHasSpoken","terminalStates","status","message","fatal","offlineMessage","form","nameIn","emailIn","msgIn","submit","inputs","f","inp","topicSel","ph","o","callbackCb","phoneForCb","row","email","callback","phone","articles","card","stars","btns","i","bb","idx","ctx","drawing","current","posFromEvent","rect","x","y","STROKE_WIDTH","finish","title","tags","k","v","btn","label","resolve","overlay","cancel","ok","close","panel","sel","opt","out","name","sys","mine","isNote","col","bubbleWrap","replyCtx","textNode","ap","links","gLink","iLink","original","translateBtn","cached","result","menu","editBtn","ta","saveBtn","cancelBtn","btnRow","editPanel","restore","newText","ke","delBtn","reactWrap","reactRow","emoji","users","pill","addBtn","picker","pb","alreadyReacted","u","meta","tick","_registry","_launcherRegistry","launcherSlot","opts","unmount","mount","anonId","persistentUid","deflectTimer","destroyed","token","linkFrom","wsUrl","httpBase","resolveRelayUrls","ChatStore","outboxKey","outbox","PersistentOutbox","cid","outboxRestored","_mql","_mqlHandler","item","launcherEl","badgeEl","unread","open","isRight","mql","applyPanelLayout","mobile","mqlHandler","showPanel","show","addUnread","playSound","osc","gain","preSendQueue","flushPreSendQueue","conversationId","conn","e2e","E2ESession","e2eStarted","pending","x3dhPending","x3dhBundleFetched","sendSealed","clientMsgId","content","sendSealedX3DH","x3dhInit","flushPending","fetchAndX3DH","targetUserId","i18n","rtlLocales","browserLang","annotations","preChatKey","renderer","file","uploadUrl","res","url","mime","size","actionId","isTyping","values","first","query","r","d","seq","oldest","messageId","remove","score","translated","userInfo","openFrame","ConnectionManager","msg","restoreHistory","toSend","x3dh","extractX3DHInit","prekeyPayload","liveKey","slot","handle"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/annotations.ts","../src/renderer.ts","../src/index.ts"],"sourcesContent":["// ── Co-browsing annotation overlay ────────────────────────────────────────────\n// Renders strokes an AGENT draws (from the dashboard's Draw mode) on top of\n// the guest's page: a fixed, full-viewport, click-through SVG. Stroke points\n// are NORMALIZED (0..1) relative to the sender's viewport, scaled to ours —\n// so \"circle the checkout button\" lands in roughly the same place on any\n// screen. Strokes fade out after a few seconds so the page never stays\n// scribbled on; `annotation_clear` wipes immediately.\nimport type { AnnotationStroke, ServerFrame } from './protocol/index.js'\n\nconst NS = 'http://www.w3.org/2000/svg'\nconst FADE_MS = 8_000\n\nexport class AnnotationOverlay {\n private svg: SVGSVGElement | null = null\n private readonly timers = new Set<ReturnType<typeof setTimeout>>()\n\n /** Feed every server frame; the overlay reacts to annotation frames only. */\n apply(frame: ServerFrame): void {\n if (frame.type === 'annotation') this.draw(frame.stroke)\n else if (frame.type === 'annotation_clear') this.clear()\n else if (frame.type === 'opened' && frame.annotations?.length) {\n for (const s of frame.annotations) this.draw(s)\n }\n }\n\n private ensureSvg(): SVGSVGElement {\n if (this.svg?.isConnected) return this.svg\n const svg = document.createElementNS(NS, 'svg')\n svg.setAttribute('data-relay-annotations', '')\n svg.setAttribute('aria-hidden', 'true')\n // Click-through and above everything except the widget itself.\n svg.setAttribute('style',\n 'position:fixed;inset:0;width:100vw;height:100vh;pointer-events:none;z-index:2147483000;')\n document.body.append(svg)\n this.svg = svg\n return svg\n }\n\n private draw(stroke: AnnotationStroke): void {\n if (typeof document === 'undefined' || !stroke.points?.length) return\n const svg = this.ensureSvg()\n const w = window.innerWidth, h = window.innerHeight\n const line = document.createElementNS(NS, 'polyline')\n line.setAttribute('points', stroke.points.map(p => `${(p.x * w).toFixed(1)},${(p.y * h).toFixed(1)}`).join(' '))\n line.setAttribute('fill', 'none')\n line.setAttribute('stroke', stroke.color || '#ff3b30')\n line.setAttribute('stroke-width', String(stroke.width || 3))\n line.setAttribute('stroke-linecap', 'round')\n line.setAttribute('stroke-linejoin', 'round')\n line.setAttribute('data-stroke-id', stroke.id)\n svg.append(line)\n // Fade + remove so annotations are ephemeral by default.\n const t = setTimeout(() => {\n line.style.transition = 'opacity 600ms'\n line.style.opacity = '0'\n const t2 = setTimeout(() => { line.remove(); this.timers.delete(t2) }, 650)\n this.timers.add(t2)\n this.timers.delete(t)\n }, FADE_MS)\n this.timers.add(t)\n }\n\n clear(): void {\n this.svg?.replaceChildren()\n }\n\n destroy(): void {\n for (const t of this.timers) clearTimeout(t)\n this.timers.clear()\n this.svg?.remove()\n this.svg = null\n }\n}\n","import type { ManifestAction, MessageContent, AnnotationStroke } from './protocol/index.js'\nimport type { ChatStore, RenderMessage } from './store.js'\n\nexport interface WidgetConfig {\n subject?: { title?: string; subtitle?: string; tags?: string[]; status?: string; ownerLabel?: string }\n quickReplies?: string[]\n accent?: string\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 }\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 /** Co-browsing: a freehand stroke was completed on the shared whiteboard. */\n onAnnotate?(stroke: Omit<AnnotationStroke, 'by'>): 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 /** Co-browsing: clear the shared whiteboard for everyone. */\n onAnnotateClear?(): 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 /** Multi-subject chat list (e.g. a marketplace with one thread per item):\n * fetch the guest's other conversations. Omit to hide the list button\n * entirely — single-conversation embeds don't need this. */\n /** Switch the active conversation to a different one from the list —\n * effectively a re-open with a different subjectId. */\n}\n\nconst STYLE_ID = 'objectchat-widget-styles'\nconst REACTION_EMOJIS = ['👍', '❤️', '😂', '😮', '😢', '🙏']\nconst CSS = `\n.ocw { --ocw-accent:#4F63F5; --ocw-bg:#f3efe9; --ocw-card:#fff; --ocw-line:#ececec; --ocw-ink:#1c1b1a; --ocw-mut:#9b9690;\n position:relative;\n display:flex; flex-direction:column; height:100%; min-height:320px; background:var(--ocw-bg);\n font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif; color:var(--ocw-ink); overflow:hidden; }\n@media (max-width:480px) {\n .ocw { min-height:100dvh; border-radius:0 !important; }\n .ocw-bubble { font-size:15px; }\n .ocw-input textarea { font-size:16px; } /* prevent iOS zoom on focus */\n .ocw-chip { padding:9px 14px; font-size:14px; }\n .ocw-modal-card { width:90%; }\n .ocw-row { max-width:94%; }\n}\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:6px; }\n[dir=\"rtl\"] .theirs .ocw-bubble { border-bottom-left-radius:18px; border-bottom-right-radius:6px; }\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:#ffe9d6; display:flex; align-items:center; justify-content:center; font-size:17px; flex:none; }\n.ocw-head-main { flex:1; min-width:0; }\n.ocw-head-name { font-weight:700; font-size:15px; }\n.ocw-head-meta { color:var(--ocw-mut); font-size:12px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n.ocw-badge { font-size:12px; font-weight:600; color:#15803d; background:#e8f6ec; border-radius:999px; padding:3px 10px; }\n.ocw-e2e { font-size:11px; font-weight:700; color:#3730a3; background:#eef2ff; border-radius:999px; padding:3px 9px; align-items:center; }\n.ocw-menu { color:var(--ocw-mut); width:30px; height:30px; border-radius:50%; border:1px solid var(--ocw-line); background:#fff; 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:1px solid #e3ded7; background:#fff; border-radius:999px; padding:7px 13px; font-size:13px; 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-card); border:1px solid var(--ocw-line); border-radius:16px; padding:14px 16px; }\n.ocw-subject-title { font-weight:700; font-size:16px; margin-bottom:2px; }\n.ocw-subject-sub { color:var(--ocw-mut); font-size:13px; margin-bottom:10px; }\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-dot { width:26px; height:26px; border-radius:50%; background:var(--ocw-accent); color:#fff; font-size:12px; font-weight:700; display:flex; align-items:center; justify-content:center; flex:none; }\n.ocw-bubble { padding:10px 14px; border-radius:18px; font-size:14.5px; line-height:1.4; word-wrap:break-word; }\n.theirs .ocw-bubble { background:#fff; border:1px solid var(--ocw-line); border-bottom-left-radius:6px; }\n.mine .ocw-bubble { background:var(--ocw-accent); color:#fff; border-bottom-right-radius:6px; }\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:#f0fdf4; border-color:#cdebd6; }\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 #e3ded7; border-radius:999px; padding:2px 7px; background:#fff; 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-add { display:none; position:absolute; bottom:100%; left:0; margin-bottom:4px; background:#fff; border:1px solid #e3ded7; border-radius:14px; padding:6px 8px; box-shadow:0 4px 16px rgba(0,0,0,.12); display:flex; gap:4px; z-index:10; }\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:#fff; border:1px solid #e3ded7; border-radius:14px; padding:6px 8px; box-shadow:0 4px 16px rgba(0,0,0,.12); display:flex; gap:4px; z-index:10; white-space:nowrap; }\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:#f3efe9; }\n.ocw-react-btn { background:none; border:1px solid #e3ded7; 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:#fff; border:1px solid #e3ded7; 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:#fff; 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 #e3ded7; 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-offline { margin:20px 14px; padding:20px; background:#fff; border:1px solid #e3ded7; border-radius:16px; text-align:center; }\n.ocw-offline-icon { font-size:32px; margin-bottom:8px; }\n.ocw-offline-title { font-weight:700; font-size:16px; margin-bottom:6px; }\n.ocw-offline-msg { font-size:13px; color:var(--ocw-mut); margin-bottom:16px; }\n.ocw-offline-form { display:flex; flex-direction:column; gap:8px; text-align:left; }\n.ocw-offline-input { border:1px solid #e3ded7; border-radius:10px; padding:10px 12px; font-size:13px; font-family:inherit; }\n.ocw-offline-input:focus { outline:none; border-color:var(--ocw-accent); }\n.ocw-offline-submit { background:var(--ocw-accent); color:#fff; border:none; border-radius:10px; padding:11px; font-size:14px; font-weight:600; cursor:pointer; }\n.ocw-offline-thanks { font-size:14px; color:#15803d; font-weight:600; }\n.ocw-prechat { margin:20px 14px; padding:20px; background:#fff; border:1px solid #e3ded7; border-radius:16px; }\n.ocw-prechat-title { font-weight:700; font-size:15px; margin-bottom:12px; }\n.ocw-prechat select { border:1px solid #e3ded7; border-radius:10px; padding:10px 12px; font:inherit; font-size:13px; background:#fff; }\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:#fff; border:1px solid #e3ded7; 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:#fff; 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:1px solid #e3ded7; background:#fff; border-radius:999px; padding:7px 14px; font-size:13px; cursor:pointer; color:#444; white-space:nowrap; transition:border-color .12s,color .12s; }\n.ocw-quick button:hover { border-color:var(--ocw-accent); color:var(--ocw-accent); }\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 #e3ded7; 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:#fff; 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; resize:none; border:1px solid #e3ded7; border-radius:22px; padding:11px 16px; font:inherit; font-size:14px; background:#fff; outline:none; max-height:96px; }\n.ocw-input textarea:focus { border-color:var(--ocw-accent); }\n.ocw-sendbtn { width:42px; height:42px; border-radius:50%; border:none; background:var(--ocw-accent); color:#fff; font-size:18px; cursor:pointer; flex:none; display:flex; align-items:center; justify-content:center; }\n.ocw-sendbtn:disabled { opacity:.5; cursor:default; }\n.ocw-cobrowse-btn { display:none; }\n.ocw-cobrowse-btn.show { display:inline-flex; }\n.ocw-cobrowse-btn.on { color:var(--ocw-accent); border-color:var(--ocw-accent); }\n.ocw-cobrowse-canvas { position:absolute; inset:0; z-index:40; touch-action:none; display:none; }\n.ocw-cobrowse-canvas.active { display:block; cursor:crosshair; }\n.ocw-cobrowse-toolbar { position:absolute; top:8px; right:8px; z-index:41; display:none; gap:6px; background:rgba(255,255,255,.92); border-radius:999px; padding:5px 8px; box-shadow:0 2px 10px rgba(0,0,0,.12); }\n.ocw-cobrowse-toolbar.active { display:flex; align-items:center; }\n.ocw-cobrowse-swatch { width:18px; height:18px; border-radius:50%; border:2px solid transparent; cursor:pointer; padding:0; }\n.ocw-cobrowse-swatch.sel { border-color:#1c1b1a; }\n.ocw-cobrowse-clear { background:none; border:1px solid #e3ded7; border-radius:999px; font-size:11px; padding:3px 8px; cursor:pointer; color:var(--ocw-mut); }\n.ocw-cobrowse-clear:hover { border-color:#e74c3c; color:#e74c3c; }\n.ocw-cobrowse-hint { position:absolute; bottom:8px; left:8px; z-index:41; font-size:11px; color:var(--ocw-mut); background:rgba(255,255,255,.9); border-radius:8px; padding:3px 8px; display:none; }\n.ocw-cobrowse-hint.active { display:block; }\n\n.ocw-translate-btn { position:absolute; bottom:2px; right:-26px; background:#fff; border:1px solid #e3ded7; 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-translated-tag { font-size:10px; color:var(--ocw-mut); margin-top:2px; }\n`\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\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 offlinePanel: HTMLElement\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\n // ── Co-browsing (shared whiteboard) ─────────────────────────────────────\n private readonly cobrowseBtn: HTMLButtonElement\n private readonly cobrowseCanvas: HTMLCanvasElement\n private readonly cobrowseToolbar: HTMLElement\n private readonly cobrowseHint: HTMLElement\n private cobrowseActive = false\n private cobrowseColor = '#f5713c'\n private lastAnnotationVersion = -1\n private storeRef: ChatStore | null = null\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 // 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\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 const avatarEl = el('div', 'ocw-avatar')\n if (cfg.userInfo?.avatar) {\n const img = document.createElement('img')\n img.src = cfg.userInfo.avatar; img.alt = cfg.userInfo.name ?? 'You'\n img.style.cssText = 'width:100%;height:100%;border-radius:50%;object-fit:cover'\n avatarEl.append(img)\n } else {\n avatarEl.textContent = cfg.userInfo?.name ? cfg.userInfo.name[0]!.toUpperCase() : '🧑'\n }\n head.append(avatarEl)\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 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 this.cobrowseBtn = el('button', 'ocw-menu ocw-cobrowse-btn', '🖍') as HTMLButtonElement\n this.cobrowseBtn.title = 'Shared whiteboard — draw to point things out together'\n this.cobrowseBtn.addEventListener('click', () => { this.cobrowseActive = !this.cobrowseActive; this.updateCobrowseUI() })\n head.append(this.cobrowseBtn)\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(el('div', 'ocw-dot', '🧑'), 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 this.offlinePanel = el('div', 'ocw-offline'); this.offlinePanel.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; this.input.placeholder = 'Message…'\n const sendBtn = el('button', 'ocw-sendbtn', cfg.i18n?.send ?? '➤')\n sendBtn.addEventListener('click', () => this.flushSend())\n this.input.addEventListener('input', () => {\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 root.append(head, this.chips, this.scroll, this.typing, this.quick, this.formHost, this.csatPanel, this.offlinePanel, this.preChatPanel, this.deflectPanel, inputRow, this.footer)\n\n // Co-browsing overlay: a freehand canvas layered over the whole widget.\n this.cobrowseCanvas = el('canvas', 'ocw-cobrowse-canvas') as HTMLCanvasElement\n this.cobrowseToolbar = el('div', 'ocw-cobrowse-toolbar')\n for (const c of ['#f5713c', '#1c1b1a', '#2563eb', '#16a34a', '#dc2626']) {\n const sw = el('button', 'ocw-cobrowse-swatch') as HTMLButtonElement\n sw.style.background = c\n sw.type = 'button'\n if (c === this.cobrowseColor) sw.classList.add('sel')\n sw.addEventListener('click', () => {\n this.cobrowseColor = c\n for (const n of this.cobrowseToolbar.querySelectorAll('.ocw-cobrowse-swatch')) n.classList.remove('sel')\n sw.classList.add('sel')\n })\n this.cobrowseToolbar.append(sw)\n }\n const cobrowseClear = el('button', 'ocw-cobrowse-clear', 'Clear')\n cobrowseClear.addEventListener('click', () => this.h.onAnnotateClear?.())\n this.cobrowseToolbar.append(cobrowseClear)\n this.cobrowseHint = el('div', 'ocw-cobrowse-hint', '🖍 Draw to point things out — visible to both sides')\n root.append(this.cobrowseCanvas, this.cobrowseToolbar, this.cobrowseHint)\n this.bindCobrowsePointerEvents()\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 }\n\n\n /** Render list rows, optionally filtered by search query. */\n\n\n /** Build a single WhatsApp-style conversation row. */\n\n /** Returns true when the chat screen is visible (not the list). */\n\n /** Navigate to the chat screen (slide list left, slide chat in from right). */\n\n /** Navigate back to the list screen. */\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.h.onTyping(false)\n this.h.onSend(text)\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 render(store: ChatStore): void {\n this.storeRef = store\n // Co-browsing toggle is only meaningful for subject-anchored conversations.\n this.cobrowseBtn.classList.toggle('show', !!store.subject)\n if (!store.subject && this.cobrowseActive) { this.cobrowseActive = false; this.updateCobrowseUI() }\n if (this.cobrowseActive && store.annotationVersion !== this.lastAnnotationVersion) {\n this.lastAnnotationVersion = store.annotationVersion\n this.resizeCobrowseCanvas()\n this.redrawCobrowse()\n }\n if (store.accent) this.root.style.setProperty('--ocw-accent', store.accent)\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 for (const m of store.messages()) {\n this.scroll.append(this.messageEl(m, store))\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.offlinePanel.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 if (store.offline) {\n if (this.offlinePanel.style.display === 'none') this.buildOfflinePanel(store.offlineMessage)\n this.offlinePanel.style.display = 'block'\n ;(this.root.querySelector('.ocw-input') as HTMLElement | null)?.style.setProperty('display', 'none')\n } else {\n this.offlinePanel.style.display = 'none'\n ;(this.root.querySelector('.ocw-input') as HTMLElement | null)?.style.removeProperty('display')\n }\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\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 buildOfflinePanel(offlineMessage?: string): void {\n this.offlinePanel.replaceChildren()\n this.offlinePanel.append(el('div', 'ocw-offline-icon', '🌙'))\n this.offlinePanel.append(el('div', 'ocw-offline-title', this.cfg.i18n?.offline ?? \"We're offline right now\"))\n this.offlinePanel.append(el('div', 'ocw-offline-msg', offlineMessage || \"Leave your details and we'll get back to you soon.\"))\n const form = el('div', 'ocw-offline-form')\n const nameIn = el('input', 'ocw-offline-input') as HTMLInputElement; nameIn.placeholder = 'Your name'; nameIn.type = 'text'\n const emailIn = el('input', 'ocw-offline-input') as HTMLInputElement; emailIn.placeholder = 'Your email'; emailIn.type = 'email'\n const msgIn = el('textarea', 'ocw-offline-input') as HTMLTextAreaElement; msgIn.placeholder = 'Your message'; msgIn.rows = 3\n const submit = el('button', 'ocw-offline-submit', 'Send message')\n submit.addEventListener('click', () => {\n if (!emailIn.value.trim() || !msgIn.value.trim()) return\n // Post as a regular message (offline form is stored as a conversation once submitted)\n this.h.onSend(`[Offline form]\\nName: ${nameIn.value || 'Anonymous'}\\nEmail: ${emailIn.value}\\nMessage: ${msgIn.value}`)\n this.offlinePanel.replaceChildren(el('div', 'ocw-offline-thanks', '✓ Message sent! We\\'ll reply to your email.'))\n })\n form.append(nameIn, emailIn, msgIn, submit)\n this.offlinePanel.append(form)\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')\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 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 private subjectBuilt = false\n /** Subject card from the server's Subject entity (per chatroom), with the\n * mount config as a fallback. Built once when data first arrives. */\n // ── Co-browsing (shared whiteboard) ──────────────────────────────────────\n private updateCobrowseUI(): void {\n this.cobrowseCanvas.classList.toggle('active', this.cobrowseActive)\n this.cobrowseToolbar.classList.toggle('active', this.cobrowseActive)\n this.cobrowseHint.classList.toggle('active', this.cobrowseActive)\n this.cobrowseBtn.classList.toggle('on', this.cobrowseActive)\n if (this.cobrowseActive) {\n this.resizeCobrowseCanvas()\n this.redrawCobrowse()\n }\n }\n\n private resizeCobrowseCanvas(): void {\n this.cobrowseCanvas.width = this.root.clientWidth || 1\n this.cobrowseCanvas.height = this.root.clientHeight || 1\n }\n\n /** Draw a stroke whose points are normalized to 0..1, scaled to the current\n * canvas size — so strokes line up across different viewport sizes. */\n private drawStroke(stroke: { points: { x: number; y: number }[]; color: string; width: number }): void {\n const ctx = this.cobrowseCanvas.getContext('2d')\n if (!ctx || stroke.points.length < 2) return\n const w = this.cobrowseCanvas.width, h = this.cobrowseCanvas.height\n ctx.strokeStyle = stroke.color\n ctx.lineWidth = Math.max(1, stroke.width * Math.min(w, h))\n ctx.lineJoin = 'round'\n ctx.lineCap = 'round'\n ctx.beginPath()\n ctx.moveTo(stroke.points[0]!.x * w, stroke.points[0]!.y * h)\n for (const p of stroke.points.slice(1)) ctx.lineTo(p.x * w, p.y * h)\n ctx.stroke()\n }\n\n private redrawCobrowse(): void {\n const ctx = this.cobrowseCanvas.getContext('2d')\n if (!ctx) return\n ctx.clearRect(0, 0, this.cobrowseCanvas.width, this.cobrowseCanvas.height)\n for (const s of this.storeRef?.annotations ?? []) this.drawStroke(s)\n }\n\n private bindCobrowsePointerEvents(): void {\n let drawing = false\n let current: { x: number; y: number }[] = []\n const posFromEvent = (e: PointerEvent): { x: number; y: number } => {\n const rect = this.cobrowseCanvas.getBoundingClientRect()\n const x = rect.width > 0 ? (e.clientX - rect.left) / rect.width : 0\n const y = rect.height > 0 ? (e.clientY - rect.top) / rect.height : 0\n return { x: Math.min(1, Math.max(0, x)), y: Math.min(1, Math.max(0, y)) }\n }\n const STROKE_WIDTH = 0.006 // normalized — ~6px on a 1000px-wide canvas\n this.cobrowseCanvas.addEventListener('pointerdown', (e) => {\n if (!this.cobrowseActive) return\n drawing = true\n current = [posFromEvent(e)]\n this.cobrowseCanvas.setPointerCapture(e.pointerId)\n })\n this.cobrowseCanvas.addEventListener('pointermove', (e) => {\n if (!drawing) return\n current.push(posFromEvent(e))\n this.redrawCobrowse()\n this.drawStroke({ points: current, color: this.cobrowseColor, width: STROKE_WIDTH })\n })\n const finish = (e: PointerEvent): void => {\n if (!drawing) return\n drawing = false\n if (current.length > 1) {\n this.h.onAnnotate?.({ id: `an_${Date.now()}_${Math.random().toString(36).slice(2)}`, points: current, color: this.cobrowseColor, width: STROKE_WIDTH })\n }\n current = []\n try { this.cobrowseCanvas.releasePointerCapture(e.pointerId) } catch { /* not captured */ }\n }\n this.cobrowseCanvas.addEventListener('pointerup', finish)\n this.cobrowseCanvas.addEventListener('pointercancel', finish)\n }\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): 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 if (!mine && !isNote) row.append(el('div', 'ocw-dot', m.senderRole === 'bot' ? '🤖' : '🧑'))\n const col = el('div')\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 {\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 // Live translation: only for the other party's plain-text messages (not notes).\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 translateBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n if (this.showingTranslation.has(m.id)) {\n this.showingTranslation.delete(m.id)\n textNode!.textContent = original\n translateBtn.textContent = '🌐'\n translateBtn.title = 'Translate'\n return\n }\n const cached = this.translationCache.get(m.id)\n if (cached !== undefined) {\n this.showingTranslation.add(m.id)\n textNode!.textContent = cached\n translateBtn.textContent = '↩'\n translateBtn.title = 'Show original'\n return\n }\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 this.showingTranslation.add(m.id)\n textNode!.textContent = result\n translateBtn.textContent = '↩'\n translateBtn.title = 'Show original'\n })\n })\n bubbleWrap.append(translateBtn)\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:#fff;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 // Reactions: existing pills + add-reaction picker (hover-revealed)\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 picker.style.display = picker.style.display === 'none' ? 'flex' : 'none'\n })\n document.addEventListener('click', () => { picker.style.display = 'none' }, { once: true })\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 void store // suppress unused warning — store available for future use\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 { AnnotationOverlay } from './annotations.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'\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 /** 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 /** 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 /** 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 /** 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 }\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/** 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\nexport function mount(opts: MountOptions): WidgetHandle {\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 // (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 — fixed 380×600, sits above the bubble ─────────────────\n const panel = document.createElement('div')\n // Responsive panel: full-screen on mobile (<480px), 380×600 on desktop.\n // Use a MediaQueryList so the layout updates if the user rotates their phone\n // or resizes the browser window — not just the state at mount time.\n const mql = typeof window !== 'undefined' ? window.matchMedia('(max-width: 479px)') : null\n const applyPanelLayout = (mobile: boolean) => {\n panel.style.cssText = mobile ? [\n 'position:fixed', 'inset:0', 'width:100%', 'height:100%',\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:380px', 'height:600px', 'border-radius:16px', 'overflow:hidden',\n 'box-shadow:0 8px 40px rgba(0,0,0,.18)',\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 const btn = document.createElement('button')\n btn.style.cssText = [\n `width:56px;height:56px;border-radius:50%`,\n `background:${opts.accent ?? '#4F63F5'}`,\n `color:#fff;border:none;font-size:24px;cursor:pointer`,\n `box-shadow:0 4px 16px rgba(0,0,0,.25)`,\n `position:relative;flex:none`,\n `transition:transform .15s`,\n ].join(';')\n btn.textContent = '💬'\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:#ef4444;color:#fff;border-radius:50%;width:20px;height:20px;font-size:11px;font-weight:700;display:none;align-items:center;justify-content:center`\n btn.append(badgeEl)\n\n launcherEl.append(panel, btn)\n document.body.append(launcherEl)\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 btn.textContent = open ? '✕' : '💬'\n btn.append(badgeEl!)\n if (open) { unread = 0; if (badgeEl) badgeEl.style.display = 'none' }\n })\n\n // Close on Escape\n document.addEventListener('keydown', (e) => {\n if (e.key === 'Escape' && open) { open = false; showPanel(false); btn.textContent = '💬'; btn.append(badgeEl!) }\n })\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 const ctx = new AudioContext()\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 const e2e = new E2ESession(`ocw-e2e-${opts.profileId}`)\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 }): 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 const annotations = new AnnotationOverlay()\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.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 headers: { 'content-type': file.type, ...(opts.token ? { authorization: `Bearer ${opts.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 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 onDeflectQuery(q) {\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 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 onReact(messageId, emoji, remove) {\n if (!cid) return\n conn.send({ type: 'react', conversationId: cid, messageId: messageId as never, emoji, remove })\n },\n onCsat(score) {\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 onAnnotate(stroke) {\n if (cid) conn.send({ type: 'annotate', conversationId: cid, stroke })\n },\n onAnnotateClear() {\n if (cid) conn.send({ type: 'annotate_clear', conversationId: cid })\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.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 annotations.apply(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 (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 (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).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 (frame.type === 'peerkey') {\n void e2e.onPeerKey(frame.key).then(() => { flushPending(); renderer.render(store) })\n return\n }\n void (async () => {\n if (store.e2e) await e2e.openFrame(frame)\n store.apply(frame)\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 (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(); annotations.destroy()\n if (_mql && _mqlHandler) _mql.removeEventListener('change', _mqlHandler)\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":["NS","FADE_MS","AnnotationOverlay","__publicField","frame","_a","svg","stroke","w","h","line","p","t","t2","STYLE_ID","REACTION_EMOJIS","CSS","injectStyles","s","el","tag","cls","text","n","fmtTime","ts","contentText","c","Renderer","root","me","cfg","head","back","avatarEl","img","_b","hm","_c","_d","_e","_f","_g","typingBubble","q","b","sendBtn","_h","m","e","attachBtn","fileInput","inputRow","footer","_i","sw","cobrowseClear","fn","preview","store","ownerLabel","actions","a","prevScrollHeight","prevScrollTop","sentinel","maxOther","typingNames","bubble","guestHasSpoken","terminalStates","status","message","fatal","offlineMessage","form","nameIn","emailIn","msgIn","submit","inputs","f","inp","topicSel","ph","o","callbackCb","phoneForCb","row","email","callback","phone","articles","card","stars","btns","i","bb","idx","ctx","drawing","current","posFromEvent","rect","x","y","STROKE_WIDTH","finish","title","tags","k","v","btn","label","resolve","overlay","cancel","ok","close","panel","sel","opt","out","name","sys","mine","isNote","col","bubbleWrap","replyCtx","textNode","ap","links","gLink","iLink","original","translateBtn","cached","result","menu","editBtn","ta","saveBtn","cancelBtn","btnRow","editPanel","restore","newText","ke","delBtn","reactWrap","reactRow","emoji","users","pill","addBtn","picker","pb","alreadyReacted","u","meta","tick","_registry","_launcherRegistry","launcherSlot","opts","unmount","mount","anonId","persistentUid","deflectTimer","destroyed","token","linkFrom","wsUrl","httpBase","resolveRelayUrls","ChatStore","outboxKey","outbox","PersistentOutbox","cid","outboxRestored","_mql","_mqlHandler","item","launcherEl","badgeEl","unread","open","isRight","mql","applyPanelLayout","mobile","mqlHandler","showPanel","show","addUnread","playSound","osc","gain","preSendQueue","flushPreSendQueue","conversationId","conn","e2e","E2ESession","e2eStarted","pending","x3dhPending","x3dhBundleFetched","sendSealed","clientMsgId","content","sendSealedX3DH","x3dhInit","flushPending","fetchAndX3DH","targetUserId","i18n","rtlLocales","browserLang","annotations","preChatKey","renderer","file","uploadUrl","res","url","mime","size","actionId","isTyping","values","first","query","r","d","seq","oldest","messageId","remove","score","translated","userInfo","openFrame","ConnectionManager","msg","restoreHistory","toSend","x3dh","extractX3DHInit","prekeyPayload","liveKey","slot","handle"],"mappings":"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"}
|