@mrrocket/sdk 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +35 -1
  3. package/dist/api-client.d.ts +213 -0
  4. package/dist/api-client.js +67 -0
  5. package/dist/api-client.js.map +1 -0
  6. package/dist/api.d.ts +2 -122
  7. package/dist/api.js +2 -61
  8. package/dist/api.js.map +1 -1
  9. package/dist/browser/mrrocket-messenger.js +313 -0
  10. package/dist/browser/mrrocket-replay.js +3 -3
  11. package/dist/browser/mrrocket.js +1 -1
  12. package/dist/browser.d.ts +2 -1
  13. package/dist/browser.js +3 -0
  14. package/dist/browser.js.map +1 -1
  15. package/dist/capture.d.ts +1 -13
  16. package/dist/capture.js +1 -22
  17. package/dist/capture.js.map +1 -1
  18. package/dist/core.d.ts +1 -1
  19. package/dist/core.js +1 -1
  20. package/dist/core.js.map +1 -1
  21. package/dist/feedback.js +5 -2
  22. package/dist/feedback.js.map +1 -1
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +4 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/messenger-ui.d.ts +47 -0
  27. package/dist/messenger-ui.js +583 -0
  28. package/dist/messenger-ui.js.map +1 -0
  29. package/dist/messenger.d.ts +37 -0
  30. package/dist/messenger.js +69 -0
  31. package/dist/messenger.js.map +1 -0
  32. package/dist/rage.d.ts +14 -0
  33. package/dist/rage.js +27 -0
  34. package/dist/rage.js.map +1 -0
  35. package/dist/react-native/index.d.cts +339 -4
  36. package/dist/react-native/index.d.ts +339 -4
  37. package/dist/react-native/index.js +1191 -31
  38. package/dist/react-native/index.js.map +1 -1
  39. package/dist/styles-css.d.ts +6 -0
  40. package/dist/styles-css.js +327 -0
  41. package/dist/styles-css.js.map +1 -0
  42. package/dist/styles.d.ts +1 -2
  43. package/dist/styles.js +3 -237
  44. package/dist/styles.js.map +1 -1
  45. package/package.json +13 -5
