@sphoro/bugcatch-web-sdk 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -5
- package/dist/bugcatch-call.es.js +586 -0
- package/dist/bugcatch-call.es.js.map +1 -0
- package/dist/bugcatch-call.umd.js +2 -0
- package/dist/bugcatch-call.umd.js.map +1 -0
- package/dist/bugcatch-core.es.js +3214 -0
- package/dist/bugcatch-core.es.js.map +1 -0
- package/dist/bugcatch-panel.es.js +2402 -0
- package/dist/bugcatch-panel.es.js.map +1 -0
- package/dist/bugcatch-panel.umd.js +1240 -0
- package/dist/bugcatch-panel.umd.js.map +1 -0
- package/dist/bugcatch.es.js +2 -5522
- package/dist/bugcatch.es.js.map +1 -1
- package/dist/bugcatch.standalone.umd.js +1536 -0
- package/dist/bugcatch.standalone.umd.js.map +1 -0
- package/dist/bugcatch.umd.js +89 -1183
- package/dist/bugcatch.umd.js.map +1 -1
- package/package.json +6 -6
- package/src/agent-issues.js +30 -0
- package/src/announcements.js +147 -0
- package/src/busy.js +18 -0
- package/src/call.js +7 -0
- package/src/capture.js +9 -0
- package/src/chunks.js +56 -0
- package/src/gesture.js +33 -0
- package/src/icons.js +18 -0
- package/src/identity.js +14 -9
- package/src/incoming-call.js +8 -30
- package/src/index.d.ts +50 -0
- package/src/index.js +170 -37
- package/src/lazy.js +23 -0
- package/src/notice.js +68 -0
- package/src/panel/call.js +242 -0
- package/src/panel/compose.js +416 -0
- package/src/panel/composer.js +235 -0
- package/src/panel/icons.js +34 -0
- package/src/panel/index.js +251 -0
- package/src/panel/known.js +130 -0
- package/src/panel/messages.js +176 -0
- package/src/panel/notices.js +34 -0
- package/src/panel/picks.js +61 -0
- package/src/panel/refs.js +321 -0
- package/src/panel/render.js +124 -0
- package/src/panel/signin.js +228 -0
- package/src/panel/styles.js +960 -0
- package/src/panel/thread.js +384 -0
- package/src/session.js +8 -3
- package/src/umd/call.js +6 -0
- package/src/umd/lazy.js +98 -0
- package/src/umd/panel.js +6 -0
- package/src/updates.js +20 -7
- package/src/widget.js +243 -3164
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bugcatch-panel.umd.js","sources":["../src/icons.js","../src/panel/icons.js","../src/panel/styles.js","../src/panel/render.js","../src/capture.js","../src/drafts.js","../src/panel/compose.js","../src/references.js","../src/panel/refs.js","../src/notice.js","../src/panel/notices.js","../src/panel/known.js","../src/panel/picks.js","../src/panel/call.js","../src/panel/signin.js","../src/agent-issues.js","../src/panel/messages.js","../src/panel/composer.js","../src/panel/thread.js","../src/panel/index.js","../src/chunks.js","../src/umd/panel.js"],"sourcesContent":["// The handful of glyphs the launcher draws before the panel exists: its own\n// two, the theme switch's three, what the load-failure card needs, and the\n// tones of an announcement beside the button (notice.js). Every other icon\n// lives with the panel (panel/icons.js), which re-exports these.\n//\n// One export per glyph rather than one object, so a bundle carries only the\n// ones something draws — imported as `* as ICONS` and read as ICONS.chat.\n\nexport const chat = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M4 11.5a7.5 7 0 0 1 7.5-7h1a7.5 7 0 0 1 0 14H9l-4.2 2.6a.5.5 0 0 1-.76-.5l.5-3.1A7 7 0 0 1 4 11.5z\"/><path d=\"M8.5 11.5h.01M12 11.5h.01M15.5 11.5h.01\" stroke-width=\"2.6\"/></svg>';\nexport const chevronDown = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M6 9.5l6 6 6-6\"/></svg>';\nexport const sun = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"4.2\"/><path d=\"M12 2.6v2.2M12 19.2v2.2M2.6 12h2.2M19.2 12h2.2M5.4 5.4l1.6 1.6M17 17l1.6 1.6M18.6 5.4L17 7M7 17l-1.6 1.6\"/></svg>';\nexport const moon = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 13.4A8.2 8.2 0 1 1 10.6 4a6.6 6.6 0 0 0 9.4 9.4z\"/></svg>';\nexport const auto = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"2.8\" y=\"4.4\" width=\"18.4\" height=\"12.6\" rx=\"2.2\"/><path d=\"M8.6 20.2h6.8\"/><path d=\"M12 4.4v12.6\"/><path d=\"M12 7.2a3.6 3.6 0 0 1 0 7.2z\" fill=\"currentColor\" stroke=\"none\"/></svg>';\nexport const alert = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"9\"/><path d=\"M12 7.5v5.2M12 16.3h.01\"/></svg>';\nexport const close = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"><path d=\"M6 6l12 12M18 6L6 18\"/></svg>';\nexport const check = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 12.8l4.6 4.4L19 7\"/></svg>';\nexport const info = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"9\"/><path d=\"M12 11v5.5M12 7.7h.01\"/></svg>';\nexport const warn = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M10.3 4.2a2 2 0 0 1 3.4 0l7.6 13.1a2 2 0 0 1-1.7 3H4.4a2 2 0 0 1-1.7-3z\"/><path d=\"M12 9.5v4.2M12 17h.01\"/></svg>';\n","// The panel's glyphs: the launcher's set (../icons.js) and everything the\n// form, the thread and the call screens draw.\n\nexport * from '../icons.js';\n\nexport const camera = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 8.5A1.5 1.5 0 0 1 4.5 7h2.2a1 1 0 0 0 .83-.45l.94-1.4A1 1 0 0 1 9.3 4.7h5.4a1 1 0 0 1 .83.45l.94 1.4a1 1 0 0 0 .83.45h2.2A1.5 1.5 0 0 1 21 8.5v9A1.5 1.5 0 0 1 19.5 19h-15A1.5 1.5 0 0 1 3 17.5z\"/><circle cx=\"12\" cy=\"13\" r=\"3.4\"/></svg>';\nexport const video = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"6\" width=\"12.5\" height=\"12\" rx=\"2.2\"/><path d=\"M15.5 10.8l4.2-2.6a.6.6 0 0 1 .92.5v6.6a.6.6 0 0 1-.92.5l-4.2-2.6z\"/></svg>';\nexport const paperclip = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 11.5l-7.6 7.6a4.6 4.6 0 0 1-6.5-6.5l8-8a3.2 3.2 0 0 1 4.5 4.5l-8 8a1.8 1.8 0 0 1-2.5-2.5l7.1-7.1\"/></svg>';\nexport const mic = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"9\" y=\"2.5\" width=\"6\" height=\"11\" rx=\"3\"/><path d=\"M5.5 11a6.5 6.5 0 0 0 13 0M12 17.5V21M9 21h6\"/></svg>';\nexport const send = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M4 12l16-8-6 16-2.6-6.2L4 12Z\"/></svg>';\nexport const stopSquare = '<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><rect x=\"7\" y=\"7\" width=\"10\" height=\"10\" rx=\"2\"/></svg>';\nexport const back = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M14.5 5.5L8 12l6.5 6.5\"/></svg>';\nexport const phone = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M6.2 3.5h3l1.5 3.7-2 1.4a11.5 11.5 0 0 0 5.7 5.7l1.4-2 3.7 1.5v3a1.8 1.8 0 0 1-2 1.8A15.6 15.6 0 0 1 4.4 5.5a1.8 1.8 0 0 1 1.8-2z\"/></svg>';\nexport const phoneOff = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M6.2 3.5h3l1.5 3.7-2 1.4a11.5 11.5 0 0 0 5.7 5.7l1.4-2 3.7 1.5v3a1.8 1.8 0 0 1-2 1.8A15.6 15.6 0 0 1 4.4 5.5a1.8 1.8 0 0 1 1.8-2z\"/><path d=\"M3 21L21 3\"/></svg>';\nexport const bug = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M8 6a4 4 0 0 1 8 0\"/><rect x=\"7\" y=\"7.5\" width=\"10\" height=\"12\" rx=\"5\"/><path d=\"M7 11H3.5M7 15.5H4M17 11h3.5M17 15.5h3M12 8v11.5\"/></svg>';\nexport const retry = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 12a8 8 0 1 1-2.6-5.9M20 4v4.5h-4.5\"/></svg>';\n// The assistant's mark: a four-point spark, drawn wherever it speaks.\nexport const sparkle = '<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2.5c.5 4.6 2.9 7 7.5 7.5v.1c-4.6.5-7 2.9-7.5 7.5h-.1c-.5-4.6-2.9-7-7.5-7.5V10c4.6-.5 7-2.9 7.5-7.5z\"/><path d=\"M18.6 15.2c.2 1.9 1.2 2.9 3.1 3.1v.1c-1.9.2-2.9 1.2-3.1 3.1h-.1c-.2-1.9-1.2-2.9-3.1-3.1v-.1c1.9-.2 2.9-1.2 3.1-3.1z\" opacity=\".7\"/></svg>';\nexport const chevronRight = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9.5 6l6 6-6 6\"/></svg>';\nexport const inbox = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3.5 13.5l2.6-7.2A1.5 1.5 0 0 1 7.5 5.3h9a1.5 1.5 0 0 1 1.4 1L20.5 13.5V18a1.5 1.5 0 0 1-1.5 1.5H5A1.5 1.5 0 0 1 3.5 18z\"/><path d=\"M3.5 13.5h4.8l1.2 2.2h5l1.2-2.2h4.8\"/></svg>';\n\n/* The sphoro lockup, as outlines rather than text.\n *\n * This SDK runs inside other people's pages, where Space Grotesk is not loaded\n * and cannot be assumed. Live <text> would render the mark in whatever the host\n * site happens to use — a different typeface on every customer. Outlines are\n * self-contained, so the mark is identical everywhere, at the cost of the path\n * data below.\n *\n * The BUGCATCH half reads --bc-brand-mark, not a bare --accent. Custom properties\n * are exempt from `all: initial`, so they cross the shadow boundary: a host page\n * that happens to define --accent would have repainted our mark in their brand\n * colour. The --bc- prefix is ours and no host is going to set it by accident. */\nexport const BRAND = '<svg class=\"bc-brand\" viewBox=\"0 0 254 60\" fill=\"none\" role=\"img\" aria-label=\"sphoro BugCatch\"><g fill=\"currentColor\"><path transform=\"translate(0.00 40.00) scale(0.04000 -0.04000)\" d=\"M276.00 -14.00Q179.00 -14.00 117.00 28.00Q55.00 70.00 42.00 148.00L158.00 178.00Q165.00 143.00 181.50 123.00Q198.00 103.00 222.50 94.50Q247.00 86.00 276.00 86.00Q320.00 86.00 341.00 101.50Q362.00 117.00 362.00 140.00Q362.00 163.00 342.00 175.50Q322.00 188.00 278.00 196.00L250.00 201.00Q198.00 211.00 155.00 228.50Q112.00 246.00 86.00 277.00Q60.00 308.00 60.00 357.00Q60.00 431.00 114.00 470.50Q168.00 510.00 256.00 510.00Q339.00 510.00 394.00 473.00Q449.00 436.00 466.00 376.00L349.00 340.00Q341.00 378.00 316.50 394.00Q292.00 410.00 256.00 410.00Q220.00 410.00 201.00 397.50Q182.00 385.00 182.00 363.00Q182.00 339.00 202.00 327.50Q222.00 316.00 256.00 310.00L284.00 305.00Q340.00 295.00 385.50 278.50Q431.00 262.00 457.50 231.50Q484.00 201.00 484.00 149.00Q484.00 71.00 427.50 28.50Q371.00 -14.00 276.00 -14.00Z\"/><path transform=\"translate(19.56 40.00) scale(0.04000 -0.04000)\" d=\"M70.00 -200.00V496.00H194.00V436.00H212.00Q229.00 465.00 265.00 487.50Q301.00 510.00 368.00 510.00Q428.00 510.00 479.00 480.50Q530.00 451.00 561.00 394.00Q592.00 337.00 592.00 256.00V240.00Q592.00 159.00 561.00 102.00Q530.00 45.00 479.00 15.50Q428.00 -14.00 368.00 -14.00Q323.00 -14.00 292.50 -3.50Q262.00 7.00 243.50 23.50Q225.00 40.00 214.00 57.00H196.00V-200.00ZM330.00 96.00Q389.00 96.00 427.50 133.50Q466.00 171.00 466.00 243.00V253.00Q466.00 325.00 427.00 362.50Q388.00 400.00 330.00 400.00Q272.00 400.00 233.00 362.50Q194.00 325.00 194.00 253.00V243.00Q194.00 171.00 233.00 133.50Q272.00 96.00 330.00 96.00Z\"/><path transform=\"translate(43.68 40.00) scale(0.04000 -0.04000)\" d=\"M70.00 0.00V700.00H196.00V435.00H214.00Q222.00 451.00 239.00 467.00Q256.00 483.00 284.50 493.50Q313.00 504.00 357.00 504.00Q415.00 504.00 458.50 477.50Q502.00 451.00 526.00 404.50Q550.00 358.00 550.00 296.00V0.00H424.00V286.00Q424.00 342.00 396.50 370.00Q369.00 398.00 318.00 398.00Q260.00 398.00 228.00 359.50Q196.00 321.00 196.00 252.00V0.00Z\"/><path transform=\"translate(66.92 40.00) scale(0.04000 -0.04000)\" d=\"M306.00 -14.00Q232.00 -14.00 173.00 16.00Q114.00 46.00 80.00 103.00Q46.00 160.00 46.00 240.00V256.00Q46.00 336.00 80.00 393.00Q114.00 450.00 173.00 480.00Q232.00 510.00 306.00 510.00Q380.00 510.00 439.00 480.00Q498.00 450.00 532.00 393.00Q566.00 336.00 566.00 256.00V240.00Q566.00 160.00 532.00 103.00Q498.00 46.00 439.00 16.00Q380.00 -14.00 306.00 -14.00ZM306.00 98.00Q364.00 98.00 402.00 135.50Q440.00 173.00 440.00 243.00V253.00Q440.00 323.00 402.50 360.50Q365.00 398.00 306.00 398.00Q248.00 398.00 210.00 360.50Q172.00 323.00 172.00 253.00V243.00Q172.00 173.00 210.00 135.50Q248.00 98.00 306.00 98.00Z\"/><path transform=\"translate(90.00 40.00) scale(0.04000 -0.04000)\" d=\"M70.00 0.00V496.00H194.00V440.00H212.00Q223.00 470.00 248.50 484.00Q274.00 498.00 308.00 498.00H368.00V386.00H306.00Q258.00 386.00 227.00 360.50Q196.00 335.00 196.00 282.00V0.00Z\"/><path transform=\"translate(104.44 40.00) scale(0.04000 -0.04000)\" d=\"M306.00 -14.00Q232.00 -14.00 173.00 16.00Q114.00 46.00 80.00 103.00Q46.00 160.00 46.00 240.00V256.00Q46.00 336.00 80.00 393.00Q114.00 450.00 173.00 480.00Q232.00 510.00 306.00 510.00Q380.00 510.00 439.00 480.00Q498.00 450.00 532.00 393.00Q566.00 336.00 566.00 256.00V240.00Q566.00 160.00 532.00 103.00Q498.00 46.00 439.00 16.00Q380.00 -14.00 306.00 -14.00ZM306.00 98.00Q364.00 98.00 402.00 135.50Q440.00 173.00 440.00 243.00V253.00Q440.00 323.00 402.50 360.50Q365.00 398.00 306.00 398.00Q248.00 398.00 210.00 360.50Q172.00 323.00 172.00 253.00V243.00Q172.00 173.00 210.00 135.50Q248.00 98.00 306.00 98.00Z\"/></g><rect x=\"139.52\" y=\"16\" width=\"1.6\" height=\"26\" rx=\"0.8\" fill=\"var(--bc-brand-mark, #5647ee)\" opacity=\"0.4\"/><g fill=\"var(--bc-brand-mark, #5647ee)\"><path transform=\"translate(154.52 35.00) scale(0.01400 -0.01400)\" d=\"M46.35 0.00V105.85H139.75V594.15H46.35V700.00H404.08Q467.38 700.00 514.79 678.41Q562.20 656.83 588.70 617.33Q615.20 577.83 615.20 523.35V513.70Q615.20 465.52 597.03 434.76Q578.85 404.00 553.91 387.24Q528.98 370.48 506.28 363.30V346.35Q528.98 340.35 555.24 323.50Q581.50 306.65 600.00 275.48Q618.50 244.30 618.50 194.48V184.48Q618.50 126.65 591.41 85.24Q564.33 43.83 516.83 21.91Q469.33 0.00 406.85 0.00ZM259.85 109.15H392.60Q441.20 109.15 469.80 132.60Q498.40 156.05 498.40 198.55V208.20Q498.40 250.70 470.21 274.15Q442.02 297.60 392.60 297.60H259.85ZM259.85 406.75H391.65Q437.55 406.75 466.32 430.11Q495.10 453.48 495.10 493.80V503.80Q495.10 544.95 466.74 567.90Q438.37 590.85 391.65 590.85H259.85Z\"/><path transform=\"translate(167.00 35.00) scale(0.01400 -0.01400)\" d=\"M335.30 -14.00Q251.22 -14.00 190.60 16.63Q129.97 47.25 97.56 104.96Q65.15 162.68 65.15 243.03V700.00H185.25V239.73Q185.25 170.48 224.03 131.99Q262.80 93.50 335.30 93.50Q407.80 93.50 446.57 131.99Q485.35 170.48 485.35 239.73V700.00H605.45V243.03Q605.45 162.68 573.13 104.96Q540.80 47.25 480.09 16.63Q419.38 -14.00 335.30 -14.00Z\"/><path transform=\"translate(179.60 35.00) scale(0.01400 -0.01400)\" d=\"M311.85 -14.00Q238.45 -14.00 179.69 18.50Q120.92 51.00 86.69 113.67Q52.45 176.35 52.45 266.87V433.13Q52.45 569.00 128.01 641.50Q203.57 714.00 332.35 714.00Q460.13 714.00 529.23 645.59Q598.33 577.17 598.33 460.87V456.70H479.87V464.52Q479.87 504.67 463.76 536.86Q447.65 569.05 414.99 587.77Q382.32 606.50 332.35 606.50Q257.63 606.50 215.09 560.70Q172.55 514.90 172.55 435.13V264.87Q172.55 185.93 215.09 138.89Q257.63 91.85 334.00 91.85Q410.02 91.85 445.12 132.83Q480.22 173.80 480.22 237.92V249.67H302.85V351.88H598.33V0.00H487.52V69.10H470.57Q462.70 50.87 446.06 31.44Q429.42 12.00 397.64 -1.00Q365.85 -14.00 311.85 -14.00Z\"/><path transform=\"translate(192.05 35.00) scale(0.01400 -0.01400)\" d=\"M332.35 -14.00Q203.57 -14.00 128.01 58.50Q52.45 131.00 52.45 266.87V433.13Q52.45 569.00 128.01 641.50Q203.57 714.00 332.35 714.00Q460.13 714.00 529.58 643.85Q599.03 573.70 599.03 451.57V445.75H480.57V455.22Q480.57 521.42 444.15 563.96Q407.72 606.50 332.35 606.50Q257.63 606.50 215.09 560.70Q172.55 514.90 172.55 435.13V264.87Q172.55 185.93 215.09 139.71Q257.63 93.50 332.35 93.50Q407.72 93.50 444.15 136.45Q480.57 179.40 480.57 244.78V262.25H599.03V248.43Q599.03 126.30 529.58 56.15Q460.13 -14.00 332.35 -14.00Z\"/><path transform=\"translate(204.25 35.00) scale(0.01400 -0.01400)\" d=\"M19.75 0.00 211.62 700.00H421.33L613.20 0.00H489.27L447.42 159.60H185.53L143.68 0.00ZM214.25 270.40H418.70L324.95 624.87H308.00Z\"/><path transform=\"translate(216.31 35.00) scale(0.01400 -0.01400)\" d=\"M233.95 0.00V590.85H24.70V700.00H563.30V590.85H354.05V0.00Z\"/><path transform=\"translate(227.75 35.00) scale(0.01400 -0.01400)\" d=\"M332.35 -14.00Q203.57 -14.00 128.01 58.50Q52.45 131.00 52.45 266.87V433.13Q52.45 569.00 128.01 641.50Q203.57 714.00 332.35 714.00Q460.13 714.00 529.58 643.85Q599.03 573.70 599.03 451.57V445.75H480.57V455.22Q480.57 521.42 444.15 563.96Q407.72 606.50 332.35 606.50Q257.63 606.50 215.09 560.70Q172.55 514.90 172.55 435.13V264.87Q172.55 185.93 215.09 139.71Q257.63 93.50 332.35 93.50Q407.72 93.50 444.15 136.45Q480.57 179.40 480.57 244.78V262.25H599.03V248.43Q599.03 126.30 529.58 56.15Q460.13 -14.00 332.35 -14.00Z\"/><path transform=\"translate(239.95 35.00) scale(0.01400 -0.01400)\" d=\"M69.50 0.00V700.00H189.60V405.58H466.40V700.00H586.50V0.00H466.40V296.42H189.60V0.00Z\"/></g></svg>';\n","// The panel's stylesheet: everything the dialog draws, from the form to the\n// thread to the call bar.\n//\n// Kept apart from the launcher's (widget.js) because it is most of the\n// widget's CSS and none of it is needed until the panel is first opened. It is\n// added to the same shadow root the first time that happens, after the\n// launcher's sheet — so the custom properties, the theme and the base rules\n// declared there are already in force, and anything here that could collide\n// with them comes later in the cascade, where it always was.\n\nexport const PANEL_CSS = `\n@keyframes bc-fade-in { from { opacity: 0 } to { opacity: 1 } }\n@keyframes bc-fade-out { to { opacity: 0 } }\n@keyframes bc-panel-in { from { opacity: 0; transform: translateY(14px) scale(.965) } to { opacity: 1; transform: none } }\n@keyframes bc-panel-out { to { opacity: 0; transform: translateY(10px) scale(.975) } }\n@keyframes bc-sheet-in { from { transform: translateY(100%) } to { transform: none } }\n@keyframes bc-sheet-out { to { transform: translateY(100%) } }\n@keyframes bc-rise { from { opacity: 0; transform: translateY(6px) } to { opacity: 1; transform: none } }\n@keyframes bc-pulse { 0%, 100% { opacity: 1; transform: scale(1) } 50% { opacity: .3; transform: scale(.78) } }\n@keyframes bc-spin { to { transform: rotate(360deg) } }\n@keyframes bc-pop { 0% { transform: scale(.5); opacity: 0 } 65% { transform: scale(1.1) } 100% { transform: scale(1); opacity: 1 } }\n@keyframes bc-nudge { 25% { transform: translateX(-3px) } 75% { transform: translateX(3px) } }\n@keyframes bc-typing { 0%, 60%, 100% { transform: translateY(0); opacity: .45 } 30% { transform: translateY(-3px); opacity: 1 } }\n@keyframes bc-shimmer { from { background-position: 200% 0 } to { background-position: -200% 0 } }\n\n/* ---------- scrim + panel ---------- */\n.scrim {\n position: fixed; inset: 0; z-index: 2147483001;\n background: rgba(10,12,16,.32);\n animation: bc-fade-in 200ms ease both;\n}\n.scrim.closing { animation: bc-fade-out 170ms ease both; }\n\n/* The panel belongs to the launcher, so it opens out of that corner rather\n than in the middle of the screen. --bc-dock-h is measured at open time\n because the dock's height depends on whether the launcher is showing.\n A column: the header and footer stay put and only the middle scrolls. */\n.panel {\n position: fixed;\n display: flex; flex-direction: column;\n width: calc(100vw - 40px); max-width: 420px;\n max-height: calc(100vh - var(--bc-dock-h, 84px) - 52px);\n overflow: hidden;\n background: var(--bc-bg); color: var(--bc-fg);\n border-radius: var(--bc-r-xl); box-shadow: var(--bc-shadow);\n border: 1px solid color-mix(in srgb, var(--bc-border) 70%, transparent);\n animation: bc-panel-in 340ms var(--bc-ease) both;\n}\n/* A conversation wants a height of its own, so the thread fills the panel\n rather than shrinking to its first two messages. */\n.panel.is-chat { height: 640px; }\n.scrim.at-right .panel { right: 20px; transform-origin: bottom right; }\n.scrim.at-left .panel { left: 20px; transform-origin: bottom left; }\n.scrim.at-bottom .panel { bottom: calc(20px + var(--bc-dock-h, 84px) + 12px); }\n.scrim.at-top .panel { top: calc(20px + var(--bc-dock-h, 84px) + 12px); transform-origin: top right; }\n.scrim.closing .panel { animation: bc-panel-out 170ms ease both; }\n\n/* ---------- header ---------- */\n.head {\n flex: none; position: relative;\n display: flex; align-items: center; gap: 12px;\n padding: 16px 14px 14px var(--bc-pad);\n background: linear-gradient(180deg, var(--bc-accent-soft), var(--bc-bg));\n border-bottom: 1px solid var(--bc-border);\n}\n.head .mark, .avatar {\n flex: none; display: grid; place-items: center; position: relative;\n width: 38px; height: 38px; border-radius: 50%;\n background: linear-gradient(140deg, var(--bc-accent), color-mix(in srgb, var(--bc-accent) 60%, #fff));\n color: var(--bc-on-accent); font-size: 19px;\n box-shadow: 0 4px 12px color-mix(in srgb, var(--bc-accent) 30%, transparent);\n}\n/* The little \"online\" dot says someone — or something — is on the other end. */\n.head .mark::after {\n content: ''; position: absolute; right: -1px; bottom: -1px;\n width: 11px; height: 11px; border-radius: 50%;\n background: #22c55e; border: 2px solid var(--bc-bg);\n}\n.head .titles { flex: 1; min-width: 0; }\n.head h2 {\n margin: 0 0 2px; font-size: var(--bc-fs-lg); font-weight: 680; letter-spacing: -.02em; line-height: 1.25;\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n}\n.head p { margin: 0; font-size: var(--bc-fs-sm); line-height: 1.45; color: var(--bc-muted); }\n.icon-btn {\n flex: none; display: grid; place-items: center;\n width: 32px; height: 32px; padding: 0; font-size: 16px;\n border: none; border-radius: 10px; background: none; color: var(--bc-muted);\n cursor: pointer; transition: background 160ms, color 160ms, transform 160ms var(--bc-ease);\n}\n.icon-btn:hover { background: color-mix(in srgb, var(--bc-fg) 7%, transparent); color: var(--bc-fg); }\n.icon-btn:active { transform: scale(.92); }\n.icon-btn.back { margin: 0 -6px 0 -8px; }\n\n.body {\n flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;\n padding: 16px var(--bc-pad) 6px;\n}\n\nlabel { display: block; font-size: var(--bc-fs-sm); font-weight: 620; color: var(--bc-fg); margin-bottom: 7px; }\nlabel .opt { font-weight: 450; color: var(--bc-faint); }\ninput[type=text], input[type=email], textarea {\n width: 100%; padding: 11px 13px; font-size: var(--bc-fs-md); line-height: 1.45;\n color: var(--bc-fg); background: var(--bc-elevated);\n border: 1px solid var(--bc-border); border-radius: var(--bc-r-md);\n transition: border-color 160ms, box-shadow 160ms, background 160ms;\n}\ninput[type=text]::placeholder, input[type=email]::placeholder, textarea::placeholder { color: var(--bc-faint); }\ninput[type=text]:hover, input[type=email]:hover, textarea:hover { border-color: color-mix(in srgb, var(--bc-fg) 18%, var(--bc-border)); }\ninput[type=text]:focus, input[type=email]:focus, textarea:focus {\n outline: none; border-color: var(--bc-accent); box-shadow: var(--bc-ring);\n}\ninput.invalid, input.invalid:focus, textarea.invalid {\n border-color: var(--bc-danger); animation: bc-nudge 240ms ease;\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--bc-danger) 16%, transparent);\n}\n.field-err {\n display: flex; align-items: center; gap: 6px;\n margin: 7px 0 0; font-size: var(--bc-fs-xs); font-weight: 550; color: var(--bc-danger);\n animation: bc-rise 200ms var(--bc-ease) both;\n}\n.field-err svg { font-size: 13px; flex: none; }\ntextarea { min-height: 88px; resize: vertical; }\n.field { margin-bottom: 16px; }\n.field-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }\n.field-head .count { font-size: var(--bc-fs-xs); color: var(--bc-faint); font-variant-numeric: tabular-nums; }\n\n/* The draft line: a quiet note, or a card offering to pick up where they left off. */\n.compose > .note { font-size: var(--bc-fs-xs); color: var(--bc-faint); margin: -4px 0 10px; }\n.compose > .note:empty { display: none; }\n.compose > .note:has(button) {\n display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px;\n margin: 0 0 14px; padding: 10px 12px; border-radius: var(--bc-r-md);\n background: var(--bc-accent-soft); border: 1px solid var(--bc-accent-line);\n font-size: var(--bc-fs-sm); color: var(--bc-fg);\n}\n.compose > .note button {\n padding: 5px 11px; font: inherit; font-size: var(--bc-fs-xs); font-weight: 620;\n border-radius: 999px; cursor: pointer;\n border: 1px solid var(--bc-accent); background: var(--bc-accent); color: var(--bc-on-accent);\n}\n.compose > .note button[data-discard] { background: transparent; color: var(--bc-muted); border-color: var(--bc-border); }\n\n/* ---------- what is already known ----------\n A help surface in front of the form, not a step in it. Everything here is\n dismissible by ignoring it: the reporter can read what is already known and\n leave, tap \"this is happening to me too\" and leave, or type straight past all\n of it and file the report they came to file. Nothing is ever in their way,\n because a reporter who feels handled rather than helped stops reporting. */\n.known { margin-bottom: 16px; }\n.known .known-h {\n margin: 0 0 8px; font-size: 11px; font-weight: 680; letter-spacing: .06em;\n text-transform: uppercase; color: var(--bc-faint);\n}\n.known .issue {\n display: flex; align-items: center; gap: 10px;\n padding: 10px 12px; margin-bottom: 6px;\n background: var(--bc-subtle); border: 1px solid var(--bc-border); border-radius: var(--bc-r-md);\n animation: bc-rise 240ms var(--bc-ease) both;\n}\n.known .issue.is-joined { border-color: color-mix(in srgb, var(--bc-ok) 55%, var(--bc-border)); }\n.known .issue .who { flex: 1; min-width: 0; }\n.known .issue .t {\n display: block; font-size: 13px; font-weight: 620; line-height: 1.4;\n color: var(--bc-fg); overflow-wrap: anywhere;\n}\n.known .issue .meta {\n display: block; margin-top: 3px; font-size: var(--bc-fs-xs); line-height: 1.45; color: var(--bc-muted);\n}\n.known .issue .join {\n flex: none; padding: 7px 12px; font-size: 12px; font-weight: 620;\n color: var(--bc-accent); background: var(--bc-bg);\n border: 1px solid var(--bc-accent-line); border-radius: 999px; cursor: pointer;\n transition: background 160ms, border-color 160ms;\n}\n.known .issue .join:hover { border-color: var(--bc-accent); background: var(--bc-accent-soft); }\n.known .issue .join:disabled { color: var(--bc-ok); border-color: var(--bc-border); background: var(--bc-bg); cursor: default; opacity: 1; }\n.known .ask { display: flex; gap: 8px; margin: 2px 0 8px; }\n.known .ask input { flex: 1; min-width: 0; }\n.known .ask button { flex: none; }\n.known .err { margin: 6px 0 0; font-size: 12px; line-height: 1.45; color: var(--bc-danger); }\n\n/* ---------- questions the assistant answers by itself ---------- */\n.picks { margin-bottom: 16px; }\n.picks .picks-h {\n margin: 0 0 8px; font-size: 11px; font-weight: 680; letter-spacing: .06em;\n text-transform: uppercase; color: var(--bc-faint);\n}\n.picks .picks-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 6px; }\n.picks .pick {\n display: flex; flex-direction: column; align-items: flex-start; gap: 2px;\n padding: 9px 11px; text-align: left; cursor: pointer; font: inherit;\n color: var(--bc-fg); background: var(--bc-subtle);\n border: 1px solid var(--bc-border); border-radius: var(--bc-r-md);\n transition: background 160ms, border-color 160ms, transform 160ms;\n animation: bc-rise 240ms var(--bc-ease) both;\n}\n.picks .pick:hover { border-color: var(--bc-accent-line); background: var(--bc-accent-soft); }\n.picks .pick[aria-pressed=\"true\"] { border-color: var(--bc-accent); background: var(--bc-accent-soft); }\n.picks .pick .t { font-size: 13px; font-weight: 620; line-height: 1.35; overflow-wrap: anywhere; }\n.picks .pick .h { font-size: var(--bc-fs-xs); line-height: 1.4; color: var(--bc-muted); }\n.picks .picks-note {\n display: flex; gap: 6px; align-items: flex-start; margin: 8px 0 0;\n font-size: 12px; line-height: 1.45; color: var(--bc-muted);\n}\n.picks .picks-note svg { flex: none; margin-top: 2px; color: var(--bc-ok); }\n\n/* ---------- who is reporting ----------\n A strip above the form rather than a screen in front of it: where signing in\n is optional this is a line the reporter may ignore, and it should read as\n one. Where it is required the composer below is not rendered at all and this\n strip is the whole panel. */\n.whoami {\n display: flex; align-items: center; flex-wrap: wrap; gap: 8px;\n margin-bottom: 16px; padding: 10px 12px;\n font-size: var(--bc-fs-sm); line-height: 1.45; color: var(--bc-muted);\n background: var(--bc-subtle); border: 1px solid var(--bc-border); border-radius: var(--bc-r-md);\n}\n.whoami.asking { display: block; padding: 14px; }\n.whoami .who { flex: 1; min-width: 0; }\n.whoami .who strong { color: var(--bc-fg); font-weight: 620; word-break: break-all; }\n.link {\n padding: 0; font: inherit; font-size: var(--bc-fs-sm); font-weight: 620; line-height: 1.45;\n color: var(--bc-accent); background: none; border: none; cursor: pointer; border-radius: 4px;\n}\n.link:hover { text-decoration: underline; text-underline-offset: 2px; }\n.link:disabled { color: var(--bc-faint); cursor: default; text-decoration: none; }\n.whoami .why { margin: 0 0 10px; font-size: var(--bc-fs-sm); line-height: 1.5; color: var(--bc-muted); }\n.whoami .why strong { color: var(--bc-fg); font-weight: 620; word-break: break-all; }\n.whoami .row { display: flex; gap: 8px; align-items: stretch; }\n.whoami .row input { flex: 1; min-width: 0; }\n.whoami .row button { flex: none; }\n.whoami .after { display: flex; gap: 14px; align-items: center; margin-top: 9px; }\n.whoami .err { color: var(--bc-danger); }\n.whoami .code { letter-spacing: .28em; font-variant-numeric: tabular-nums; }\n\n/* ---------- which record is this about ----------\n Drawn only after Send has been pressed and only when what they wrote did not\n already answer it, so most people never see this at all. Presented as the\n assistant asking one friendly follow-up, not as a form field that appeared. */\n.refs {\n position: relative;\n margin-bottom: 16px; padding: 14px;\n font-size: var(--bc-fs-sm); line-height: 1.45; color: var(--bc-muted);\n background: var(--bc-accent-soft);\n border: 1px solid var(--bc-accent-line); border-radius: var(--bc-r-lg);\n animation: bc-item-in 280ms var(--bc-ease) both;\n}\n.refs .ask-head { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 11px; }\n.refs .ask-head .avatar { width: 28px; height: 28px; font-size: 14px; box-shadow: none; }\n.refs .ask-head strong { display: block; font-size: 13.5px; font-weight: 650; color: var(--bc-fg); margin-bottom: 1px; }\n.refs .why { margin: 0; font-size: var(--bc-fs-sm); line-height: 1.5; color: var(--bc-muted); }\n.refs .row { display: flex; gap: 8px; align-items: stretch; }\n.refs .row input { flex: 1; min-width: 0; background: var(--bc-bg); }\n.refs select {\n flex: none; max-width: 45%; padding: 10px 10px; font-size: 13px;\n color: var(--bc-fg); background: var(--bc-bg);\n border: 1px solid var(--bc-border); border-radius: var(--bc-r-md);\n}\n.refs select:focus { outline: none; border-color: var(--bc-accent); box-shadow: var(--bc-ring); }\n.refs .chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 9px; }\n.refs .chips button {\n padding: 5px 11px; font: inherit; font-size: 12px; font-weight: 600; border-radius: 999px; cursor: pointer;\n color: var(--bc-muted); background: var(--bc-bg); border: 1px solid var(--bc-border);\n transition: background 160ms, color 160ms, border-color 160ms;\n}\n.refs .chips button[aria-pressed=\"true\"] { color: var(--bc-on-accent); background: var(--bc-accent); border-color: var(--bc-accent); }\n.refs .hint { margin: 8px 0 0; font-size: 12px; line-height: 1.45; color: var(--bc-muted); }\n.refs .after { display: flex; gap: 12px; align-items: center; margin-top: 9px; }\n.refs .err { color: var(--bc-danger); font-weight: 550; }\n.refs .link { margin-top: 10px; font-size: 12px; color: var(--bc-muted); }\n.refs .link:hover { color: var(--bc-fg); }\n.refs .chips.cands button { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-weight: 550; }\n/* \"We read your ID\" — under the description, while they type. */\n.ref-found {\n display: flex; align-items: center; gap: 6px;\n margin: -6px 0 14px; font-size: 12px; line-height: 1.45; color: var(--bc-muted);\n}\n.ref-found svg { width: 14px; height: 14px; flex: none; color: var(--bc-ok); }\n.ref-found b { font-weight: 600; color: var(--bc-fg); }\n\n/* ---------- severity ---------- */\n.seg { display: grid; grid-template-columns: repeat(4, 1fr); gap: 3px; padding: 3px; background: var(--bc-subtle); border: 1px solid var(--bc-border); border-radius: var(--bc-r-md); }\n.seg button {\n display: inline-flex; align-items: center; justify-content: center; gap: 6px;\n padding: 8px 4px; font-size: var(--bc-fs-sm); font-weight: 550; line-height: 1;\n color: var(--bc-muted); background: none; border: none; border-radius: 9px;\n cursor: pointer; transition: background 180ms var(--bc-ease), color 180ms, box-shadow 180ms;\n}\n.seg button::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: var(--sev, var(--bc-faint)); opacity: .55; transition: opacity 180ms; }\n.seg button[data-prio=\"LOW\"] { --sev: #94a3b8; }\n.seg button[data-prio=\"MEDIUM\"] { --sev: #3b82f6; }\n.seg button[data-prio=\"HIGH\"] { --sev: #f59e0b; }\n.seg button[data-prio=\"CRITICAL\"] { --sev: #ef4444; }\n.seg button:hover { color: var(--bc-fg); }\n.seg button[aria-pressed=true] {\n background: var(--bc-raised); color: var(--bc-fg); font-weight: 650;\n box-shadow: 0 1px 3px rgba(10,12,16,.14), 0 0 0 1px color-mix(in srgb, var(--bc-border) 60%, transparent);\n}\n.seg button[aria-pressed=true]::before { opacity: 1; }\n.seg button:focus-visible { outline-offset: -2px; }\n\n/* ---------- attachments ---------- */\n.tools { display: grid; grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); gap: 8px; }\n.tool {\n display: inline-flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;\n padding: 12px 8px; font-size: 12px; font-weight: 600; line-height: 1;\n color: var(--bc-fg); background: var(--bc-elevated);\n border: 1px dashed color-mix(in srgb, var(--bc-fg) 20%, var(--bc-border)); border-radius: var(--bc-r-md); cursor: pointer;\n transition: border-color 160ms, background 160ms, color 160ms, transform 160ms var(--bc-ease);\n}\n.tool svg { font-size: 19px; color: var(--bc-muted); transition: color 160ms; }\n.tool:hover:not(:disabled) { border-color: var(--bc-accent); border-style: solid; background: var(--bc-accent-soft); transform: translateY(-1px); }\n.tool:hover:not(:disabled) svg { color: var(--bc-accent); }\n.tool:active:not(:disabled) { transform: translateY(0) scale(.98); }\n.tool:disabled { opacity: .45; cursor: not-allowed; }\n.attach-hint { margin: 8px 0 0; font-size: var(--bc-fs-xs); color: var(--bc-faint); }\n\n.thumbs { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }\n.thumbs:empty { display: none; }\n.thumb {\n position: relative; width: 76px; height: 76px; flex: none;\n border-radius: var(--bc-r-md); overflow: hidden;\n border: 1px solid var(--bc-border); background: var(--bc-subtle);\n box-shadow: var(--bc-shadow-sm);\n animation: bc-item-in 300ms var(--bc-ease) both;\n}\n.thumb img, .thumb video { width: 100%; height: 100%; object-fit: cover; display: block; }\n.thumb .kind {\n position: absolute; left: 4px; bottom: 4px;\n display: inline-flex; align-items: center; gap: 3px;\n padding: 2px 5px; border-radius: 5px;\n background: rgba(10,12,16,.72); color: #fff; font-size: 9.5px; font-weight: 600; line-height: 1.3;\n}\n.thumb .drop {\n position: absolute; top: 4px; right: 4px;\n width: 20px; height: 20px; font-size: 11px; border-radius: 50%;\n background: rgba(10,12,16,.7); color: #fff; border: none;\n display: grid; place-items: center; cursor: pointer;\n opacity: 0; transition: opacity 160ms, background 160ms;\n}\n.thumb:hover .drop, .thumb:focus-within .drop { opacity: 1; }\n.thumb .drop:hover { background: var(--bc-danger); }\n.thumb .veil {\n position: absolute; inset: 0; display: grid; place-items: center; gap: 6px;\n background: rgba(10,12,16,.62); color: #fff; font-size: 10.5px; font-weight: 600;\n align-content: center;\n}\n.thumb .veil.err { background: color-mix(in srgb, var(--bc-danger) 82%, transparent); }\n.spinner {\n width: 17px; height: 17px; border-radius: 50%;\n border: 2px solid rgba(255,255,255,.35); border-top-color: #fff;\n animation: bc-spin 720ms linear infinite;\n}\n.bar { width: 46px; height: 3px; border-radius: 2px; background: rgba(255,255,255,.3); overflow: hidden; }\n.bar i { display: block; height: 100%; width: 0; background: #fff; transition: width 180ms linear; }\n.veil button {\n background: none; border: none; color: #fff; font-size: 14px; padding: 2px;\n cursor: pointer; display: grid; place-items: center;\n}\n\n/* ---------- footer ---------- */\n.foot {\n flex: none;\n display: flex; align-items: center; flex-wrap: wrap; gap: 8px;\n padding: 12px var(--bc-pad); border-top: 1px solid var(--bc-border);\n background: var(--bc-bg);\n}\n.foot .spacer { flex: 1; }\n.foot .note { flex-basis: 100%; order: -1; font-size: var(--bc-fs-xs); color: var(--bc-muted); }\n.foot .note:empty { display: none; }\n/* Quiet, but still legible: this sits in someone else's product, so it identifies\n where the report goes without competing with their interface. Quiet is bought\n with colour (--bc-brand-ink sits a step below body text) rather than with a\n blanket opacity — the BUGCATCH half is only about 3px of cap height at this\n size, and dimming it on top of that left it as a smudge in both themes. */\n.powered {\n flex: none; display: flex; align-items: center; justify-content: center; gap: 6px;\n padding: 7px var(--bc-pad) 9px; font-size: 10.5px; color: var(--bc-faint);\n background: var(--bc-subtle); border-top: 1px solid var(--bc-border);\n}\n.brand-credit { display: flex; align-items: center; transition: color 0.15s; border-radius: 4px; }\n.bc-brand { height: 16px; width: auto; display: block; color: var(--bc-brand-ink); }\n.brand-credit:hover .bc-brand { color: var(--bc-fg); }\nbutton.ghost, button.primary {\n display: inline-flex; align-items: center; justify-content: center; gap: 7px;\n height: 38px; padding: 0 15px; font: inherit; font-size: 13px; font-weight: 620; line-height: 1;\n border-radius: 999px; cursor: pointer; white-space: nowrap;\n transition: transform 160ms var(--bc-ease), filter 160ms, background 160ms, border-color 160ms, color 160ms, box-shadow 160ms;\n}\nbutton.ghost { background: var(--bc-bg); border: 1px solid var(--bc-border); color: var(--bc-fg); }\nbutton.ghost:hover:not(:disabled) { background: var(--bc-subtle); border-color: color-mix(in srgb, var(--bc-fg) 20%, var(--bc-border)); }\nbutton.ghost svg, button.primary svg { font-size: 15px; }\nbutton.primary {\n padding: 0 18px;\n background: var(--bc-accent); border: 1px solid var(--bc-accent); color: var(--bc-on-accent);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--bc-accent) 30%, transparent);\n}\nbutton.primary:hover:not(:disabled) { transform: translateY(-1px); filter: brightness(1.07); }\nbutton.primary:active:not(:disabled) { transform: none; }\nbutton:disabled { opacity: .5; cursor: not-allowed; }\nbutton.primary:disabled { box-shadow: none; }\nbutton.primary .spinner { border-color: rgba(255,255,255,.4); border-top-color: #fff; width: 14px; height: 14px; }\nbutton.quiet {\n padding: 0 4px; height: 38px; font: inherit; font-size: 13px; font-weight: 600;\n border: none; background: none; color: var(--bc-muted); cursor: pointer; border-radius: 8px;\n}\nbutton.quiet:hover { color: var(--bc-accent); }\n\n/* ---------- your reports ---------- */\n.history {\n flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;\n padding: 8px 12px 14px;\n}\n/* On the conversation screen the inner thread does the scrolling, so this\n wrapper must not — two nested scrollers means the composer drifts out of\n reach exactly when someone reaches for it. */\n.history.chatting {\n overflow: hidden; display: flex; flex-direction: column; padding: 0;\n}\n.history .row {\n display: flex; align-items: center; gap: 12px;\n padding: 12px 10px; border-bottom: 1px solid var(--bc-border);\n}\n.history .row:last-child { border-bottom: 0; }\n.history .who { flex: 1; min-width: 0; }\n.history .ttl {\n display: block; font-size: 13.5px; font-weight: 600; color: var(--bc-fg);\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n}\n.history .sub { display: block; margin-top: 3px; font-size: var(--bc-fs-xs); color: var(--bc-faint); }\n.history .row-ico {\n flex: none; display: grid; place-items: center; width: 34px; height: 34px; border-radius: 10px;\n background: var(--bc-subtle); color: var(--bc-muted); font-size: 16px;\n}\n.history .chev { flex: none; color: var(--bc-faint); font-size: 15px; transition: transform 160ms var(--bc-ease), color 160ms; }\n.empty {\n display: flex; flex-direction: column; align-items: center; gap: 10px;\n padding: 40px 16px; text-align: center; font-size: var(--bc-fs-sm); color: var(--bc-muted);\n}\n.empty .empty-ico {\n display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%;\n background: var(--bc-accent-soft); color: var(--bc-accent); font-size: 20px;\n}\n.skel {\n display: block; height: 12px; border-radius: 6px; margin: 6px 0;\n background: linear-gradient(90deg, var(--bc-subtle) 25%, color-mix(in srgb, var(--bc-fg) 8%, var(--bc-subtle)) 50%, var(--bc-subtle) 75%);\n background-size: 200% 100%; animation: bc-shimmer 1.4s linear infinite;\n}\n/* A history row is a button now — it opens the report — so the UA styling has\n to go, and the hit area has to read as one. */\n.history button.row {\n width: 100%; background: none; border: 0; margin-bottom: 2px;\n font: inherit; color: inherit; text-align: left; cursor: pointer;\n border-radius: var(--bc-r-md); transition: background 160ms;\n animation: bc-rise 260ms var(--bc-ease) both;\n}\n.history button.row:hover { background: var(--bc-subtle); }\n.history button.row:hover .chev { transform: translateX(2px); color: var(--bc-accent); }\n.history button.row:focus-visible { outline-offset: -2px; }\n\n/* ---------- one report, and what happened to it ---------- */\n.report-head {\n flex: none;\n display: grid; grid-template-columns: 1fr auto; gap: 2px 10px; align-items: center;\n padding: 10px var(--bc-pad); border-bottom: 1px solid var(--bc-border);\n background: var(--bc-bg);\n}\n.report-head .ttl { font-size: 13px; font-weight: 620; color: var(--bc-fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }\n.report-head .sub { font-size: var(--bc-fs-xs); color: var(--bc-faint); }\n.report-head .pill { grid-row: 1 / span 2; grid-column: 2; align-self: center; }\n\n/* The thread scrolls; the composer under it does not. Both live inside the\n panel, which stops scrolling as a whole on this screen — a composer that\n scrolls away is a composer people think isn't there. */\n.thread {\n flex: 1 1 auto; min-height: 140px;\n display: flex; flex-direction: column; gap: 4px;\n padding: 14px var(--bc-pad) 12px; overflow-y: auto; overscroll-behavior: contain;\n}\n\n/* The day a run of messages happened on, as a centred label on a rule. */\n.day-sep {\n display: flex; align-items: center; gap: 10px; margin: 10px 0 6px;\n font-size: 11px; font-weight: 620; letter-spacing: .02em; color: var(--bc-faint);\n}\n.day-sep::before, .day-sep::after { content: ''; flex: 1; height: 1px; background: var(--bc-border); }\n\n/* A status change is not a message and is not drawn as one: no side, no\n bubble, no author. Centred and quiet, the way every chat marks the things\n that happened rather than the things that were said. */\n.sys {\n align-self: center; max-width: 90%; text-align: center; margin: 6px 0;\n padding: 4px 10px; border-radius: 999px; background: var(--bc-subtle);\n font-size: var(--bc-fs-xs); line-height: 1.45; color: var(--bc-muted);\n animation: bc-fade-in 240ms ease both;\n}\n.sys .dot {\n display: inline-block; width: 6px; height: 6px; margin-right: 6px;\n border-radius: 50%; background: var(--bc-muted); vertical-align: middle;\n}\n.sys .t { color: var(--bc-faint); }\n.sys.k-status .dot { background: #4da3ff; }\n.sys.k-severity .dot { background: #e8912a; }\n.sys.k-assignment .dot { background: #8b7dff; }\n.sys.k-filed .dot { background: var(--bc-faint); }\n\n/* ---------- the conversation ----------\n\n A status change and a message are different things and are drawn as such: a\n change is a line on the rail above, a message is a bubble with a side. The\n reporter's own words sit right and accented, the other side's left beside an\n avatar that says who it is — the arrangement every chat has taught people to\n read without a legend. */\n.msg-row, .typing-row {\n display: grid; grid-template-columns: 28px minmax(0, 1fr); column-gap: 8px;\n max-width: 88%; margin-top: 10px;\n animation: bc-rise 260ms var(--bc-ease) both;\n}\n.msg-row.cont { margin-top: 3px; }\n.msg-row > *, .typing-row > * { grid-column: 2; }\n/* Fixed rows, so the avatar lines up with the bubble whatever else the\n message carries (a name above it, answers or a time below it). */\n.msg-row > .msg-who, .typing-row > .msg-who { grid-row: 1; }\n.msg-row > .bubble, .typing-row > .bubble { grid-row: 2; }\n.msg-row > .quick { grid-row: 3; }\n.msg-row > .msg-when { grid-row: 4; }\n.msg-row > .av, .typing-row > .av { grid-column: 1; grid-row: 2; align-self: end; }\n/* In a run of messages the avatar sits by the last bubble and the time under\n it, the way a chat marks where one turn ends. */\n.msg-row:has(+ .msg-row.cont) > .av { visibility: hidden; }\n.msg-row:has(+ .msg-row.cont) > .msg-when { display: none; }\n.msg-row.theirs, .typing-row { align-self: flex-start; }\n.msg-row.mine { align-self: flex-end; grid-template-columns: minmax(0, 1fr); justify-items: end; }\n.msg-row.mine > * { grid-column: 1; }\n.av {\n display: grid; place-items: center; width: 28px; height: 28px; border-radius: 50%;\n font-size: 11px; font-weight: 700; letter-spacing: .02em;\n background: color-mix(in srgb, var(--bc-fg) 10%, var(--bc-bg)); color: var(--bc-fg);\n}\n.av.bot {\n background: linear-gradient(140deg, var(--bc-accent), color-mix(in srgb, var(--bc-accent) 60%, #fff));\n color: var(--bc-on-accent); font-size: 14px;\n}\n.msg-who { font-size: var(--bc-fs-xs); font-weight: 620; color: var(--bc-muted); padding: 0 4px 4px; }\n.msg-who.auto { color: var(--bc-accent); display: inline-flex; align-items: center; gap: 5px; }\n.msg-who .tag {\n padding: 1px 6px; border-radius: 999px; font-size: 9.5px; font-weight: 700; letter-spacing: .04em;\n text-transform: uppercase; background: var(--bc-accent-soft); color: var(--bc-accent);\n}\n.bubble {\n justify-self: start; max-width: 100%;\n padding: 9px 13px; border-radius: 18px; font-size: 13.5px; line-height: 1.5;\n background: var(--bc-subtle); color: var(--bc-fg);\n border: 1px solid var(--bc-border);\n border-bottom-left-radius: 6px;\n}\n/* pre-wrap belongs to the text, not the box — on the box it would also render\n the markup's own indentation. */\n.msg-text { display: block; white-space: pre-wrap; overflow-wrap: anywhere; }\n.msg-row.mine .bubble {\n justify-self: end;\n background: var(--bc-accent); border-color: var(--bc-accent); color: var(--bc-on-accent);\n border-bottom-left-radius: 18px; border-bottom-right-radius: 6px;\n}\n.msg-row.cont.theirs .bubble { border-top-left-radius: 6px; }\n.msg-row.cont.mine .bubble { border-top-right-radius: 6px; }\n/* A reply nobody wrote: tinted with the accent and labelled \"Assistant\" above\n it, so it reads the same to someone who cannot see the colour at all. */\n.msg-row.auto .bubble { background: var(--bc-accent-soft); border-color: var(--bc-accent-line); }\n/* An attachment-only message has no text to wrap, so the bubble would be an\n empty coloured box around it. */\n.bubble.silent { padding: 0; background: none !important; border: 0; }\n.msg-when { font-size: 10.5px; color: var(--bc-faint); padding: 4px 4px 0; }\n.msg-row.pending { opacity: .6; }\n.msg-row.pending .msg-when::after { content: ' · Sending…'; }\n.msg-row.failed .bubble { border: 1px solid var(--bc-danger); }\n.msg-row.failed .msg-when { color: var(--bc-danger); }\n.msg-row.failed .msg-when::after { content: ' · Not sent'; }\n\n/* The assistant, thinking. Three dots in a bubble where its next message will be. */\n.typing { display: inline-flex; align-items: center; gap: 4px; padding: 12px 14px; justify-self: start; }\n.typing i {\n width: 7px; height: 7px; border-radius: 50%; background: var(--bc-accent); opacity: .45;\n animation: bc-typing 1.2s ease-in-out infinite;\n}\n.typing i:nth-child(2) { animation-delay: .15s; }\n.typing i:nth-child(3) { animation-delay: .3s; }\n.typing-row .msg-when { padding-top: 4px; }\n\n.att { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }\n.bubble.silent .att { margin-top: 0; }\n.att img {\n display: block; max-width: 100%; max-height: 190px; border-radius: var(--bc-r-md);\n border: 1px solid var(--bc-border); object-fit: cover; cursor: zoom-in;\n}\n.att audio { width: 232px; max-width: 100%; height: 34px; }\n.att video { max-width: 100%; max-height: 190px; border-radius: var(--bc-r-md); }\n.att .file {\n display: flex; align-items: center; gap: 8px; text-decoration: none;\n padding: 8px 10px; border-radius: 10px; font-size: 12px;\n background: var(--bc-elevated); border: 1px solid var(--bc-border); color: var(--bc-fg);\n}\n.att .file:hover { border-color: var(--bc-accent); }\n.att .file .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.att .file .sz { flex: none; color: var(--bc-faint); font-size: 11px; }\n\n/* ---------- the assistant's live card ----------\n A status header pinned between the report and the conversation: where the\n investigation is, as words and as a stepper. It does not scroll with the\n thread, because \"is anything happening?\" is the question on screen the\n whole time. */\n.assistant {\n flex: none; position: relative; overflow: hidden;\n margin: 10px 12px 0; padding: 12px 14px 12px;\n border-radius: var(--bc-r-lg); border: 1px solid var(--bc-border);\n background: var(--bc-subtle);\n transition: background 400ms ease, border-color 400ms ease;\n}\n.assistant.analyzing, .assistant.confirming, .assistant.needs_info {\n background: var(--bc-accent-soft); border-color: var(--bc-accent-line);\n}\n.assistant.resolved { background: color-mix(in srgb, var(--bc-ok) 9%, var(--bc-bg)); border-color: color-mix(in srgb, var(--bc-ok) 35%, var(--bc-border)); }\n/* On a call: the call's own card is what they are watching. This one states\n what is happening and stops moving — no spinner, no travelling bar. */\n.assistant.calling { background: var(--bc-bg); }\n.assistant.calling .as-progress i { transition: none; }\n.as-body.entering { animation: bc-rise 360ms var(--bc-ease) both; }\n.as-top { display: flex; gap: 11px; align-items: center; }\n.as-orb {\n flex: none; width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center;\n color: var(--bc-on-accent); font-size: 15px;\n background: linear-gradient(140deg, var(--bc-accent), color-mix(in srgb, var(--bc-accent) 60%, #fff));\n}\n.assistant.resolved .as-orb { background: var(--bc-ok); }\n.assistant.reviewing .as-orb { background: color-mix(in srgb, var(--bc-fg) 55%, var(--bc-bg)); }\n.as-spin { width: 16px; height: 16px; border-radius: 50%; border: 2px solid rgba(255,255,255,.4); border-top-color: #fff; animation: as-rot .8s linear infinite; }\n@keyframes as-rot { to { transform: rotate(360deg); } }\n.as-text { min-width: 0; flex: 1; }\n.as-title { font-weight: 650; font-size: 13.5px; letter-spacing: -.01em; color: var(--bc-fg); }\n.as-detail { font-size: 12px; line-height: 1.45; color: var(--bc-muted); margin-top: 2px; }\n.as-progress {\n height: 4px; margin: 12px 0 0; border-radius: 999px; overflow: hidden;\n background: color-mix(in srgb, var(--bc-fg) 9%, transparent);\n}\n.as-progress i {\n display: block; height: 100%; border-radius: inherit; background: var(--bc-accent);\n transition: width 700ms var(--bc-ease);\n}\n.assistant.resolved .as-progress i { background: var(--bc-ok); }\n.assistant.analyzing .as-progress i {\n background: linear-gradient(90deg, var(--bc-accent) 0%, color-mix(in srgb, var(--bc-accent) 45%, #fff) 50%, var(--bc-accent) 100%);\n background-size: 200% 100%; animation: bc-shimmer 1.6s linear infinite;\n}\n.as-steps {\n list-style: none; display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px;\n margin: 8px 0 0; padding: 0; font-size: 10.5px; color: var(--bc-faint);\n}\n.as-steps li { display: flex; align-items: center; gap: 5px; min-width: 0; white-space: nowrap; }\n.as-steps li .lbl { min-width: 0; overflow: hidden; text-overflow: ellipsis; }\n.as-steps li .dot { flex: none; width: 8px; height: 8px; border-radius: 50%; border: 1.5px solid currentColor; }\n.as-steps li.done { color: var(--bc-muted); }\n.as-steps li.done .dot { background: var(--bc-accent); border-color: var(--bc-accent); }\n.assistant.resolved .as-steps li.done .dot { background: var(--bc-ok); border-color: var(--bc-ok); }\n.as-steps li.active { color: var(--bc-fg); font-weight: 650; }\n.as-steps li.active .dot { border-color: var(--bc-accent); background: var(--bc-bg); animation: as-pulse 1.2s ease-in-out infinite; }\n@keyframes as-pulse { 50% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--bc-accent) 25%, transparent); } }\n@media (prefers-reduced-motion: reduce) { .as-spin, .as-steps li.active .dot, .assistant.analyzing .as-progress i, .typing i { animation: none; } }\n\n/* Quick answers under the confirm question — only on the latest message. */\n.msg-row .quick { display: none; gap: 6px; flex-wrap: wrap; margin-top: 8px; }\n.msg-row:last-child .quick { display: flex; }\n.quick button {\n display: inline-flex; align-items: center; gap: 6px;\n font: inherit; font-size: 13px; font-weight: 600; padding: 8px 14px; border-radius: 999px; cursor: pointer;\n border: 1px solid var(--bc-accent); background: var(--bc-bg); color: var(--bc-accent);\n transition: background 160ms, color 160ms, transform 160ms var(--bc-ease);\n animation: bc-item-in 300ms var(--bc-ease) both;\n}\n.quick button:first-child { background: var(--bc-accent); color: var(--bc-on-accent); }\n.quick button:nth-child(2) { animation-delay: 60ms; }\n.quick button svg { font-size: 13px; }\n.quick button:hover { transform: translateY(-1px); background: var(--bc-accent-soft); }\n.quick button:first-child:hover { background: var(--bc-accent); filter: brightness(1.07); }\n\n/* ---------- the composer ---------- */\n.composer {\n flex: none; position: relative;\n padding: 10px 12px max(12px, env(safe-area-inset-bottom, 0px));\n /* The panel's own background, not --bc-elevated: the two are the same white\n in light mode but differ in dark, where a raised composer reads as a band\n laid over the panel rather than the foot of it. */\n background: var(--bc-bg);\n border-top: 1px solid var(--bc-border);\n}\n/* One rounded field holding the tools, the text and the send button — the\n shape every chat box has, so nobody has to look for where to type. */\n.composer .row {\n display: flex; align-items: flex-end; gap: 4px;\n padding: 5px; border-radius: 22px;\n background: var(--bc-subtle); border: 1px solid var(--bc-border);\n transition: border-color 160ms, box-shadow 160ms;\n}\n.composer .row:focus-within { border-color: var(--bc-accent); box-shadow: var(--bc-ring); }\n.composer textarea {\n flex: 1; min-width: 0; min-height: 34px; max-height: 120px; resize: none; padding: 7px 6px;\n font: inherit; font-size: 13.5px; line-height: 1.45;\n color: var(--bc-fg); background: transparent; border: 0; border-radius: 0;\n}\n.composer textarea:hover, .composer textarea:focus { border: 0; box-shadow: none; outline: none; }\n.composer .tools { display: flex; gap: 0; grid-template-columns: none; }\n.composer .tool {\n width: 34px; height: 34px; min-height: 0; padding: 0; display: grid; place-items: center; cursor: pointer;\n border-radius: 50%; border: 0; background: none; font-size: 17px;\n color: var(--bc-muted);\n}\n.composer .tool svg { font-size: 17px; }\n.composer .tool:hover:not(:disabled) { background: color-mix(in srgb, var(--bc-fg) 8%, transparent); color: var(--bc-fg); border: 0; transform: none; }\n.composer .tool:hover:not(:disabled) svg { color: var(--bc-fg); }\n.composer .tool:disabled { opacity: .4; cursor: default; }\n.composer .tool.recording { color: var(--bc-danger); background: color-mix(in srgb, var(--bc-danger) 12%, transparent); }\n.composer .tool.recording svg { color: var(--bc-danger); }\n.composer .send {\n width: 34px; height: 34px; flex: none; border-radius: 50%; cursor: pointer; font-size: 16px;\n background: var(--bc-accent); border: 0; color: var(--bc-on-accent);\n display: grid; place-items: center;\n transition: transform 160ms var(--bc-ease), opacity 160ms, filter 160ms;\n}\n.composer .send:hover:not(:disabled) { filter: brightness(1.08); transform: scale(1.05); }\n.composer .send:disabled { opacity: .35; cursor: default; background: var(--bc-faint); }\n.composer .staged { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 0 8px; }\n.composer .chip {\n display: flex; align-items: center; gap: 6px; max-width: 200px;\n padding: 5px 7px 5px 10px; border-radius: 999px; font-size: var(--bc-fs-xs);\n background: var(--bc-subtle); border: 1px solid var(--bc-border); color: var(--bc-muted);\n animation: bc-item-in 240ms var(--bc-ease) both;\n}\n.composer .chip .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--bc-fg); }\n.composer .chip button {\n flex: none; width: 18px; height: 18px; display: grid; place-items: center;\n border: 0; border-radius: 50%; background: none; color: var(--bc-faint); cursor: pointer;\n}\n.composer .chip button:hover { color: var(--bc-danger); }\n.composer .chip.uploading { border-style: dashed; }\n.composer .note { padding: 6px 6px 0; font-size: var(--bc-fs-xs); color: var(--bc-muted); }\n.composer .note.err { color: var(--bc-danger); }\n.reccount { font-variant-numeric: tabular-nums; }\n\n/* Status carries the only colour in this list, so it is the thing the eye\n lands on — which is what someone opening this screen came to check. */\n.pill {\n flex: none; display: inline-flex; align-items: center; gap: 5px;\n font-size: 11px; font-weight: 650;\n padding: 3px 9px; border-radius: 999px;\n color: var(--bc-muted); background: var(--bc-subtle);\n}\n.pill::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }\n.pill.open { color: #c2410c; background: color-mix(in srgb, #f59e0b 14%, var(--bc-bg)); }\n.pill.in_progress { color: #1d6fd1; background: color-mix(in srgb, #3b82f6 14%, var(--bc-bg)); }\n.pill.resolved { color: #0f8a5a; background: color-mix(in srgb, #10b981 14%, var(--bc-bg)); }\n.pill.closed { color: var(--bc-muted); }\n:host(.bc-dark) .pill.open { color: #fbbf24; }\n:host(.bc-dark) .pill.in_progress { color: #7cb8ff; }\n:host(.bc-dark) .pill.resolved { color: #4ade9a; }\n@media (prefers-color-scheme: dark) {\n :host(.bc-auto) .pill.open { color: #fbbf24; }\n :host(.bc-auto) .pill.in_progress { color: #7cb8ff; }\n :host(.bc-auto) .pill.resolved { color: #4ade9a; }\n}\n\n/* ---------- does the fix work ---------- */\n.fix-verification {\n flex: none; margin: 10px 12px 0; padding: 12px 14px;\n border-radius: var(--bc-r-lg); border: 1px solid color-mix(in srgb, var(--bc-ok) 35%, var(--bc-border));\n background: color-mix(in srgb, var(--bc-ok) 8%, var(--bc-bg));\n font-size: var(--bc-fs-sm); color: var(--bc-fg);\n animation: bc-rise 280ms var(--bc-ease) both;\n}\n.fix-verification p { margin: 0 0 8px; font-weight: 620; }\n.fix-verification textarea { min-height: 56px; margin-bottom: 8px; font-size: 13px; background: var(--bc-bg); }\n.fix-verification button {\n padding: 7px 13px; font: inherit; font-size: 12.5px; font-weight: 620; border-radius: 999px; cursor: pointer;\n border: 1px solid var(--bc-border); background: var(--bc-bg); color: var(--bc-fg);\n}\n.fix-verification button[data-result=\"CONFIRMED\"] { background: var(--bc-ok); border-color: var(--bc-ok); color: #fff; }\n.fix-verification .feedback-status { margin: 6px 0 0; font-weight: 450; color: var(--bc-danger); font-size: var(--bc-fs-xs); }\n.fix-verification .feedback-status:empty { display: none; }\n\n/* A call in progress, in place of the form.\n ---------------------------------------------------------------------------\n Deliberately not styled as a chat. Nothing arrives here to read — the\n conversation is happening out loud — so this is a state, a line of\n reassurance about the report, and a way to hang up. */\n.call { display: flex; flex-direction: column; padding: 26px 0 0; animation: bc-fade-in 240ms ease both; }\n.call-head, .call-note { padding: 0 var(--bc-pad); }\n.call-head { display: flex; align-items: center; gap: 9px; margin-bottom: 8px; }\n.call-head strong { font-size: 15.5px; font-weight: 680; letter-spacing: -.01em; }\n.call-dot {\n width: 9px; height: 9px; border-radius: 50%; background: var(--bc-accent);\n animation: bc-call-pulse 1.6s ease-in-out infinite;\n}\n.call-dot.off { background: var(--bc-faint); animation: none; }\n@keyframes bc-call-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }\n/* Said while it is still ringing, because that is when somebody is deciding\n whether it is worth waiting. */\n.call-note { margin: 0 0 18px; font-size: 13px; color: var(--bc-muted); line-height: 1.55; }\n\n/* ---------- the same call, on top of the report it is about ---------- */\n/* A bar rather than its own screen, unlike the one the composer opens. The\n thread underneath is exactly what both people are talking about, so taking it\n off screen would be taking away the only shared reference. */\n.livecall {\n flex: none;\n display: flex; align-items: center; gap: 9px;\n margin: 10px 12px 0; padding: 9px 11px;\n border: 1px solid var(--bc-accent-line); border-radius: var(--bc-r-md);\n background: var(--bc-accent-soft); font-size: var(--bc-fs-sm); color: var(--bc-fg);\n animation: bc-rise 240ms var(--bc-ease) both;\n}\n.livecall .who { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.livecall .call-kept { font-size: 11px; opacity: .75; }\n.livecall .secs { font-variant-numeric: tabular-nums; color: var(--bc-muted); }\n.livecall .act {\n padding: 5px 11px; border-radius: 999px; border: 1px solid var(--bc-border);\n background: var(--bc-bg); color: var(--bc-fg);\n font: inherit; font-size: 12px; font-weight: 600; cursor: pointer;\n}\n.livecall .act:hover { background: var(--bc-subtle); }\n.livecall .act.danger { border-color: transparent; background: var(--bc-danger); color: #fff; }\n.livecall.over { color: var(--bc-muted); background: var(--bc-subtle); border-color: var(--bc-border); }\n.call-note code { font-size: 11px; }\n@media (prefers-reduced-motion: reduce) { .call-dot { animation: none; } }\n\n/* div.done, not .done: the assistant's stepper marks finished steps li.done,\n and this screen's padding was landing on every one of them. */\ndiv.done { padding: 44px 26px 44px; text-align: center; animation: bc-fade-in 240ms ease both; }\n.done .tick {\n width: 56px; height: 56px; margin: 0 auto 16px; border-radius: 50%;\n display: grid; place-items: center; font-size: 26px;\n background: color-mix(in srgb, var(--bc-ok) 14%, transparent); color: var(--bc-ok);\n box-shadow: 0 0 0 8px color-mix(in srgb, var(--bc-ok) 6%, transparent);\n animation: bc-pop 420ms var(--bc-ease) both;\n}\n.done h3 { margin: 0 0 6px; font-size: 17px; font-weight: 680; letter-spacing: -.015em; }\n.done p { margin: 0; font-size: 13px; line-height: 1.5; color: var(--bc-muted); }\n.done code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; padding: 1px 5px; border-radius: 5px; background: var(--bc-subtle); color: var(--bc-fg); }\n/* The likely wait. Set apart from the reference line above it by a rule rather\n than by weight: one is bookkeeping and the other is the answer to the\n question the reporter actually has, and they should not read as one\n paragraph. */\n.done .eta {\n margin: 14px auto 0;\n padding-top: 12px;\n border-top: 1px solid var(--bc-border);\n max-width: 30ch;\n line-height: 1.5;\n}\n.done .eta strong { color: var(--bc-fg); font-weight: 620; }\n/* The same figure on the report the reporter came back to. Sits under the\n status pill in the header block, muted — it is context, not the headline the\n status is. */\n.report-head .eta-line {\n grid-column: 1 / -1;\n font-size: var(--bc-fs-xs);\n color: var(--bc-muted);\n margin-top: 2px;\n}\n\n/* ---------- recording bar ---------- */\n.recbar {\n position: fixed; left: 50%; bottom: calc(24px + env(safe-area-inset-bottom, 0px)); transform: translateX(-50%);\n z-index: 2147483002;\n display: flex; align-items: center; gap: 12px;\n padding: 8px 8px 8px 16px; border-radius: 999px;\n background: var(--bc-elevated); color: var(--bc-fg);\n border: 1px solid var(--bc-border); box-shadow: var(--bc-shadow);\n animation: bc-item-in 300ms var(--bc-ease) both;\n}\n.recbar .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--bc-danger); animation: bc-pulse 1.4s ease-in-out infinite; }\n.recbar .time { font-variant-numeric: tabular-nums; font-size: 13px; font-weight: 650; min-width: 38px; }\n.recbar .hint { font-size: 12px; color: var(--bc-muted); }\n.recbar button { height: 34px; padding: 0 14px; font-size: 12.5px; font-weight: 620; border-radius: 999px; cursor: pointer; }\n.recbar .stop { background: var(--bc-danger); border: 1px solid var(--bc-danger); color: #fff; }\n.recbar .stop:hover { filter: brightness(1.08); }\n\n/* ---------- phones ----------------------------------------------------------\n This widget runs inside a shadow root on somebody else's site, so its own\n stylesheet is the whole story: the host page cannot correct any of this, and\n anything that goes wrong here goes wrong on their site, in front of their\n visitor, at the moment that visitor is already annoyed enough to file a bug.\n -------------------------------------------------------------------------- */\n\n/* 100vh on a phone is the viewport with the browser chrome hidden, so the\n panel could be taller than the space it actually has and put \"Send report\"\n under the address bar. */\n@supports (height: 100dvh) {\n .panel { max-height: calc(100dvh - var(--bc-dock-h, 84px) - 52px); }\n}\n\n/* Short desktop windows: the chat height gives way to the space there is. */\n@media (max-height: 820px) { .panel.is-chat { height: calc(100vh - var(--bc-dock-h, 84px) - 52px); } }\n\n/* A phone gets the whole screen: a sheet that slides up from the bottom, with\n the notch and the home indicator kept clear. The dock steps aside while it\n is open — the sheet has its own close button, and a launcher floating over\n the send button would be in the way of the one thing that matters. */\n@media (max-width: 480px) {\n .scrim { background: rgba(10,12,16,.4); }\n .panel, .scrim.at-right .panel, .scrim.at-left .panel, .scrim.at-bottom .panel, .scrim.at-top .panel {\n inset: 0; width: 100%; max-width: none; height: 100%; max-height: none;\n border-radius: 0; border: 0;\n animation: bc-sheet-in 380ms var(--bc-ease) both;\n }\n @supports (height: 100dvh) { .panel { height: 100dvh; max-height: none; } }\n .panel.is-chat { height: 100%; }\n .scrim.closing .panel { animation: bc-sheet-out 200ms ease both; }\n .head { padding-top: max(14px, env(safe-area-inset-top, 0px)); padding-left: max(var(--bc-pad), env(safe-area-inset-left, 0px)); padding-right: max(12px, env(safe-area-inset-right, 0px)); }\n .foot, .powered { padding-bottom: max(10px, env(safe-area-inset-bottom, 0px)); }\n .foot + .powered { padding-bottom: max(9px, env(safe-area-inset-bottom, 0px)); }\n .foot:has(+ .powered) { padding-bottom: 12px; }\n .recbar .hint { display: none; }\n .msg-row, .typing-row { max-width: 92%; }\n}\n\n@media (pointer: coarse) {\n /* iOS zooms the whole page when a field it focuses is under 16px, and does\n not zoom back out. On the host's site that is not the widget's page to\n zoom — the visitor is left on a magnified checkout wondering what they\n touched. !important because this is a property of the device, not a\n preference about type. */\n input, textarea, select { font-size: 16px !important; }\n\n /* 44px is the floor a thumb hits reliably. Every control in this form was\n under it, on the one surface in the product designed for someone who is\n not an operator and gets no second attempt. */\n .seg button { min-height: 44px; }\n .tool { min-height: 56px; }\n button.primary, button.ghost { height: 44px; }\n .composer .send, .composer .tool { width: 40px; height: 40px; min-height: 0; }\n textarea { min-height: 104px; }\n .composer textarea { min-height: 40px; }\n\n /* The close button and the thumbnails' remove buttons are small glyphs whose\n boxes cannot grow without moving what is beside them, so each keeps its\n size and gets an invisible target drawn around it instead. The theme\n switch gets the same in the launcher's own sheet (widget.js). */\n .head .icon-btn, .thumb .drop, .brand-credit { position: relative; }\n .thumb .drop { position: absolute; opacity: 1; }\n .head .icon-btn::after, .thumb .drop::after, .brand-credit::after {\n content: '';\n position: absolute;\n inset: -8px;\n }\n}\n\n/* Hover is a lie on a touchscreen: it latches on tap and stays. */\n@media (hover: none) {\n .tool:hover:not(:disabled) { transform: none; }\n}\n`;\n","// Markup the panel's screens share: the header, empty and loading states, and\n// the small formatting helpers every screen reaches for — dates, statuses and\n// escaping. Pure functions of their arguments; nothing here touches the DOM\n// except the two scroll helpers, which are handed the element they move.\n\nimport * as ICONS from './icons.js';\n\n/**\n * The panel's header, on every screen. The assistant's avatar carries the\n * identity; `back` adds the way out of a report to the list of them.\n */\nexport function headHtml({ title, sub, id = '', back = false }) {\n return `\n <div class=\"head\">\n ${back ? `<button class=\"icon-btn back\" type=\"button\" data-act=\"back\" aria-label=\"Back to your reports\">${ICONS.back}</button>` : ''}\n <div class=\"mark\" aria-hidden=\"true\">${ICONS.sparkle}</div>\n <div class=\"titles\">\n <h2${id ? ` id=\"${id}\"` : ''}>${escapeHtml(title)}</h2>\n <p>${escapeHtml(sub)}</p>\n </div>\n <button class=\"icon-btn\" type=\"button\" data-act=\"close\" aria-label=\"Close\">${ICONS.close}</button>\n </div>`;\n}\n\nexport function emptyHtml(text, icon) {\n return `<div class=\"empty\"><span class=\"empty-ico\" aria-hidden=\"true\">${icon}</span>${escapeHtml(text)}</div>`;\n}\n\n/** Placeholder rows while the list loads — the shape of what is coming. */\nexport function skeletonRows(n) {\n return Array.from({ length: n }, () => `\n <div class=\"row\" aria-hidden=\"true\">\n <span class=\"row-ico\"></span>\n <span class=\"who\"><span class=\"skel\" style=\"width:70%\"></span><span class=\"skel\" style=\"width:40%;height:9px\"></span></span>\n </div>`).join('') + '<span class=\"sr-only\">Loading…</span>';\n}\n\nexport function skeletonBubbles() {\n return `\n <div aria-hidden=\"true\" style=\"display:flex;flex-direction:column;gap:10px;padding-top:6px\">\n <span class=\"skel\" style=\"width:42%;height:34px;border-radius:16px;align-self:center\"></span>\n <span class=\"skel\" style=\"width:66%;height:52px;border-radius:18px\"></span>\n <span class=\"skel\" style=\"width:48%;height:38px;border-radius:18px;align-self:flex-end\"></span>\n </div>\n <span class=\"sr-only\">Loading…</span>`;\n}\n\nexport function initials(name) {\n const parts = String(name).trim().split(/\\s+/).filter(Boolean);\n if (!parts.length) return '?';\n return (parts[0][0] + (parts.length > 1 ? parts[parts.length - 1][0] : '')).toUpperCase();\n}\n\nexport function dayKey(iso) {\n const d = new Date(iso);\n return Number.isFinite(d.getTime()) ? `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}` : null;\n}\n\n/** \"Today\" / \"Yesterday\" / \"Mon, 14 Sep\" — the label on a day separator. */\nexport function dayLabel(iso) {\n const d = new Date(iso);\n const today = new Date();\n const yesterday = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1);\n if (dayKey(d) === dayKey(today)) return 'Today';\n if (dayKey(d) === dayKey(yesterday)) return 'Yesterday';\n try {\n return d.toLocaleDateString(undefined, {\n weekday: 'short', day: 'numeric', month: 'short',\n ...(d.getFullYear() !== today.getFullYear() ? { year: 'numeric' } : {}),\n });\n } catch {\n return relativeDay(iso);\n }\n}\n\n/** \"14:05\" or \"2:05 PM\", in the reader's own convention. */\nexport function clockTime(iso) {\n const d = new Date(iso);\n if (!Number.isFinite(d.getTime())) return '';\n try {\n return d.toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' });\n } catch {\n return '';\n }\n}\n\n/** Whether the reader is at the foot of the thread, within a line or two. */\nexport function atLatest(thread) {\n return thread.scrollHeight - thread.scrollTop - thread.clientHeight < 40;\n}\n\nexport function scrollToLatest(thread, { smooth = false } = {}) {\n const calm = typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;\n if (smooth && !calm && thread.scrollTo) thread.scrollTo({ top: thread.scrollHeight, behavior: 'smooth' });\n else thread.scrollTop = thread.scrollHeight;\n}\n\n/** Status as a word a reporter would use, not the enum the database stores. */\nexport function statusWord(status) {\n return {\n OPEN: 'Open',\n IN_PROGRESS: 'In progress',\n RESOLVED: 'Fixed',\n CLOSED: 'Closed',\n }[status] || String(status || '').toLowerCase();\n}\n\n/** \"today\" / \"3 days ago\" — enough for a history list, no library. */\nexport function relativeDay(iso) {\n const then = new Date(iso).getTime();\n if (!Number.isFinite(then)) return '';\n const days = Math.floor((Date.now() - then) / 86400000);\n if (days <= 0) return 'today';\n if (days === 1) return 'yesterday';\n if (days < 30) return `${days} days ago`;\n const months = Math.floor(days / 30);\n return months === 1 ? 'a month ago' : `${months} months ago`;\n}\n\nexport function escapeHtml(s) {\n return String(s).replace(/[&<>\"']/g, (c) => (\n { '&': '&', '<': '<', '>': '>', '\"': '"', \"'\": ''' }[c]\n ));\n}\n","// Screenshots, screen recordings and file attachments.\n//\n// Capture always goes through getDisplayMedia, so the browser shows its own\n// picker and permission prompt — nothing leaves the page without the person\n// explicitly choosing what to share. There is no DOM-rasterising fallback on\n// purpose: it would need a heavy dependency and would still silently miss\n// cross-origin iframes, canvas and video, which is worse than not offering it.\n\nimport { tooManyMessage } from './busy.js';\n\n/**\n * Pristine references, taken at module-evaluation time — which happens on\n * import, before init() patches anything.\n *\n * Uploads must not be recorded by our own network capture: a report would\n * otherwise describe its own attachment upload, and a retry would append to a\n * buffer that is already being serialised.\n */\nconst nativeFetch = typeof window !== 'undefined' && window.fetch\n ? window.fetch.bind(window)\n : null;\nconst nativeXhrOpen = typeof XMLHttpRequest !== 'undefined'\n ? XMLHttpRequest.prototype.open\n : null;\nconst nativeXhrSend = typeof XMLHttpRequest !== 'undefined'\n ? XMLHttpRequest.prototype.send\n : null;\n\n/**\n * What the API will presign. Anything else is rejected before we ask.\n *\n * Mirrors UPLOAD_MIMES in the API's storage.service.ts. The two are checked\n * independently — this one to fail early with a message the person can act on,\n * that one because a browser is not a place to enforce anything.\n */\nexport const UPLOAD_MIMES = [\n 'image/png', 'image/jpeg', 'image/webp', 'image/gif', 'video/webm', 'video/mp4',\n 'audio/webm', 'audio/ogg', 'audio/mpeg', 'audio/mp4', 'audio/wav',\n 'application/pdf', 'text/plain', 'text/csv', 'application/json', 'application/zip',\n 'application/msword',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n 'application/vnd.ms-excel',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n];\n\n/**\n * What a *bug report* may carry, which is narrower than what may be uploaded.\n *\n * A report's attachments are evidence of the bug — what the screen looked like,\n * what the session did — and the API types them SCREENSHOT or VIDEO. A document\n * has no honest value there: filed as a SCREENSHOT it would render as a broken\n * image in the dashboard's media viewer.\n *\n * Documents and voice notes belong on a *message*, where the type is derived\n * from the mime and the reader gets a player or a download.\n */\nexport const REPORT_MIMES = [\n 'image/png', 'image/jpeg', 'image/webp', 'video/webm', 'video/mp4',\n];\n\nexport const voiceSupported = () =>\n Boolean(navigator?.mediaDevices?.getUserMedia && typeof MediaRecorder !== 'undefined');\n\nexport const captureSupported = () =>\n Boolean(navigator?.mediaDevices?.getDisplayMedia && typeof MediaRecorder !== 'undefined');\n\nexport const screenshotSupported = () =>\n Boolean(navigator?.mediaDevices?.getDisplayMedia);\n\n/**\n * MediaRecorder reports `video/webm;codecs=vp9`, but the presign signature\n * binds an exact Content-Type and the API only allows the base types. Strip\n * the codec parameter so the type we sign, the header we send and the type the\n * API stored all agree.\n */\nexport function normalizeMime(mime, allowed = UPLOAD_MIMES) {\n const base = String(mime || '').split(';')[0].trim().toLowerCase();\n return allowed.includes(base) ? base : null;\n}\n\n/** Grab a single frame of a chosen screen/window/tab as a PNG blob. */\nexport async function captureScreenshot() {\n const stream = await navigator.mediaDevices.getDisplayMedia({\n video: { frameRate: 1 },\n audio: false,\n });\n try {\n const track = stream.getVideoTracks()[0];\n const video = document.createElement('video');\n video.srcObject = stream;\n video.muted = true;\n video.playsInline = true;\n await video.play();\n\n // Wait one frame so the first paint isn't blank.\n await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));\n\n const { width, height } = track.getSettings();\n const canvas = document.createElement('canvas');\n canvas.width = width || video.videoWidth;\n canvas.height = height || video.videoHeight;\n canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);\n\n const blob = await new Promise((res) => canvas.toBlob(res, 'image/png'));\n if (!blob) throw new Error('The browser returned an empty screenshot.');\n return { blob, mime: 'image/png', kind: 'SCREENSHOT' };\n } finally {\n // Always release the capture, including on failure — otherwise the browser\n // keeps showing \"sharing your screen\".\n stream.getTracks().forEach((t) => t.stop());\n }\n}\n\n/**\n * Start recording a chosen screen/window/tab. Returns a handle with stop() and\n * cancel(); the caller decides which. Recording ends on its own if the user\n * clicks the browser's \"Stop sharing\" button or the cap is reached.\n */\nexport async function startRecording({ maxMs = 120_000, onTick, onAutoStop } = {}) {\n const stream = await navigator.mediaDevices.getDisplayMedia({\n video: { frameRate: 15 },\n audio: false,\n });\n\n const mime = ['video/webm;codecs=vp9', 'video/webm;codecs=vp8', 'video/webm']\n .find((t) => MediaRecorder.isTypeSupported(t)) || 'video/webm';\n\n const recorder = new MediaRecorder(stream, { mimeType: mime, videoBitsPerSecond: 2_500_000 });\n const chunks = [];\n recorder.ondataavailable = (e) => { if (e.data.size) chunks.push(e.data); };\n recorder.start(1000);\n\n const startedAt = Date.now();\n const timer = setInterval(() => onTick?.(Math.floor((Date.now() - startedAt) / 1000)), 500);\n\n const cleanup = () => {\n clearInterval(timer);\n clearTimeout(capTimer);\n stream.getTracks().forEach((t) => t.stop());\n };\n\n const finished = new Promise((resolve) => {\n recorder.onstop = () => {\n cleanup();\n resolve(new Blob(chunks, { type: mime }));\n };\n });\n\n const endEarly = () => {\n if (recorder.state !== 'inactive') recorder.stop();\n onAutoStop?.();\n };\n\n // The user can stop sharing from the browser's own UI; treat that as \"done\".\n stream.getVideoTracks()[0].addEventListener('ended', endEarly);\n\n // Hard cap so a forgotten recording can't grow without bound.\n const capTimer = setTimeout(endEarly, maxMs);\n\n return {\n mime,\n maxMs,\n async stop() {\n clearTimeout(capTimer);\n if (recorder.state !== 'inactive') recorder.stop();\n return { blob: await finished, mime, kind: 'VIDEO' };\n },\n cancel() {\n cleanup();\n if (recorder.state !== 'inactive') recorder.stop();\n },\n };\n}\n\n/**\n * Record a voice note from the microphone. Returns a handle with stop() and\n * cancel(), like startRecording() above.\n *\n * Audio only, and from getUserMedia rather than getDisplayMedia: this is\n * somebody talking into their laptop to explain a bug, not a capture of the\n * screen. The browser shows its own microphone prompt, so nothing is recorded\n * without an explicit grant.\n *\n * The container is whichever the browser will give us — Chromium and Firefox\n * produce webm/opus, Safari mp4/aac — because MediaRecorder cannot be made to\n * agree on one, and the API allows both.\n */\nexport async function startVoiceNote({ maxMs = 120_000, onTick, onAutoStop } = {}) {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n\n const mime = ['audio/webm;codecs=opus', 'audio/webm', 'audio/mp4', 'audio/ogg']\n .find((t) => MediaRecorder.isTypeSupported(t)) || '';\n\n const recorder = new MediaRecorder(stream, mime ? { mimeType: mime } : undefined);\n const chunks = [];\n recorder.ondataavailable = (e) => { if (e.data.size) chunks.push(e.data); };\n recorder.start(1000);\n\n const startedAt = Date.now();\n const timer = setInterval(() => onTick?.(Math.floor((Date.now() - startedAt) / 1000)), 500);\n\n const cleanup = () => {\n clearInterval(timer);\n clearTimeout(capTimer);\n stream.getTracks().forEach((t) => t.stop());\n };\n\n const finished = new Promise((resolve) => {\n recorder.onstop = () => {\n cleanup();\n resolve(new Blob(chunks, { type: recorder.mimeType || mime }));\n };\n });\n\n const endEarly = () => {\n if (recorder.state !== 'inactive') recorder.stop();\n onAutoStop?.();\n };\n\n // A hard cap, as for screen recording: a forgotten open microphone is worse\n // than a truncated note.\n const capTimer = setTimeout(endEarly, maxMs);\n\n return {\n mime: recorder.mimeType || mime,\n maxMs,\n async stop() {\n clearTimeout(capTimer);\n if (recorder.state !== 'inactive') recorder.stop();\n const blob = await finished;\n return { blob, mime: blob.type || recorder.mimeType || mime, kind: 'AUDIO' };\n },\n cancel() {\n cleanup();\n if (recorder.state !== 'inactive') recorder.stop();\n },\n };\n}\n\n/**\n * Ask the API for a presigned URL and PUT the blob straight at it, so media\n * never passes through the API request path.\n *\n * Resolves to the storage key, which is what a report references.\n */\nexport async function uploadAttachment({ apiBase, apiKey, blob, mime, onProgress }) {\n const type = normalizeMime(mime || blob.type);\n if (!type) {\n throw new Error(`[BugCatch] ${mime || blob.type || 'that file'} can't be attached.`);\n }\n\n const res = await nativeFetch(`${apiBase}/ingest/presign`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'X-BugCatch-Key': apiKey },\n body: JSON.stringify({ mime: type }),\n });\n if (res.status === 429) {\n // Uploading faster than the API allows. The server's sentence when it sent\n // one; either way words, not a status code, because the widget shows this.\n let detail = '';\n try { detail = (await res.json())?.message || ''; } catch { /* not JSON */ }\n throw new Error(`[BugCatch] ${tooManyMessage(typeof detail === 'string' ? detail : '')}`);\n }\n if (!res.ok) throw new Error(`[BugCatch] could not start the upload (HTTP ${res.status}).`);\n const { key, uploadUrl } = await res.json();\n\n await put(uploadUrl, blob, type, onProgress);\n return { storageKey: key, mime: type };\n}\n\n/**\n * XHR rather than fetch purely for upload progress — a recording is megabytes,\n * and a progress bar is the difference between \"working\" and \"frozen\".\n */\nfunction put(uploadUrl, blob, mime, onProgress) {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n nativeXhrOpen.call(xhr, 'PUT', uploadUrl, true);\n xhr.setRequestHeader('Content-Type', mime);\n xhr.upload.onprogress = (e) => {\n if (e.lengthComputable) onProgress?.(e.loaded / e.total);\n };\n xhr.onload = () => {\n if (xhr.status >= 200 && xhr.status < 300) resolve();\n else reject(new Error(`[BugCatch] upload failed (HTTP ${xhr.status}).`));\n };\n // The PUT goes to the bucket, not to the API, so a missing bucket CORS\n // policy arrives here as a bare network error with no status. Reporting it\n // as \"network error\" sends people to read API logs that are perfectly clean.\n xhr.onerror = () => reject(new Error(\n '[BugCatch] the browser blocked the upload to storage. If this is a new '\n + 'environment, the media bucket is probably missing its CORS policy.',\n ));\n xhr.onabort = () => reject(new Error('[BugCatch] upload cancelled.'));\n nativeXhrSend.call(xhr, blob);\n });\n}\n\nexport function humanSize(bytes) {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;\n return `${(bytes / 1024 / 1024).toFixed(1)} MB`;\n}\n\nexport function humanDuration(seconds) {\n const m = Math.floor(seconds / 60);\n const s = seconds % 60;\n return `${m}:${String(s).padStart(2, '0')}`;\n}\n","// Drafts are scoped to the API, project and reporter. Never store credentials.\nconst MAX_AGE = 7 * 24 * 60 * 60 * 1000;\nlet database;\nfunction open() {\n if (!database) database = new Promise((resolve, reject) => {\n const request = indexedDB.open('bugcatch-drafts', 1);\n request.onupgradeneeded = () => request.result.createObjectStore('drafts');\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n request.onblocked = () => reject(new Error('Draft storage is blocked.'));\n }).catch((err) => { database = null; throw err; });\n return database;\n}\nexport function draftKey(config, email) {\n return JSON.stringify([config.apiBase, config.projectId, email || config.user?.email || config.user?.id || 'anonymous']);\n}\nexport async function draftOperation(key, value) {\n const db = await open();\n return new Promise((resolve, reject) => {\n const tx = db.transaction('drafts', value === undefined ? 'readonly' : 'readwrite');\n const store = tx.objectStore('drafts');\n const request = value === undefined ? store.get(key) : value === null ? store.delete(key) : store.put({ ...value, savedAt: Date.now() }, key);\n tx.oncomplete = () => resolve(request.result);\n tx.onerror = () => reject(tx.error);\n tx.onabort = () => reject(tx.error || new Error('Draft was not saved.'));\n });\n}\nexport async function readDraft(key) {\n const value = await draftOperation(key);\n if (value && Date.now() - value.savedAt > MAX_AGE) { await draftOperation(key, null); return null; }\n return value || null;\n}\n","// The report form: what went wrong, how much it is in the way, anything that\n// shows it — and the Send that files it.\n//\n// Also the draft this device keeps of it, because a form that loses a\n// half-written report to a reload is a form people stop starting.\n\nimport * as ICONS from './icons.js';\nimport {\n captureScreenshot, startRecording, screenshotSupported, captureSupported,\n humanSize, humanDuration, normalizeMime, REPORT_MIMES,\n} from '../capture.js';\nimport { draftKey, draftOperation, readDraft } from '../drafts.js';\nimport { escapeHtml } from './render.js';\n\nconst PRIORITIES = [\n { value: 'LOW', label: 'Low' },\n { value: 'MEDIUM', label: 'Medium' },\n { value: 'HIGH', label: 'High' },\n { value: 'CRITICAL', label: 'Critical' },\n];\n\nconst DEFAULT_PRIORITY = 'MEDIUM';\n\n/** The form's markup, for the panel's body. */\nexport function composeHtml(cfg) {\n const canCapture = cfg.attachments !== false;\n const canShoot = canCapture && screenshotSupported();\n const canRecord = canCapture && captureSupported();\n return `\n <div class=\"compose\">\n <div class=\"field\">\n <label for=\"bc-title\">What went wrong?</label>\n <input id=\"bc-title\" type=\"text\" placeholder=\"e.g. The checkout button does nothing\" autocomplete=\"off\"\n aria-describedby=\"bc-title-err\" />\n <p class=\"field-err\" id=\"bc-title-err\" hidden></p>\n </div>\n <div class=\"field\">\n <label for=\"bc-desc\">Anything else that helps <span class=\"opt\">(optional)</span></label>\n <textarea id=\"bc-desc\" placeholder=\"What you were doing, what you expected, what happened instead.\"></textarea>\n </div>\n <!-- Drawn only when this project asks for an identifier and the report\n does not already carry one. Between the description and the\n severity because that is where it belongs in the reading order:\n it is a question about what they just typed. -->\n <div class=\"refs\" hidden></div>\n <div class=\"field\">\n <label id=\"bc-prio-l\">How much is it getting in your way?</label>\n <div class=\"seg\" role=\"group\" aria-labelledby=\"bc-prio-l\">\n ${PRIORITIES.map((p) => `\n <button type=\"button\" data-prio=\"${p.value}\" aria-pressed=\"${p.value === DEFAULT_PRIORITY}\">${p.label}</button>\n `).join('')}\n </div>\n </div>\n ${canCapture ? `\n <div class=\"field\">\n <div class=\"field-head\">\n <label id=\"bc-att-l\">Show us <span class=\"opt\">(optional)</span></label>\n <span class=\"count\" aria-live=\"polite\"></span>\n </div>\n <div class=\"tools\" role=\"group\" aria-labelledby=\"bc-att-l\">\n ${canShoot ? `<button class=\"tool\" type=\"button\" data-act=\"shot\">${ICONS.camera}Screenshot</button>` : ''}\n ${canRecord ? `<button class=\"tool\" type=\"button\" data-act=\"rec\">${ICONS.video}Record screen</button>` : ''}\n <button class=\"tool\" type=\"button\" data-act=\"file\">${ICONS.paperclip}Upload file</button>\n </div>\n <div class=\"thumbs\"></div>\n <input type=\"file\" accept=\"${REPORT_MIMES.join(',')}\" multiple hidden />\n </div>` : ''}\n </div>`;\n}\n\nexport function mountCompose(ctx) {\n const {\n client, cfg, root, host, dock, scrim, $, say, titleInput, descInput, compose, sendBtn, note,\n } = ctx;\n const thumbs = $('.thumbs');\n const filePicker = scrim.querySelector('input[type=file]');\n\n // Uploaded as they are added rather than on send: the upload is the slow\n // part, and doing it while the reporter is still typing means Send is\n // instant and a failure is visible next to the thing that failed.\n const items = [];\n let priority = DEFAULT_PRIORITY;\n let recorder = null;\n\n /* ---------- the draft this device keeps ---------- */\n\n const draftStatus = document.createElement('div');\n draftStatus.setAttribute('role', 'status');\n draftStatus.className = 'note';\n compose.prepend(draftStatus);\n const savedDraftKey = draftKey(cfg, client.signedInAs()?.email);\n let draftLoaded = cfg.drafts === false;\n let draftSubmitted = false;\n let draftWrites = Promise.resolve();\n let draftTimer;\n function persistDraft() {\n clearTimeout(draftTimer);\n if (!draftLoaded || draftSubmitted || cfg.drafts === false) return draftWrites;\n const value = titleInput.value || descInput.value || items.length ? {\n title: titleInput.value, description: descInput.value, priority,\n items: items.map(({ blob, mime, kind }) => ({ blob, mime, kind })),\n } : null;\n draftWrites = draftWrites.catch(() => {}).then(() => draftOperation(savedDraftKey, value))\n .then(() => { draftStatus.textContent = value ? 'Draft saved on this device' : ''; })\n .catch(() => { draftStatus.textContent = 'Draft could not be saved. Keep this tab open.'; });\n return draftWrites;\n }\n function scheduleDraft() { clearTimeout(draftTimer); draftTimer = setTimeout(persistDraft, 250); }\n compose.addEventListener('input', scheduleDraft);\n compose.addEventListener('change', scheduleDraft);\n const onPageHide = () => { void persistDraft(); };\n window.addEventListener('pagehide', onPageHide);\n function reconnectUploads() { items.filter((it) => it.status === 'failed').forEach(upload); }\n window.addEventListener('online', reconnectUploads);\n if (cfg.drafts !== false) {\n // Disable composition briefly so a late storage read cannot replace new text.\n compose.inert = true;\n readDraft(savedDraftKey).then((draft) => {\n if (ctx.closed || !draft) return;\n draftStatus.innerHTML = '<span>You have an unfinished report on this device.</span> <button type=\"button\" data-resume>Resume report</button> <button type=\"button\" data-discard>Discard</button>';\n draftStatus.querySelector('[data-resume]').onclick = () => {\n titleInput.value = draft.title || '';\n descInput.value = draft.description || '';\n priority = PRIORITIES.some((p) => p.value === draft.priority) ? draft.priority : 'MEDIUM';\n scrim.querySelectorAll('[data-prio]').forEach((b) => b.setAttribute('aria-pressed', String(b.dataset.prio === priority)));\n for (const item of (draft.items || []).slice(0, cfg.maxAttachments ?? 5)) addItem(item);\n draftLoaded = true;\n void persistDraft();\n };\n draftStatus.querySelector('[data-discard]').onclick = () => {\n draftLoaded = true;\n void persistDraft();\n };\n }).catch(() => { draftStatus.textContent = 'Draft storage is unavailable. Keep this tab open.'; })\n .finally(() => { compose.inert = false; if (!draftStatus.querySelector('[data-resume]')) draftLoaded = true; });\n }\n\n // Saved on the way out, before anything else is taken down.\n ctx.onDestroy(() => {\n void persistDraft();\n clearTimeout(draftTimer);\n window.removeEventListener('pagehide', onPageHide);\n window.removeEventListener('online', reconnectUploads);\n recorder?.cancel();\n items.forEach((it) => it.previewUrl && URL.revokeObjectURL(it.previewUrl));\n root.querySelector('.recbar')?.remove();\n });\n\n /* ---------- severity ---------- */\n\n scrim.querySelectorAll('[data-prio]').forEach((btn) => {\n btn.addEventListener('click', () => {\n priority = btn.dataset.prio;\n scheduleDraft();\n scrim.querySelectorAll('[data-prio]').forEach((b) => {\n b.setAttribute('aria-pressed', String(b === btn));\n });\n });\n });\n\n /* ---------- attachments ---------- */\n\n // The one required field says what is missing right under itself, rather\n // than in a banner the reporter has to connect back to it.\n const titleErr = $('#bc-title-err');\n function flagTitle(text) {\n titleErr.hidden = !text;\n titleErr.innerHTML = text ? `${ICONS.alert}<span>${escapeHtml(text)}</span>` : '';\n titleInput.classList.toggle('invalid', Boolean(text));\n titleInput.setAttribute('aria-invalid', String(Boolean(text)));\n }\n titleInput.addEventListener('input', () => { if (titleInput.value.trim()) flagTitle(''); });\n\n function refreshSendState() {\n const busy = items.some((it) => it.status === 'uploading');\n sendBtn.disabled = ctx.sending || busy || items.some((it) => it.status === 'failed');\n note.textContent = busy ? 'Waiting for the upload to finish…' : items.some((it) => it.status === 'failed') ? 'Retry or remove failed attachments before sending.' : '';\n const max = cfg.maxAttachments ?? 5;\n scrim.querySelectorAll('.tool').forEach((t) => {\n t.disabled = ctx.sending || items.length >= max;\n });\n const count = $('.field-head .count');\n if (count) count.textContent = items.length ? `${items.length} of ${max}` : '';\n }\n ctx.refreshSendState = refreshSendState;\n\n function drawThumb(item) {\n const el = item.el;\n const label = item.kind === 'VIDEO' ? 'Video' : 'Shot';\n const preview = item.kind === 'VIDEO'\n ? `<video src=\"${item.previewUrl}\" muted playsinline preload=\"metadata\"></video>`\n : `<img src=\"${item.previewUrl}\" alt=\"\" />`;\n\n let veil = '';\n if (item.status === 'uploading') {\n veil = `<div class=\"veil\"><div class=\"spinner\"></div><div class=\"bar\"><i style=\"width:${Math.round(item.progress * 100)}%\"></i></div></div>`;\n } else if (item.status === 'failed') {\n veil = `<div class=\"veil err\"><button type=\"button\" data-act=\"retry\" aria-label=\"Retry upload\">${ICONS.retry}</button>Failed</div>`;\n }\n\n el.innerHTML = `\n ${preview}\n <span class=\"kind\">${label} · ${humanSize(item.size)}</span>\n <button class=\"drop\" type=\"button\" data-act=\"drop\" aria-label=\"Remove ${label.toLowerCase()}\">${ICONS.close}</button>\n ${veil}`;\n\n el.querySelector('[data-act=\"drop\"]').addEventListener('click', () => removeItem(item));\n el.querySelector('[data-act=\"retry\"]')?.addEventListener('click', () => upload(item));\n }\n\n function removeItem(item) {\n const i = items.indexOf(item);\n if (i >= 0) items.splice(i, 1);\n if (item.previewUrl) URL.revokeObjectURL(item.previewUrl);\n item.el.remove();\n void persistDraft();\n refreshSendState();\n }\n\n async function upload(item) {\n item.status = 'uploading';\n item.progress = 0;\n drawThumb(item);\n refreshSendState();\n try {\n const { storageKey, mime } = await client.uploadAttachment(item.blob, item.mime, (p) => {\n item.progress = p;\n // Only the width changes while uploading — repainting the whole tile\n // would restart the entrance animation on every progress event.\n const fill = item.el.querySelector('.bar i');\n if (fill) fill.style.width = `${Math.round(p * 100)}%`;\n });\n item.storageKey = storageKey;\n item.mime = mime;\n item.status = 'ready';\n } catch (err) {\n item.status = 'failed';\n say(err.message.replace('[BugCatch] ', ''));\n }\n drawThumb(item);\n refreshSendState();\n }\n\n function addItem({ blob, mime, kind }) {\n const max = cfg.maxAttachmentBytes ?? 25 * 1024 * 1024;\n if (blob.size > max) {\n say(`That file is ${humanSize(blob.size)} — the limit is ${humanSize(max)}.`);\n return;\n }\n // Held to the report set, not the full upload set. A report's attachments\n // are typed SCREENSHOT or VIDEO, so a document accepted here would be filed\n // as a screenshot and render as a broken image. Documents and voice notes\n // go on a message instead, where they keep their own type.\n const normalized = normalizeMime(mime || blob.type, REPORT_MIMES);\n if (!normalized) {\n say('Only PNG, JPEG, WebP, WebM and MP4 files can be attached to a report.');\n return;\n }\n const item = {\n blob, kind, mime: normalized, size: blob.size,\n previewUrl: URL.createObjectURL(blob),\n status: 'uploading', progress: 0, storageKey: null,\n el: document.createElement('div'),\n };\n item.el.className = 'thumb';\n items.push(item);\n thumbs.appendChild(item.el);\n say('');\n void persistDraft();\n upload(item);\n }\n\n /* Hide our own UI so the capture shows the page, not this dialog. */\n async function withWidgetHidden(fn) {\n host.style.opacity = '0';\n host.style.pointerEvents = 'none';\n // One frame so the browser has actually painted the hidden state before\n // the picker — and the frame we grab — is taken.\n await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)));\n try {\n return await fn();\n } finally {\n host.style.opacity = '';\n host.style.pointerEvents = '';\n }\n }\n\n scrim.querySelector('[data-act=\"shot\"]')?.addEventListener('click', async () => {\n try {\n const shot = await withWidgetHidden(captureScreenshot);\n addItem(shot);\n } catch (err) {\n // NotAllowedError is the user dismissing the picker — not a failure.\n if (err?.name !== 'NotAllowedError') say(`Screenshot failed: ${err.message}`);\n }\n });\n\n scrim.querySelector('[data-act=\"file\"]')?.addEventListener('click', () => filePicker.click());\n filePicker?.addEventListener('change', () => {\n const room = (cfg.maxAttachments ?? 5) - items.length;\n [...filePicker.files].slice(0, Math.max(room, 0)).forEach((file) => {\n addItem({\n blob: file,\n mime: file.type,\n kind: file.type.startsWith('video/') ? 'VIDEO' : 'SCREENSHOT',\n });\n });\n filePicker.value = '';\n });\n\n /* ---------- recording ---------- */\n\n scrim.querySelector('[data-act=\"rec\"]')?.addEventListener('click', async () => {\n const maxMs = cfg.maxRecordingMs ?? 120_000;\n let bar;\n try {\n recorder = await startRecording({\n maxMs,\n onTick: (s) => { if (bar) bar.querySelector('.time').textContent = humanDuration(s); },\n onAutoStop: () => { if (recorder) stopRecording({ keep: true }); },\n });\n } catch (err) {\n if (err?.name !== 'NotAllowedError') say(`Recording failed: ${err.message}`);\n return;\n }\n\n // Step out of the way for the duration: a recording of this dialog sitting\n // over the page is not what anyone means by \"record the bug\".\n scrim.style.display = 'none';\n dock.hidden = true;\n\n bar = document.createElement('div');\n bar.className = 'recbar';\n bar.innerHTML = `\n <span class=\"dot\"></span>\n <span class=\"time\">0:00</span>\n <span class=\"hint\">Recording — up to ${humanDuration(Math.round(maxMs / 1000))}</span>\n <button class=\"stop\" type=\"button\">Stop</button>\n <button class=\"ghost\" type=\"button\">Discard</button>`;\n bar.querySelector('.stop').addEventListener('click', () => stopRecording({ keep: true }));\n bar.querySelector('.ghost').addEventListener('click', () => stopRecording({ keep: false }));\n root.appendChild(bar);\n bar.querySelector('.stop').focus();\n });\n\n async function stopRecording({ keep }) {\n const rec = recorder;\n if (!rec) return;\n recorder = null;\n root.querySelector('.recbar')?.remove();\n scrim.style.display = '';\n dock.hidden = false;\n\n if (!keep) { rec.cancel(); titleInput.focus(); return; }\n try {\n const clip = await rec.stop();\n if (clip.blob.size) addItem(clip);\n else say('That recording came back empty — nothing was captured.');\n } catch (err) {\n say(`Recording failed: ${err.message}`);\n }\n titleInput.focus();\n }\n ctx.isRecording = () => Boolean(recorder);\n ctx.stopRecording = stopRecording;\n\n /* ---------- send ---------- */\n\n sendBtn.addEventListener('click', async () => {\n const title = titleInput.value;\n if (!title.trim()) {\n flagTitle('Add a few words about what went wrong — a short sentence is plenty.');\n titleInput.focus();\n return;\n }\n\n // Ask, if this project asks and nothing they wrote answered it — the\n // first press opens the question rather than sending. See refs.js.\n if (ctx.holdForReference()) return;\n\n ctx.sending = true;\n say('');\n refreshSendState();\n const original = sendBtn.innerHTML;\n sendBtn.innerHTML = '<span class=\"spinner\" aria-hidden=\"true\"></span>Sending…';\n\n try {\n const res = await client.report({\n title,\n description: descInput.value,\n priority,\n references: ctx.answeredReference(),\n issue: ctx.pickedIssue()?.key || null,\n attachments: items\n .filter((it) => it.status === 'ready')\n .map((it) => ({ type: it.kind, storageKey: it.storageKey, mime: it.mime })),\n });\n if (res?.cancelled) { ctx.sending = false; sendBtn.innerHTML = original; refreshSendState(); return; }\n draftSubmitted = true;\n clearTimeout(draftTimer);\n await draftWrites;\n if (cfg.drafts !== false) await draftOperation(savedDraftKey, null).catch(() => {});\n ctx.succeed(res);\n } catch (err) {\n ctx.sending = false;\n sendBtn.innerHTML = original;\n say(err.message.replace('[BugCatch] ', ''));\n refreshSendState();\n // One reason a send fails is that this browser was signed out while the\n // panel sat open — the same address signed in elsewhere. The message\n // above already says so; this puts the panel back in step with it\n // instead of leaving a Send button that will fail the same way again.\n ctx.recheckSignIn();\n }\n });\n}\n","/**\n * Reading the reporter's own text for something that identifies their record.\n *\n * A browser-side twin of bugcatch-api/src/settings/reference-extract.ts, and\n * deliberately only the half this side needs. The server does the extraction\n * that counts — what a payload claims it found is not evidence, and the value\n * an investigation eventually joins on is the one the API read out of the text\n * it stored. This copy exists to answer one question, locally and instantly:\n *\n * does what this person has already typed contain an identifier, or do we\n * have to interrupt them for one?\n *\n * Getting that wrong in the lenient direction costs an unnecessary question.\n * Getting it wrong in the strict direction means asking a reporter who has\n * already told us, in the sentence above the box, which is the fastest way to\n * make somebody close a bug reporter and not come back. So the matching rule\n * here is the same rule, run over the same patterns, which the API ships with\n * the field list precisely so that both sides can agree.\n *\n * The patterns arrive over the wire and are compiled here. That is a thing to\n * be deliberate about rather than nervous about: they are shapes, not secrets —\n * \"a loan number looks like LN- followed by digits\" is not a fact worth hiding\n * from somebody already looking at their own loan number — and the alternative\n * is a round trip per keystroke or a question for everybody.\n */\n\nconst MAX_SCAN_CHARS = 20000;\nconst MAX_VALUE_CHARS = 120;\nconst MAX_MATCHES = 200;\n\n/**\n * Compile, or null.\n *\n * Null on anything that will not compile, which in a browser is a real case\n * rather than a defensive one: a regex feature one engine lacks throws here\n * rather than failing to match. A field that cannot be compiled is simply not\n * scanned, the reporter gets asked, and the server — which compiled it fine —\n * still reads the text. Degrading to the question is the whole design.\n */\nfunction compile(source) {\n if (typeof source !== 'string' || !source.trim() || source.length > 400) return null;\n try {\n return new RegExp(source.trim(), 'gi');\n } catch {\n return null;\n }\n}\n\nconst tidy = (value) => String(value).replace(/\\s+/g, ' ').trim().slice(0, MAX_VALUE_CHARS);\n\n/**\n * The value as the customer's database stores it — the same rule as\n * normalizeReferenceValue in bugcatch-api/src/settings/reference-extract.ts,\n * driven by the `normalize` the API sends with each built-in field.\n */\nexport function normalizeValue(field, value) {\n const how = field && field.normalize;\n if (how === 'phone') {\n const digits = value.replace(/\\D/g, '');\n if (digits.length === 10) return digits;\n if (digits.length === 12 && digits.startsWith('91')) return digits.slice(2);\n if (digits.length === 11 && digits.startsWith('0')) return digits.slice(1);\n return value;\n }\n if (how === 'upper') return value.toUpperCase();\n return value;\n}\n\n/** Group 1 where the pattern has one, the whole match otherwise. */\nfunction valueOf(match) {\n const raw = match.length > 1 && match[1] !== undefined ? match[1] : match[0];\n if (typeof raw !== 'string' || !raw.trim()) return null;\n // The span claimed is the whole match, never the capture: a labelled rule has\n // eaten the words in front of the value too, and leaving them unclaimed lets\n // a later field match them again.\n return { value: tidy(raw), start: match.index, end: match.index + match[0].length };\n}\n\n/**\n * Every configured identifier found in the given text.\n *\n * Fields are taken in the order the project arranged them and a span an earlier\n * field claimed is not offered to a later one — which is what keeps the rule for\n * \"alternate number\" and the rule for \"mobile number\", which match the same ten\n * digits, from both claiming it.\n */\nexport function matchReferences(fields, texts) {\n const found = [];\n const seen = new Set();\n\n for (const block of texts || []) {\n const text = typeof block === 'string' ? block.slice(0, MAX_SCAN_CHARS) : '';\n if (!text) continue;\n\n const claimed = [];\n const overlaps = (a, b) => claimed.some(([s, e]) => a < e && b > s);\n\n for (const field of fields || []) {\n if (!field || field.scan === false) continue;\n const re = compile(field.pattern);\n if (!re) continue;\n\n let match;\n let guard = 0;\n re.lastIndex = 0;\n while ((match = re.exec(text)) !== null) {\n if (++guard > MAX_MATCHES) break;\n // exec does not advance past a zero-width match, so this would spin.\n if (match[0] === '') { re.lastIndex += 1; continue; }\n\n const hit = valueOf(match);\n if (hit && !overlaps(hit.start, hit.end)) {\n claimed.push([hit.start, hit.end]);\n const value = normalizeValue(field, hit.value);\n const dedupe = `${field.key}:${value.toLowerCase()}`;\n if (!seen.has(dedupe)) {\n seen.add(dedupe);\n found.push({ key: field.key, label: field.label, value });\n }\n }\n }\n }\n }\n\n return found;\n}\n\n/** Letters the match read outside its value — the label words, roughly. */\nfunction labelWeight(text, hit) {\n const around = text.slice(hit.start, hit.valueStart) + text.slice(hit.valueEnd, hit.end);\n return (around.match(/[a-z]/gi) || []).length;\n}\n\n/**\n * Things in the text that look like an identifier nobody labelled: a UUID, a\n * run of six or more digits, a code mixing letters and digits. Only ever used\n * to pre-fill the question, never to skip it.\n */\n// No lookbehind: older Safari throws on it at parse time, which here would\n// take the whole SDK down rather than one field. Boundaries are checked by hand.\nconst LOOSE_ID = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[A-Za-z0-9][\\w-]{5,40}/gi;\nconst WORDISH = /[\\w-]/;\n\n/**\n * What the reporter's text says about which record this is, sorted by how\n * sure we can be.\n *\n * found — each value one field reads best, and the only value for that\n * field. Enough to send without asking.\n * conflicts — a value two fields read equally well (two bare UUID rules), or\n * a field that found two different values. Asking which is the\n * only honest answer; picking the first is a coin toss filed as\n * a fact.\n * loose — ID-shaped strings no rule claimed, with the fields whose value\n * shape they fit. Offered as the answer when we have to ask\n * anyway, so the reporter confirms instead of retyping.\n *\n * \"Reads best\" is how many label letters the match consumed: `partner user id\n * 4821` is matched by the partner rule and the plain user rule, and the one\n * that read \"partner\" knows more. Two rules that read the same words tie, and\n * a tie is a question.\n */\nexport function readReferences(fields, texts) {\n const scannable = (fields || []).filter((f) => f && f.scan !== false && f.pattern);\n const hits = [];\n const loose = [];\n const blocks = (texts || []).map((t) => (typeof t === 'string' ? t.slice(0, MAX_SCAN_CHARS) : ''));\n\n blocks.forEach((text, block) => {\n if (!text) return;\n scannable.forEach((field, order) => {\n const re = compile(field.pattern);\n if (!re) return;\n let match;\n let guard = 0;\n re.lastIndex = 0;\n while ((match = re.exec(text)) !== null) {\n if (++guard > MAX_MATCHES) break;\n if (match[0] === '') { re.lastIndex += 1; continue; }\n const hit = valueOf(match);\n if (!hit) continue;\n const raw = match.length > 1 && match[1] !== undefined ? match[1] : match[0];\n const valueStart = match.index + Math.max(0, match[0].indexOf(raw));\n const h = {\n block, order, key: field.key, label: field.label, value: normalizeValue(field, hit.value),\n start: hit.start, end: hit.end, valueStart, valueEnd: valueStart + raw.length,\n };\n h.weight = labelWeight(text, h);\n hits.push(h);\n }\n });\n });\n\n // A hit loses to one that covers the same value and read more label.\n const survivors = hits.filter((h) => !hits.some((o) => o !== h && o.block === h.block\n && o.key !== h.key && o.valueStart <= h.valueStart && o.valueEnd >= h.valueEnd\n && o.weight > h.weight));\n\n // Group what survived by value: one value, several fields = which kind is it?\n const byValue = new Map();\n for (const h of survivors) {\n const id = h.value.toLowerCase();\n if (!byValue.has(id)) byValue.set(id, { value: h.value, keys: new Map() });\n const entry = byValue.get(id);\n if (!entry.keys.has(h.key)) entry.keys.set(h.key, { key: h.key, label: h.label, order: h.order });\n }\n\n const found = [];\n const conflicts = [];\n const settledKey = new Map(); // key -> values found for it\n for (const entry of byValue.values()) {\n const kinds = [...entry.keys.values()].sort((a, b) => a.order - b.order);\n if (kinds.length > 1) {\n conflicts.push({ value: entry.value, kinds: kinds.map(({ key, label }) => ({ key, label })) });\n continue;\n }\n const [{ key, label }] = kinds;\n if (!settledKey.has(key)) settledKey.set(key, []);\n settledKey.get(key).push({ key, label, value: entry.value });\n }\n for (const list of settledKey.values()) {\n if (list.length === 1) found.push(list[0]);\n else for (const r of list) conflicts.push({ value: r.value, kinds: [{ key: r.key, label: r.label }] });\n }\n\n // Loose candidates: ID-shaped, untouched by any rule, and fitting at least\n // one field's value shape. Nothing that is a URL path or an email.\n const seen = new Set(byValue.keys());\n blocks.forEach((text, block) => {\n if (!text) return;\n const taken = hits.filter((h) => h.block === block);\n const re = new RegExp(LOOSE_ID.source, 'gi');\n let m;\n let guard = 0;\n while ((m = re.exec(text)) !== null && ++guard <= MAX_MATCHES) {\n const s = m.index;\n const e = s + m[0].length;\n if (taken.some((h) => s < h.end && e > h.start)) continue;\n const before = text[s - 1] || '';\n const after = text[e] || '';\n if (WORDISH.test(before) || WORDISH.test(after)) continue;\n // Part of a URL, an address or a file name, not a thing someone quoted.\n if (before === '/' || before === '@' || after === '@' || before === '.') continue;\n if (after === '.' && /\\w/.test(text[e + 1] || '')) continue;\n if (!/\\d/.test(m[0])) continue;\n const value = tidy(m[0]);\n const id = value.toLowerCase();\n if (seen.has(id)) continue;\n const kinds = (fields || [])\n .filter((f) => f && validateReference(f, value))\n .map(({ key, label }) => ({ key, label }));\n if (!kinds.length) continue;\n seen.add(id);\n loose.push({ value, kinds });\n }\n });\n\n return { found, conflicts, loose };\n}\n\n/**\n * Whether what somebody typed into the box is the shape that field asks for.\n *\n * Anchored, unlike the scan above: reading prose means finding a value inside a\n * sentence, and checking an answer means the answer *is* the value. `valuePattern`\n * where the field has one — a rule that finds \"alternate number: 98…\" in prose\n * needs the words, and a box already headed \"Alternate number\" will not have\n * them typed into it again.\n *\n * Returns the tidied value, or null. Advisory only: the widget uses it to say\n * \"that does not look like a loan number\" before sending, and the API keeps a\n * value that fails it rather than dropping what the reporter believes is their\n * reference.\n */\nexport function validateReference(field, raw) {\n const value = typeof raw === 'string' ? raw.trim() : '';\n if (!value || value.length > MAX_VALUE_CHARS) return null;\n\n const re = compile(field && (field.valuePattern || field.pattern));\n if (!re) return null;\n\n let match;\n let guard = 0;\n re.lastIndex = 0;\n while ((match = re.exec(value)) !== null) {\n if (++guard > MAX_MATCHES) break;\n if (match[0] === '') { re.lastIndex += 1; continue; }\n // One character of slack at the front and only there, for a pattern whose\n // left boundary consumes rather than looks behind — see the phone rule,\n // written that way so it compiles in older Safari.\n const toEnd = match.index + match[0].length === value.length;\n const atEdge = match.index === 0 || match.index === 1;\n if (toEnd && atEdge) {\n const hit = valueOf(match);\n if (hit) return normalizeValue(field, hit.value);\n }\n }\n return null;\n}\n\n/**\n * What this project needs in order to look a report up.\n *\n * Every failure answers `{ policy: 'off', fields: [] }`, which is the answer\n * that draws nothing. A widget that could not reach this route must not be a\n * widget that refuses to send a report — the whole feature is a help, and the\n * server reads the text either way.\n */\nexport async function referenceFields({ apiBase, apiKey, projectId }) {\n const off = { policy: 'off', fields: [] };\n try {\n const res = await fetch(\n `${apiBase}/ingest/reference-fields?projectId=${encodeURIComponent(projectId)}`,\n { headers: { 'X-BugCatch-Key': apiKey } },\n );\n if (!res.ok) return off;\n const body = await res.json();\n const policy = body.policy === 'optional' || body.policy === 'required' ? body.policy : 'off';\n const fields = Array.isArray(body.fields) ? body.fields : [];\n return fields.length ? { policy, fields } : off;\n } catch {\n return off;\n }\n}\n","// Which record is this about.\n//\n// What this project asks for, and what the reporter has answered.\n//\n// The question is asked at Send and not before, which is the whole design of\n// it. A form that opens with \"loan number\" above \"what went wrong\" tells\n// somebody they have paperwork to do before they may complain, and most of\n// them will not — while the same question, asked once, after they have said\n// their piece, and only when what they said did not already contain the\n// answer, is a reasonable thing to be asked. Most reporters never see it: the\n// loan number was in the sentence.\n\nimport * as ICONS from './icons.js';\nimport { readReferences, validateReference } from '../references.js';\nimport { escapeHtml } from './render.js';\n\nexport function mountReferences(ctx) {\n const {\n client, say, titleInput, descInput, sendBtn, refsBox,\n } = ctx;\n\n let refConfig = { policy: 'off', fields: [] };\n let refOpen = false;\n let refChoice = null; // which field they are answering; null = not picked yet\n let refValue = '';\n let refError = '';\n // What we read in their text but could not place for sure, offered as taps:\n // [{ value, kinds: [{ key, label }] }]. `refMode` says why the card is open —\n // 'confirm' when the text had two answers, 'suggest' when it had an unlabelled\n // one, 'ask' when it had none.\n let refCandidates = [];\n let refMode = 'ask';\n // Set once they have answered or explicitly skipped, so Send stops asking.\n let refSettled = false;\n // Set once we have said \"that does not look right\" about the current value.\n // Its own flag rather than reusing refSettled, which was the first attempt\n // and was a loop: the warning returns early, the next press re-validates the\n // same unchanged value, warns again, and the report can never be sent. One\n // warning per value, and then it is their call.\n let refWarned = false;\n\n client.referenceFields()\n .then((cfg) => { refConfig = cfg; showFound(); })\n .catch(() => { /* off is the safe answer to every failure */ });\n\n /** What the text says: found (sure), conflicts (ask which), loose (offer). */\n function readText() {\n if (refConfig.policy === 'off' || !refConfig.fields.length) return { found: [], conflicts: [], loose: [] };\n return readReferences(refConfig.fields, [titleInput.value, descInput.value]);\n }\n\n /** Sure enough to send without a question: something found, nothing contested. */\n function textAnswers(read = readText()) {\n return read.found.length > 0 && read.conflicts.length === 0;\n }\n\n /** The answer to send, or an empty list. */\n function answeredReference() {\n if (!refChoice || !refValue.trim()) return [];\n return [{ key: refChoice, value: refValue.trim().slice(0, 120) }];\n }\n\n // A quiet line under the description while they type, so somebody who put\n // their loan number in the sentence can see it was read and does not go\n // looking for a box to type it into again.\n const foundNote = document.createElement('p');\n foundNote.className = 'ref-found';\n foundNote.setAttribute('aria-live', 'polite');\n foundNote.hidden = true;\n descInput.closest('.field').after(foundNote);\n let foundTimer;\n function showFound() {\n const read = readText();\n if (textAnswers(read)) {\n const list = read.found.slice(0, 2).map((r) => `${escapeHtml(r.label)} <b>${escapeHtml(shortId(r.value))}</b>`).join(', ');\n foundNote.innerHTML = `${ICONS.check}<span>Found your ${list} — no need to add it again.</span>`;\n foundNote.hidden = false;\n // They have answered in the text; a card still asking would be not listening.\n if (refOpen && !answeredReference().length) { refOpen = false; drawRefs(); }\n } else {\n foundNote.hidden = true;\n foundNote.innerHTML = '';\n }\n }\n const queueFound = () => { clearTimeout(foundTimer); foundTimer = setTimeout(showFound, 250); };\n titleInput.addEventListener('input', queueFound);\n descInput.addEventListener('input', queueFound);\n\n /** A UUID is long; the chip shows its ends, the title shows all of it. */\n function shortId(value) {\n return value.length > 20 ? `${value.slice(0, 8)}…${value.slice(-4)}` : value;\n }\n\n function candidateFor(value) {\n const v = value.trim().toLowerCase();\n return refCandidates.find((c) => c.value.toLowerCase() === v) || null;\n }\n\n /** Pick a candidate: its value, and its kind when only one kind fits. */\n function takeCandidate(c) {\n refValue = c.value;\n if (c.kinds.length === 1) refChoice = c.kinds[0].key;\n else if (!c.kinds.some((k) => k.key === refChoice)) refChoice = null;\n refError = '';\n refWarned = false;\n }\n\n function drawRefs() {\n if (!refOpen) { refsBox.hidden = true; refsBox.innerHTML = ''; return; }\n const field = refConfig.fields.find((f) => f.key === refChoice) || null;\n const optional = refConfig.policy !== 'required';\n const current = candidateFor(refValue);\n // Kinds worth offering: the ones the picked value could be, when we know;\n // every kind otherwise. Never zero — a guess of ours must not lock out the\n // right answer.\n const kinds = current && current.kinds.length > 1\n ? refConfig.fields.filter((f) => current.kinds.some((k) => k.key === f.key))\n : refConfig.fields;\n // Two loan numbers: the kind is not in question, only which one — so the\n // kind chips would be a second question nobody needs to answer.\n const sharedKind = refCandidates.length > 1\n && refCandidates.every((c) => c.kinds.length === 1 && c.kinds[0].key === refCandidates[0].kinds[0].key);\n\n const head = {\n confirm: refCandidates.length > 1\n ? ['Which one is this about?', 'We found more than one ID in your message. Pick the one this problem is about.']\n : ['Is this the right ID?', 'This could be more than one kind of ID. Tell us which it is.'],\n suggest: ['Is this your ID?', 'We spotted this in your message. Tell us what it is so we can find your record.'],\n ask: optional\n ? ['One more thing', 'So we can find your record — this helps us answer faster.']\n : ['We need one detail', 'We need one of these to look this up.'],\n }[refMode];\n\n refsBox.hidden = false;\n // A handful of kinds read best as chips to tap; a long list goes back to\n // a select so the card does not turn into a wall of pills.\n const many = kinds.length > 6;\n refsBox.innerHTML = `\n <div class=\"ask-head\">\n <span class=\"avatar\" aria-hidden=\"true\">${ICONS.sparkle}</span>\n <div>\n <strong>${escapeHtml(head[0])}</strong>\n <p class=\"why\">${escapeHtml(head[1])}</p>\n </div>\n </div>\n ${refCandidates.length > 1 ? `\n <div class=\"chips cands\" role=\"group\" aria-label=\"IDs found in your message\">\n ${refCandidates.map((c, i) => `\n <button type=\"button\" data-ref-cand=\"${i}\" title=\"${escapeHtml(c.value)}\"\n aria-pressed=\"${!!current && current === c}\">${escapeHtml(shortId(c.value))}</button>\n `).join('')}\n </div>` : ''}\n ${kinds.length > 1 && !many && !sharedKind ? `\n <div class=\"chips\" role=\"group\" aria-label=\"Which detail you are giving\">\n ${kinds.map((f) => `\n <button type=\"button\" data-ref-kind=\"${escapeHtml(f.key)}\" aria-pressed=\"${!!field && f.key === field.key}\">${escapeHtml(f.label)}</button>\n `).join('')}\n </div>` : ''}\n <div class=\"row\">\n ${many ? `\n <select class=\"ref-kind\" aria-label=\"Which detail you are giving\">\n ${field ? '' : '<option value=\"\" selected disabled>What is it?</option>'}\n ${kinds.map((f) => `\n <option value=\"${escapeHtml(f.key)}\" ${field && f.key === field.key ? 'selected' : ''}>${escapeHtml(f.label)}</option>\n `).join('')}\n </select>` : ''}\n <input class=\"ref-value${refError ? ' invalid' : ''}\" type=\"${field?.inputMode === 'email' ? 'email' : 'text'}\"\n inputmode=\"${escapeHtml(field?.inputMode === 'numeric' || field?.inputMode === 'tel' ? 'numeric' : 'text')}\"\n value=\"${escapeHtml(refValue)}\"\n placeholder=\"${escapeHtml(field ? (field.placeholder || field.label) : 'Your ID')}\"\n aria-label=\"${escapeHtml(field ? field.label : 'Your ID')}\" ${refError ? 'aria-invalid=\"true\"' : ''} />\n </div>\n ${field?.hint ? `<p class=\"hint\">${escapeHtml(field.hint)}</p>` : ''}\n ${refError ? `<p class=\"field-err\" role=\"alert\">${ICONS.alert}<span>${escapeHtml(refError)}</span></p>` : ''}\n ${optional ? `<button type=\"button\" class=\"link\" data-act=\"ref-skip\">${\n refMode === 'ask' ? 'I don\\'t have this — send without it' : 'Not sure — send as it is'}</button>` : ''}`;\n\n refsBox.querySelectorAll('[data-ref-cand]').forEach((chip) => {\n chip.addEventListener('click', () => {\n takeCandidate(refCandidates[Number(chip.dataset.refCand)]);\n drawRefs();\n });\n });\n refsBox.querySelectorAll('[data-ref-kind]').forEach((chip) => {\n chip.addEventListener('click', () => {\n refChoice = chip.dataset.refKind;\n refError = '';\n refWarned = false;\n drawRefs();\n });\n });\n const value = refsBox.querySelector('.ref-value');\n value.addEventListener('input', () => {\n refValue = value.value;\n refError = '';\n // A changed value has not been warned about yet.\n refWarned = false;\n });\n // Enter in the box is the same gesture as pressing Send, which is what\n // somebody who has just been asked one question expects it to be.\n value.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') { e.preventDefault(); sendBtn.click(); }\n });\n refsBox.querySelector('.ref-kind')?.addEventListener('change', (e) => {\n refChoice = e.target.value;\n refError = '';\n drawRefs();\n refsBox.querySelector('.ref-value')?.focus();\n });\n refsBox.querySelector('[data-act=\"ref-skip\"]')?.addEventListener('click', () => {\n // Skipping is answering. Without this the panel asks again on the next\n // press and the reporter is in a loop with a button that does nothing.\n // The text still goes; the server reads it either way.\n refSettled = true;\n refOpen = false;\n refChoice = null;\n refValue = '';\n refError = '';\n refWarned = false;\n drawRefs();\n say('');\n });\n\n // Focus where the next decision is: the kind, when that is all that is\n // missing; the box otherwise.\n const next = !field && refValue ? refsBox.querySelector('[data-ref-kind], .ref-kind') : null;\n (next || value).focus();\n }\n\n /**\n * Open the question, pre-filled with the best of what the text offered.\n *\n * Conflicts first — the text said two things and only the reporter knows\n * which — then loose IDs, which are probably the answer and only need a\n * name. Nothing, and it is the plain question it always was.\n */\n function openRefs(read) {\n refCandidates = read.conflicts.length ? read.conflicts : read.loose.slice(0, 4);\n refMode = read.conflicts.length ? 'confirm' : read.loose.length ? 'suggest' : 'ask';\n refOpen = true;\n if (refCandidates.length) {\n // One contested value: pre-fill it so they only choose what it is. Two\n // or more: pick nothing, because picking is the question.\n if (refCandidates.length === 1) takeCandidate(refCandidates[0]);\n else {\n refValue = '';\n const [first] = refCandidates;\n const same = refCandidates.every((c) => c.kinds.length === 1 && c.kinds[0].key === first.kinds[0].key);\n refChoice = same ? first.kinds[0].key : null;\n }\n } else {\n refChoice = refChoice || refConfig.fields[0].key;\n }\n drawRefs();\n }\n\n /**\n * Whether Send should stop and ask.\n *\n * Never when the text answered it outright. Once when the text said two\n * things or something unlabelled — on every policy, because sending the\n * wrong record's id with confidence is worse than one extra tap. And the\n * plain question on `optional`/`required` projects as before.\n */\n function needsReference() {\n if (refConfig.policy === 'off' || !refConfig.fields.length) return false;\n if (answeredReference().length) return false;\n const read = readText();\n if (textAnswers(read)) return false;\n if (refSettled && refConfig.policy !== 'required') return false;\n return read;\n }\n\n /**\n * Whether Send should stop here: true when it asked, or warned, instead.\n *\n * Ask, if this project asks and nothing they wrote answered it. The first\n * press opens the question rather than sending; the second sends. That\n * costs one extra press on projects that want an identifier, and it is the\n * only shape that does not put paperwork in front of a person who came here\n * to say something is broken.\n */\n function holdForReference() {\n // First, though: a value in the box but no kind picked. Asked rather than\n // guessed — a wrong kind joins on the wrong table.\n if (refOpen && refValue.trim() && !refChoice) {\n refError = 'Pick what this ID is.';\n drawRefs();\n return true;\n }\n\n const read = needsReference();\n if (read) {\n if (!refOpen) openRefs(read); else drawRefs();\n say('');\n return true;\n }\n\n // They chose a field and typed something that is not that shape. Advisory\n // on the server — a value that fails is still what they believe identifies\n // them, and it is kept — but worth one chance to fix a typo here, where the\n // number is still on screen.\n if (refChoice && refValue.trim()) {\n const field = refConfig.fields.find((f) => f.key === refChoice);\n if (!refWarned && field && !validateReference(field, refValue)) {\n refError = `That does not look like a ${field.label.toLowerCase()}. Press Send again to use it anyway.`;\n // Once, and then it is their call. Our idea of the shape of somebody's\n // account number is not better than theirs, and refusing outright is\n // how a genuine reference gets lost — so the second press sends it and\n // the API keeps it, marked as not matching the expected format.\n refWarned = true;\n drawRefs();\n return true;\n }\n }\n return false;\n }\n\n ctx.holdForReference = holdForReference;\n ctx.answeredReference = answeredReference;\n}\n","// One announcement as a card: beside the launcher, or at the top of the panel.\n// The rules for it are in the launcher's sheet (widget.js), because the card\n// beside the button is drawn before the panel's code exists.\n//\n// Built from nodes rather than markup. Every word on it was typed into a\n// settings page and lands on somebody else's site, and textContent cannot be\n// talked into being HTML.\n\nimport * as ICONS from './icons.js';\n\nconst TONE_ICON = {\n info: ICONS.info, success: ICONS.check, warning: ICONS.warn, critical: ICONS.alert,\n};\n\n/**\n * `where` is 'launcher' or 'panel'. `onDismiss` is called with the\n * announcement when its × is pressed; a notice that may not be closed has none.\n */\nexport function noticeNode(a, { where, onDismiss }) {\n const card = document.createElement('div');\n card.className = `notice tone-${a.tone} at-${where}`;\n // A status, not an alert: it is news the visitor did not ask for, and a\n // screen reader should get to it when it is done with what they were doing.\n card.setAttribute('role', 'status');\n\n const ico = document.createElement('span');\n ico.className = 'notice-ico';\n ico.setAttribute('aria-hidden', 'true');\n ico.innerHTML = TONE_ICON[a.tone] || ICONS.info;\n\n const text = document.createElement('div');\n text.className = 'notice-text';\n const title = document.createElement('strong');\n title.textContent = a.title;\n text.append(title);\n if (a.body) {\n const body = document.createElement('p');\n body.textContent = a.body;\n text.append(body);\n }\n if (a.ctaHref && a.ctaLabel) {\n const link = document.createElement('a');\n link.className = 'notice-cta';\n link.href = a.ctaHref;\n link.textContent = a.ctaLabel;\n // A path is a page on this site and opens here, like any other link on\n // it. An address is somewhere else, and leaving the page for it would\n // take a half-written report with it.\n if (/^https?:/i.test(a.ctaHref)) {\n link.target = '_blank';\n link.rel = 'noopener noreferrer';\n }\n text.append(link);\n }\n\n card.append(ico, text);\n\n if (a.dismissible && onDismiss) {\n const close = document.createElement('button');\n close.type = 'button';\n close.className = 'notice-x';\n close.setAttribute('aria-label', 'Dismiss this notice');\n close.innerHTML = ICONS.close;\n close.addEventListener('click', () => onDismiss(a));\n card.append(close);\n }\n return card;\n}\n","// What the project's team is telling visitors, at the top of the form: the place\n// to say \"we know, it's being fixed\" before somebody types it out.\n\nimport { noticeNode } from '../notice.js';\n\n// A panel opened to report a bug, not to read news. A project with more live\n// than this has written a page, and the newest are the ones still true — the\n// API sends them newest first.\nconst MOST = 3;\n\nexport function mountNotices(ctx) {\n const { client, cfg, notices } = ctx;\n let list = [];\n\n function draw() {\n const shown = list.slice(0, MOST);\n notices.replaceChildren(...shown.map((a) => noticeNode(a, { where: 'panel', onDismiss })));\n notices.hidden = shown.length === 0;\n }\n\n function onDismiss(a) {\n client.dismissAnnouncement(a);\n list = list.filter((x) => x !== a);\n draw();\n // The button pressed has gone; the form is where they were headed.\n ctx.titleInput.focus();\n }\n\n if (cfg.announcements !== false && typeof client.announcements === 'function') {\n client.announcements()\n .then((rows) => { if (!ctx.closed) { list = rows; draw(); } })\n .catch(() => { /* a panel with no notices is still a panel that files reports */ });\n }\n}\n","// What is already known: the project's open issues that look like what the\n// reporter is describing, above the form, with \"me too\" instead of a second\n// report about the same thing.\n\nimport { escapeHtml, relativeDay } from './render.js';\n\nexport function mountKnownIssues(ctx) {\n const {\n client, cfg, titleInput, known,\n } = ctx;\n\n // The list as it was last fetched, and who has said what about it. Held here\n // rather than re-read from the DOM so a re-render after a join does not have\n // to parse its own markup back out.\n let issues = [];\n let issueError = '';\n let askingFor = null;\n let matchAbort = null;\n\n // Asked once when the dialog opens, like the call offer (call.js) and for the\n // same reason: it is one request per opened widget rather than one per page\n // load of every site running this SDK, and most projects will answer with an\n // empty list forever.\n function loadKnownIssues() {\n if (cfg.knownIssues === false) return;\n client.knownIssues()\n .then((rows) => { issues = rows; drawKnown(); })\n .catch(() => { /* a help panel that cannot load is simply not drawn */ });\n }\n\n // Re-run as the title is typed, so the panel narrows to what they are\n // actually describing. Debounced, and the previous request is aborted rather\n // than left to land: a fast typist otherwise renders whichever response comes\n // back last, which is not necessarily the one matching the box.\n let matchTimer = null;\n function matchKnown(query) {\n if (cfg.knownIssues === false) return;\n clearTimeout(matchTimer);\n matchAbort?.abort();\n const q = (query || '').trim();\n if (q.length < 3) { loadKnownIssues(); return; }\n matchTimer = setTimeout(() => {\n matchAbort = new AbortController();\n client.matchKnownIssues(q, { signal: matchAbort.signal })\n .then((rows) => {\n // An empty result means \"nothing like this is known\", which is not\n // the same as \"nothing is known\" — fall back to the full list rather\n // than blanking a panel the reporter was reading.\n if (rows.length) { issues = rows; drawKnown(); }\n })\n .catch(() => {});\n }, 250);\n }\n\n function drawKnown() {\n if (!issues.length) { known.hidden = true; known.innerHTML = ''; return; }\n known.hidden = false;\n known.innerHTML = `\n <p class=\"known-h\">Already reported</p>\n ${issues.map((issue) => `\n <div class=\"issue ${issue.joined ? 'is-joined' : ''}\">\n <span class=\"who\">\n <span class=\"t\">${escapeHtml(issue.title)}</span>\n <span class=\"meta\">${escapeHtml(knownMeta(issue))}</span>\n </span>\n <button class=\"join\" type=\"button\" data-join=\"${escapeHtml(issue.id)}\"\n ${issue.joined ? 'disabled' : ''}>${issue.joined ? 'Following' : 'Me too'}</button>\n </div>\n ${askingFor === issue.id ? `\n <div class=\"ask\">\n <input type=\"email\" class=\"join-email\" placeholder=\"you@example.com\"\n aria-label=\"Your email, so we can tell you when this is fixed\" />\n <button class=\"ghost\" type=\"button\" data-join-confirm=\"${escapeHtml(issue.id)}\">Follow</button>\n </div>` : ''}\n `).join('')}\n ${issueError ? `<p class=\"err\">${escapeHtml(issueError)}</p>` : ''}`;\n }\n\n // What a reporter is told about an issue beside its title. Deliberately not\n // its severity: how bad the team thinks this is, published to everybody who\n // opens the widget, is an internal judgement that reads as a promise.\n function knownMeta(issue) {\n const state = issue.status === 'IN_PROGRESS' ? 'Being worked on now' : 'Not fixed yet';\n // The count is dropped entirely at zero rather than written as \"0 people\",\n // or as a bare \"Reported\" that says nothing the heading above has not\n // already said. Nobody having pressed the button yet is not a fact about\n // this issue worth a reporter's attention.\n const others = issue.affectedCount === 0\n ? ''\n : ` · ${issue.affectedCount} ${issue.affectedCount === 1 ? 'person has' : 'people have'} said this is happening to them`;\n return `${state} · reported ${relativeDay(issue.since)}${others}`;\n }\n\n known.addEventListener('click', async (e) => {\n const start = e.target.closest('[data-join]');\n const confirm = e.target.closest('[data-join-confirm]');\n if (!start && !confirm) return;\n\n const bugId = (confirm || start).getAttribute(confirm ? 'data-join-confirm' : 'data-join');\n issueError = '';\n\n // An address is what joining buys them — being told when it is fixed — so\n // ask for one when neither a sign-in nor identify() has supplied it, rather\n // than recording somebody nobody can ever write to.\n const email = known.querySelector('.join-email')?.value?.trim()\n || client.signedInAs()?.email\n || client.config.user?.email;\n if (!email) {\n askingFor = bugId;\n drawKnown();\n known.querySelector('.join-email')?.focus();\n return;\n }\n\n try {\n const result = await client.meToo(bugId, { email });\n askingFor = null;\n issues = issues.map((issue) => (issue.id === bugId\n ? { ...issue, joined: true, affectedCount: result.affectedCount ?? issue.affectedCount }\n : issue));\n drawKnown();\n } catch (err) {\n issueError = err.message.replace('[BugCatch] ', '');\n drawKnown();\n }\n });\n\n loadKnownIssues();\n titleInput.addEventListener('input', () => matchKnown(titleInput.value));\n}\n","// Questions the assistant answers by itself, offered as one tap above the form.\n\nimport * as ICONS from './icons.js';\nimport { escapeHtml } from './render.js';\n\nexport function mountPicks(ctx) {\n const {\n client, cfg, titleInput, descInput, picks,\n } = ctx;\n\n // One tap instead of a sentence: the report's title becomes the question,\n // and the assistant knows exactly what is being asked — it looks the\n // reporter up, answers from their account, and asks whether that settled\n // it. Tapping the chosen one again lets it go.\n let agentIssues = [];\n let pickedIssue = null;\n\n function drawPicks() {\n if (!agentIssues.length) { picks.hidden = true; picks.innerHTML = ''; return; }\n picks.hidden = false;\n picks.innerHTML = `\n <p class=\"picks-h\">Get an answer right away</p>\n <div class=\"picks-grid\" role=\"group\" aria-label=\"Common questions\">\n ${agentIssues.map((issue) => `\n <button type=\"button\" class=\"pick\" data-pick=\"${escapeHtml(issue.key)}\"\n aria-pressed=\"${pickedIssue?.key === issue.key}\">\n <span class=\"t\">${escapeHtml(issue.title)}</span>\n ${issue.hint ? `<span class=\"h\">${escapeHtml(issue.hint)}</span>` : ''}\n </button>`).join('')}\n </div>\n ${pickedIssue ? `<p class=\"picks-note\">${ICONS.check}<span>Our assistant will check your account and reply here. Add anything else below, then send.</span></p>` : ''}`;\n }\n\n picks.addEventListener('click', (e) => {\n const btn = e.target.closest('[data-pick]');\n if (!btn) return;\n const issue = agentIssues.find((i) => i.key === btn.dataset.pick);\n if (!issue) return;\n const titleWasPick = pickedIssue && titleInput.value.trim() === pickedIssue.title;\n if (pickedIssue?.key === issue.key) {\n pickedIssue = null;\n if (titleWasPick) titleInput.value = '';\n } else {\n pickedIssue = issue;\n // Their own words are never overwritten — only an empty title, or the\n // one the previous tap put there.\n if (!titleInput.value.trim() || titleWasPick) titleInput.value = issue.title;\n }\n titleInput.dispatchEvent(new Event('input', { bubbles: true }));\n drawPicks();\n if (pickedIssue) descInput.focus();\n });\n\n if (cfg.agentIssues !== false && typeof client.agentIssues === 'function') {\n client.agentIssues()\n .then((rows) => { agentIssues = rows; drawPicks(); })\n .catch(() => { /* no tiles is the safe answer to every failure */ });\n }\n\n ctx.pickedIssue = () => pickedIssue;\n}\n","// Talking to the team instead of typing: the offer on the form, the screen\n// that replaces the form while the team is rung, and the bar that sits over a\n// report's thread during a call about it.\n//\n// Only what the reporter sees. Placing the call, the microphone and the\n// connection are the client's (startCall, startLiveCall in index.js), and their\n// code is fetched only when a call is actually placed.\n\nimport * as ICONS from './icons.js';\nimport { humanDuration } from '../capture.js';\nimport { escapeHtml } from './render.js';\n\nexport function mountCallOffer(ctx) {\n const {\n client, scrim, say, callBtn, close,\n } = ctx;\n\n // Asked once, when the dialog opens, and the button stays hidden unless the\n // answer is yes. Doing it here rather than at init keeps one request off the\n // page load of every site this SDK is on, for a button most of them will\n // never show.\n //\n // The wording is fixed because there is one kind of call: it rings a room and\n // may go unanswered. A button that said \"talk to us\" would have somebody\n // expecting an instant answer sit through twenty seconds of ringing.\n let callOffered = false;\n function offerCall() {\n client.callKind()\n .then((kind) => {\n if (!kind) return;\n callOffered = true;\n // Offered, so fetch what placing it needs now: the press should ring,\n // not wait for code.\n ctx.preloadCall();\n // Never over a panel that is still asking who this is: the call would\n // file a report, and on a project that requires sign-in that is the one\n // thing this panel is holding back.\n callBtn.hidden = ctx.mustSignIn();\n })\n .catch(() => { /* not available is the safe answer to every failure */ });\n }\n offerCall();\n\n callBtn.addEventListener('click', async () => {\n ctx.sending = true;\n say('');\n ctx.refreshSendState();\n callBtn.disabled = true;\n callBtn.innerHTML = '<span class=\"spinner\"></span>Ringing…';\n\n try {\n // No id: this files the report from what the page was doing and rings the\n // team about it, in one round trip.\n const handle = await client.startCall();\n showCall(handle);\n } catch (err) {\n ctx.sending = false;\n callBtn.disabled = false;\n callBtn.innerHTML = `${ICONS.phone}Talk to the team`;\n ctx.refreshSendState();\n // A report filed with no call is still a report. Saying \"the call\n // failed\" and nothing else would leave the reporter thinking their\n // context went nowhere, and pressing Send again would file it twice.\n if (err.filed) ctx.succeed(err.filed, { callFailed: err.reason || 'The call could not be started.' });\n else {\n say(err.message.replace('[BugCatch] ', ''));\n // The same follow-up the Send button does, because a call can fail for\n // the same reason a report can: this browser was signed out while the\n // panel sat open. Without it the message says sign in while the row\n // under it still names who signed in, which reads as a broken widget.\n ctx.recheckSignIn();\n }\n }\n });\n\n /**\n * The call, once the team is being rung.\n *\n * Replaces the panel for the same reason the history view does: it is the\n * same dialog answering a different question, and the reporter got here from\n * one launcher. There is no way back to the form — the report has already\n * been filed, and a Cancel that implied otherwise would be a lie.\n */\n function showCall(handle) {\n const panel = scrim.querySelector('.panel');\n const ref = handle?.bugId ? String(handle.bugId).slice(0, 8) : null;\n\n panel.innerHTML = `\n <div class=\"call\">\n <div class=\"call-head\">\n <span class=\"call-dot\" aria-hidden=\"true\"></span>\n <strong class=\"call-state\">Ringing…</strong>\n </div>\n <p class=\"call-note\">\n We're ringing the team now. Someone will pick up if they're free — your report${\n ref ? ` <code>${escapeHtml(ref)}</code>` : ''} is already filed either way, so nothing is lost if nobody answers.\n </p>\n <div class=\"foot\">\n <span class=\"spacer\"></span>\n <button class=\"primary\" type=\"button\" data-act=\"hangup\">Cancel</button>\n </div>\n </div>`;\n\n const stateEl = panel.querySelector('.call-state');\n const dot = panel.querySelector('.call-dot');\n\n // A call spends its first twenty seconds ringing, and can end by nobody\n // picking up — which is not a failure and must not read as one.\n const WORDING = {\n ringing: 'Ringing the team…',\n connecting: 'Someone answered — connecting…',\n connected: 'Connected',\n missed: 'Nobody was free',\n ended: 'Call ended',\n failed: 'The call dropped',\n };\n\n // `missed` is over too, and is the ordinary way a live call ends when the\n // team is busy. Left out of this list it would leave the dialog showing\n // \"Nobody was free\" above a Cancel button for a call that has already\n // stopped.\n const OVER = ['ended', 'failed', 'missed'];\n\n const offState = client.on('call', ({ state }) => {\n stateEl.textContent = WORDING[state] || state;\n dot.classList.toggle('off', OVER.includes(state));\n if (OVER.includes(state)) {\n panel.querySelector('[data-act=\"hangup\"]').textContent = 'Close';\n if (state === 'missed') {\n // Said plainly, and said as the non-event it is. The report is filed\n // and the thread already says the call went unanswered, so the one\n // thing left to tell them is that they have not lost anything.\n const note = panel.querySelector('.call-note');\n if (note) {\n note.textContent = 'Nobody was free to pick up just now. Your report is filed '\n + 'and the team will reply in the chat.';\n }\n }\n }\n });\n\n panel.querySelector('[data-act=\"hangup\"]').addEventListener('click', () => {\n offState();\n client.endCall();\n close();\n });\n }\n\n /**\n * The call, on top of the report it is about.\n *\n * A bar rather than a screen of its own, unlike the one the composer's button\n * opens — and for a reason that only applies here. A call has nothing to show\n * while it runs, because nobody is listening to it but the person on the\n * other end, so what is worth looking at is the report both people are\n * talking about.\n */\n function showLiveCall(body, thread, handle) {\n const bar = document.createElement('div');\n bar.className = 'livecall';\n bar.innerHTML = `\n <span class=\"call-dot\" aria-hidden=\"true\"></span>\n <span class=\"who\" role=\"status\" aria-live=\"polite\">Calling the team…</span>\n <span class=\"secs\" hidden></span>\n ${handle?.recorded ? '<span class=\"call-kept\">This call is recorded.</span>' : ''}\n <button class=\"act\" type=\"button\" data-act=\"mute\" hidden>Mute</button>\n <button class=\"act danger\" type=\"button\" data-act=\"hangup\">Cancel</button>`;\n body.insertBefore(bar, thread);\n\n const who = bar.querySelector('.who');\n const secs = bar.querySelector('.secs');\n const dot = bar.querySelector('.call-dot');\n const muteBtn = bar.querySelector('[data-act=\"mute\"]');\n const hangBtn = bar.querySelector('[data-act=\"hangup\"]');\n\n let ticker = null;\n const stopTicking = () => { if (ticker) { clearInterval(ticker); ticker = null; } };\n\n /** Left up rather than removed, so somebody who looked away can see how it\n * ended — and dismissed by hand, or by the next call replacing it. */\n const settle = (text) => {\n stopTicking();\n dot.classList.add('off');\n bar.classList.add('over');\n who.textContent = text;\n secs.hidden = true;\n muteBtn.hidden = true;\n hangBtn.textContent = 'Dismiss';\n hangBtn.classList.remove('danger');\n };\n\n const off = client.on('call', ({ state }) => {\n if (state === 'ringing') who.textContent = 'Calling the team…';\n\n if (state === 'connecting') {\n who.textContent = 'Someone answered — connecting…';\n }\n\n if (state === 'connected') {\n who.textContent = \"You're on a call with the team\";\n muteBtn.hidden = false;\n secs.hidden = false;\n const from = Date.now();\n stopTicking();\n // Started on connect rather than on answer: the seconds a person is\n // charged for looking at are the ones they could hear.\n ticker = setInterval(() => {\n secs.textContent = humanDuration(Math.floor((Date.now() - from) / 1000));\n }, 1000);\n secs.textContent = '0s';\n }\n\n if (state === 'missed') {\n settle('Nobody was free. Send a message instead and the team will pick it up.');\n }\n if (state === 'ended') settle('Call ended.');\n if (state === 'failed') settle('The call dropped.');\n });\n\n muteBtn.addEventListener('click', () => {\n const muted = handle.setMuted(!handle.isMuted());\n muteBtn.textContent = muted ? 'Unmute' : 'Mute';\n // Announced rather than only shown: somebody who muted by accident finds\n // out when the other person stops replying, which is a long way in.\n who.textContent = muted ? \"You're muted\" : \"You're on a call with the team\";\n });\n\n hangBtn.addEventListener('click', () => {\n off();\n stopTicking();\n client.endCall();\n bar.remove();\n // The note about the call lands in the thread a moment after it ends, and\n // the reporter is looking straight at where it goes.\n ctx.refreshThread(ctx.openReportId());\n });\n }\n\n ctx.offerCall = offerCall;\n ctx.callOffered = () => callOffered;\n ctx.showLiveCall = showLiveCall;\n}\n","// Who is reporting.\n//\n// Three states, and the project picks which: 'off' is every project that has\n// not asked for this and draws nothing at all; 'optional' offers a sign-in\n// above the form that the reporter may ignore; 'required' replaces the form\n// with it until they have.\n//\n// Asked on every open rather than remembered for the page, because the answer\n// includes whether this browser's session is still live — and it can end\n// somewhere else, when the same address signs in on another device. A widget\n// holding a cached \"signed in as you\" would find out at the moment the report\n// failed, which is the worst moment to discover it.\n\nimport { escapeHtml } from './render.js';\n\nexport function mountSignIn(ctx) {\n const {\n client, say, compose, sendBtn, historyBtn, callBtn, whoami,\n } = ctx;\n\n let login = { mode: client.lastLoginMode || 'off', signedIn: null };\n // 'idle' — showing who they are; 'email' / 'code' — the two steps of getting there.\n let step = 'idle';\n let pendingEmail = client.config.user?.email || '';\n let pendingCode = '';\n let signInBusy = false;\n let signInError = '';\n\n const mustSignIn = () => login.mode === 'required' && !login.signedIn;\n\n /** Show or hide everything that only makes sense once we know who is filing. */\n function applyGate() {\n const shut = mustSignIn();\n compose.hidden = shut;\n sendBtn.hidden = shut;\n if (historyBtn) historyBtn.hidden = shut;\n // The call offer is the same errand as the form, so it goes with it — and\n // comes back only if the API had already said this project can take one.\n if (shut) callBtn.hidden = true;\n else if (ctx.callOffered()) callBtn.hidden = false;\n }\n\n function drawWhoami() {\n if (login.mode === 'off') {\n whoami.hidden = true;\n whoami.innerHTML = '';\n return;\n }\n whoami.hidden = false;\n whoami.classList.toggle('asking', step !== 'idle');\n\n const problem = signInError ? `<div class=\"after err\">${escapeHtml(signInError)}</div>` : '';\n\n if (step === 'idle') {\n if (login.signedIn) {\n whoami.innerHTML = `<span class=\"who\">Signed in as <strong>${escapeHtml(login.signedIn.email)}</strong></span>`\n + '<button type=\"button\" class=\"link\" data-act=\"signout\">Sign out</button>';\n return;\n }\n // An address the host application passed for its own signed-in user is\n // not nothing — the report will carry it — so saying \"anonymously\" over\n // the top of one would be untrue. It is simply not proved, which is what\n // the offer beside it is for.\n const claimed = client.config.user?.email;\n whoami.innerHTML = `<span class=\"who\">${login.mode === 'required'\n ? 'Sign in to report a bug here.'\n : claimed\n ? `Reporting as <strong>${escapeHtml(claimed)}</strong>.`\n : 'Reporting anonymously — sign in and we can tell you what happens next.'}</span>`\n + '<button type=\"button\" class=\"link\" data-act=\"signin\">Sign in</button>';\n return;\n }\n\n if (step === 'email') {\n whoami.innerHTML = `\n <p class=\"why\">Sign in with any email address — we'll send you a code.\n It's how you hear back about this report${login.mode === 'required' ? ', and this project asks for it' : ''}.</p>\n <div class=\"row\">\n <input type=\"email\" data-fld=\"email\" value=\"${escapeHtml(pendingEmail)}\"\n placeholder=\"you@example.com\" autocomplete=\"email\" spellcheck=\"false\"\n aria-label=\"Your email address\" ${signInBusy ? 'disabled' : ''} />\n <button type=\"button\" class=\"primary\" data-act=\"code\" ${signInBusy ? 'disabled' : ''}>\n ${signInBusy ? 'Sending…' : 'Send code'}\n </button>\n </div>\n ${problem}\n ${login.mode === 'optional'\n ? '<div class=\"after\"><button type=\"button\" class=\"link\" data-act=\"skip\">Not now</button></div>'\n : ''}`;\n return;\n }\n\n whoami.innerHTML = `\n <p class=\"why\">We sent a code to <strong>${escapeHtml(pendingEmail)}</strong>. It expires in ten minutes.</p>\n <div class=\"row\">\n <input type=\"text\" class=\"code\" data-fld=\"code\" value=\"${escapeHtml(pendingCode)}\"\n placeholder=\"123456\" inputmode=\"numeric\" autocomplete=\"one-time-code\"\n maxlength=\"8\" aria-label=\"The code we emailed you\" ${signInBusy ? 'disabled' : ''} />\n <button type=\"button\" class=\"primary\" data-act=\"confirm\" ${signInBusy ? 'disabled' : ''}>\n ${signInBusy ? 'Checking…' : 'Sign in'}\n </button>\n </div>\n ${problem}\n <div class=\"after\">\n <button type=\"button\" class=\"link\" data-act=\"resend\" ${signInBusy ? 'disabled' : ''}>Send it again</button>\n <button type=\"button\" class=\"link\" data-act=\"signin-back\" ${signInBusy ? 'disabled' : ''}>Use another address</button>\n </div>`;\n }\n\n /** Redraw, and put the cursor where the reporter is being asked to type. */\n function drawSignIn({ focus = false } = {}) {\n drawWhoami();\n applyGate();\n if (!focus || signInBusy) return;\n const field = whoami.querySelector('[data-fld]');\n if (field) { field.focus(); field.setSelectionRange?.(field.value.length, field.value.length); }\n }\n\n // Typed values survive a redraw because they are held here, not read back off\n // a node that the next render replaces.\n whoami.addEventListener('input', (e) => {\n const field = e.target.closest('[data-fld]');\n if (!field) return;\n if (field.dataset.fld === 'email') pendingEmail = field.value;\n else pendingCode = field.value;\n });\n\n whoami.addEventListener('keydown', (e) => {\n if (e.key !== 'Enter' || !e.target.closest('[data-fld]')) return;\n e.preventDefault();\n if (step === 'email') requestCode();\n else confirmCode();\n });\n\n async function requestCode({ resent = false } = {}) {\n const email = pendingEmail.trim();\n if (!email) { signInError = 'Enter the address to send the code to.'; drawSignIn({ focus: true }); return; }\n\n signInBusy = true; signInError = ''; drawSignIn();\n try {\n await client.signIn(email);\n pendingEmail = email;\n if (!resent) pendingCode = '';\n step = 'code';\n } catch (err) {\n signInError = err.message;\n } finally {\n signInBusy = false;\n drawSignIn({ focus: true });\n }\n }\n\n async function confirmCode() {\n const code = pendingCode.trim();\n if (!code) { signInError = 'Enter the code we emailed you.'; drawSignIn({ focus: true }); return; }\n\n signInBusy = true; signInError = ''; drawSignIn();\n try {\n const who = await client.confirmSignIn(pendingEmail.trim(), code);\n login.signedIn = who;\n step = 'idle';\n pendingCode = '';\n say('');\n // Signing in is what makes a call possible on most projects — there is\n // now somebody to attribute it to — so the offer is asked for again\n // rather than staying hidden until the panel is reopened.\n ctx.offerCall();\n } catch (err) {\n signInError = err.message;\n } finally {\n signInBusy = false;\n drawSignIn({ focus: step !== 'idle' });\n }\n }\n\n whoami.addEventListener('click', async (e) => {\n const btn = e.target.closest('button[data-act]');\n if (!btn) return;\n const act = btn.dataset.act;\n\n if (act === 'signin') { step = 'email'; signInError = ''; drawSignIn({ focus: true }); return; }\n if (act === 'skip') { step = 'idle'; signInError = ''; drawSignIn(); return; }\n // Namespaced: the report screen has a back button of its own, and the two\n // must not be one selector away from each other.\n if (act === 'signin-back') { step = 'email'; signInError = ''; pendingCode = ''; drawSignIn({ focus: true }); return; }\n if (act === 'code') { requestCode(); return; }\n if (act === 'resend') { requestCode({ resent: true }); return; }\n if (act === 'confirm') { confirmCode(); return; }\n if (act === 'signout') {\n signInBusy = true; drawSignIn();\n try {\n await client.signOut();\n } finally {\n login.signedIn = null;\n signInBusy = false;\n // On a project that requires it, signing out puts the form away again\n // and asks — which is what applyGate does off the back of this.\n step = login.mode === 'required' ? 'email' : 'idle';\n drawSignIn({ focus: step !== 'idle' });\n }\n }\n });\n\n /** Re-read the project's setting and this browser's session, and redraw. */\n async function refreshLogin({ announce = false } = {}) {\n const state = await client.loginState();\n login = { mode: state.mode, signedIn: state.signedIn };\n client.lastLoginMode = state.mode;\n // Nothing to type on a project that requires sign-in and has nobody in it.\n if (mustSignIn() && step === 'idle') step = 'email';\n if (login.signedIn) step = 'idle';\n if (announce && state.signedOutReason) say(state.signedOutReason, 'info');\n drawSignIn();\n }\n\n refreshLogin({ announce: true }).catch(() => { /* a widget that can't ask stays open */ });\n\n /**\n * Put the panel back in step after a send or a call was refused: the reason\n * may be that this browser was signed out while the panel sat open.\n */\n function recheckSignIn() {\n if (login.mode !== 'off') refreshLogin().catch(() => {});\n }\n\n ctx.mustSignIn = mustSignIn;\n ctx.recheckSignIn = recheckSignIn;\n}\n","/**\n * The predefined issues: questions the project's assistant can answer from the\n * reporter's own account and close, offered in the widget as one-tap choices.\n *\n * The API decides the list (GET /ingest/agent-issues) — it is empty unless the\n * project has the Sphoro Agent answering questions and a data source that can\n * answer them. Every failure is an empty list: the tiles are a shortcut, and a\n * widget that could not load them is still a widget that files reports.\n *\n * The agent ends its answer with SOLVED_QUESTION's wording; the widget draws\n * Yes / No under it (widget.js messageHtml). Keep this pattern in step with\n * agent-issues.ts SOLVED_QUESTION in the API and workers.\n */\nexport const SOLVED_PROMPT = /Did this answer your question\\?/i;\n\nexport async function fetchAgentIssues({ apiBase, apiKey, projectId }) {\n try {\n const res = await fetch(\n `${apiBase}/ingest/agent-issues?projectId=${encodeURIComponent(projectId)}`,\n { headers: { 'X-BugCatch-Key': apiKey } },\n );\n if (!res.ok) return [];\n const body = await res.json();\n return (Array.isArray(body?.issues) ? body.issues : [])\n .filter((i) => i && typeof i.key === 'string' && typeof i.title === 'string')\n .map((i) => ({ key: i.key, title: i.title, hint: typeof i.hint === 'string' ? i.hint : '' }));\n } catch {\n return [];\n }\n}\n","// The thread's markup: one entry per event or message, oldest first, grouped\n// into turns the way a chat groups them. Strings in, strings out — the thread\n// screen (thread.js) decides when to draw them and what to keep on screen.\n\nimport * as ICONS from './icons.js';\nimport { SOLVED_PROMPT } from '../agent-issues.js';\nimport { humanSize } from '../capture.js';\nimport {\n escapeHtml, dayKey, dayLabel, clockTime, relativeDay, initials,\n} from './render.js';\n\n/**\n * The whole thread, oldest first.\n *\n * The API hands back one ordered list holding both kinds of entry, which is\n * what keeps a reply sitting under the status change that prompted it. Entries\n * carrying `from` are messages; the rest are things that happened.\n */\nexport function threadHtml(timeline) {\n const entries = timeline || [];\n if (!entries.length) return '';\n let day = null;\n let prev = null;\n return entries.map((e) => {\n // A rule with the date on it whenever the calendar day turns, the way a\n // chat marks that the next message came later.\n const d = dayKey(e.at);\n const sep = d && d !== day ? `<div class=\"day-sep\" role=\"separator\">${escapeHtml(dayLabel(e.at))}</div>` : '';\n if (sep) { day = d; prev = null; }\n if (!e.from) { prev = null; return sep + systemHtml(e); }\n const message = {\n from: e.from,\n author: e.reply?.author,\n automated: e.reply?.automated,\n body: e.reply?.body,\n at: e.at,\n attachments: e.reply?.attachments,\n };\n // Consecutive messages from the same voice within a few minutes are one\n // turn: one avatar, one name, the bubbles stacked under it.\n const who = speakerOf(message);\n const cont = prev && prev.who === who\n && Math.abs(new Date(e.at).getTime() - new Date(prev.at).getTime()) < 5 * 60_000;\n prev = { who, at: e.at };\n return sep + messageHtml(message, cont ? 'cont' : '');\n }).join('');\n}\n\n/** Which voice a message is in, for grouping: the reporter, the assistant, or a named person. */\nfunction speakerOf(message) {\n if (message.from === 'reporter') return 'me';\n if (message.automated) return 'assistant';\n return `team:${message.author || ''}`;\n}\n\n/**\n * Something that happened, rather than something someone said.\n *\n * `summary` is written by the API, which knows what changed and phrases it for\n * the person who reported it — so the wording here matches the toast and the\n * portal exactly rather than being a third attempt at the same sentence.\n */\nfunction systemHtml(entry) {\n const when = clockTime(entry.at);\n return `\n <div class=\"sys k-${escapeHtml(entry.kind)}\">\n <span class=\"dot\" aria-hidden=\"true\"></span>${escapeHtml(entry.summary || '')}${when ? `\n <span class=\"t\">· ${escapeHtml(when)}</span>` : ''}\n </div>`;\n}\n\n/**\n * One bubble. `state` marks a message that is still in flight, or failed, or\n * one that continues the turn above it.\n *\n * A reply that was sent automatically says so, above the words rather than\n * after them. The person reading it is deciding whether their problem has been\n * understood, and that decision is different when nobody has read the report —\n * so it is not a footnote, and it is not left to be inferred from the tone.\n */\nexport function messageHtml(message, state = '') {\n const mine = message.from === 'reporter';\n const body = message.body || '';\n const attachments = message.attachments || [];\n const automated = !mine && Boolean(message.automated);\n\n // The text goes in its own element rather than as a bare child of .bubble:\n // it is the only part that wants `white-space: pre-wrap`, and with that rule\n // on the bubble the template's own newlines and indentation render as a\n // blank line above and below every message.\n const text = body ? `<span class=\"msg-text\">${escapeHtml(body)}</span>` : '';\n const files = attachments.length\n ? `<div class=\"att\">${attachments.map(attachmentHtml).join('')}</div>`\n : '';\n\n // Named, or labelled, never both and never neither: an automated message has\n // no author to name, and naming one would be the lie the label exists to\n // prevent. The avatar says the same thing at a glance.\n let who = '';\n let avatar = '';\n if (automated) {\n who = '<span class=\"msg-who auto\">Assistant <span class=\"tag\">AI</span></span>';\n avatar = `<span class=\"av bot\" aria-hidden=\"true\">${ICONS.sparkle}</span>`;\n } else if (!mine) {\n const name = message.author || 'Support team';\n who = `<span class=\"msg-who\">${escapeHtml(name)}</span>`;\n avatar = `<span class=\"av\" aria-hidden=\"true\">${escapeHtml(initials(name))}</span>`;\n }\n if (state.includes('cont')) who = '';\n\n // The assistant's one answer ends by inviting a correction (check-rules\n // confirmMessage). One tap sends it, and what comes back goes to the person\n // who now owns the report rather than to another automated round — so the\n // wording is \"that's right\" and \"that's not it\", not a yes/no question.\n // The Sphoro Agent's answer ends by asking whether it settled things\n // (SOLVED_PROMPT). Yes closes the report; No puts it in front of a person.\n const confirm = automated && /it goes to the same person/i.test(body)\n ? `<div class=\"quick\" role=\"group\" aria-label=\"Answer\">\n <button type=\"button\" data-quick=\"Yes, this is the problem\">${ICONS.check}That's right</button>\n <button type=\"button\" data-quick=\"No, that is not the problem\">That's not it</button>\n </div>`\n : automated && SOLVED_PROMPT.test(body)\n ? `<div class=\"quick\" role=\"group\" aria-label=\"Did this answer your question?\">\n <button type=\"button\" data-quick=\"Yes, that answers it\">${ICONS.check}Yes, that answers it</button>\n <button type=\"button\" data-quick=\"No, I still need help\">No, I still need help</button>\n </div>`\n : '';\n\n return `\n <div class=\"msg-row ${mine ? 'mine' : 'theirs'} ${automated ? 'auto' : ''} ${state}\">\n ${avatar}\n ${who}\n <div class=\"bubble ${body ? '' : 'silent'}\">${text}${files}</div>\n ${confirm}\n <span class=\"msg-when\">${escapeHtml(clockTime(message.at) || relativeDay(message.at))}</span>\n </div>`;\n}\n\n/** The assistant, thinking: where its next message will appear. */\nexport function typingHtml() {\n return `\n <div class=\"typing-row\">\n <span class=\"av bot\" aria-hidden=\"true\">${ICONS.sparkle}</span>\n <span class=\"msg-who auto\">Assistant <span class=\"tag\">AI</span></span>\n <div class=\"bubble typing\" role=\"img\" aria-label=\"The assistant is working on it\"><i></i><i></i><i></i></div>\n </div>`;\n}\n\n/**\n * One attachment, rendered as what it is.\n *\n * An image people can see, a voice note they can play and a document they can\n * download are three different things, and collapsing them all to a link is\n * how a chat stops feeling like one. A pending upload has no URL yet, so it\n * falls through to the file row with nothing to click.\n */\nfunction attachmentHtml(a) {\n const name = escapeHtml(a.name || 'attachment');\n if (!a.url) return `<span class=\"file\"><span class=\"nm\">${name}</span></span>`;\n const url = escapeHtml(a.url);\n\n if (a.type === 'SCREENSHOT') {\n return `<a href=\"${url}\" target=\"_blank\" rel=\"noopener noreferrer\"><img src=\"${url}\" alt=\"${name}\" loading=\"lazy\"></a>`;\n }\n if (a.type === 'AUDIO') {\n return `<audio controls preload=\"metadata\" src=\"${url}\"></audio>`;\n }\n if (a.type === 'VIDEO') {\n return `<video controls preload=\"metadata\" src=\"${url}\"></video>`;\n }\n return `\n <a class=\"file\" href=\"${url}\" target=\"_blank\" rel=\"noopener noreferrer\" download>\n <span class=\"nm\">${name}</span>\n ${a.size ? `<span class=\"sz\">${escapeHtml(humanSize(a.size))}</span>` : ''}\n </a>`;\n}\n","// The reply box under a report's thread.\n\nimport * as ICONS from './icons.js';\nimport {\n startVoiceNote, voiceSupported, humanSize, humanDuration, UPLOAD_MIMES,\n} from '../capture.js';\nimport { messageHtml } from './messages.js';\nimport { escapeHtml, scrollToLatest } from './render.js';\n\n/**\n * The box at the bottom: text, files, and a voice note.\n *\n * Files are uploaded as they are picked, not on send, so the wait happens\n * while the person is still writing rather than after they have pressed the\n * button — and so a failed upload is visible as a failed chip instead of a\n * message that never went.\n */\nexport function mountComposer(ctx, body, thread, bugId) {\n const { client, cfg } = ctx;\n const composer = document.createElement('div');\n composer.className = 'composer';\n composer.innerHTML = `\n <div class=\"staged\" hidden></div>\n <div class=\"row\">\n <div class=\"tools\">\n ${cfg.attachments === false ? '' : `\n <button class=\"tool\" type=\"button\" data-act=\"attach\" title=\"Attach a file\" aria-label=\"Attach a file\">${ICONS.paperclip}</button>\n <button class=\"tool\" type=\"button\" data-act=\"voice\" title=\"Record a voice note\" aria-label=\"Record a voice note\">${ICONS.mic}</button>`}\n <button class=\"tool\" type=\"button\" data-act=\"livecall\" title=\"Call the team\" aria-label=\"Call the team\" hidden>${ICONS.phone}</button>\n </div>\n <textarea rows=\"1\" placeholder=\"Write a reply…\" aria-label=\"Write a reply\"></textarea>\n <button class=\"send\" type=\"button\" data-act=\"send\" title=\"Send\" aria-label=\"Send\" disabled>${ICONS.send}</button>\n </div>\n <div class=\"note\" hidden></div>\n <input type=\"file\" hidden multiple accept=\"${UPLOAD_MIMES.join(',')}\">`;\n body.appendChild(composer);\n\n const staged = composer.querySelector('.staged');\n const input = composer.querySelector('textarea');\n const sendBtn = composer.querySelector('[data-act=\"send\"]');\n const voiceBtn = composer.querySelector('[data-act=\"voice\"]');\n const liveBtn = composer.querySelector('[data-act=\"livecall\"]');\n const picker = composer.querySelector('input[type=file]');\n const note = composer.querySelector('.note');\n\n // Uploaded files waiting to ride on the next message.\n const files = [];\n let recorder = null;\n let sending = false;\n\n function say(text, kind = '') {\n note.hidden = !text;\n note.className = `note ${kind}`;\n note.textContent = text || '';\n }\n\n function refresh() {\n const ready = files.filter((f) => f.storageKey);\n sendBtn.disabled = sending || (!input.value.trim() && !ready.length);\n staged.hidden = !files.length;\n staged.innerHTML = files.map((f, i) => `\n <span class=\"chip ${f.storageKey ? '' : 'uploading'}\">\n <span class=\"nm\">${escapeHtml(f.name)}</span>\n <span class=\"sz\">${f.storageKey ? humanSize(f.size) : 'sending…'}</span>\n <button type=\"button\" data-drop=\"${i}\" aria-label=\"Remove ${escapeHtml(f.name)}\">${ICONS.close}</button>\n </span>`).join('');\n for (const btn of staged.querySelectorAll('[data-drop]')) {\n btn.addEventListener('click', () => {\n files.splice(Number(btn.dataset.drop), 1);\n refresh();\n });\n }\n }\n\n /** Upload one blob and stage it. Its chip is on screen for the whole trip. */\n async function stage(blob, mime, name) {\n const entry = { name, size: blob.size, storageKey: null };\n files.push(entry);\n refresh();\n try {\n const { storageKey, mime: stored } = await client.uploadAttachment(blob, mime);\n entry.storageKey = storageKey;\n entry.mime = stored;\n } catch (err) {\n files.splice(files.indexOf(entry), 1);\n say(err.message || 'That file could not be sent.', 'err');\n }\n refresh();\n }\n\n input.addEventListener('input', () => {\n // Grow with the text, up to the CSS cap. A reply about a bug is often\n // three lines, and a one-line box makes people write one line.\n input.style.height = 'auto';\n input.style.height = `${Math.min(input.scrollHeight, 120)}px`;\n refresh();\n });\n\n // Enter sends, Shift+Enter breaks the line — the chat convention. Anyone\n // who wants a paragraph gets it; nobody has to hunt for the button.\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' && !e.shiftKey && !e.isComposing) {\n e.preventDefault();\n send();\n }\n });\n\n // Absent when the host set `attachments: false` — which the report form\n // has always honoured and this composer did not, so a host keeping files\n // and microphone audio outside its compliance perimeter still had both\n // offered in the follow-up thread.\n composer.querySelector('[data-act=\"attach\"]')?.addEventListener('click', () => picker.click());\n picker.addEventListener('change', () => {\n say('');\n for (const file of picker.files) {\n if (file.size > client.config.maxAttachmentBytes) {\n say(`${file.name} is larger than ${humanSize(client.config.maxAttachmentBytes)}.`, 'err');\n continue;\n }\n stage(file, file.type, file.name);\n }\n picker.value = '';\n });\n\n if (voiceBtn && !voiceSupported()) {\n voiceBtn.disabled = true;\n voiceBtn.title = 'This browser cannot record audio';\n }\n\n voiceBtn?.addEventListener('click', async () => {\n if (recorder) {\n const handle = recorder;\n recorder = null;\n voiceBtn.classList.remove('recording');\n voiceBtn.innerHTML = ICONS.mic;\n say('');\n const { blob, mime } = await handle.stop();\n if (blob.size) stage(blob, mime, 'Voice note');\n return;\n }\n try {\n say('');\n recorder = await startVoiceNote({\n maxMs: client.config.maxRecordingMs,\n onTick: (s) => say(`Recording — ${humanDuration(s)}. Tap the mic to stop.`),\n onAutoStop: () => voiceBtn.click(),\n });\n voiceBtn.classList.add('recording');\n voiceBtn.innerHTML = ICONS.stopSquare;\n } catch {\n recorder = null;\n say('The microphone is not available — check the browser permission.', 'err');\n }\n });\n\n /* ---------- calling the team ---------- */\n\n // Asked once, when a report is opened, and the button stays hidden unless\n // the answer is yes. Most workspaces have live calling off, and a phone\n // icon that apologises on press is worse than no phone icon.\n client.canCallTeam(bugId)\n .then((available) => {\n if (!available) return;\n liveBtn.hidden = false;\n // Offered, so fetch what placing it needs now: the press should ring,\n // not wait for code.\n ctx.preloadCall();\n })\n .catch(() => { /* not available is the safe answer to every failure */ });\n\n liveBtn.addEventListener('click', async () => {\n liveBtn.disabled = true;\n say('');\n try {\n const handle = await client.startLiveCall(bugId);\n ctx.showLiveCall(body, thread, handle);\n } catch (err) {\n say(err.message.replace('[BugCatch] ', ''), 'err');\n } finally {\n liveBtn.disabled = false;\n }\n });\n\n sendBtn.addEventListener('click', send);\n\n // The confirm question's quick answers: the same as typing and sending.\n thread.addEventListener('click', (e) => {\n const quick = e.target.closest?.('[data-quick]');\n if (!quick || sending) return;\n input.value = quick.dataset.quick;\n send();\n });\n\n async function send() {\n const text = input.value.trim();\n const ready = files.filter((f) => f.storageKey);\n if (sending || (!text && !ready.length)) return;\n\n sending = true;\n refresh();\n say('');\n\n // Shown immediately, greyed, and replaced by the refresh below. Waiting\n // for the round trip before anything appears makes a slow network look\n // like a dropped message, which is when people send it twice.\n thread.insertAdjacentHTML('beforeend', messageHtml({\n from: 'reporter',\n body: text,\n at: new Date().toISOString(),\n attachments: ready.map((f) => ({ type: 'FILE', name: f.name, size: f.size })),\n }, 'pending'));\n scrollToLatest(thread, { smooth: true });\n\n try {\n await client.replyToReport(bugId, {\n body: text,\n attachments: ready.map((f) => ({\n storageKey: f.storageKey, mime: f.mime, name: f.name, size: f.size,\n })),\n });\n input.value = '';\n input.style.height = 'auto';\n files.length = 0;\n await ctx.refreshThread(bugId);\n } catch (err) {\n thread.querySelector('.msg-row.pending')?.classList.replace('pending', 'failed');\n say(err.message || 'That could not be sent — try again.', 'err');\n } finally {\n sending = false;\n refresh();\n }\n }\n\n refresh();\n}\n","// What happens to a report once it is filed: the confirmation, the\n// reporter's list of everything they have filed, and one report as a\n// conversation — with the assistant's live card over it, the \"does the fix\n// work\" question under it, and the reply box at the bottom (composer.js).\n\nimport * as ICONS from './icons.js';\nimport { threadHtml, typingHtml } from './messages.js';\nimport {\n headHtml, emptyHtml, skeletonRows, skeletonBubbles, escapeHtml, statusWord, relativeDay,\n atLatest, scrollToLatest,\n} from './render.js';\nimport { mountComposer } from './composer.js';\n\n// What each open thread was last drawn from, so a poll that brings nothing new\n// leaves the DOM (and the reader's place in it) alone.\nconst threadDrawn = new WeakMap();\n\nexport function mountThread(ctx) {\n const {\n client, scrim, close,\n } = ctx;\n\n // The report currently open, if any. A live update for a different report\n // must not redraw the one being read.\n let openReportId = null;\n\n /**\n * The confirmation, and the one answer somebody actually wants from it.\n *\n * \"How long will this take\" is the first thing anybody thinks after reporting\n * a bug, and until now the answer was nothing at all — so the honest reading\n * of a confirmation screen was \"into the void\". The API computes the figure\n * on the same request that files the report, precisely so it can be said\n * here: an estimate that needed a second call would arrive after this panel\n * had closed itself.\n *\n * Absent whenever the project has decided not to tell reporters, or has\n * nothing to base a figure on. Nothing is drawn in that case — no \"unknown\",\n * no placeholder — because a confirmation that admits it cannot answer is\n * worse than one that does not raise the question.\n *\n * The dwell is longer when there is something to read. Two seconds is right\n * for one line and is not enough for two, and a panel that closes over the\n * sentence somebody started reading is the reason they open it again.\n */\n function succeed(res, { callFailed = null } = {}) {\n // The assistant: straight into the report's conversation, where the\n // investigation shows itself working (\"Analyzing the issue…\") and asks\n // what it needs. The plain \"Thanks\" below stays for when that view cannot\n // load — a report filed without a way to follow it.\n if (res?.id && !callFailed) {\n showReport(res.id, { fresh: true }).then((ok) => { if (!ok) doneScreen(res, { callFailed }); });\n return;\n }\n doneScreen(res, { callFailed });\n }\n\n function doneScreen(res, { callFailed = null } = {}) {\n const ref = res?.id ? String(res.id).slice(0, 8) : null;\n const eta = res?.estimate?.label ? String(res.estimate.label) : null;\n // A press of Call that filed the report and then could not ring anybody.\n // Shown as a plain \"Thanks\" it read as a call that had quietly worked, and\n // the reporter sat waiting for a phone that was never going to ring.\n const failed = callFailed\n ? `<p class=\"eta\">${escapeHtml(callFailed)} Your report is filed, and the team will reply in the chat.</p>`\n : '';\n scrim.querySelector('.panel').innerHTML = `\n <div class=\"done\">\n <div class=\"tick\">${ICONS.check}</div>\n <h3>Thanks — that's on its way.</h3>\n <p>${ref ? `Filed as <code>${escapeHtml(ref)}</code>.` : 'The team has what they need to reproduce it.'}</p>\n ${failed}\n ${eta ? `<p class=\"eta\">Reports like this usually take <strong>${escapeHtml(eta)}</strong>. We'll let you know when it moves.</p>` : ''}\n </div>`;\n setTimeout(close, failed ? 6000 : eta ? 4000 : 2000);\n }\n\n /**\n * The reporter's own history: what they have filed and where each one got to.\n *\n * Replaces the panel rather than opening a second surface — it is the same\n * dialog answering a different question, and the reporter came here from one\n * launcher.\n */\n async function showHistory() {\n const panel = scrim.querySelector('.panel');\n clearTimeout(assistantTimer);\n openReportId = null;\n panel.classList.remove('is-chat');\n panel.innerHTML = `\n ${headHtml({ title: 'Your reports', sub: \"Everything you've reported, and where each one got to.\" })}\n <div class=\"history\" aria-busy=\"true\">${skeletonRows(3)}</div>`;\n\n panel.querySelector('[data-act=\"close\"]').addEventListener('click', close);\n\n let reports = [];\n try {\n ({ reports } = await client.myReports());\n } catch {\n const failed = panel.querySelector('.history');\n if (failed) failed.innerHTML = emptyHtml('Couldn\\'t load your reports just now.', ICONS.alert);\n return;\n }\n\n // The panel can be closed while the request is in flight.\n const list = panel.querySelector('.history');\n if (!list) return;\n\n list.removeAttribute('aria-busy');\n list.innerHTML = reports.length\n ? reports.map((r, i) => `\n <button class=\"row\" type=\"button\" data-report=\"${escapeHtml(r.id)}\" style=\"animation-delay:${Math.min(i, 8) * 30}ms\">\n <span class=\"row-ico\" aria-hidden=\"true\">${ICONS.chat}</span>\n <span class=\"who\">\n <span class=\"ttl\">${escapeHtml(r.title)}</span>\n <span class=\"sub\">${escapeHtml(r.reference)} · ${escapeHtml(relativeDay(r.createdAt))}</span>\n </span>\n <span class=\"pill ${escapeHtml(String(r.status).toLowerCase())}\">${escapeHtml(statusWord(r.status))}</span>\n <span class=\"chev\" aria-hidden=\"true\">${ICONS.chevronRight}</span>\n </button>`).join('')\n : emptyHtml('You haven\\'t reported anything yet.', ICONS.inbox);\n\n for (const row of list.querySelectorAll('[data-report]')) {\n row.addEventListener('click', () => showReport(row.dataset.report));\n }\n }\n\n /**\n * One report as a conversation.\n *\n * This screen used to be a timeline: everything that had happened, read-only,\n * newest first. It answered \"what happened to my report\" and nothing else —\n * so when the team's reply was a question (\"which browser?\"), the person best\n * placed to answer it was looking straight at it with no way to.\n *\n * Now it is a thread. Status changes still appear, as the quiet centred lines\n * that mark events in any chat; the messages between the two sides are\n * bubbles either side of them; and there is a box at the bottom. Oldest at\n * the top, because that is the direction a conversation is read.\n */\n async function showReport(bugId, { fresh = false } = {}) {\n const panel = scrim.querySelector('.panel');\n clearTimeout(assistantTimer);\n panel.classList.add('is-chat');\n panel.innerHTML = `\n ${headHtml({\n back: true,\n title: fresh ? 'Looking into it' : 'Your report',\n sub: fresh ? 'Our assistant is on it — it may ask you a question here.' : 'Everything that has happened, and a way to answer.',\n })}\n <div class=\"history chatting\" aria-busy=\"true\"><div class=\"thread\">${skeletonBubbles()}</div></div>`;\n\n panel.querySelector('[data-act=\"close\"]').addEventListener('click', close);\n panel.querySelector('[data-act=\"back\"]').addEventListener('click', showHistory);\n\n let report = null;\n try {\n report = await client.myReport(bugId);\n } catch {\n report = null;\n }\n\n const body = panel.querySelector('.history');\n // The panel can be closed, or navigated away from, while this is in flight.\n if (!body || !panel.querySelector('[data-act=\"back\"]')) return false;\n\n if (!report) {\n if (fresh) { panel.classList.remove('is-chat'); return false; }\n body.removeAttribute('aria-busy');\n body.innerHTML = emptyHtml('Couldn\\'t load that report just now.', ICONS.alert);\n return false;\n }\n\n openReportId = bugId;\n // Reading the report is reading its updates. Leaving a \"your report was\n // fixed\" card up over the screen where they are reading that it was fixed\n // would be the notification arguing with itself.\n client.markReportRead(bugId);\n const drawn = threadHtml(report.timeline);\n body.classList.add('chatting');\n body.removeAttribute('aria-busy');\n body.innerHTML = `\n <div class=\"report-head\">\n <span class=\"ttl\">${escapeHtml(report.title)}</span>\n <span class=\"sub\">${escapeHtml(report.reference)} · ${escapeHtml(report.project || '')}</span>\n <span class=\"pill ${escapeHtml(String(report.status).toLowerCase())}\">${escapeHtml(statusWord(report.status))}</span>\n ${report.estimate?.label\n // The same figure they were given when they filed it, so coming back\n // to check is not a worse experience than not checking. Absent on a\n // report that has already been settled — the API stops sending it —\n // because a prediction beside a finished report is either redundant\n // or an argument.\n ? `<span class=\"eta-line\">Usually takes ${escapeHtml(String(report.estimate.label))}</span>`\n : ''}\n </div>\n <div class=\"thread\" role=\"log\" aria-live=\"polite\" aria-label=\"Conversation\">${drawn}</div>`;\n\n const thread = body.querySelector('.thread');\n threadDrawn.set(thread, drawn);\n scrollToLatest(thread);\n mountVerification(body, report);\n if (report.canReply) mountComposer(ctx, body, thread, bugId);\n mountAssistant(body, report, bugId);\n // Again, now the cards above it have taken their share of the height: the\n // latest message — and any quick answers under it — is what they came for.\n scrollToLatest(thread);\n return true;\n }\n\n /**\n * The assistant's live card: where the investigation is, and a gentle\n * refresh while it works. Analyzing polls every few seconds until the\n * outcome lands — a question, a found cause, or a hand-off — so the\n * reporter watches it happen rather than wondering whether anything is.\n */\n let assistantTimer = null;\n function mountAssistant(body, report, bugId) {\n clearTimeout(assistantTimer);\n const a = report.assistant;\n const thread = body.querySelector('.thread');\n let card = body.querySelector('.assistant');\n if (!a) {\n card?.remove();\n thread?.querySelector('.typing-row')?.remove();\n return;\n }\n\n // Updated in place rather than rebuilt: the card is polled every few\n // seconds while the assistant works, and a card that re-entered on every\n // poll would flicker — and re-announce itself to a screen reader — for\n // nothing. It only animates when the phase actually moves.\n const previous = card?.dataset.phase || null;\n if (!card) {\n card = document.createElement('div');\n card.setAttribute('role', 'status');\n card.setAttribute('aria-live', 'polite');\n body.querySelector('.report-head')?.after(card);\n }\n card.className = `assistant ${escapeHtml(a.phase)}`;\n card.dataset.phase = a.phase;\n\n const steps = a.steps || [];\n const done = steps.filter((st) => st.state === 'done').length;\n const active = steps.some((st) => st.state === 'active') ? 0.5 : 0;\n const pct = steps.length ? Math.round(((done + active) / steps.length) * 100) : 0;\n const content = `\n <div class=\"as-body${previous && previous !== a.phase ? ' entering' : ''}\">\n <div class=\"as-top\">\n <span class=\"as-orb\" aria-hidden=\"true\">${a.phase === 'analyzing' ? '<span class=\"as-spin\"></span>'\n : a.phase === 'calling' ? ICONS.phone\n : a.phase === 'resolved' ? ICONS.check : ICONS.sparkle}</span>\n <div class=\"as-text\">\n <div class=\"as-title\">${escapeHtml(a.title)}</div>\n <div class=\"as-detail\">${escapeHtml(a.detail)}</div>\n </div>\n </div>\n </div>\n ${steps.length ? `\n <div class=\"as-progress\" role=\"progressbar\" aria-label=\"Progress\" aria-valuemin=\"0\" aria-valuemax=\"100\" aria-valuenow=\"${pct}\"><i style=\"width:${pct}%\"></i></div>\n <ol class=\"as-steps\">${steps.map((st) => `<li class=\"${escapeHtml(st.state)}\"${st.state === 'active' ? ' aria-current=\"step\"' : ''}><span class=\"dot\"></span><span class=\"lbl\">${escapeHtml(st.label)}</span></li>`).join('')}</ol>` : ''}`;\n const key = JSON.stringify([a.phase, a.title, a.detail, steps]);\n if (card.dataset.key !== key) {\n const bar = card.querySelector('.as-progress i');\n const from = bar?.style.width;\n card.innerHTML = content;\n card.dataset.key = key;\n // Let the bar travel from where it was to where it is now.\n const next = card.querySelector('.as-progress i');\n if (next && from) {\n next.style.width = from;\n requestAnimationFrame(() => { next.style.width = `${pct}%`; });\n }\n }\n\n // While it works, a \"thinking\" bubble sits where its next message will land.\n // Never during a call: somebody is on the phone about this report, nothing\n // is being analysed (the analysis waits for the transcript), and a bubble\n // typing away underneath a live call is the assistant talking over them.\n if (thread) {\n const pinned = atLatest(thread);\n const typing = thread.querySelector('.typing-row');\n if (a.phase !== 'analyzing') typing?.remove();\n else if (!typing || typing !== thread.lastElementChild) {\n typing?.remove();\n thread.insertAdjacentHTML('beforeend', typingHtml());\n if (pinned) scrollToLatest(thread);\n }\n }\n\n if (a.phase === 'analyzing') {\n assistantTimer = setTimeout(() => refreshThread(bugId), 3000);\n } else if (a.phase === 'calling') {\n // Slower: the call's own card is what the reporter is watching, and this\n // only has to notice when the call ends and the analysis begins.\n assistantTimer = setTimeout(() => refreshThread(bugId), 10000);\n } else if (a.phase === 'needs_info' && previous !== 'needs_info') {\n body.querySelector('.composer textarea')?.focus();\n }\n }\n\n function mountVerification(body, report) {\n body.querySelector('.fix-verification')?.remove();\n if (!report.resolvedAt || !['RESOLVED', 'CLOSED'].includes(report.status)) return;\n const box = document.createElement('div'); box.className = 'fix-verification';\n if (report.verification) { box.textContent = report.verification === 'CONFIRMED' ? 'You confirmed that this fix works.' : 'Your feedback was recorded.'; }\n else {\n box.innerHTML = '<p>Does the fix work for you?</p><textarea maxlength=\"4000\" aria-label=\"Fix feedback\" placeholder=\"What happens when you try again? (optional)\"></textarea><button type=\"button\" data-result=\"CONFIRMED\">It works now</button> <button type=\"button\" data-result=\"STILL_HAPPENING\">Still happening</button><p class=\"feedback-status\" role=\"status\"></p>';\n box.querySelectorAll('[data-result]').forEach((btn) => { btn.onclick = async () => {\n box.querySelectorAll('button').forEach((b) => { b.disabled = true; });\n try { await client.verifyFix(report.id, { result: btn.dataset.result, resolvedAt: report.resolvedAt, note: box.querySelector('textarea').value }); await showReport(report.id); }\n catch (err) { box.querySelector('.feedback-status').textContent = err.message; box.querySelectorAll('button').forEach((b) => { b.disabled = false; }); }\n }; });\n }\n body.querySelector('.report-head')?.after(box);\n }\n\n /**\n * Redraw the open thread in place, keeping whatever is half-typed.\n *\n * Rebuilding the whole screen on every incoming message would be simpler and\n * would also throw away a draft, a staged file and the scroll position — for\n * someone mid-sentence, a reply arriving would look like the widget crashed.\n */\n async function refreshThread(bugId) {\n const body = scrim.querySelector('.history.chatting');\n const thread = body?.querySelector('.thread');\n if (!thread || openReportId !== bugId) return;\n\n let report = null;\n try {\n report = await client.myReport(bugId);\n } catch { /* a failed refresh leaves what is on screen, which is not wrong */ }\n // Closed while it was asking: nothing to draw, and no next poll to set.\n if (ctx.closed || !report || openReportId !== bugId) return;\n\n mountVerification(body, report);\n const pinned = atLatest(thread);\n const html = threadHtml(report.timeline);\n // Only rewritten when something changed, so a poll that brings nothing new\n // does not replay every bubble's entrance or re-announce the log.\n if (threadDrawn.get(thread) !== html) {\n thread.innerHTML = html;\n threadDrawn.set(thread, html);\n }\n mountAssistant(body, report, bugId);\n // Only follow the conversation down if they were already at the bottom.\n // Yanking someone back from something they scrolled up to read is worse\n // than making them scroll.\n if (pinned) scrollToLatest(thread);\n\n const pill = body.querySelector('.report-head .pill');\n if (pill) {\n pill.className = `pill ${String(report.status).toLowerCase()}`;\n pill.textContent = statusWord(report.status);\n }\n }\n\n // Re-read the open thread when something happens to it. Without this the\n // reader is looking at a conversation that stopped being true while they read\n // it — the very screen where a stale view is most obvious, and now the screen\n // where the other side may be typing.\n const stopListening = client.on('update', (event) => {\n if (!openReportId || event?.bugId !== openReportId) return;\n // Already on screen, so already read — and marking it here, before the\n // client draws its notifications, is what keeps a card from appearing over\n // the thread that is about to show the same thing.\n client.markReportRead(openReportId);\n refreshThread(openReportId);\n });\n // Stopped with the panel: a frame arriving after it has gone would redraw a\n // panel that is no longer on the page. The assistant's poll too — it reads\n // the thread out of the detached panel, finds it still there, and would go\n // on asking every three seconds for as long as the analysis ran.\n ctx.onDestroy(() => {\n stopListening();\n clearTimeout(assistantTimer);\n });\n\n scrim.querySelector('[data-act=\"history\"]')?.addEventListener('click', showHistory);\n\n ctx.succeed = succeed;\n ctx.refreshThread = refreshThread;\n ctx.openReportId = () => openReportId;\n}\n","// The report panel: the dialog the launcher opens, and everything on it.\n//\n// None of this is in the script every page loads. Most visitors never press\n// the button, and the panel is most of the widget's code and nearly all of its\n// CSS, so it is fetched the first time it is wanted — see widget.js for when,\n// and lazy.js for how.\n//\n// One dialog doing several jobs, one file each:\n//\n// compose.js the report form: drafts, severity, attachments, recording, Send\n// refs.js \"which record is this about\", asked at Send\n// notices.js what the project's team is telling visitors, at the top\n// known.js what the project already knows is broken, and \"me too\"\n// picks.js questions the assistant answers by itself, as one tap\n// signin.js who is reporting, proved with a one-time code\n// call.js the call offer, the ringing screen, and the bar over a thread\n// thread.js the reporter's reports, one of them as a conversation\n// composer.js the reply box under that conversation\n//\n// with markup they share in render.js and messages.js.\n//\n// They were one closure, and what they share is still one closure's worth of\n// state: `ctx`, built below. It is passed rather than kept in module variables\n// because two panels must never share it — a widget torn down and mounted again\n// by a second init(), or a page running BugCatch.Client twice, would otherwise\n// find the last panel's draft and the last panel's report in the new one.\n\nimport * as ICONS from './icons.js';\nimport { BRAND } from './icons.js';\nimport { PANEL_CSS } from './styles.js';\nimport { headHtml, escapeHtml } from './render.js';\nimport { composeHtml, mountCompose } from './compose.js';\nimport { mountReferences } from './refs.js';\nimport { mountNotices } from './notices.js';\nimport { mountKnownIssues } from './known.js';\nimport { mountPicks } from './picks.js';\nimport { mountCallOffer } from './call.js';\nimport { mountSignIn } from './signin.js';\nimport { mountThread } from './thread.js';\n\n/**\n * Open the panel over the page. `preloadCall` fetches the call code ahead of a\n * call being placed; the launcher hands it over because the panel has no way\n * to load code of its own.\n */\nexport function openPanel({\n root, host, dock, client, onClose, preloadCall = () => {},\n}) {\n addStyles(root);\n const cfg = client.config;\n\n const scrim = document.createElement('div');\n // The panel opens out of whichever corner the dock is in, clearing it by the\n // dock's measured height rather than a guess at the launcher's size.\n scrim.className = `scrim ${dock.classList.contains('at-left') ? 'at-left' : 'at-right'} `\n + `${dock.classList.contains('at-top') ? 'at-top' : 'at-bottom'}`;\n scrim.style.setProperty('--bc-dock-h', `${Math.round(dock.getBoundingClientRect().height)}px`);\n scrim.innerHTML = `\n <div class=\"panel\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"bc-h\">\n ${headHtml({\n id: 'bc-h',\n title: 'Tell us what went wrong',\n sub: \"Our assistant starts looking right away. We attach the page details for you.\",\n })}\n <div class=\"body\">\n <div class=\"slot\" role=\"alert\"></div>\n <!-- What the project's team is telling visitors. Drawn only when it\n has published something. -->\n <div class=\"notices\" hidden></div>\n <div class=\"whoami\" hidden></div>\n <!-- The questions the assistant can answer from their account and\n close by itself. Drawn only when the project offers any. -->\n <div class=\"picks\" hidden></div>\n <div class=\"known\" hidden></div>\n ${composeHtml(cfg)}\n </div>\n <div class=\"foot\">\n <span class=\"note\" aria-live=\"polite\"></span>\n ${client.hasReports() ? '<button class=\"quiet\" type=\"button\" data-act=\"history\">Your reports</button>' : ''}\n <span class=\"spacer\"></span>\n <!-- Hidden until the API confirms this project can take a call, which\n most cannot. Drawn here rather than as a second launcher because it\n is the same errand as Send report — the difference is only whether\n the reporter would rather say it than type it. -->\n <button class=\"ghost\" type=\"button\" data-act=\"call\" hidden>${ICONS.phone}Talk to the team</button>\n <button class=\"primary\" type=\"button\" data-act=\"send\">Send report ${ICONS.send}</button>\n </div>\n <div class=\"powered\">Powered by\n <a class=\"brand-credit\" href=\"https://bugcatch.sphoro.com\" target=\"_blank\" rel=\"noopener noreferrer\" aria-label=\"BugCatch\">${BRAND}</a>\n </div>\n </div>`;\n\n const $ = (sel) => scrim.querySelector(sel);\n const slot = $('.slot');\n\n // Run as the panel goes, in the order the parts added them: the draft saved,\n // a recording stopped, timers and listeners dropped.\n const cleanups = [];\n\n /**\n * What the parts share.\n *\n * The nodes are looked up once and held, not looked up again: the history\n * and report screens replace the panel's markup, and a draft saved on the way\n * out must still read what was typed. The functions left null are filled in\n * by the part that owns them (named beside each), and nothing calls one until\n * every part has mounted — they are reached from events and replies, and from\n * the end of this function.\n */\n const ctx = {\n client,\n cfg,\n root,\n host,\n dock,\n scrim,\n $,\n titleInput: $('#bc-title'),\n descInput: $('#bc-desc'),\n compose: $('.compose'),\n sendBtn: $('[data-act=\"send\"]'),\n callBtn: $('[data-act=\"call\"]'),\n historyBtn: $('[data-act=\"history\"]'),\n note: $('.foot .note'),\n whoami: $('.whoami'),\n refsBox: $('.refs'),\n notices: $('.notices'),\n known: $('.known'),\n picks: $('.picks'),\n // A report or a call is on its way. Escape and a click outside wait for it.\n sending: false,\n closed: false,\n say,\n close,\n preloadCall,\n onDestroy: (fn) => { cleanups.push(fn); },\n // compose.js\n refreshSendState: null,\n isRecording: null,\n stopRecording: null,\n // refs.js\n holdForReference: null,\n answeredReference: null,\n // picks.js\n pickedIssue: null,\n // call.js\n offerCall: null,\n callOffered: null,\n showLiveCall: null,\n // signin.js\n mustSignIn: null,\n recheckSignIn: null,\n // thread.js\n succeed: null,\n refreshThread: null,\n openReportId: null,\n };\n\n /* ---------- the parts, in the order they ask the API anything ---------- */\n\n mountCompose(ctx);\n mountReferences(ctx);\n mountNotices(ctx);\n mountKnownIssues(ctx);\n mountPicks(ctx);\n mountCallOffer(ctx);\n mountSignIn(ctx);\n mountThread(ctx);\n\n /* ---------- lifecycle ---------- */\n\n const previouslyFocused = document.activeElement;\n const rootStyle = document.documentElement.style;\n const previousOverflow = rootStyle.overflow;\n rootStyle.overflow = 'hidden';\n\n function destroy() {\n if (ctx.closed) return;\n cleanups.forEach((fn) => fn());\n ctx.closed = true;\n rootStyle.overflow = previousOverflow;\n document.removeEventListener('keydown', onKeydown, true);\n scrim.remove();\n dock.hidden = false;\n host.style.opacity = '';\n onClose?.();\n }\n\n function close() {\n if (ctx.closed) return;\n // Let the exit animation play, but never wait on an event that a\n // reduced-motion user's near-zero animation might deliver unpredictably.\n scrim.classList.add('closing');\n setTimeout(destroy, 160);\n try { previouslyFocused?.focus?.(); } catch { /* element may be gone */ }\n }\n\n function onKeydown(e) {\n if (e.key === 'Escape') {\n e.stopPropagation();\n if (ctx.isRecording()) { ctx.stopRecording({ keep: false }); return; }\n if (!ctx.sending) close();\n return;\n }\n if (e.key !== 'Tab' || ctx.isRecording()) return;\n // Focus trap: the panel is modal, so Tab must not reach the host page.\n const focusable = [...scrim.querySelectorAll(\n 'button:not(:disabled), input:not([hidden]), textarea, select, [href]',\n )].filter((el) => el.offsetParent !== null || el === document.activeElement);\n if (!focusable.length) return;\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = scrim.getRootNode().activeElement;\n if (e.shiftKey && active === first) { e.preventDefault(); last.focus(); }\n else if (!e.shiftKey && active === last) { e.preventDefault(); first.focus(); }\n }\n\n document.addEventListener('keydown', onKeydown, true);\n scrim.addEventListener('click', (e) => { if (e.target === scrim && !ctx.sending) close(); });\n $('[data-act=\"close\"]').addEventListener('click', close);\n\n /** The banner at the top of the form: an error, or with `info`, a notice. */\n function say(text, kind = 'err') {\n slot.innerHTML = text\n ? `<div class=\"msg ${kind}\">${kind === 'err' ? ICONS.alert : ''}<span>${escapeHtml(text)}</span></div>`\n : '';\n }\n\n root.appendChild(scrim);\n ctx.refreshSendState();\n ctx.titleInput.focus();\n\n return { destroy, close };\n}\n\n/**\n * Add the panel's stylesheet to the widget's shadow root, once.\n *\n * Straight after the launcher's sheet rather than at the end of the root, so\n * the two stay together at the top and the panel's rules keep their place after\n * the launcher's in the cascade — the order they had as one stylesheet.\n */\nfunction addStyles(root) {\n if (root.querySelector('style[data-panel]')) return;\n const style = document.createElement('style');\n style.setAttribute('data-panel', '');\n style.textContent = PANEL_CSS;\n const launcherSheet = root.querySelector('style');\n if (launcherSheet) launcherSheet.after(style);\n else root.prepend(style);\n}\n","// What the two loaders (lazy.js, umd/lazy.js) share: how a part of the\n// script-tag build that arrived on its own is found, and what a part that\n// never arrived is called.\n//\n// The ES build does not use the registry: a bundler splits it at its import()\n// calls and wires the pieces back together itself. A <script> tag has no such\n// machinery, so each part of that build is a plain script that, when it runs,\n// leaves its exports under a name on `window` — and umd/lazy.js, having added\n// the script, reads them from there once it has loaded.\n//\n// The name carries the version, so a part left in a cache by an older release\n// is never mistaken for this one's: it registers under its own version, this\n// release finds nothing under its, and says so rather than running code that\n// expects a different client.\n\n// Replaced at build time from package.json; see vite.config.js.\nconst VERSION = typeof __SDK_VERSION__ === 'string' ? __SDK_VERSION__ : '0.0.0-dev';\n\nconst REGISTRY = '__bugcatchChunks';\n\nconst key = (name) => `${name}@${VERSION}`;\n\n/** Called by a part as its script runs: here is what I export. */\nexport function registerChunk(name, exports) {\n const registry = window[REGISTRY] || (window[REGISTRY] = {});\n registry[key(name)] = exports;\n}\n\n/** What a part registered, once its script has run; undefined until then. */\nexport function registeredChunk(name) {\n return typeof window !== 'undefined' ? window[REGISTRY]?.[key(name)] : undefined;\n}\n\n/**\n * A part that could not be loaded, said twice: the details for whoever is\n * debugging the page, in the console, and a sentence for the person using it,\n * as the error — which is what the panel shows when a call cannot start.\n */\nexport function chunkUnavailable(detail, cause) {\n // eslint-disable-next-line no-console\n console.warn(`[BugCatch] ${detail}`, ...(cause ? [cause] : []));\n const err = new Error('[BugCatch] part of the widget could not be loaded — check the connection and try again.');\n if (cause) err.cause = cause;\n return err;\n}\n\n/** One fetch per part at a time; a failure is forgotten, so the next ask retries. */\nexport function once(load) {\n let pending = null;\n return (...args) => {\n pending ||= load(...args).catch((err) => { pending = null; throw err; });\n return pending;\n };\n}\n\nexport { VERSION as CHUNK_VERSION };\n","// The report panel as a script of its own, for the script-tag build: run, it\n// hands its exports to the loader that added it (lazy.js beside this file).\nimport * as panel from '../panel/index.js';\nimport { registerChunk } from '../chunks.js';\n\nregisterChunk('panel', panel);\n"],"names":["chat","alert","close","check","info","warn","camera","video","paperclip","mic","send","stopSquare","back","phone","retry","sparkle","chevronRight","inbox","BRAND","PANEL_CSS","headHtml","title","sub","id","ICONS.back","ICONS.sparkle","escapeHtml","ICONS.close","emptyHtml","text","icon","skeletonRows","n","skeletonBubbles","initials","name","parts","dayKey","iso","d","dayLabel","today","yesterday","relativeDay","clockTime","atLatest","thread","scrollToLatest","smooth","calm","statusWord","status","then","days","months","s","c","UPLOAD_MIMES","REPORT_MIMES","voiceSupported","_a","captureSupported","screenshotSupported","normalizeMime","mime","allowed","base","captureScreenshot","stream","track","resolve","width","height","canvas","blob","res","startRecording","maxMs","onTick","onAutoStop","t","recorder","chunks","e","startedAt","timer","cleanup","capTimer","finished","endEarly","startVoiceNote","humanSize","bytes","humanDuration","seconds","m","MAX_AGE","database","open","reject","request","err","draftKey","config","email","_b","draftOperation","key","value","db","tx","store","readDraft","PRIORITIES","DEFAULT_PRIORITY","composeHtml","cfg","canCapture","canShoot","canRecord","p","ICONS.camera","ICONS.video","ICONS.paperclip","mountCompose","ctx","client","root","host","dock","scrim","$","say","titleInput","descInput","compose","sendBtn","note","thumbs","filePicker","items","priority","draftStatus","savedDraftKey","draftLoaded","draftSubmitted","draftWrites","draftTimer","persistDraft","kind","scheduleDraft","onPageHide","reconnectUploads","it","upload","draft","b","item","addItem","btn","titleErr","flagTitle","ICONS.alert","refreshSendState","busy","max","count","drawThumb","el","label","preview","veil","ICONS.retry","removeItem","i","storageKey","fill","normalized","withWidgetHidden","fn","r","shot","_c","room","file","_d","bar","stopRecording","keep","rec","clip","original","MAX_SCAN_CHARS","MAX_VALUE_CHARS","MAX_MATCHES","compile","source","tidy","normalizeValue","field","how","digits","valueOf","match","raw","labelWeight","hit","LOOSE_ID","WORDISH","readReferences","fields","texts","scannable","f","hits","loose","blocks","block","order","re","guard","valueStart","h","survivors","o","byValue","entry","found","conflicts","settledKey","kinds","a","list","seen","taken","before","after","validateReference","toEnd","atEdge","mountReferences","refsBox","refConfig","refOpen","refChoice","refValue","refError","refCandidates","refMode","refSettled","refWarned","showFound","readText","textAnswers","read","answeredReference","foundNote","foundTimer","shortId","ICONS.check","drawRefs","queueFound","candidateFor","v","takeCandidate","k","optional","current","sharedKind","head","many","chip","openRefs","first","needsReference","holdForReference","TONE_ICON","ICONS.info","ICONS.warn","noticeNode","where","onDismiss","card","ico","body","link","MOST","mountNotices","notices","draw","shown","x","rows","mountKnownIssues","known","issues","issueError","askingFor","matchAbort","loadKnownIssues","drawKnown","matchTimer","matchKnown","query","q","issue","knownMeta","state","others","start","confirm","bugId","_e","result","mountPicks","picks","agentIssues","pickedIssue","drawPicks","titleWasPick","mountCallOffer","callBtn","callOffered","offerCall","handle","showCall","ICONS.phone","panel","ref","stateEl","dot","WORDING","OVER","offState","showLiveCall","who","secs","muteBtn","hangBtn","ticker","stopTicking","settle","off","from","muted","mountSignIn","historyBtn","whoami","login","step","pendingEmail","pendingCode","signInBusy","signInError","mustSignIn","applyGate","shut","drawWhoami","problem","claimed","drawSignIn","focus","requestCode","confirmCode","resent","code","act","refreshLogin","announce","recheckSignIn","SOLVED_PROMPT","threadHtml","timeline","entries","day","prev","sep","systemHtml","message","speakerOf","cont","messageHtml","when","mine","attachments","automated","files","attachmentHtml","avatar","typingHtml","url","mountComposer","composer","ICONS.mic","ICONS.send","staged","input","voiceBtn","liveBtn","picker","sending","refresh","ready","stage","stored","ICONS.stopSquare","available","quick","threadDrawn","mountThread","openReportId","succeed","callFailed","showReport","ok","doneScreen","eta","failed","showHistory","assistantTimer","reports","ICONS.chat","ICONS.chevronRight","ICONS.inbox","row","fresh","report","drawn","mountVerification","mountAssistant","previous","steps","done","st","active","pct","content","next","pinned","typing","refreshThread","box","html","pill","stopListening","event","openPanel","onClose","preloadCall","addStyles","sel","slot","cleanups","previouslyFocused","rootStyle","previousOverflow","destroy","onKeydown","focusable","last","style","launcherSheet","VERSION","REGISTRY","registerChunk","exports","registry"],"mappings":"yBAQO,MAAMA,GAAO,0TAKPC,EAAQ,sMACRC,EAAQ,4IACRC,EAAQ,sKACRC,GAAO,oMACPC,GAAO,wPCZPC,GAAS,sXACTC,GAAQ,yQACRC,GAAY,sPACZC,GAAM,gPACNC,GAAO,+KACPC,GAAa,uGACbC,GAAO,sKACPC,GAAQ,mRAGRC,GAAQ,sLAERC,EAAU,qTACVC,GAAe,8JACfC,GAAQ,yTAcRC,GAAQ,2tOCvBRC,GAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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"}
|