@mrrocket/sdk 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/messenger-ui.ts"],"sourcesContent":["/**\n * The web messenger, after Featurebase's: a round launcher and a panel with three tabs, Home (greeting, the latest\n * chat, help search, top articles), Messages (the chat with the team, answered in MRRocket › Support by the owner or\n * the Customer experience agent) and Help (the product's published help articles, searchable, read in the panel).\n * While the panel is closed it checks for answers now and then; a new answer plays a short sound, shows a count on\n * the launcher and a preview card above it.\n *\n * Plain DOM, no React, so `<Messenger />` (Next.js) and the script tag share it; both load it only when it is used (a\n * dynamic import, and `sdk-messenger.js` for the script tag). Every text is set with textContent; articles arrive as\n * blocks, never HTML.\n */\nimport { type ArticleBlock, type ArticleSpan, type ArticleSummary, type ChatMessage, type ChatThread, type MessengerLook, createApi } from \"./api-client\";\nimport { injectStyles } from \"./styles-css\";\n\nexport interface MessengerUiOptions {\n host: string;\n apiKey: string;\n /** The visitor id now (it changes on identify and reset). */\n distinctId: () => string;\n /** The signed-in person's id, name and email, when the site called identify. */\n user: () => { id?: string; name?: string; email?: string } | undefined;\n /** Show the round launcher (default true). Without it the panel opens only through `open()`. */\n launcher?: boolean;\n position?: \"bottom-right\" | \"bottom-left\";\n /** The panel's title. */\n title?: string;\n /** The big line on Home and the first line of an empty chat. */\n greeting?: string;\n /** Ask anonymous visitors for an email (optional for them). Default true. */\n askEmail?: boolean;\n /** Text on the launcher; empty shows the icon only. */\n label?: string;\n /** The Help tab with the product's published help articles (default true; hidden while there are none). */\n help?: boolean;\n /** A short sound when an answer arrives (default true). */\n sound?: boolean;\n /** A preview of a new answer above the closed launcher (default true). */\n preview?: boolean;\n theme?: \"light\" | \"dark\" | \"auto\";\n accent?: string;\n}\n\nexport type MessengerView = \"home\" | \"messages\" | \"help\";\n\nexport interface MessengerHandle {\n /** Opens the panel, on Home or the given tab; `{ article }` opens a help article. */\n open(view?: MessengerView | { article: string }): void;\n close(): void;\n toggle(): void;\n /** Removes the launcher and the panel from the page. */\n destroy(): void;\n}\n\nconst POLL_OPEN_MS = 4000;\n/** While the panel is closed: soon after the visitor wrote, answers are looked for often, later now and then. */\nconst POLL_WAITING_MS = 8000;\nconst POLL_IDLE_MS = 30000;\nconst WAITING_FOR_MS = 10 * 60_000;\nconst EMAIL = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]{2,}$/;\nconst SEEN_KEY = \"ckw_msgr_seen\";\nconst whoLabel: Record<ChatMessage[\"from\"], string> = { visitor: \"You\", team: \"Team\", ai: \"AI agent\", auto: \"Team\" };\n\nconst readSeen = () => {\n try {\n return window.localStorage.getItem(SEEN_KEY) ?? \"\";\n } catch {\n return \"\";\n }\n};\nconst writeSeen = (id: string) => {\n try {\n window.localStorage.setItem(SEEN_KEY, id);\n } catch {\n // Private mode: the count just shows again next time.\n }\n};\n\nconst timeAgo = (iso: string) => {\n const t = new Date(iso).getTime();\n if (Number.isNaN(t)) return \"\";\n const d = Math.max(0, Date.now() - t) / 1000;\n if (d < 60) return \"just now\";\n if (d < 3600) return `${Math.floor(d / 60)}m ago`;\n if (d < 86400) return `${Math.floor(d / 3600)}h ago`;\n if (d < 86400 * 30) return `${Math.floor(d / 86400)}d ago`;\n return new Date(iso).toLocaleDateString(undefined, { month: \"short\", day: \"numeric\", year: \"numeric\" });\n};\n\nconst el = <K extends keyof HTMLElementTagNameMap>(tag: K, attrs: Record<string, string> = {}, ...kids: (Node | string | null | false | undefined)[]) => {\n const e = document.createElement(tag);\n for (const [k, v] of Object.entries(attrs)) e.setAttribute(k, v);\n for (const k of kids) if (k !== null && k !== false && k !== undefined) e.append(k);\n return e;\n};\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\";\nconst icon = (d: string, size = 16) => {\n const svg = document.createElementNS(SVG_NS, \"svg\");\n for (const [k, v] of Object.entries({ class: \"ckw-icon\", width: String(size), height: String(size), viewBox: \"0 0 16 16\", fill: \"none\", stroke: \"currentColor\", \"stroke-width\": \"1.7\", \"stroke-linecap\": \"round\", \"stroke-linejoin\": \"round\", \"aria-hidden\": \"true\" }))\n svg.setAttribute(k, v);\n const path = document.createElementNS(SVG_NS, \"path\");\n path.setAttribute(\"d\", d);\n svg.appendChild(path);\n return svg;\n};\nconst I = {\n cross: \"m4 4 8 8M12 4l-8 8\",\n chat: \"M2.5 4.5a2 2 0 0 1 2-2h7a2 2 0 0 1 2 2v4.5a2 2 0 0 1-2 2H7l-3 2.5V11a2 2 0 0 1-1.5-2z\",\n send: \"M2.5 8h10M8.5 3.5 13 8l-4.5 4.5\",\n home: \"M2.5 7 8 2.5 13.5 7v6a1 1 0 0 1-1 1h-3v-4h-3v4h-3a1 1 0 0 1-1-1z\",\n help: \"M8 14.5A6.5 6.5 0 1 0 8 1.5a6.5 6.5 0 0 0 0 13zM6.2 6.2a1.9 1.9 0 1 1 2.6 1.8c-.5.2-.8.6-.8 1.1v.4M8 11.6v.1\",\n back: \"M10 3.5 5.5 8l4.5 4.5\",\n next: \"M6 3.5 10.5 8 6 12.5\",\n search: \"M7 11.5a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9zM10.5 10.5l3 3\",\n doc: \"M4 1.5h5.5l3 3V14a.5.5 0 0 1-.5.5H4a.5.5 0 0 1-.5-.5V2a.5.5 0 0 1 .5-.5zM9.5 1.5v3h3M6 8.5h4M6 11h4\",\n};\n\n/** A soft two-note chime made with Web Audio (no file to load). Browsers allow it once the visitor has clicked. */\nconst chime = (() => {\n let ctx: AudioContext | null = null;\n const get = () => {\n const AC = window.AudioContext ?? (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!AC) return null;\n ctx ??= new AC();\n return ctx;\n };\n return {\n /** Call from a click so later sounds may play. */\n unlock: () => {\n try {\n const c = get();\n if (c?.state === \"suspended\") void c.resume();\n } catch {\n // No audio: no sound.\n }\n },\n play: () => {\n try {\n const c = get();\n if (!c || c.state !== \"running\") return;\n const t = c.currentTime;\n [880, 1318.5].forEach((f, i) => {\n const o = c.createOscillator();\n const g = c.createGain();\n const at = t + i * 0.11;\n o.type = \"sine\";\n o.frequency.value = f;\n g.gain.setValueAtTime(0.0001, at);\n g.gain.exponentialRampToValueAtTime(0.12, at + 0.012);\n g.gain.exponentialRampToValueAtTime(0.0001, at + 0.32);\n o.connect(g);\n g.connect(c.destination);\n o.start(at);\n o.stop(at + 0.34);\n });\n } catch {\n // No audio: no sound.\n }\n },\n };\n})();\n\nconst spansOf = (spans: ArticleSpan[]) =>\n spans.map((s) => {\n let node: HTMLElement = s.href ? el(\"a\", { href: s.href, target: \"_blank\", rel: \"noopener noreferrer\" }, s.text) : el(\"span\", {}, s.text);\n if (s.code) node = el(\"code\", {}, node);\n if (s.italic) node = el(\"em\", {}, node);\n if (s.bold) node = el(\"strong\", {}, node);\n return node;\n });\n\nconst blockOf = (b: ArticleBlock): HTMLElement => {\n switch (b.type) {\n case \"h2\":\n case \"h3\":\n return el(b.type, {}, ...spansOf(b.spans));\n case \"quote\":\n return el(\"blockquote\", {}, ...spansOf(b.spans));\n case \"ul\":\n case \"ol\":\n return el(b.type, {}, ...b.items.map((it) => el(\"li\", {}, ...spansOf(it))));\n case \"code\":\n return el(\"pre\", {}, el(\"code\", {}, b.text));\n case \"hr\":\n return el(\"hr\");\n default:\n return el(\"p\", {}, ...spansOf(b.spans));\n }\n};\n\n/** How long the launcher waits for the look from MRRocket before it shows with the code's options and the defaults. */\nconst LOOK_WAIT_MS = 2500;\n\n/**\n * Mounts the messenger with its look from MRRocket (Support › Messenger) underneath what the code passes: an option the\n * code sets wins, one it leaves out comes from MRRocket, then the default. Calls made before the look arrived wait.\n */\nexport const mountMessenger = (o: MessengerUiOptions): MessengerHandle => {\n let real: MessengerHandle | null = null;\n let gone = false;\n const queued: ((h: MessengerHandle) => void)[] = [];\n const later = (fn: (h: MessengerHandle) => void) => (real ? fn(real) : queued.push(fn));\n const ctrl = new AbortController();\n const start = (look: Partial<MessengerLook> | null) => {\n if (real || gone) return;\n const defined = Object.fromEntries(Object.entries(o).filter(([, v]) => v !== undefined)) as MessengerUiOptions;\n real = build({ ...(look ?? {}), ...defined, accent: o.accent ?? look?.accent, theme: o.theme ?? look?.theme });\n for (const fn of queued.splice(0)) fn(real);\n };\n const timer = setTimeout(() => start(null), LOOK_WAIT_MS);\n createApi({ host: o.host, apiKey: o.apiKey, distinctId: o.distinctId(), user: o.user() })\n .messengerLook(ctrl.signal)\n .then((look) => start(look))\n .catch(() => start(null))\n .finally(() => clearTimeout(timer));\n return {\n open: (v) => later((h) => h.open(v)),\n close: () => real?.close(),\n toggle: () => later((h) => h.toggle()),\n destroy: () => {\n gone = true;\n ctrl.abort();\n clearTimeout(timer);\n real?.destroy();\n },\n };\n};\n\nconst build = (o: MessengerUiOptions): MessengerHandle => {\n injectStyles();\n const position = o.position ?? \"bottom-right\";\n const title = o.title || \"Chat with us\";\n const greeting = o.greeting || \"Hi! How can we help?\";\n const askEmail = o.askEmail ?? true;\n const label = o.label ?? \"\";\n const launcherOn = o.launcher ?? true;\n const helpOn = o.help ?? true;\n const soundOn = o.sound ?? true;\n const previewOn = o.preview ?? true;\n const api = () => createApi({ host: o.host, apiKey: o.apiKey, distinctId: o.distinctId(), user: o.user() });\n\n const root = el(\"div\", { class: \"ckw-root\", \"data-theme\": o.theme ?? \"auto\" });\n if (o.accent) root.style.setProperty(\"--ckw-accent\", o.accent);\n document.body.appendChild(root);\n\n let open = false;\n let view: MessengerView | { article: string } = \"home\";\n let thread: ChatThread | null = null;\n let loaded = false;\n let error = \"\";\n let pending: ChatMessage | null = null;\n /** The newest team message seen in this page load: what counts as new after it. */\n let lastKnown: string | null = null;\n let unseen: ChatMessage[] = [];\n let previewGone = false;\n let poll: ReturnType<typeof setTimeout> | undefined;\n let ctrl: AbortController | null = null;\n let articles: ArticleSummary[] | null = null;\n let query = \"\";\n let rated = new Set<string>();\n /** Whether the product has any published article: the Help tab and Home's help card hide when it has none. */\n let hasArticles: boolean | null = null;\n\n const teamMsgs = (t: ChatThread | null) => (t?.messages ?? []).filter((m) => m.from !== \"visitor\");\n const lastTeamId = (t: ChatThread | null) => teamMsgs(t).at(-1)?.id ?? \"\";\n const watching = () => open && view === \"messages\" && document.visibilityState !== \"hidden\";\n\n /** Takes a thread from the server: new answers count, chime and show in the preview. */\n const take = (t: ChatThread) => {\n const team = teamMsgs(t);\n if (lastKnown === null) {\n // First read: answers the visitor never saw in an earlier visit count, without a sound.\n const seen = readSeen();\n const i = team.findIndex((m) => m.id === seen);\n unseen = t.messages.some((m) => m.from === \"visitor\") ? team.slice(i + 1).filter((m) => m.from !== \"auto\") : [];\n } else {\n const i = team.findIndex((m) => m.id === lastKnown);\n const fresh = team.slice(i + 1).filter((m) => m.from !== \"auto\");\n if (fresh.length) {\n if (soundOn) chime.play();\n if (!watching()) {\n unseen = [...unseen, ...fresh];\n previewGone = false;\n }\n }\n }\n lastKnown = lastTeamId(t);\n thread = t;\n if (watching()) markSeen();\n };\n const markSeen = () => {\n unseen = [];\n const id = lastTeamId(thread);\n if (id) writeSeen(id);\n };\n\n // ---------- Launcher and preview ----------\n const fab = el(\"button\", { type: \"button\", class: `ckw-fab ckw-msgr-fab${label ? \"\" : \" ckw-msgr-round\"}`, \"data-pos\": position, \"aria-haspopup\": \"dialog\" });\n const prev = el(\"div\", { class: \"ckw-msgr-preview\", \"data-pos\": position, role: \"status\" });\n const drawFab = () => {\n fab.replaceChildren(icon(open ? I.cross : I.chat, label ? 16 : 20));\n if (label) fab.append(el(\"span\", {}, label));\n if (unseen.length && !open) fab.append(el(\"span\", { class: \"ckw-msgr-count\", \"aria-hidden\": \"true\" }, String(Math.min(unseen.length, 9))));\n fab.setAttribute(\"aria-expanded\", String(open));\n fab.setAttribute(\"aria-label\", open ? \"Close chat\" : unseen.length ? `Open chat, ${unseen.length} new ${unseen.length === 1 ? \"reply\" : \"replies\"}` : \"Open chat\");\n drawPreview();\n };\n const drawPreview = () => {\n const m = unseen.at(-1);\n if (!previewOn || open || previewGone || !m || !launcherOn) return prev.remove();\n const x = el(\"button\", { type: \"button\", class: \"ckw-msgr-preview-x\", \"aria-label\": \"Dismiss\" }, icon(I.cross, 12));\n x.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n previewGone = true;\n drawPreview();\n });\n const card = el(\n \"button\",\n { type: \"button\", class: \"ckw-msgr-preview-card\" },\n el(\"span\", { class: \"ckw-msgr-preview-who\" }, `${whoLabel[m.from]}${unseen.length > 1 ? ` · ${unseen.length} new` : \"\"}`),\n el(\"span\", { class: \"ckw-msgr-preview-text\" }, m.body),\n );\n card.addEventListener(\"click\", () => show(\"messages\"));\n prev.replaceChildren(card, x);\n if (!prev.isConnected) root.appendChild(prev);\n };\n fab.addEventListener(\"click\", () => {\n chime.unlock();\n if (open) close();\n else show(unseen.length ? \"messages\" : \"home\");\n });\n if (launcherOn) root.appendChild(fab);\n\n // ---------- Panel ----------\n const pop = el(\"div\", { class: \"ckw-pop ckw-msgr\", \"data-pos\": position, role: \"dialog\", \"aria-label\": title });\n const head = el(\"div\", { class: \"ckw-pop-head ckw-msgr-head\" });\n const body = el(\"div\", { class: \"ckw-pop-body ckw-msgr-body\" });\n const tabs = el(\"nav\", { class: \"ckw-msgr-tabs\", \"aria-label\": \"Messenger\" });\n pop.addEventListener(\"click\", () => chime.unlock());\n\n // The chat's compose form keeps its elements between draws, so the field never loses the focus.\n const form = el(\"form\", { class: \"ckw-msgr-compose\" });\n const err = el(\"p\", { class: \"ckw-msgr-error\", role: \"alert\" });\n const email = el(\"input\", { class: \"ckw-input\", type: \"email\", inputmode: \"email\", autocomplete: \"email\", placeholder: \"Your email (optional), so we can follow up\", \"aria-label\": \"Your email (optional)\", maxlength: \"200\" });\n const row = el(\"div\", { class: \"ckw-msgr-row\" });\n const text = el(\"textarea\", { class: \"ckw-textarea ckw-msgr-input\", rows: \"1\", placeholder: \"Write a message…\", \"aria-label\": \"Message\", maxlength: \"4000\" });\n const send = el(\"button\", { type: \"submit\", class: \"ckw-btn ckw-btn-primary ckw-msgr-send\", \"aria-label\": \"Send\" }, icon(I.send));\n row.append(text, send);\n form.append(row);\n\n const list = el(\"ol\", { class: \"ckw-msgs\", \"aria-live\": \"polite\", \"aria-label\": \"Messages\" });\n const needEmail = () => askEmail && !o.user()?.email && !thread?.hasEmail;\n const emailOk = () => !email.value.trim() || EMAIL.test(email.value.trim());\n const canSend = () => !!text.value.trim() && !pending && emailOk();\n\n const closeBtn = () => {\n const b = el(\"button\", { type: \"button\", class: \"ckw-btn ckw-btn-ghost ckw-msgr-x\", \"aria-label\": \"Close messenger\" }, icon(I.cross));\n b.addEventListener(\"click\", () => close());\n return b;\n };\n const go = (v: MessengerView | { article: string }) => {\n view = v;\n drawn = -1;\n draw();\n if (v === \"messages\") {\n markSeen();\n drawFab();\n text.focus();\n } else if (v === \"help\") helpInput.focus();\n schedule(0);\n };\n\n const bubble = (m: ChatMessage, isPending = false) => {\n const li = el(\"li\", { class: \"ckw-msg\", \"data-from\": m.from === \"visitor\" ? \"me\" : \"them\" }, el(\"div\", { class: \"ckw-bubble\" }, m.body));\n if (isPending) li.setAttribute(\"aria-busy\", \"true\");\n for (const a of m.articles ?? []) {\n const chip = el(\"button\", { type: \"button\", class: \"ckw-msgr-cite\" }, icon(I.doc, 14), el(\"span\", {}, a.title));\n chip.addEventListener(\"click\", () => go({ article: a.id }));\n li.append(chip);\n }\n li.append(el(\"span\", { class: \"ckw-msg-meta\" }, `${m.from !== \"visitor\" ? `${whoLabel[m.from]} · ` : \"\"}${isPending ? \"Sending…\" : timeAgo(m.at)}`));\n return li;\n };\n\n const articleRow = (a: ArticleSummary) => {\n const b = el(\"button\", { type: \"button\", class: \"ckw-msgr-art\" }, el(\"span\", { class: \"ckw-msgr-art-t\" }, a.title), icon(I.next, 14));\n b.addEventListener(\"click\", () => go({ article: a.id }));\n return b;\n };\n\n const searchBox = () => {\n const wrap = el(\"label\", { class: \"ckw-msgr-search\" }, icon(I.search, 14));\n const input = el(\"input\", { class: \"ckw-msgr-search-in\", type: \"search\", placeholder: \"Search for help\", \"aria-label\": \"Search for help\", maxlength: \"200\" });\n input.value = query;\n let t: ReturnType<typeof setTimeout> | undefined;\n input.addEventListener(\"input\", () => {\n query = input.value;\n clearTimeout(t);\n t = setTimeout(() => {\n if (view !== \"help\") return go(\"help\");\n void loadArticles(true);\n }, 250);\n });\n input.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n if (view !== \"help\") go(\"help\");\n }\n });\n wrap.append(input);\n return wrap;\n };\n // Help's own field stays the same element between draws, so typing never loses the cursor.\n const helpSearch = searchBox();\n const helpInput = helpSearch.querySelector(\"input\")!;\n\n let loadingArticles = false;\n const loadArticles = async (force = false) => {\n if (!helpOn || (loadingArticles && !force)) return;\n loadingArticles = true;\n const q = query.trim();\n try {\n const r = await api().articles(q || undefined);\n if (q !== query.trim()) return;\n articles = r.articles;\n if (!q) hasArticles = r.articles.length > 0;\n } catch {\n articles = articles ?? [];\n } finally {\n loadingArticles = false;\n }\n draw();\n };\n const drawHome = () => {\n head.replaceChildren(el(\"span\", { class: \"ckw-msgr-brand\" }, title), closeBtn());\n const hero = el(\"div\", { class: \"ckw-msgr-hero\" }, el(\"p\", { class: \"ckw-msgr-hello\" }, greeting));\n const last = (thread?.messages ?? []).at(-1);\n const msgCard = el(\n \"button\",\n { type: \"button\", class: \"ckw-msgr-card\" },\n el(\n \"span\",\n { class: \"ckw-msgr-card-main\" },\n el(\"b\", {}, last ? \"Recent message\" : \"Send us a message\"),\n el(\"span\", { class: \"ckw-msgr-card-sub\" }, last ? `${last.from === \"visitor\" ? \"You\" : whoLabel[last.from]}: ${last.body}` : \"We usually answer within a few hours\"),\n ),\n unseen.length ? el(\"span\", { class: \"ckw-msgr-badge\" }, String(unseen.length)) : icon(I.send),\n );\n msgCard.addEventListener(\"click\", () => go(\"messages\"));\n const parts: HTMLElement[] = [hero, msgCard];\n if (helpOn && hasArticles !== false) {\n const helpCard = el(\"div\", { class: \"ckw-msgr-card ckw-msgr-card-col\" }, searchBox());\n const top = (articles ?? []).slice(0, 4);\n if (top.length) helpCard.append(...top.map(articleRow));\n else if (articles === null) helpCard.append(el(\"div\", { class: \"ckw-skel ckw-msgr-skel\" }));\n parts.push(helpCard);\n }\n body.replaceChildren(...parts);\n };\n\n const drawHelp = () => {\n head.replaceChildren(el(\"span\", {}, \"Help\"), closeBtn());\n if (helpInput.value !== query) helpInput.value = query;\n const parts: HTMLElement[] = [helpSearch];\n if (articles === null) parts.push(el(\"div\", { class: \"ckw-skel\" }));\n else if (!articles.length) {\n const ask = el(\"button\", { type: \"button\", class: \"ckw-btn ckw-btn-primary\" }, \"Send us a message\");\n ask.addEventListener(\"click\", () => go(\"messages\"));\n parts.push(el(\"div\", { class: \"ckw-msgr-empty\" }, el(\"p\", {}, query.trim() ? `Nothing found for “${query.trim()}”.` : \"No help articles yet.\"), ask));\n } else if (query.trim()) parts.push(el(\"div\", { class: \"ckw-msgr-card ckw-msgr-card-col\" }, ...articles.map(articleRow)));\n else {\n const groups = new Map<string, ArticleSummary[]>();\n for (const a of articles) groups.set(a.collection, [...(groups.get(a.collection) ?? []), a]);\n for (const [name, items] of groups)\n parts.push(el(\"section\", { class: \"ckw-msgr-group\" }, el(\"h3\", { class: \"ckw-msgr-group-h\" }, `${name} · ${items.length}`), el(\"div\", { class: \"ckw-msgr-card ckw-msgr-card-col\" }, ...items.map(articleRow))));\n }\n // The field stays in place (moving it would take its focus); only what is under it changes.\n if (body.firstChild !== helpSearch) body.replaceChildren(helpSearch);\n while (body.childNodes.length > 1) body.lastChild!.remove();\n body.append(...parts.slice(1));\n };\n\n let articleShown: { id: string; node: HTMLElement } | null = null;\n const drawArticle = (id: string) => {\n const back = el(\"button\", { type: \"button\", class: \"ckw-btn ckw-btn-ghost ckw-msgr-x\", \"aria-label\": \"Back to help\" }, icon(I.back));\n back.addEventListener(\"click\", () => go(\"help\"));\n head.replaceChildren(back, el(\"span\", { class: \"ckw-msgr-head-t\" }, \"Help\"), closeBtn());\n if (articleShown?.id === id) return body.replaceChildren(articleShown.node);\n const node = el(\"article\", { class: \"ckw-msgr-doc\" }, el(\"div\", { class: \"ckw-skel\" }));\n articleShown = { id, node };\n body.replaceChildren(node);\n api()\n .article(id)\n .then((a) => {\n const foot = el(\"div\", { class: \"ckw-msgr-rate\" });\n const drawRate = () => {\n if (rated.has(id)) return foot.replaceChildren(el(\"span\", {}, \"Thanks for letting us know.\"));\n const yes = el(\"button\", { type: \"button\", class: \"ckw-btn\" }, \"Yes\");\n const no = el(\"button\", { type: \"button\", class: \"ckw-btn\" }, \"No\");\n const rate = (helpful: boolean) => {\n rated = new Set(rated).add(id);\n drawRate();\n api()\n .rateArticle(id, helpful)\n .catch(() => undefined);\n if (!helpful) go(\"messages\");\n };\n yes.addEventListener(\"click\", () => rate(true));\n no.addEventListener(\"click\", () => rate(false));\n foot.replaceChildren(el(\"span\", {}, \"Did this answer your question?\"), el(\"span\", { class: \"ckw-msgr-rate-b\" }, yes, no));\n };\n drawRate();\n const ask = el(\"button\", { type: \"button\", class: \"ckw-btn ckw-msgr-ask\" }, icon(I.chat), \"Still need help? Send us a message\");\n ask.addEventListener(\"click\", () => go(\"messages\"));\n node.replaceChildren(\n el(\"p\", { class: \"ckw-msgr-doc-c\" }, a.collection),\n el(\"h2\", { class: \"ckw-msgr-doc-t\" }, a.title),\n el(\"p\", { class: \"ckw-msgr-doc-c\" }, `Updated ${timeAgo(a.updatedAt)}`),\n el(\"div\", { class: \"ckw-msgr-doc-b\" }, ...a.blocks.map(blockOf)),\n foot,\n ask,\n );\n })\n .catch((e: Error) => {\n articleShown = null;\n node.replaceChildren(el(\"p\", { class: \"ckw-msgr-error\" }, e.message));\n });\n };\n\n let drawn = -1;\n /** What the list shows, so a read that brings nothing new leaves it alone (no flicker, links stay where they are). */\n let listSig = \"\";\n const drawMessages = () => {\n head.replaceChildren(el(\"span\", {}, title), closeBtn());\n const msgs = thread?.messages ?? [];\n const parts: HTMLElement[] = [];\n if (!loaded) parts.push(el(\"div\", { class: \"ckw-skel\" }));\n else {\n const sig = `${msgs.map((m) => m.id).join(\",\")}|${pending ? pending.body : \"\"}|${Math.floor(Date.now() / 60_000)}`;\n if (sig !== listSig) {\n listSig = sig;\n const greet = el(\"li\", { class: \"ckw-msg\", \"data-from\": \"them\" }, el(\"div\", { class: \"ckw-bubble\" }, greeting));\n list.replaceChildren(greet, ...msgs.map((m) => bubble(m)), ...(pending ? [bubble(pending, true)] : []));\n }\n parts.push(list);\n }\n if (thread?.conversation?.status === \"solved\" && !pending) parts.push(el(\"p\", { class: \"ckw-fine ckw-msgr-note\" }, \"This conversation is closed. Write to start a new one.\"));\n if (body.childNodes.length !== parts.length || parts.some((x, i) => body.childNodes[i] !== x)) body.replaceChildren(...parts);\n const count = msgs.length + (pending ? 1 : 0);\n if (count !== drawn) {\n drawn = count;\n body.scrollTop = body.scrollHeight;\n }\n err.textContent = error;\n if (error && !err.isConnected) form.prepend(err);\n else if (!error) err.remove();\n if (needEmail()) {\n if (emailOk()) email.removeAttribute(\"aria-invalid\");\n else email.setAttribute(\"aria-invalid\", \"true\");\n if (!email.isConnected) form.insertBefore(email, row);\n } else email.remove();\n send.disabled = !canSend();\n };\n\n const drawTabs = () => {\n const current = typeof view === \"string\" ? view : \"help\";\n const tab = (v: MessengerView, name: string, d: string, badge = 0) => {\n const b = el(\"button\", { type: \"button\", class: \"ckw-msgr-tab\", \"aria-current\": current === v ? \"page\" : \"false\" }, icon(d, 18), el(\"span\", {}, name));\n if (badge) b.append(el(\"span\", { class: \"ckw-msgr-count ckw-msgr-count-tab\" }, String(Math.min(badge, 9))));\n b.addEventListener(\"click\", () => go(v));\n return b;\n };\n tabs.replaceChildren(tab(\"home\", \"Home\", I.home), tab(\"messages\", \"Messages\", I.chat, view === \"messages\" ? 0 : unseen.length), ...(helpOn && hasArticles !== false ? [tab(\"help\", \"Help\", I.help)] : []));\n };\n\n const draw = () => {\n if (!open) return;\n pop.dataset.view = typeof view === \"string\" ? view : \"article\";\n if (view === \"home\") drawHome();\n else if (view === \"messages\") drawMessages();\n else if (view === \"help\") drawHelp();\n else drawArticle(view.article);\n const kids: HTMLElement[] = [head, body];\n if (view === \"messages\") kids.push(form);\n kids.push(tabs);\n drawTabs();\n if (pop.childNodes.length !== kids.length || kids.some((k, i) => pop.childNodes[i] !== k)) pop.replaceChildren(...kids);\n };\n\n // ---------- Reading the chat ----------\n const tick = async () => {\n ctrl?.abort();\n ctrl = new AbortController();\n try {\n take(await api().chat(ctrl.signal));\n error = \"\";\n } catch (e) {\n if ((e as Error).name === \"AbortError\") return;\n if (open && view === \"messages\") error = (e as Error).message;\n }\n loaded = true;\n draw();\n drawFab();\n schedule();\n };\n /** The next read: often while the chat is on screen, now and then while the visitor waits for an answer. */\n function schedule(now?: number) {\n clearTimeout(poll);\n const waiting = !!thread?.messages.some((m) => m.from === \"visitor\") && thread?.conversation?.status !== \"solved\";\n const wrote = (thread?.messages ?? []).filter((m) => m.from === \"visitor\").at(-1)?.at;\n const recent = !!wrote && Date.now() - new Date(wrote).getTime() < WAITING_FOR_MS;\n const ms = now ?? (watching() ? POLL_OPEN_MS : waiting && recent ? POLL_WAITING_MS : open || waiting ? POLL_IDLE_MS : 0);\n if (ms || now === 0) poll = setTimeout(() => void tick(), ms);\n }\n document.addEventListener(\"visibilitychange\", () => {\n if (document.visibilityState === \"visible\") schedule(0);\n else clearTimeout(poll);\n });\n\n const submit = async () => {\n const msg = text.value.trim();\n if (!msg || pending || !emailOk()) return;\n chime.unlock();\n pending = { id: \"pending\", from: \"visitor\", body: msg, at: new Date().toISOString() };\n text.value = \"\";\n error = \"\";\n draw();\n try {\n take(await api().sendChat({ body: msg, email: needEmail() && email.value.trim() ? email.value.trim() : undefined, url: window.location.href.slice(0, 500) }));\n } catch (e) {\n text.value = msg;\n error = (e as Error).message;\n }\n pending = null;\n draw();\n schedule();\n };\n form.addEventListener(\"submit\", (e) => {\n e.preventDefault();\n void submit();\n });\n text.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" && !e.shiftKey && !e.isComposing) {\n e.preventDefault();\n void submit();\n }\n });\n text.addEventListener(\"input\", () => (send.disabled = !canSend()));\n email.addEventListener(\"input\", () => draw());\n\n function show(v: MessengerView | { article: string } = \"home\") {\n open = true;\n view = v === \"help\" && !helpOn ? \"home\" : v;\n drawn = -1;\n previewGone = true;\n if (!pop.isConnected) root.appendChild(pop);\n if (articles === null || (view === \"help\" && query)) void loadArticles();\n draw();\n drawFab();\n if (view === \"messages\") {\n markSeen();\n text.focus();\n }\n schedule(0);\n }\n function close() {\n if (!open) return;\n open = false;\n pop.remove();\n drawFab();\n schedule();\n if (launcherOn) fab.focus();\n }\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\" && open) close();\n };\n document.addEventListener(\"keydown\", onKey);\n\n // One read on load (the count and preview of answers not seen yet), and the article count for the Help tab.\n void tick();\n if (helpOn) void loadArticles();\n drawFab();\n\n return {\n open: (v) => show(v ?? \"home\"),\n close,\n toggle: () => (open ? close() : show()),\n destroy: () => {\n clearTimeout(poll);\n ctrl?.abort();\n document.removeEventListener(\"keydown\", onKey);\n root.remove();\n },\n };\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/messenger-ui.ts"],"sourcesContent":["/**\n * The web messenger, after Featurebase's: a round launcher and a panel with three tabs, Home (greeting, the latest\n * chat, help search, top articles), Messages (the chat with the team, answered in MRRocket › Support by the owner or\n * the Customer experience agent) and Help (the product's published help articles, searchable, read in the panel).\n * While the panel is closed it checks for answers now and then; a new answer plays a short sound, shows a count on\n * the launcher and a preview card above it.\n *\n * Plain DOM, no React, so `<Messenger />` (Next.js) and the script tag share it; both load it only when it is used (a\n * dynamic import, and `sdk-messenger.js` for the script tag). Every text is set with textContent; articles arrive as\n * blocks, never HTML.\n */\nimport { type ArticleBlock, type ArticleSpan, type ArticleSummary, type ChatMessage, type ChatThread, type MessengerLook, createApi, inkOn, replyLine } from \"./api-client\";\nimport { injectStyles } from \"./styles-css\";\n\nexport interface MessengerUiOptions {\n host: string;\n apiKey: string;\n /** The visitor id now (it changes on identify and reset). */\n distinctId: () => string;\n /** The signed-in person's id, name and email, when the site called identify. */\n user: () => { id?: string; name?: string; email?: string } | undefined;\n /** Show the round launcher (default true). Without it the panel opens only through `open()`. */\n launcher?: boolean;\n position?: \"bottom-right\" | \"bottom-left\";\n /** The panel's title. */\n title?: string;\n /** The big line on Home and the first line of an empty chat. */\n greeting?: string;\n /** Ask anonymous visitors for an email (optional for them). Default true. */\n askEmail?: boolean;\n /** Text on the launcher; empty shows the icon only. */\n label?: string;\n /** How soon the team usually answers, said under \"Send us a message\" and in the chat's head. */\n replyTime?: \"minutes\" | \"hours\" | \"day\";\n /** The Help tab with the product's published help articles (default true; hidden while there are none). */\n help?: boolean;\n /** A short sound when an answer arrives (default true). */\n sound?: boolean;\n /** A preview of a new answer above the closed launcher (default true). */\n preview?: boolean;\n theme?: \"light\" | \"dark\" | \"auto\";\n accent?: string;\n}\n\nexport type MessengerView = \"home\" | \"messages\" | \"help\";\n\nexport interface MessengerHandle {\n /** Opens the panel, on Home or the given tab; `{ article }` opens a help article. */\n open(view?: MessengerView | { article: string }): void;\n close(): void;\n toggle(): void;\n /** Removes the launcher and the panel from the page. */\n destroy(): void;\n}\n\nconst POLL_OPEN_MS = 4000;\n/** While the panel is closed: soon after the visitor wrote, answers are looked for often, later now and then. */\nconst POLL_WAITING_MS = 8000;\nconst POLL_IDLE_MS = 30000;\nconst WAITING_FOR_MS = 10 * 60_000;\nconst EMAIL = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]{2,}$/;\nconst SEEN_KEY = \"ckw_msgr_seen\";\nconst whoLabel: Record<ChatMessage[\"from\"], string> = { visitor: \"You\", team: \"Team\", ai: \"AI agent\", auto: \"Team\" };\n\nconst readSeen = () => {\n try {\n return window.localStorage.getItem(SEEN_KEY) ?? \"\";\n } catch {\n return \"\";\n }\n};\nconst writeSeen = (id: string) => {\n try {\n window.localStorage.setItem(SEEN_KEY, id);\n } catch {\n // Private mode: the count just shows again next time.\n }\n};\n\nconst timeAgo = (iso: string) => {\n const t = new Date(iso).getTime();\n if (Number.isNaN(t)) return \"\";\n const d = Math.max(0, Date.now() - t) / 1000;\n if (d < 60) return \"just now\";\n if (d < 3600) return `${Math.floor(d / 60)}m ago`;\n if (d < 86400) return `${Math.floor(d / 3600)}h ago`;\n if (d < 86400 * 30) return `${Math.floor(d / 86400)}d ago`;\n return new Date(iso).toLocaleDateString(undefined, { month: \"short\", day: \"numeric\", year: \"numeric\" });\n};\n\nconst el = <K extends keyof HTMLElementTagNameMap>(tag: K, attrs: Record<string, string> = {}, ...kids: (Node | string | null | false | undefined)[]) => {\n const e = document.createElement(tag);\n for (const [k, v] of Object.entries(attrs)) e.setAttribute(k, v);\n for (const k of kids) if (k !== null && k !== false && k !== undefined) e.append(k);\n return e;\n};\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\";\nconst icon = (d: string, size = 16) => {\n const svg = document.createElementNS(SVG_NS, \"svg\");\n for (const [k, v] of Object.entries({ class: \"ckw-icon\", width: String(size), height: String(size), viewBox: \"0 0 16 16\", fill: \"none\", stroke: \"currentColor\", \"stroke-width\": \"1.7\", \"stroke-linecap\": \"round\", \"stroke-linejoin\": \"round\", \"aria-hidden\": \"true\" }))\n svg.setAttribute(k, v);\n const path = document.createElementNS(SVG_NS, \"path\");\n path.setAttribute(\"d\", d);\n svg.appendChild(path);\n return svg;\n};\nconst I = {\n cross: \"m4 4 8 8M12 4l-8 8\",\n chat: \"M2.5 4.5a2 2 0 0 1 2-2h7a2 2 0 0 1 2 2v4.5a2 2 0 0 1-2 2H7l-3 2.5V11a2 2 0 0 1-1.5-2z\",\n send: \"M2.5 8h10M8.5 3.5 13 8l-4.5 4.5\",\n up: \"M8 13V3M3.5 7.5 8 3l4.5 4.5\",\n home: \"M2.5 7 8 2.5 13.5 7v6a1 1 0 0 1-1 1h-3v-4h-3v4h-3a1 1 0 0 1-1-1z\",\n help: \"M8 14.5A6.5 6.5 0 1 0 8 1.5a6.5 6.5 0 0 0 0 13zM6.2 6.2a1.9 1.9 0 1 1 2.6 1.8c-.5.2-.8.6-.8 1.1v.4M8 11.6v.1\",\n back: \"M10 3.5 5.5 8l4.5 4.5\",\n next: \"M6 3.5 10.5 8 6 12.5\",\n search: \"M7 11.5a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9zM10.5 10.5l3 3\",\n doc: \"M4 1.5h5.5l3 3V14a.5.5 0 0 1-.5.5H4a.5.5 0 0 1-.5-.5V2a.5.5 0 0 1 .5-.5zM9.5 1.5v3h3M6 8.5h4M6 11h4\",\n};\n\n/** A soft two-note chime made with Web Audio (no file to load). Browsers allow it once the visitor has clicked. */\nconst chime = (() => {\n let ctx: AudioContext | null = null;\n const get = () => {\n const AC = window.AudioContext ?? (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!AC) return null;\n ctx ??= new AC();\n return ctx;\n };\n return {\n /** Call from a click so later sounds may play. */\n unlock: () => {\n try {\n const c = get();\n if (c?.state === \"suspended\") void c.resume();\n } catch {\n // No audio: no sound.\n }\n },\n play: () => {\n try {\n const c = get();\n if (!c || c.state !== \"running\") return;\n const t = c.currentTime;\n [880, 1318.5].forEach((f, i) => {\n const o = c.createOscillator();\n const g = c.createGain();\n const at = t + i * 0.11;\n o.type = \"sine\";\n o.frequency.value = f;\n g.gain.setValueAtTime(0.0001, at);\n g.gain.exponentialRampToValueAtTime(0.12, at + 0.012);\n g.gain.exponentialRampToValueAtTime(0.0001, at + 0.32);\n o.connect(g);\n g.connect(c.destination);\n o.start(at);\n o.stop(at + 0.34);\n });\n } catch {\n // No audio: no sound.\n }\n },\n };\n})();\n\nconst spansOf = (spans: ArticleSpan[]) =>\n spans.map((s) => {\n let node: HTMLElement = s.href ? el(\"a\", { href: s.href, target: \"_blank\", rel: \"noopener noreferrer\" }, s.text) : el(\"span\", {}, s.text);\n if (s.code) node = el(\"code\", {}, node);\n if (s.italic) node = el(\"em\", {}, node);\n if (s.bold) node = el(\"strong\", {}, node);\n return node;\n });\n\nconst blockOf = (b: ArticleBlock): HTMLElement => {\n switch (b.type) {\n case \"h2\":\n case \"h3\":\n return el(b.type, {}, ...spansOf(b.spans));\n case \"quote\":\n return el(\"blockquote\", {}, ...spansOf(b.spans));\n case \"ul\":\n case \"ol\":\n return el(b.type, {}, ...b.items.map((it) => el(\"li\", {}, ...spansOf(it))));\n case \"code\":\n return el(\"pre\", {}, el(\"code\", {}, b.text));\n case \"hr\":\n return el(\"hr\");\n default:\n return el(\"p\", {}, ...spansOf(b.spans));\n }\n};\n\n/** How long the launcher waits for the look from MRRocket before it shows with the code's options and the defaults. */\nconst LOOK_WAIT_MS = 2500;\n\n/**\n * Mounts the messenger with its look from MRRocket (Support › Messenger) underneath what the code passes: an option the\n * code sets wins, one it leaves out comes from MRRocket, then the default. Calls made before the look arrived wait.\n */\nexport const mountMessenger = (o: MessengerUiOptions): MessengerHandle => {\n let real: MessengerHandle | null = null;\n let gone = false;\n const queued: ((h: MessengerHandle) => void)[] = [];\n const later = (fn: (h: MessengerHandle) => void) => (real ? fn(real) : queued.push(fn));\n const ctrl = new AbortController();\n const start = (look: Partial<MessengerLook> | null) => {\n if (real || gone) return;\n const defined = Object.fromEntries(Object.entries(o).filter(([, v]) => v !== undefined)) as MessengerUiOptions;\n real = build({ ...(look ?? {}), ...defined, accent: o.accent ?? look?.accent, theme: o.theme ?? look?.theme });\n for (const fn of queued.splice(0)) fn(real);\n };\n const timer = setTimeout(() => start(null), LOOK_WAIT_MS);\n createApi({ host: o.host, apiKey: o.apiKey, distinctId: o.distinctId(), user: o.user() })\n .messengerLook(ctrl.signal)\n .then((look) => start(look))\n .catch(() => start(null))\n .finally(() => clearTimeout(timer));\n return {\n open: (v) => later((h) => h.open(v)),\n close: () => real?.close(),\n toggle: () => later((h) => h.toggle()),\n destroy: () => {\n gone = true;\n ctrl.abort();\n clearTimeout(timer);\n real?.destroy();\n },\n };\n};\n\nconst build = (o: MessengerUiOptions): MessengerHandle => {\n injectStyles();\n const position = o.position ?? \"bottom-right\";\n const title = o.title || \"Chat with us\";\n const greeting = o.greeting || \"Hi! How can we help?\";\n const askEmail = o.askEmail ?? true;\n const label = o.label ?? \"\";\n const reply = replyLine(o.replyTime);\n /** The team's mark: the first letter of the title, on the accent. */\n const initial = (title.trim()[0] ?? \"?\").toUpperCase();\n const launcherOn = o.launcher ?? true;\n const helpOn = o.help ?? true;\n const soundOn = o.sound ?? true;\n const previewOn = o.preview ?? true;\n const api = () => createApi({ host: o.host, apiKey: o.apiKey, distinctId: o.distinctId(), user: o.user() });\n\n const root = el(\"div\", { class: \"ckw-root\", \"data-theme\": o.theme ?? \"auto\" });\n if (o.accent) {\n root.style.setProperty(\"--ckw-accent\", o.accent);\n root.style.setProperty(\"--ckw-accent-ink\", inkOn(o.accent));\n }\n document.body.appendChild(root);\n\n let open = false;\n let view: MessengerView | { article: string } = \"home\";\n let thread: ChatThread | null = null;\n let loaded = false;\n let error = \"\";\n let pending: ChatMessage | null = null;\n /** The newest team message seen in this page load: what counts as new after it. */\n let lastKnown: string | null = null;\n let unseen: ChatMessage[] = [];\n let previewGone = false;\n let poll: ReturnType<typeof setTimeout> | undefined;\n let ctrl: AbortController | null = null;\n let articles: ArticleSummary[] | null = null;\n let query = \"\";\n let rated = new Set<string>();\n /** Whether the product has any published article: the Help tab and Home's help card hide when it has none. */\n let hasArticles: boolean | null = null;\n\n const teamMsgs = (t: ChatThread | null) => (t?.messages ?? []).filter((m) => m.from !== \"visitor\");\n const lastTeamId = (t: ChatThread | null) => teamMsgs(t).at(-1)?.id ?? \"\";\n const watching = () => open && view === \"messages\" && document.visibilityState !== \"hidden\";\n\n /** Takes a thread from the server: new answers count, chime and show in the preview. */\n const take = (t: ChatThread) => {\n const team = teamMsgs(t);\n if (lastKnown === null) {\n // First read: answers the visitor never saw in an earlier visit count, without a sound.\n const seen = readSeen();\n const i = team.findIndex((m) => m.id === seen);\n unseen = t.messages.some((m) => m.from === \"visitor\") ? team.slice(i + 1).filter((m) => m.from !== \"auto\") : [];\n } else {\n const i = team.findIndex((m) => m.id === lastKnown);\n const fresh = team.slice(i + 1).filter((m) => m.from !== \"auto\");\n if (fresh.length) {\n if (soundOn) chime.play();\n if (!watching()) {\n unseen = [...unseen, ...fresh];\n previewGone = false;\n }\n }\n }\n lastKnown = lastTeamId(t);\n thread = t;\n if (watching()) markSeen();\n };\n const markSeen = () => {\n unseen = [];\n const id = lastTeamId(thread);\n if (id) writeSeen(id);\n };\n\n const avatar = (size?: \"sm\") => el(\"span\", { class: `ckw-msgr-av${size ? \" ckw-msgr-av-sm\" : \"\"}`, \"aria-hidden\": \"true\" }, initial);\n\n // ---------- Launcher and preview ----------\n const fab = el(\"button\", { type: \"button\", class: `ckw-fab ckw-msgr-fab${label ? \"\" : \" ckw-msgr-round\"}`, \"data-pos\": position, \"aria-haspopup\": \"dialog\" });\n const prev = el(\"div\", { class: \"ckw-msgr-preview\", \"data-pos\": position, role: \"status\" });\n /** The open state the launcher's icon last showed: the icon turns only when it changes, not on every read. */\n let fabShows: boolean | null = null;\n const drawFab = () => {\n const glyph = icon(open ? I.cross : I.chat, label ? 18 : 24);\n if (fabShows !== null && fabShows !== open) glyph.classList.add(\"ckw-turn\");\n fabShows = open;\n fab.replaceChildren(glyph);\n if (label) fab.append(el(\"span\", {}, label));\n if (unseen.length && !open) fab.append(el(\"span\", { class: \"ckw-msgr-count\", \"aria-hidden\": \"true\" }, String(Math.min(unseen.length, 9))));\n fab.setAttribute(\"aria-expanded\", String(open));\n fab.setAttribute(\"aria-label\", open ? \"Close chat\" : unseen.length ? `Open chat, ${unseen.length} new ${unseen.length === 1 ? \"reply\" : \"replies\"}` : \"Open chat\");\n drawPreview();\n };\n const drawPreview = () => {\n const m = unseen.at(-1);\n if (!previewOn || open || previewGone || !m || !launcherOn) return prev.remove();\n const x = el(\"button\", { type: \"button\", class: \"ckw-msgr-preview-x\", \"aria-label\": \"Dismiss\" }, icon(I.cross, 12));\n x.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n previewGone = true;\n drawPreview();\n });\n const card = el(\n \"button\",\n { type: \"button\", class: \"ckw-msgr-preview-card\" },\n avatar(),\n el(\n \"span\",\n { class: \"ckw-msgr-preview-main\" },\n el(\"span\", { class: \"ckw-msgr-preview-who\" }, `${whoLabel[m.from]}${unseen.length > 1 ? ` · ${unseen.length} new` : \"\"}`),\n el(\"span\", { class: \"ckw-msgr-preview-text\" }, m.body),\n ),\n );\n card.addEventListener(\"click\", () => show(\"messages\"));\n prev.replaceChildren(card, x);\n if (!prev.isConnected) root.appendChild(prev);\n };\n fab.addEventListener(\"click\", () => {\n chime.unlock();\n if (open) close();\n else show(unseen.length ? \"messages\" : \"home\");\n });\n if (launcherOn) root.appendChild(fab);\n\n // ---------- Panel ----------\n const pop = el(\"div\", { class: \"ckw-pop ckw-msgr\", \"data-pos\": position, role: \"dialog\", \"aria-label\": title });\n const head = el(\"div\", { class: \"ckw-pop-head ckw-msgr-head\" });\n const body = el(\"div\", { class: \"ckw-pop-body ckw-msgr-body\" });\n const tabs = el(\"nav\", { class: \"ckw-msgr-tabs\", \"aria-label\": \"Messenger\" });\n pop.addEventListener(\"click\", () => chime.unlock());\n\n // The chat's compose form keeps its elements between draws, so the field never loses the focus.\n const form = el(\"form\", { class: \"ckw-msgr-compose\" });\n const err = el(\"p\", { class: \"ckw-msgr-error\", role: \"alert\" });\n const email = el(\"input\", { class: \"ckw-input\", type: \"email\", inputmode: \"email\", autocomplete: \"email\", placeholder: \"Your email (optional), so we can follow up\", \"aria-label\": \"Your email (optional)\", maxlength: \"200\" });\n const row = el(\"div\", { class: \"ckw-msgr-row\" });\n const text = el(\"textarea\", { class: \"ckw-textarea ckw-msgr-input\", rows: \"1\", placeholder: \"Write a message…\", \"aria-label\": \"Message\", maxlength: \"4000\" });\n const send = el(\"button\", { type: \"submit\", class: \"ckw-msgr-send\", \"aria-label\": \"Send\" }, icon(I.up));\n row.append(text, send);\n form.append(row);\n\n const list = el(\"ol\", { class: \"ckw-msgs\", \"aria-live\": \"polite\", \"aria-label\": \"Messages\" });\n const needEmail = () => askEmail && !o.user()?.email && !thread?.hasEmail;\n const emailOk = () => !email.value.trim() || EMAIL.test(email.value.trim());\n const canSend = () => !!text.value.trim() && !pending && emailOk();\n\n const closeBtn = () => {\n const b = el(\"button\", { type: \"button\", class: \"ckw-btn ckw-btn-ghost ckw-msgr-x\", \"aria-label\": \"Close messenger\" }, icon(I.cross));\n b.addEventListener(\"click\", () => close());\n return b;\n };\n const go = (v: MessengerView | { article: string }) => {\n view = v;\n drawn = -1;\n draw();\n if (v === \"messages\") {\n markSeen();\n drawFab();\n text.focus();\n } else if (v === \"help\") helpInput.focus();\n schedule(0);\n };\n\n const bubble = (m: ChatMessage, isPending = false) => {\n const li = el(\"li\", { class: \"ckw-msg\", \"data-from\": m.from === \"visitor\" ? \"me\" : \"them\" }, el(\"div\", { class: \"ckw-bubble\" }, m.body));\n if (isPending) li.setAttribute(\"aria-busy\", \"true\");\n for (const a of m.articles ?? []) {\n const chip = el(\"button\", { type: \"button\", class: \"ckw-msgr-cite\" }, icon(I.doc, 14), el(\"span\", {}, a.title));\n chip.addEventListener(\"click\", () => go({ article: a.id }));\n li.append(chip);\n }\n li.append(el(\"span\", { class: \"ckw-msg-meta\" }, `${m.from !== \"visitor\" ? `${whoLabel[m.from]} · ` : \"\"}${isPending ? \"Sending…\" : timeAgo(m.at)}`));\n return li;\n };\n\n const articleRow = (a: ArticleSummary) => {\n const b = el(\"button\", { type: \"button\", class: \"ckw-msgr-art\" }, el(\"span\", { class: \"ckw-msgr-art-t\" }, a.title), icon(I.next, 14));\n b.addEventListener(\"click\", () => go({ article: a.id }));\n return b;\n };\n\n const searchBox = () => {\n const wrap = el(\"label\", { class: \"ckw-msgr-search\" });\n const input = el(\"input\", { class: \"ckw-msgr-search-in\", type: \"search\", placeholder: \"Search for help\", \"aria-label\": \"Search for help\", maxlength: \"200\" });\n input.value = query;\n let t: ReturnType<typeof setTimeout> | undefined;\n input.addEventListener(\"input\", () => {\n query = input.value;\n clearTimeout(t);\n t = setTimeout(() => {\n if (view !== \"help\") return go(\"help\");\n void loadArticles(true);\n }, 250);\n });\n input.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n if (view !== \"help\") go(\"help\");\n }\n });\n wrap.append(input, icon(I.search, 16));\n return wrap;\n };\n // Help's own field stays the same element between draws, so typing never loses the cursor.\n const helpSearch = searchBox();\n const helpInput = helpSearch.querySelector(\"input\")!;\n\n let loadingArticles = false;\n const loadArticles = async (force = false) => {\n if (!helpOn || (loadingArticles && !force)) return;\n loadingArticles = true;\n const q = query.trim();\n try {\n const r = await api().articles(q || undefined);\n if (q !== query.trim()) return;\n articles = r.articles;\n if (!q) hasArticles = r.articles.length > 0;\n } catch {\n articles = articles ?? [];\n } finally {\n loadingArticles = false;\n }\n draw();\n };\n const drawHome = () => {\n head.replaceChildren(el(\"span\", { class: \"ckw-msgr-brand\" }, avatar(\"sm\"), el(\"span\", {}, title)), closeBtn());\n const hero = el(\"div\", { class: \"ckw-msgr-hero\" }, el(\"p\", { class: \"ckw-msgr-hello\" }, greeting));\n const last = (thread?.messages ?? []).at(-1);\n const parts: HTMLElement[] = [hero];\n if (last) {\n const recent = el(\n \"button\",\n { type: \"button\", class: \"ckw-msgr-card ckw-msgr-recent\" },\n el(\"span\", { class: \"ckw-msgr-card-label\" }, \"Recent message\"),\n el(\n \"span\",\n { class: \"ckw-msgr-recent-row\" },\n avatar(),\n el(\n \"span\",\n { class: \"ckw-msgr-card-main\" },\n el(\"span\", { class: \"ckw-msgr-card-sub ckw-msgr-recent-text\" }, last.body),\n el(\"span\", { class: \"ckw-msgr-card-meta\" }, `${last.from === \"visitor\" ? \"You\" : whoLabel[last.from]} · ${timeAgo(last.at)}`),\n ),\n unseen.length ? el(\"span\", { class: \"ckw-msgr-badge\" }, String(unseen.length)) : icon(I.next, 14),\n ),\n );\n recent.addEventListener(\"click\", () => go(\"messages\"));\n parts.push(recent);\n }\n const msgCard = el(\n \"button\",\n { type: \"button\", class: \"ckw-msgr-card\" },\n el(\"span\", { class: \"ckw-msgr-card-main\" }, el(\"b\", {}, \"Send us a message\"), el(\"span\", { class: \"ckw-msgr-card-sub\" }, reply)),\n el(\"span\", { class: \"ckw-msgr-go\" }, icon(I.send)),\n );\n msgCard.addEventListener(\"click\", () => go(\"messages\"));\n parts.push(msgCard);\n if (helpOn && hasArticles !== false) {\n const helpCard = el(\"div\", { class: \"ckw-msgr-card ckw-msgr-card-col\" }, searchBox());\n const top = (articles ?? []).slice(0, 4);\n if (top.length) helpCard.append(...top.map(articleRow));\n else if (articles === null) helpCard.append(el(\"div\", { class: \"ckw-skel ckw-msgr-skel\" }));\n parts.push(helpCard);\n }\n body.replaceChildren(...parts);\n };\n\n const drawHelp = () => {\n head.replaceChildren(el(\"span\", {}, \"Help\"), closeBtn());\n if (helpInput.value !== query) helpInput.value = query;\n const parts: HTMLElement[] = [helpSearch];\n if (articles === null) parts.push(el(\"div\", { class: \"ckw-skel\" }));\n else if (!articles.length) {\n const ask = el(\"button\", { type: \"button\", class: \"ckw-btn ckw-btn-primary\" }, \"Send us a message\");\n ask.addEventListener(\"click\", () => go(\"messages\"));\n parts.push(el(\"div\", { class: \"ckw-msgr-empty\" }, el(\"p\", {}, query.trim() ? `Nothing found for “${query.trim()}”.` : \"No help articles yet.\"), ask));\n } else if (query.trim()) parts.push(el(\"div\", { class: \"ckw-msgr-card ckw-msgr-card-col\" }, ...articles.map(articleRow)));\n else {\n const groups = new Map<string, ArticleSummary[]>();\n for (const a of articles) groups.set(a.collection, [...(groups.get(a.collection) ?? []), a]);\n for (const [name, items] of groups)\n parts.push(el(\"section\", { class: \"ckw-msgr-group\" }, el(\"h3\", { class: \"ckw-msgr-group-h\" }, `${name} · ${items.length}`), el(\"div\", { class: \"ckw-msgr-card ckw-msgr-card-col\" }, ...items.map(articleRow))));\n }\n // The field stays in place (moving it would take its focus); only what is under it changes.\n if (body.firstChild !== helpSearch) body.replaceChildren(helpSearch);\n while (body.childNodes.length > 1) body.lastChild!.remove();\n body.append(...parts.slice(1));\n };\n\n let articleShown: { id: string; node: HTMLElement } | null = null;\n const drawArticle = (id: string) => {\n const back = el(\"button\", { type: \"button\", class: \"ckw-btn ckw-btn-ghost ckw-msgr-x\", \"aria-label\": \"Back to help\" }, icon(I.back));\n back.addEventListener(\"click\", () => go(\"help\"));\n head.replaceChildren(back, el(\"span\", { class: \"ckw-msgr-head-t\" }, \"Help\"), closeBtn());\n if (articleShown?.id === id) return body.replaceChildren(articleShown.node);\n const node = el(\"article\", { class: \"ckw-msgr-doc\" }, el(\"div\", { class: \"ckw-skel\" }));\n articleShown = { id, node };\n body.replaceChildren(node);\n api()\n .article(id)\n .then((a) => {\n const foot = el(\"div\", { class: \"ckw-msgr-rate\" });\n const drawRate = () => {\n if (rated.has(id)) return foot.replaceChildren(el(\"span\", {}, \"Thanks for letting us know.\"));\n const yes = el(\"button\", { type: \"button\", class: \"ckw-btn\" }, \"Yes\");\n const no = el(\"button\", { type: \"button\", class: \"ckw-btn\" }, \"No\");\n const rate = (helpful: boolean) => {\n rated = new Set(rated).add(id);\n drawRate();\n api()\n .rateArticle(id, helpful)\n .catch(() => undefined);\n if (!helpful) go(\"messages\");\n };\n yes.addEventListener(\"click\", () => rate(true));\n no.addEventListener(\"click\", () => rate(false));\n foot.replaceChildren(el(\"span\", {}, \"Did this answer your question?\"), el(\"span\", { class: \"ckw-msgr-rate-b\" }, yes, no));\n };\n drawRate();\n const ask = el(\"button\", { type: \"button\", class: \"ckw-btn ckw-msgr-ask\" }, icon(I.chat), \"Still need help? Send us a message\");\n ask.addEventListener(\"click\", () => go(\"messages\"));\n node.replaceChildren(\n el(\"p\", { class: \"ckw-msgr-doc-c\" }, a.collection),\n el(\"h2\", { class: \"ckw-msgr-doc-t\" }, a.title),\n el(\"p\", { class: \"ckw-msgr-doc-c\" }, `Updated ${timeAgo(a.updatedAt)}`),\n el(\"div\", { class: \"ckw-msgr-doc-b\" }, ...a.blocks.map(blockOf)),\n foot,\n ask,\n );\n })\n .catch((e: Error) => {\n articleShown = null;\n node.replaceChildren(el(\"p\", { class: \"ckw-msgr-error\" }, e.message));\n });\n };\n\n let drawn = -1;\n /** What the list shows, so a read that brings nothing new leaves it alone (no flicker, links stay where they are). */\n let listSig = \"\";\n const drawMessages = () => {\n head.replaceChildren(\n el(\"span\", { class: \"ckw-msgr-who\" }, avatar(), el(\"span\", { class: \"ckw-msgr-who-t\" }, el(\"b\", {}, title), el(\"span\", {}, reply))),\n closeBtn(),\n );\n const msgs = thread?.messages ?? [];\n const parts: HTMLElement[] = [];\n if (!loaded) parts.push(el(\"div\", { class: \"ckw-skel\" }));\n else {\n const sig = `${msgs.map((m) => m.id).join(\",\")}|${pending ? pending.body : \"\"}|${Math.floor(Date.now() / 60_000)}`;\n if (sig !== listSig) {\n listSig = sig;\n const greet = el(\"li\", { class: \"ckw-msg\", \"data-from\": \"them\" }, el(\"div\", { class: \"ckw-bubble\" }, greeting));\n list.replaceChildren(greet, ...msgs.map((m) => bubble(m)), ...(pending ? [bubble(pending, true)] : []));\n }\n parts.push(list);\n }\n if (thread?.conversation?.status === \"solved\" && !pending) parts.push(el(\"p\", { class: \"ckw-fine ckw-msgr-note\" }, \"This conversation is closed. Write to start a new one.\"));\n if (body.childNodes.length !== parts.length || parts.some((x, i) => body.childNodes[i] !== x)) body.replaceChildren(...parts);\n const count = msgs.length + (pending ? 1 : 0);\n if (count !== drawn) {\n drawn = count;\n body.scrollTop = body.scrollHeight;\n }\n err.textContent = error;\n if (error && !err.isConnected) form.prepend(err);\n else if (!error) err.remove();\n if (needEmail()) {\n if (emailOk()) email.removeAttribute(\"aria-invalid\");\n else email.setAttribute(\"aria-invalid\", \"true\");\n if (!email.isConnected) form.insertBefore(email, row);\n } else email.remove();\n send.disabled = !canSend();\n };\n\n const drawTabs = () => {\n const current = typeof view === \"string\" ? view : \"help\";\n const tab = (v: MessengerView, name: string, d: string, badge = 0) => {\n const b = el(\"button\", { type: \"button\", class: \"ckw-msgr-tab\", \"aria-current\": current === v ? \"page\" : \"false\" }, icon(d, 18), el(\"span\", {}, name));\n if (badge) b.append(el(\"span\", { class: \"ckw-msgr-count ckw-msgr-count-tab\" }, String(Math.min(badge, 9))));\n b.addEventListener(\"click\", () => go(v));\n return b;\n };\n tabs.replaceChildren(tab(\"home\", \"Home\", I.home), tab(\"messages\", \"Messages\", I.chat, view === \"messages\" ? 0 : unseen.length), ...(helpOn && hasArticles !== false ? [tab(\"help\", \"Help\", I.help)] : []));\n };\n\n const draw = () => {\n if (!open) return;\n pop.dataset.view = typeof view === \"string\" ? view : \"article\";\n if (view === \"home\") drawHome();\n else if (view === \"messages\") drawMessages();\n else if (view === \"help\") drawHelp();\n else drawArticle(view.article);\n const kids: HTMLElement[] = [head, body];\n if (view === \"messages\") kids.push(form);\n kids.push(tabs);\n drawTabs();\n if (pop.childNodes.length !== kids.length || kids.some((k, i) => pop.childNodes[i] !== k)) pop.replaceChildren(...kids);\n };\n\n // ---------- Reading the chat ----------\n const tick = async () => {\n ctrl?.abort();\n ctrl = new AbortController();\n try {\n take(await api().chat(ctrl.signal));\n error = \"\";\n } catch (e) {\n if ((e as Error).name === \"AbortError\") return;\n if (open && view === \"messages\") error = (e as Error).message;\n }\n loaded = true;\n draw();\n drawFab();\n schedule();\n };\n /** The next read: often while the chat is on screen, now and then while the visitor waits for an answer. */\n function schedule(now?: number) {\n clearTimeout(poll);\n const waiting = !!thread?.messages.some((m) => m.from === \"visitor\") && thread?.conversation?.status !== \"solved\";\n const wrote = (thread?.messages ?? []).filter((m) => m.from === \"visitor\").at(-1)?.at;\n const recent = !!wrote && Date.now() - new Date(wrote).getTime() < WAITING_FOR_MS;\n const ms = now ?? (watching() ? POLL_OPEN_MS : waiting && recent ? POLL_WAITING_MS : open || waiting ? POLL_IDLE_MS : 0);\n if (ms || now === 0) poll = setTimeout(() => void tick(), ms);\n }\n document.addEventListener(\"visibilitychange\", () => {\n if (document.visibilityState === \"visible\") schedule(0);\n else clearTimeout(poll);\n });\n\n const submit = async () => {\n const msg = text.value.trim();\n if (!msg || pending || !emailOk()) return;\n chime.unlock();\n pending = { id: \"pending\", from: \"visitor\", body: msg, at: new Date().toISOString() };\n text.value = \"\";\n error = \"\";\n draw();\n try {\n take(await api().sendChat({ body: msg, email: needEmail() && email.value.trim() ? email.value.trim() : undefined, url: window.location.href.slice(0, 500) }));\n } catch (e) {\n text.value = msg;\n error = (e as Error).message;\n }\n pending = null;\n draw();\n schedule();\n };\n form.addEventListener(\"submit\", (e) => {\n e.preventDefault();\n void submit();\n });\n text.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" && !e.shiftKey && !e.isComposing) {\n e.preventDefault();\n void submit();\n }\n });\n text.addEventListener(\"input\", () => (send.disabled = !canSend()));\n email.addEventListener(\"input\", () => draw());\n\n function show(v: MessengerView | { article: string } = \"home\") {\n open = true;\n view = v === \"help\" && !helpOn ? \"home\" : v;\n drawn = -1;\n previewGone = true;\n if (!pop.isConnected) root.appendChild(pop);\n if (articles === null || (view === \"help\" && query)) void loadArticles();\n draw();\n drawFab();\n if (view === \"messages\") {\n markSeen();\n text.focus();\n }\n schedule(0);\n }\n function close() {\n if (!open) return;\n open = false;\n pop.remove();\n drawFab();\n schedule();\n if (launcherOn) fab.focus();\n }\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\" && open) close();\n };\n document.addEventListener(\"keydown\", onKey);\n\n // One read on load (the count and preview of answers not seen yet), and the article count for the Help tab.\n void tick();\n if (helpOn) void loadArticles();\n drawFab();\n\n return {\n open: (v) => show(v ?? \"home\"),\n close,\n toggle: () => (open ? close() : show()),\n destroy: () => {\n clearTimeout(poll);\n ctrl?.abort();\n document.removeEventListener(\"keydown\", onKey);\n root.remove();\n },\n };\n};\n"],"mappings":"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","names":[]}
package/dist/pick.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * "Pick on your site" (0.11.0). MRRocket's onboarding editor opens the site in a frame with
3
+ * `?ck_embed=1&ck_pick=1&ck_origin=<the editor's origin>`: nothing is tracked (ck_embed), the element under the pointer
4
+ * gets an outline with its selector, and a click picks it instead of doing what it does on the page. The selector goes to
5
+ * the editor by postMessage, to that origin only; Esc tells it the person stopped. Only inside a frame, only with the
6
+ * parameter: a normal visit never sees any of this.
7
+ *
8
+ * Messages to the editor: `{ type: "mrrocket:pick-ready" }` once listening, `{ type: "mrrocket:picked", selector, text }`
9
+ * on a click, `{ type: "mrrocket:pick-cancel" }` on Esc.
10
+ */
11
+ export declare const PICK_PARAM = "ck_pick";
12
+ export declare const PICK_ORIGIN_PARAM = "ck_origin";
13
+ /** A short CSS selector that finds this element, and only it, on the page. */
14
+ export declare const pickSelector: (el: Element) => string;
15
+ /** Starts picking when the page was opened for it; returns how to stop, or null when it was not. */
16
+ export declare const startPicker: () => (() => void) | null;
package/dist/pick.js ADDED
@@ -0,0 +1,117 @@
1
+ // src/pick.ts
2
+ var PICK_PARAM = "ck_pick";
3
+ var PICK_ORIGIN_PARAM = "ck_origin";
4
+ var esc = (s) => typeof CSS !== "undefined" && CSS.escape ? CSS.escape(s) : s.replace(/[^a-zA-Z0-9_-]/g, (c) => `\\${c}`);
5
+ var stableClasses = (el) => [...el.classList].filter((c) => c.length < 30 && !/\d{2,}|[_-][a-z0-9]{5,}$|^(is|has)-|hover|active|focus|open|selected|:|\[/.test(c)).slice(0, 2);
6
+ var pickSelector = (el) => {
7
+ const doc = el.ownerDocument;
8
+ const unique = (sel) => {
9
+ try {
10
+ return doc.querySelectorAll(sel).length === 1;
11
+ } catch {
12
+ return false;
13
+ }
14
+ };
15
+ const named = el.getAttribute("data-ck-target");
16
+ if (named) return `[data-ck-target="${named.replace(/"/g, '\\"')}"]`;
17
+ if (el.id && !/\d{3,}/.test(el.id) && unique(`#${esc(el.id)}`)) return `#${esc(el.id)}`;
18
+ const parts = [];
19
+ let node = el;
20
+ while (node && node !== doc.documentElement && parts.length < 6) {
21
+ if (node !== el && node.id && !/\d{3,}/.test(node.id)) {
22
+ parts.unshift(`#${esc(node.id)}`);
23
+ break;
24
+ }
25
+ const tag = node.tagName.toLowerCase();
26
+ const cls = stableClasses(node).map((c) => `.${esc(c)}`).join("");
27
+ const parent = node.parentElement;
28
+ const same = parent ? [...parent.children].filter((c) => c.tagName === node.tagName) : [];
29
+ const nth = same.length > 1 ? `:nth-of-type(${same.indexOf(node) + 1})` : "";
30
+ parts.unshift(`${tag}${cls}${nth}`);
31
+ const sel = parts.join(" > ");
32
+ if (unique(sel)) return sel;
33
+ node = parent;
34
+ }
35
+ return parts.join(" > ");
36
+ };
37
+ var startPicker = () => {
38
+ if (typeof window === "undefined" || window.top === window) return null;
39
+ const params = new URLSearchParams(window.location.search);
40
+ if (params.get(PICK_PARAM) !== "1") return null;
41
+ let origin;
42
+ try {
43
+ origin = new URL(params.get(PICK_ORIGIN_PARAM) ?? "").origin;
44
+ } catch {
45
+ return null;
46
+ }
47
+ if (!/^https?:\/\//.test(origin)) return null;
48
+ const send = (data) => window.parent.postMessage(data, origin);
49
+ const box = document.createElement("div");
50
+ box.setAttribute("aria-hidden", "true");
51
+ Object.assign(box.style, {
52
+ position: "fixed",
53
+ zIndex: "2147483647",
54
+ pointerEvents: "none",
55
+ border: "2px solid #0ea5e9",
56
+ background: "rgba(14,165,233,.12)",
57
+ borderRadius: "6px",
58
+ transition: "all 60ms linear",
59
+ display: "none"
60
+ });
61
+ const tag = document.createElement("div");
62
+ Object.assign(tag.style, {
63
+ position: "absolute",
64
+ left: "-2px",
65
+ bottom: "calc(100% + 4px)",
66
+ font: "600 11px/1.4 system-ui, sans-serif",
67
+ color: "#fff",
68
+ background: "#0c4a6e",
69
+ padding: "2px 6px",
70
+ borderRadius: "4px",
71
+ whiteSpace: "nowrap",
72
+ maxWidth: "360px",
73
+ overflow: "hidden",
74
+ textOverflow: "ellipsis"
75
+ });
76
+ box.appendChild(tag);
77
+ document.body.appendChild(box);
78
+ let current = null;
79
+ const show = (el) => {
80
+ current = el && el !== box && !box.contains(el) ? el : null;
81
+ if (!current) {
82
+ box.style.display = "none";
83
+ return;
84
+ }
85
+ const r = current.getBoundingClientRect();
86
+ Object.assign(box.style, { display: "block", left: `${r.left - 2}px`, top: `${r.top - 2}px`, width: `${r.width + 4}px`, height: `${r.height + 4}px` });
87
+ tag.textContent = pickSelector(current);
88
+ };
89
+ const move = (e) => show(document.elementFromPoint(e.clientX, e.clientY));
90
+ const click = (e) => {
91
+ e.preventDefault();
92
+ e.stopPropagation();
93
+ const el = document.elementFromPoint(e.clientX, e.clientY) ?? current;
94
+ if (!el || el === box) return;
95
+ send({ type: "mrrocket:picked", selector: pickSelector(el), text: (el.textContent ?? "").trim().replace(/\s+/g, " ").slice(0, 80) });
96
+ };
97
+ const key = (e) => {
98
+ if (e.key === "Escape") send({ type: "mrrocket:pick-cancel" });
99
+ };
100
+ window.addEventListener("mousemove", move, true);
101
+ window.addEventListener("click", click, true);
102
+ window.addEventListener("keydown", key, true);
103
+ send({ type: "mrrocket:pick-ready" });
104
+ return () => {
105
+ window.removeEventListener("mousemove", move, true);
106
+ window.removeEventListener("click", click, true);
107
+ window.removeEventListener("keydown", key, true);
108
+ box.remove();
109
+ };
110
+ };
111
+ export {
112
+ PICK_ORIGIN_PARAM,
113
+ PICK_PARAM,
114
+ pickSelector,
115
+ startPicker
116
+ };
117
+ //# sourceMappingURL=pick.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pick.ts"],"sourcesContent":["/**\n * \"Pick on your site\" (0.11.0). MRRocket's onboarding editor opens the site in a frame with\n * `?ck_embed=1&ck_pick=1&ck_origin=<the editor's origin>`: nothing is tracked (ck_embed), the element under the pointer\n * gets an outline with its selector, and a click picks it instead of doing what it does on the page. The selector goes to\n * the editor by postMessage, to that origin only; Esc tells it the person stopped. Only inside a frame, only with the\n * parameter: a normal visit never sees any of this.\n *\n * Messages to the editor: `{ type: \"mrrocket:pick-ready\" }` once listening, `{ type: \"mrrocket:picked\", selector, text }`\n * on a click, `{ type: \"mrrocket:pick-cancel\" }` on Esc.\n */\nexport const PICK_PARAM = \"ck_pick\";\nexport const PICK_ORIGIN_PARAM = \"ck_origin\";\n\nconst esc = (s: string) => (typeof CSS !== \"undefined\" && CSS.escape ? CSS.escape(s) : s.replace(/[^a-zA-Z0-9_-]/g, (c) => `\\\\${c}`));\n\n/** Classes worth naming: no state classes, no generated hashes or numbers (css-modules, tailwind arbitrary values). */\nconst stableClasses = (el: Element) =>\n [...el.classList].filter((c) => c.length < 30 && !/\\d{2,}|[_-][a-z0-9]{5,}$|^(is|has)-|hover|active|focus|open|selected|:|\\[/.test(c)).slice(0, 2);\n\n/** A short CSS selector that finds this element, and only it, on the page. */\nexport const pickSelector = (el: Element): string => {\n const doc = el.ownerDocument;\n const unique = (sel: string) => {\n try {\n return doc.querySelectorAll(sel).length === 1;\n } catch {\n return false;\n }\n };\n const named = el.getAttribute(\"data-ck-target\");\n if (named) return `[data-ck-target=\"${named.replace(/\"/g, '\\\\\"')}\"]`;\n if (el.id && !/\\d{3,}/.test(el.id) && unique(`#${esc(el.id)}`)) return `#${esc(el.id)}`;\n const parts: string[] = [];\n let node: Element | null = el;\n while (node && node !== doc.documentElement && parts.length < 6) {\n if (node !== el && node.id && !/\\d{3,}/.test(node.id)) {\n parts.unshift(`#${esc(node.id)}`);\n break;\n }\n const tag = node.tagName.toLowerCase();\n const cls = stableClasses(node)\n .map((c) => `.${esc(c)}`)\n .join(\"\");\n const parent: Element | null = node.parentElement;\n const same = parent ? [...parent.children].filter((c) => c.tagName === node!.tagName) : [];\n const nth = same.length > 1 ? `:nth-of-type(${same.indexOf(node) + 1})` : \"\";\n parts.unshift(`${tag}${cls}${nth}`);\n const sel = parts.join(\" > \");\n if (unique(sel)) return sel;\n node = parent;\n }\n return parts.join(\" > \");\n};\n\n/** Starts picking when the page was opened for it; returns how to stop, or null when it was not. */\nexport const startPicker = (): (() => void) | null => {\n if (typeof window === \"undefined\" || window.top === window) return null;\n const params = new URLSearchParams(window.location.search);\n if (params.get(PICK_PARAM) !== \"1\") return null;\n let origin: string;\n try {\n origin = new URL(params.get(PICK_ORIGIN_PARAM) ?? \"\").origin;\n } catch {\n return null;\n }\n if (!/^https?:\\/\\//.test(origin)) return null;\n const send = (data: Record<string, unknown>) => window.parent.postMessage(data, origin);\n\n const box = document.createElement(\"div\");\n box.setAttribute(\"aria-hidden\", \"true\");\n Object.assign(box.style, {\n position: \"fixed\",\n zIndex: \"2147483647\",\n pointerEvents: \"none\",\n border: \"2px solid #0ea5e9\",\n background: \"rgba(14,165,233,.12)\",\n borderRadius: \"6px\",\n transition: \"all 60ms linear\",\n display: \"none\",\n });\n const tag = document.createElement(\"div\");\n Object.assign(tag.style, {\n position: \"absolute\",\n left: \"-2px\",\n bottom: \"calc(100% + 4px)\",\n font: \"600 11px/1.4 system-ui, sans-serif\",\n color: \"#fff\",\n background: \"#0c4a6e\",\n padding: \"2px 6px\",\n borderRadius: \"4px\",\n whiteSpace: \"nowrap\",\n maxWidth: \"360px\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n });\n box.appendChild(tag);\n document.body.appendChild(box);\n\n let current: Element | null = null;\n const show = (el: Element | null) => {\n current = el && el !== box && !box.contains(el) ? el : null;\n if (!current) {\n box.style.display = \"none\";\n return;\n }\n const r = current.getBoundingClientRect();\n Object.assign(box.style, { display: \"block\", left: `${r.left - 2}px`, top: `${r.top - 2}px`, width: `${r.width + 4}px`, height: `${r.height + 4}px` });\n tag.textContent = pickSelector(current);\n };\n const move = (e: MouseEvent) => show(document.elementFromPoint(e.clientX, e.clientY));\n const click = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n const el = document.elementFromPoint(e.clientX, e.clientY) ?? current;\n if (!el || el === box) return;\n send({ type: \"mrrocket:picked\", selector: pickSelector(el), text: (el.textContent ?? \"\").trim().replace(/\\s+/g, \" \").slice(0, 80) });\n };\n const key = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") send({ type: \"mrrocket:pick-cancel\" });\n };\n // Capturing, so the page's own handlers (links, buttons) never see the picking click.\n window.addEventListener(\"mousemove\", move, true);\n window.addEventListener(\"click\", click, true);\n window.addEventListener(\"keydown\", key, true);\n send({ type: \"mrrocket:pick-ready\" });\n return () => {\n window.removeEventListener(\"mousemove\", move, true);\n window.removeEventListener(\"click\", click, true);\n window.removeEventListener(\"keydown\", key, true);\n box.remove();\n };\n};\n"],"mappings":"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","names":[]}
@@ -134,6 +134,10 @@ interface MessengerLook {
134
134
  title: string;
135
135
  greeting: string;
136
136
  label: string;
137
+ /** How soon the team usually answers (MRRocket from 0.10; older servers leave it out). */
138
+ replyTime?: "minutes" | "hours" | "day";
139
+ /** A round chat button in the app (React Native) too. */
140
+ appLauncher?: boolean;
137
141
  help: boolean;
138
142
  sound: boolean;
139
143
  preview: boolean;
@@ -220,7 +224,9 @@ type Api = ReturnType<typeof createApi>;
220
224
  * `finished` with `outcome: "saved"` into a save and with `outcome: "cancelled"` into a cancellation on the Churn page.
221
225
  * MRRocket never changes a subscription: an accepted offer goes to the app's `onSave`, a confirmed cancel to its
222
226
  * `onCancel`, and only after they succeed is the outcome recorded.
223
- * - `$onboarding` `{flow, step?, action: "started" | "shown" | "completed" | "dismissed"}`.
227
+ * - `$onboarding` `{flow, step?, action: "started" | "shown" | "completed" | "dismissed"}`; since 0.11.0 a wait records
228
+ * "shown" when the person reaches it and "completed" when it is over, a condition `{action: "answered", answer: "yes"
229
+ * | "no"}` once it is decided.
224
230
  * - `$inapp` `{message, journey, action: "shown" | "clicked" | "dismissed"}`; the journey moves the person on.
225
231
  */
226
232
 
@@ -272,13 +278,37 @@ interface OnboardingStep {
272
278
  /** Checklist items: the event that ticks them (`track("<event>")`). Without it the person ticks it themselves. */
273
279
  event?: string;
274
280
  }
281
+ /** A pause in a flow (0.11.0): the next step shows this long after the one before was done. */
282
+ interface OnboardingWait {
283
+ id: string;
284
+ kind: "wait";
285
+ amount: number;
286
+ unit: "hours" | "days";
287
+ }
288
+ /**
289
+ * A fork in a flow (0.11.0): "Did <event> happen?" (the person's events, here and on MRRocket) or "Is on <page>?" (the
290
+ * page they are on when they reach it; `*` at the end for everything below). Decided once, when the person reaches it;
291
+ * like a journey's branch it ends its line, the rest of the flow lives in its two paths.
292
+ */
293
+ interface OnboardingCondition {
294
+ id: string;
295
+ kind: "condition";
296
+ event?: string;
297
+ page?: string;
298
+ yes: OnboardingNode[];
299
+ no: OnboardingNode[];
300
+ }
301
+ type OnboardingNode = OnboardingStep | OnboardingWait | OnboardingCondition;
275
302
  /** A live onboarding flow's in-app steps (MRRocket › Conversion › Onboarding); its emails go out from MRRocket. */
276
303
  interface OnboardingFlow {
277
304
  id: string;
278
305
  name: string;
279
306
  /** When it starts: an event's words ("Signed up", "First export"), or "First launch" / "First visit". */
280
307
  trigger: string;
308
+ /** What every version shows: with `nodes`, only the in-app steps before the first wait or condition. */
281
309
  steps: OnboardingStep[];
310
+ /** 0.11.0+: the whole flow with its waits and conditions. Read instead of `steps` when it is there. */
311
+ nodes?: OnboardingNode[];
282
312
  }
283
313
  /** An in-app message a Lifecycle journey has for this person. */
284
314
  interface InAppMessage {
@@ -391,7 +421,7 @@ interface FlowsController {
391
421
 
392
422
  type VariantId = string;
393
423
  /** This build of the SDK. Sent with the config request so MRRocket knows what the site runs (error tracking, flags). */
394
- declare const SDK_VERSION = "0.9.0";
424
+ declare const SDK_VERSION = "0.11.0";
395
425
  /** One variant of an experiment. `changes` replaces texts that the page marks with `<Text id>`. */
396
426
  interface VariantConfig {
397
427
  id: VariantId;
@@ -742,11 +772,17 @@ declare const useMessenger: () => {
742
772
  toggle: () => void;
743
773
  };
744
774
  interface NativeMessengerOptions {
745
- /** A round chat button bottom right (default false: open it with `useMessenger().open()` from your own button). */
775
+ /** A round chat button (default false: open it with `useMessenger().open()` from your own button). */
746
776
  launcher?: boolean;
777
+ /** Where the chat button sits (default bottom right). */
778
+ position?: "bottom-right" | "bottom-left";
779
+ /** Text beside the chat button's icon; empty: the round button. */
780
+ label?: string;
747
781
  title?: string;
748
782
  /** The big line on Home and the first line of an empty chat. */
749
783
  greeting?: string;
784
+ /** How soon the team usually answers, said under "Send us a message" and in the chat's head. */
785
+ replyTime?: "minutes" | "hours" | "day";
750
786
  /** Ask people the app did not identify with an email for an email (optional for them). Default true. */
751
787
  askEmail?: boolean;
752
788
  /** The Help tab with your published help articles (default true; hidden while there are none). */
@@ -134,6 +134,10 @@ interface MessengerLook {
134
134
  title: string;
135
135
  greeting: string;
136
136
  label: string;
137
+ /** How soon the team usually answers (MRRocket from 0.10; older servers leave it out). */
138
+ replyTime?: "minutes" | "hours" | "day";
139
+ /** A round chat button in the app (React Native) too. */
140
+ appLauncher?: boolean;
137
141
  help: boolean;
138
142
  sound: boolean;
139
143
  preview: boolean;
@@ -220,7 +224,9 @@ type Api = ReturnType<typeof createApi>;
220
224
  * `finished` with `outcome: "saved"` into a save and with `outcome: "cancelled"` into a cancellation on the Churn page.
221
225
  * MRRocket never changes a subscription: an accepted offer goes to the app's `onSave`, a confirmed cancel to its
222
226
  * `onCancel`, and only after they succeed is the outcome recorded.
223
- * - `$onboarding` `{flow, step?, action: "started" | "shown" | "completed" | "dismissed"}`.
227
+ * - `$onboarding` `{flow, step?, action: "started" | "shown" | "completed" | "dismissed"}`; since 0.11.0 a wait records
228
+ * "shown" when the person reaches it and "completed" when it is over, a condition `{action: "answered", answer: "yes"
229
+ * | "no"}` once it is decided.
224
230
  * - `$inapp` `{message, journey, action: "shown" | "clicked" | "dismissed"}`; the journey moves the person on.
225
231
  */
226
232
 
@@ -272,13 +278,37 @@ interface OnboardingStep {
272
278
  /** Checklist items: the event that ticks them (`track("<event>")`). Without it the person ticks it themselves. */
273
279
  event?: string;
274
280
  }
281
+ /** A pause in a flow (0.11.0): the next step shows this long after the one before was done. */
282
+ interface OnboardingWait {
283
+ id: string;
284
+ kind: "wait";
285
+ amount: number;
286
+ unit: "hours" | "days";
287
+ }
288
+ /**
289
+ * A fork in a flow (0.11.0): "Did <event> happen?" (the person's events, here and on MRRocket) or "Is on <page>?" (the
290
+ * page they are on when they reach it; `*` at the end for everything below). Decided once, when the person reaches it;
291
+ * like a journey's branch it ends its line, the rest of the flow lives in its two paths.
292
+ */
293
+ interface OnboardingCondition {
294
+ id: string;
295
+ kind: "condition";
296
+ event?: string;
297
+ page?: string;
298
+ yes: OnboardingNode[];
299
+ no: OnboardingNode[];
300
+ }
301
+ type OnboardingNode = OnboardingStep | OnboardingWait | OnboardingCondition;
275
302
  /** A live onboarding flow's in-app steps (MRRocket › Conversion › Onboarding); its emails go out from MRRocket. */
276
303
  interface OnboardingFlow {
277
304
  id: string;
278
305
  name: string;
279
306
  /** When it starts: an event's words ("Signed up", "First export"), or "First launch" / "First visit". */
280
307
  trigger: string;
308
+ /** What every version shows: with `nodes`, only the in-app steps before the first wait or condition. */
281
309
  steps: OnboardingStep[];
310
+ /** 0.11.0+: the whole flow with its waits and conditions. Read instead of `steps` when it is there. */
311
+ nodes?: OnboardingNode[];
282
312
  }
283
313
  /** An in-app message a Lifecycle journey has for this person. */
284
314
  interface InAppMessage {
@@ -391,7 +421,7 @@ interface FlowsController {
391
421
 
392
422
  type VariantId = string;
393
423
  /** This build of the SDK. Sent with the config request so MRRocket knows what the site runs (error tracking, flags). */
394
- declare const SDK_VERSION = "0.9.0";
424
+ declare const SDK_VERSION = "0.11.0";
395
425
  /** One variant of an experiment. `changes` replaces texts that the page marks with `<Text id>`. */
396
426
  interface VariantConfig {
397
427
  id: VariantId;
@@ -742,11 +772,17 @@ declare const useMessenger: () => {
742
772
  toggle: () => void;
743
773
  };
744
774
  interface NativeMessengerOptions {
745
- /** A round chat button bottom right (default false: open it with `useMessenger().open()` from your own button). */
775
+ /** A round chat button (default false: open it with `useMessenger().open()` from your own button). */
746
776
  launcher?: boolean;
777
+ /** Where the chat button sits (default bottom right). */
778
+ position?: "bottom-right" | "bottom-left";
779
+ /** Text beside the chat button's icon; empty: the round button. */
780
+ label?: string;
747
781
  title?: string;
748
782
  /** The big line on Home and the first line of an empty chat. */
749
783
  greeting?: string;
784
+ /** How soon the team usually answers, said under "Send us a message" and in the chat's head. */
785
+ replyTime?: "minutes" | "hours" | "day";
750
786
  /** Ask people the app did not identify with an email for an email (optional for them). Default true. */
751
787
  askEmail?: boolean;
752
788
  /** The Help tab with your published help articles (default true; hidden while there are none). */