@@ -0,0 +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":[]}
@@ -0,0 +1,37 @@
1
+ import type { MessengerView } from "./messenger-ui.js";
2
+ import type { Theme } from "./ui.js";
3
+ /** Open (on Home, a tab or an article), close or toggle the messenger from your own button (or mark any element `data-ck-messenger`). */
4
+ export declare const useMessenger: () => {
5
+ open: (view?: MessengerView | {
6
+ article: string;
7
+ }) => void;
8
+ close: () => void | undefined;
9
+ toggle: () => void;
10
+ };
11
+ export interface MessengerProps {
12
+ position?: "bottom-right" | "bottom-left";
13
+ /** The panel's title. */
14
+ title?: string;
15
+ /** The big line on Home and the first line of an empty chat. */
16
+ greeting?: string;
17
+ /** Ask anonymous visitors for an email (optional for them). Default true. */
18
+ askEmail?: boolean;
19
+ /** Text on the launcher; empty shows the icon only. */
20
+ label?: string;
21
+ /** Show the round launcher (default true). False: open it with `useMessenger()` or `data-ck-messenger`. */
22
+ launcher?: boolean;
23
+ /** The Help tab with your published help articles (default true; hidden while there are none). */
24
+ help?: boolean;
25
+ /** A short sound when an answer arrives (default true). */
26
+ sound?: boolean;
27
+ /** A preview of a new answer above the closed launcher (default true). */
28
+ preview?: boolean;
29
+ theme?: Theme;
30
+ accent?: string;
31
+ }
32
+ /**
33
+ * A floating launcher that opens the messenger. Put it once inside `<CockpitProvider>`:
34
+ *
35
+ * <Messenger title="Acme" greeting="Hi! How can we help?" />
36
+ */
37
+ export declare const Messenger: ({ position, title, greeting, askEmail, label, launcher, help, sound, preview, theme, accent }: MessengerProps) => null;
@@ -0,0 +1,69 @@
1
+ "use client";
2
+
3
+ // src/messenger.tsx
4
+ import { useEffect, useRef } from "react";
5
+ import { useCtx } from "./context.js";
6
+ var handle = null;
7
+ var waiting = null;
8
+ var withHandle = (fn) => {
9
+ if (handle) fn(handle);
10
+ else waiting = fn;
11
+ };
12
+ var useMessenger = () => ({
13
+ open: (view) => withHandle((h) => h.open(view)),
14
+ close: () => handle?.close(),
15
+ toggle: () => withHandle((h) => h.toggle())
16
+ });
17
+ var Messenger = ({ position, title, greeting, askEmail, label, launcher, help, sound, preview, theme, accent }) => {
18
+ const ctx = useCtx();
19
+ const who = useRef({ distinctId: ctx.distinctId, user: ctx.user });
20
+ who.current = { distinctId: ctx.distinctId, user: ctx.user };
21
+ const { apiKey, host, embedded } = ctx;
22
+ useEffect(() => {
23
+ if (embedded || !apiKey) return;
24
+ let gone = false;
25
+ let mine = null;
26
+ void import("./messenger-ui.js").then(({ mountMessenger }) => {
27
+ if (gone) return;
28
+ mine = mountMessenger({
29
+ host,
30
+ apiKey,
31
+ distinctId: () => who.current.distinctId,
32
+ user: () => who.current.user,
33
+ launcher,
34
+ position,
35
+ title,
36
+ greeting,
37
+ askEmail,
38
+ label,
39
+ help,
40
+ sound,
41
+ preview,
42
+ theme,
43
+ accent
44
+ });
45
+ handle = mine;
46
+ waiting?.(mine);
47
+ waiting = null;
48
+ });
49
+ const click = (e) => {
50
+ if (e.target?.closest("[data-ck-messenger]")) {
51
+ e.preventDefault();
52
+ withHandle((h) => h.open());
53
+ }
54
+ };
55
+ document.addEventListener("click", click);
56
+ return () => {
57
+ gone = true;
58
+ document.removeEventListener("click", click);
59
+ mine?.destroy();
60
+ if (handle === mine) handle = null;
61
+ };
62
+ }, [apiKey, host, embedded, launcher, position, title, greeting, askEmail, label, help, sound, preview, theme, accent]);
63
+ return null;
64
+ };
65
+ export {
66
+ Messenger,
67
+ useMessenger
68
+ };
69
+ //# sourceMappingURL=messenger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/messenger.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef } from \"react\";\nimport { useCtx } from \"./context\";\nimport type { MessengerHandle, MessengerView } from \"./messenger-ui\";\nimport type { Theme } from \"./ui\";\n\n/**\n * The messenger, after Featurebase's: a launcher and a panel with Home, Messages (a chat with the product's team) and\n * Help (the product's published help articles). A visitor's messages become a Support conversation (channel \"chat\")\n * in MRRocket's inbox, where the owner answers or the Customer experience agent does (Support › Automation › AI\n * answers). A new answer plays a short sound and shows a preview above the closed launcher.\n *\n * The panel is plain DOM (messenger-ui.ts, shared with the script tag), loaded with a dynamic import after the page\n * rendered. The visitor is the provider's id; a person the site identified carries their name and email.\n */\n\nlet handle: MessengerHandle | null = null;\n/** A call made before the panel loaded runs once it has. */\nlet waiting: ((h: MessengerHandle) => void) | null = null;\nconst withHandle = (fn: (h: MessengerHandle) => void) => {\n if (handle) fn(handle);\n else waiting = fn;\n};\n\n/** Open (on Home, a tab or an article), close or toggle the messenger from your own button (or mark any element `data-ck-messenger`). */\nexport const useMessenger = () => ({\n open: (view?: MessengerView | { article: string }) => withHandle((h) => h.open(view)),\n close: () => handle?.close(),\n toggle: () => withHandle((h) => h.toggle()),\n});\n\nexport interface MessengerProps {\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 /** Show the round launcher (default true). False: open it with `useMessenger()` or `data-ck-messenger`. */\n launcher?: boolean;\n /** The Help tab with your 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?: Theme;\n accent?: string;\n}\n\n/**\n * A floating launcher that opens the messenger. Put it once inside `<CockpitProvider>`:\n *\n * <Messenger title=\"Acme\" greeting=\"Hi! How can we help?\" />\n */\nexport const Messenger = ({ position, title, greeting, askEmail, label, launcher, help, sound, preview, theme, accent }: MessengerProps) => {\n const ctx = useCtx();\n // The panel reads the visitor at each request, so a later identify or reset needs no remount.\n const who = useRef({ distinctId: ctx.distinctId, user: ctx.user });\n who.current = { distinctId: ctx.distinctId, user: ctx.user };\n const { apiKey, host, embedded } = ctx;\n\n useEffect(() => {\n if (embedded || !apiKey) return;\n let gone = false;\n let mine: MessengerHandle | null = null;\n void import(\"./messenger-ui\").then(({ mountMessenger }) => {\n if (gone) return;\n mine = mountMessenger({\n host,\n apiKey,\n distinctId: () => who.current.distinctId,\n user: () => who.current.user as { id?: string; name?: string; email?: string } | undefined,\n launcher,\n position,\n title,\n greeting,\n askEmail,\n label,\n help,\n sound,\n preview,\n theme,\n accent,\n });\n handle = mine;\n waiting?.(mine);\n waiting = null;\n });\n const click = (e: MouseEvent) => {\n if ((e.target as HTMLElement | null)?.closest(\"[data-ck-messenger]\")) {\n e.preventDefault();\n withHandle((h) => h.open());\n }\n };\n document.addEventListener(\"click\", click);\n return () => {\n gone = true;\n document.removeEventListener(\"click\", click);\n mine?.destroy();\n if (handle === mine) handle = null;\n };\n }, [apiKey, host, embedded, launcher, position, title, greeting, askEmail, label, help, sound, preview, theme, accent]);\n\n return null;\n};\n"],"mappings":";;;AAEA,SAAS,WAAW,cAAc;AAClC,SAAS,cAAc;AAcvB,IAAI,SAAiC;AAErC,IAAI,UAAiD;AACrD,IAAM,aAAa,CAAC,OAAqC;AACrD,MAAI,OAAQ,IAAG,MAAM;AAAA,MAChB,WAAU;AACnB;AAGO,IAAM,eAAe,OAAO;AAAA,EAC/B,MAAM,CAAC,SAA+C,WAAW,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC;AAAA,EACpF,OAAO,MAAM,QAAQ,MAAM;AAAA,EAC3B,QAAQ,MAAM,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC;AAC9C;AA6BO,IAAM,YAAY,CAAC,EAAE,UAAU,OAAO,UAAU,UAAU,OAAO,UAAU,MAAM,OAAO,SAAS,OAAO,OAAO,MAAsB;AACxI,QAAM,MAAM,OAAO;AAEnB,QAAM,MAAM,OAAO,EAAE,YAAY,IAAI,YAAY,MAAM,IAAI,KAAK,CAAC;AACjE,MAAI,UAAU,EAAE,YAAY,IAAI,YAAY,MAAM,IAAI,KAAK;AAC3D,QAAM,EAAE,QAAQ,MAAM,SAAS,IAAI;AAEnC,YAAU,MAAM;AACZ,QAAI,YAAY,CAAC,OAAQ;AACzB,QAAI,OAAO;AACX,QAAI,OAA+B;AACnC,SAAK,OAAO,mBAAgB,EAAE,KAAK,CAAC,EAAE,eAAe,MAAM;AACvD,UAAI,KAAM;AACV,aAAO,eAAe;AAAA,QAClB;AAAA,QACA;AAAA,QACA,YAAY,MAAM,IAAI,QAAQ;AAAA,QAC9B,MAAM,MAAM,IAAI,QAAQ;AAAA,QACxB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACJ,CAAC;AACD,eAAS;AACT,gBAAU,IAAI;AACd,gBAAU;AAAA,IACd,CAAC;AACD,UAAM,QAAQ,CAAC,MAAkB;AAC7B,UAAK,EAAE,QAA+B,QAAQ,qBAAqB,GAAG;AAClE,UAAE,eAAe;AACjB,mBAAW,CAAC,MAAM,EAAE,KAAK,CAAC;AAAA,MAC9B;AAAA,IACJ;AACA,aAAS,iBAAiB,SAAS,KAAK;AACxC,WAAO,MAAM;AACT,aAAO;AACP,eAAS,oBAAoB,SAAS,KAAK;AAC3C,YAAM,QAAQ;AACd,UAAI,WAAW,KAAM,UAAS;AAAA,IAClC;AAAA,EACJ,GAAG,CAAC,QAAQ,MAAM,UAAU,UAAU,UAAU,OAAO,UAAU,UAAU,OAAO,MAAM,OAAO,SAAS,OAAO,MAAM,CAAC;AAEtH,SAAO;AACX;","names":[]}
package/dist/rage.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ /** Rage clicks without the DOM, so the web SDK and the apps (taps) count them the same. */
2
+ /**
3
+ * Three or more clicks within one second on the same spot (30 pixels) are a rage click. `push` answers true once per
4
+ * burst, on the third click.
5
+ */
6
+ export declare class RageDetector {
7
+ private readonly windowMs;
8
+ private readonly radius;
9
+ private readonly count;
10
+ private clicks;
11
+ private fired;
12
+ constructor(windowMs?: number, radius?: number, count?: number);
13
+ push(x: number, y: number, t: number): boolean;
14
+ }
package/dist/rage.js ADDED
@@ -0,0 +1,27 @@
1
+ // src/rage.ts
2
+ var RageDetector = class {
3
+ constructor(windowMs = 1e3, radius = 30, count = 3) {
4
+ this.windowMs = windowMs;
5
+ this.radius = radius;
6
+ this.count = count;
7
+ }
8
+ windowMs;
9
+ radius;
10
+ count;
11
+ clicks = [];
12
+ fired = false;
13
+ push(x, y, t) {
14
+ const near = this.clicks.filter((c) => t - c.t <= this.windowMs && Math.hypot(c.x - x, c.y - y) <= this.radius);
15
+ if (near.length !== this.clicks.length) this.fired = false;
16
+ this.clicks = [...near, { x, y, t }];
17
+ if (this.clicks.length >= this.count && !this.fired) {
18
+ this.fired = true;
19
+ return true;
20
+ }
21
+ return false;
22
+ }
23
+ };
24
+ export {
25
+ RageDetector
26
+ };
27
+ //# sourceMappingURL=rage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rage.ts"],"sourcesContent":["/** Rage clicks without the DOM, so the web SDK and the apps (taps) count them the same. */\n\n/**\n * Three or more clicks within one second on the same spot (30 pixels) are a rage click. `push` answers true once per\n * burst, on the third click.\n */\nexport class RageDetector {\n private clicks: { x: number; y: number; t: number }[] = [];\n private fired = false;\n constructor(\n private readonly windowMs = 1000,\n private readonly radius = 30,\n private readonly count = 3,\n ) {}\n push(x: number, y: number, t: number) {\n const near = this.clicks.filter((c) => t - c.t <= this.windowMs && Math.hypot(c.x - x, c.y - y) <= this.radius);\n if (near.length !== this.clicks.length) this.fired = false;\n this.clicks = [...near, { x, y, t }];\n if (this.clicks.length >= this.count && !this.fired) {\n this.fired = true;\n return true;\n }\n return false;\n }\n}\n"],"mappings":";AAMO,IAAM,eAAN,MAAmB;AAAA,EAGtB,YACqB,WAAW,KACX,SAAS,IACT,QAAQ,GAC3B;AAHmB;AACA;AACA;AAAA,EAClB;AAAA,EAHkB;AAAA,EACA;AAAA,EACA;AAAA,EALb,SAAgD,CAAC;AAAA,EACjD,QAAQ;AAAA,EAMhB,KAAK,GAAW,GAAW,GAAW;AAClC,UAAM,OAAO,KAAK,OAAO,OAAO,CAAC,MAAM,IAAI,EAAE,KAAK,KAAK,YAAY,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,IAAI,CAAC,KAAK,KAAK,MAAM;AAC9G,QAAI,KAAK,WAAW,KAAK,OAAO,OAAQ,MAAK,QAAQ;AACrD,SAAK,SAAS,CAAC,GAAG,MAAM,EAAE,GAAG,GAAG,EAAE,CAAC;AACnC,QAAI,KAAK,OAAO,UAAU,KAAK,SAAS,CAAC,KAAK,OAAO;AACjD,WAAK,QAAQ;AACb,aAAO;AAAA,IACX;AACA,WAAO;AAAA,EACX;AACJ;","names":[]}