@mediansh/widget 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/README.md +6 -0
  2. package/dist/c-JXDHGUIF.js +9 -0
  3. package/dist/chunk-34CC4UFU.js +12 -0
  4. package/dist/chunk-34CC4UFU.js.map +1 -0
  5. package/dist/chunk-5BN2UMLM.js +12 -0
  6. package/dist/chunk-5BN2UMLM.js.map +1 -0
  7. package/dist/{chunk-HLALDSPM.js → chunk-6HESK4TV.js} +381 -62
  8. package/dist/chunk-6HESK4TV.js.map +1 -0
  9. package/dist/chunk-7BJPJ4OJ.js +21 -0
  10. package/dist/chunk-7BJPJ4OJ.js.map +1 -0
  11. package/dist/{chunk-R3S6Y434.js → chunk-BJXQMEXJ.js} +3359 -1449
  12. package/dist/chunk-BJXQMEXJ.js.map +1 -0
  13. package/dist/chunk-BSHZLMYB.js +12 -0
  14. package/dist/chunk-BSHZLMYB.js.map +1 -0
  15. package/dist/chunk-BTORE6PD.js +20 -0
  16. package/dist/chunk-BTORE6PD.js.map +1 -0
  17. package/dist/chunk-DUNILJ52.js +12 -0
  18. package/dist/chunk-DUNILJ52.js.map +1 -0
  19. package/dist/chunk-G5MMT3KQ.js +39 -0
  20. package/dist/chunk-G5MMT3KQ.js.map +1 -0
  21. package/dist/chunk-GMUADSA2.js +12 -0
  22. package/dist/chunk-GMUADSA2.js.map +1 -0
  23. package/dist/chunk-GVLMMFTR.js +12 -0
  24. package/dist/chunk-GVLMMFTR.js.map +1 -0
  25. package/dist/chunk-HBW32VOK.js +28 -0
  26. package/dist/chunk-HBW32VOK.js.map +1 -0
  27. package/dist/chunk-I7GCRIDC.js +12 -0
  28. package/dist/chunk-I7GCRIDC.js.map +1 -0
  29. package/dist/chunk-J3ET7D3V.js +12 -0
  30. package/dist/chunk-J3ET7D3V.js.map +1 -0
  31. package/dist/chunk-NVTQO56D.js +12 -0
  32. package/dist/chunk-NVTQO56D.js.map +1 -0
  33. package/dist/chunk-SNYO3HWZ.js +12 -0
  34. package/dist/chunk-SNYO3HWZ.js.map +1 -0
  35. package/dist/chunk-WPFEMDHJ.js +12 -0
  36. package/dist/chunk-WPFEMDHJ.js.map +1 -0
  37. package/dist/chunk-XXT4O5AT.js +16 -0
  38. package/dist/chunk-XXT4O5AT.js.map +1 -0
  39. package/dist/chunk-ZG63ZWXG.js +1379 -0
  40. package/dist/chunk-ZG63ZWXG.js.map +1 -0
  41. package/dist/core-X5EL4ACB.js +6179 -0
  42. package/dist/core-X5EL4ACB.js.map +1 -0
  43. package/dist/cpp-UFM3S5JK.js +10 -0
  44. package/dist/cpp-UFM3S5JK.js.map +1 -0
  45. package/dist/csharp-SS3YMXWT.js +12 -0
  46. package/dist/csharp-SS3YMXWT.js.map +1 -0
  47. package/dist/css-HP3HPRAF.js +9 -0
  48. package/dist/css-HP3HPRAF.js.map +1 -0
  49. package/dist/dart-DFSNOCJU.js +12 -0
  50. package/dist/dart-DFSNOCJU.js.map +1 -0
  51. package/dist/diff-J4T5ECLQ.js +12 -0
  52. package/dist/diff-J4T5ECLQ.js.map +1 -0
  53. package/dist/docker-MMKXE2DQ.js +12 -0
  54. package/dist/docker-MMKXE2DQ.js.map +1 -0
  55. package/dist/dotenv-2YCL424V.js +12 -0
  56. package/dist/dotenv-2YCL424V.js.map +1 -0
  57. package/dist/elixir-WSTF2ZPA.js +18 -0
  58. package/dist/elixir-WSTF2ZPA.js.map +1 -0
  59. package/dist/engine-javascript-4K6F4KL5.js +3242 -0
  60. package/dist/engine-javascript-4K6F4KL5.js.map +1 -0
  61. package/dist/github-dark-XHWJ4A4W.js +9 -0
  62. package/dist/github-dark-XHWJ4A4W.js.map +1 -0
  63. package/dist/github-light-2RDLQDFL.js +9 -0
  64. package/dist/github-light-2RDLQDFL.js.map +1 -0
  65. package/dist/go-D7KDTP2F.js +12 -0
  66. package/dist/go-D7KDTP2F.js.map +1 -0
  67. package/dist/graphql-WHHYDJCW.js +13 -0
  68. package/dist/graphql-WHHYDJCW.js.map +1 -0
  69. package/dist/{highlighted-body-OFNGDK62-T25BCWKJ.js → highlighted-body-OFNGDK62-MDI6EUKH.js} +4 -3
  70. package/dist/highlighted-body-OFNGDK62-MDI6EUKH.js.map +1 -0
  71. package/dist/html-YMRWH5LQ.js +11 -0
  72. package/dist/html-YMRWH5LQ.js.map +1 -0
  73. package/dist/index.d.ts +9 -7
  74. package/dist/index.js +2588 -2058
  75. package/dist/index.js.map +1 -1
  76. package/dist/ini-HC2LCVSW.js +12 -0
  77. package/dist/ini-HC2LCVSW.js.map +1 -0
  78. package/dist/java-DRYXJ2ZD.js +9 -0
  79. package/dist/java-DRYXJ2ZD.js.map +1 -0
  80. package/dist/javascript-JG6W5B7H.js +9 -0
  81. package/dist/javascript-JG6W5B7H.js.map +1 -0
  82. package/dist/json-DTKAB2JP.js +9 -0
  83. package/dist/json-DTKAB2JP.js.map +1 -0
  84. package/dist/jsonc-5XQSX3HW.js +12 -0
  85. package/dist/jsonc-5XQSX3HW.js.map +1 -0
  86. package/dist/jsx-BQ6TEX75.js +9 -0
  87. package/dist/jsx-BQ6TEX75.js.map +1 -0
  88. package/dist/kotlin-VQIGNFHN.js +12 -0
  89. package/dist/kotlin-VQIGNFHN.js.map +1 -0
  90. package/dist/mermaid-GHXKKRXX-FX4OMDLK.js +12 -0
  91. package/dist/mermaid-GHXKKRXX-FX4OMDLK.js.map +1 -0
  92. package/dist/message-markdown-GNTNT3WJ.js +368 -0
  93. package/dist/message-markdown-GNTNT3WJ.js.map +1 -0
  94. package/dist/php-2C36DJ7I.js +37 -0
  95. package/dist/php-2C36DJ7I.js.map +1 -0
  96. package/dist/powershell-D7BEPXW6.js +12 -0
  97. package/dist/powershell-D7BEPXW6.js.map +1 -0
  98. package/dist/prisma-EU35L45H.js +12 -0
  99. package/dist/prisma-EU35L45H.js.map +1 -0
  100. package/dist/python-OBX5XH3P.js +12 -0
  101. package/dist/python-OBX5XH3P.js.map +1 -0
  102. package/dist/ruby-HWPYCEXB.js +73 -0
  103. package/dist/ruby-HWPYCEXB.js.map +1 -0
  104. package/dist/rust-KFI5KHCG.js +12 -0
  105. package/dist/rust-KFI5KHCG.js.map +1 -0
  106. package/dist/scss-NLXXJO5U.js +16 -0
  107. package/dist/scss-NLXXJO5U.js.map +1 -0
  108. package/dist/shellscript-MCPUYTZ6.js +9 -0
  109. package/dist/shellscript-MCPUYTZ6.js.map +1 -0
  110. package/dist/sql-KWTK6KCU.js +9 -0
  111. package/dist/sql-KWTK6KCU.js.map +1 -0
  112. package/dist/swift-TETGEM4C.js +12 -0
  113. package/dist/swift-TETGEM4C.js.map +1 -0
  114. package/dist/toml-XCKXGRSV.js +12 -0
  115. package/dist/toml-XCKXGRSV.js.map +1 -0
  116. package/dist/tsx-7WRVGXFH.js +9 -0
  117. package/dist/tsx-7WRVGXFH.js.map +1 -0
  118. package/dist/typescript-AEC2XLIU.js +9 -0
  119. package/dist/typescript-AEC2XLIU.js.map +1 -0
  120. package/dist/widget.css +1 -1
  121. package/dist/xml-BKVIJK6L.js +10 -0
  122. package/dist/xml-BKVIJK6L.js.map +1 -0
  123. package/dist/yaml-3AYDY36X.js +9 -0
  124. package/dist/yaml-3AYDY36X.js.map +1 -0
  125. package/package.json +1 -1
  126. package/dist/chunk-3ZXE5RBN.js +0 -15
  127. package/dist/chunk-3ZXE5RBN.js.map +0 -1
  128. package/dist/chunk-HLALDSPM.js.map +0 -1
  129. package/dist/chunk-R3S6Y434.js.map +0 -1
  130. package/dist/highlighted-body-OFNGDK62-T25BCWKJ.js.map +0 -1
  131. package/dist/mermaid-GHXKKRXX-U6EYEPWB.js +0 -11
  132. package/dist/message-markdown-KQVNAD42.js +0 -121
  133. package/dist/message-markdown-KQVNAD42.js.map +0 -1
  134. /package/dist/{mermaid-GHXKKRXX-U6EYEPWB.js.map → c-JXDHGUIF.js.map} +0 -0
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../apps/web/src/components/support-widget/sdk/median-support.tsx","../../../apps/web/src/components/support-widget/markdown-chunk.tsx","../../../apps/web/src/components/support-widget/widget-launcher.tsx","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/lib/SSRBase.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ArrowDown.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/ArrowDown.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ArrowRight.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/ArrowRight.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ArrowUp.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/ArrowUp.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ArrowsClockwise.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/ArrowsClockwise.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ArrowsIn.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/ArrowsIn.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ArrowsOut.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/ArrowsOut.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Bug.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/Bug.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/CaretDown.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/CaretDown.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/CaretRight.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/CaretRight.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ChatCircleDots.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/ChatCircleDots.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Check.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/Check.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/EnvelopeSimple.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/EnvelopeSimple.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/File.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/File.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ImageSquare.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/ImageSquare.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Lightbulb.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/Lightbulb.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/LockSimple.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/LockSimple.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Play.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/Play.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Plus.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/Plus.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Sparkle.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/Sparkle.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Star.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/Star.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/UserCircle.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/UserCircle.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/WarningCircle.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/WarningCircle.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/X.es.js","../../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/defs/X.es.js","../../../apps/web/src/components/icons.tsx","../../../apps/web/src/components/support-widget/launcher-position.ts","../../../apps/web/src/components/support-widget/support-widget.tsx","../../../apps/web/src/components/ui/collapse.tsx","../../../apps/web/src/components/ui/message-scroller.tsx","../../../node_modules/.bun/@shadcn+react@0.3.0+03aa47d54245a271/node_modules/@shadcn/react/dist/chunk-HBS6WEDP.js","../../../node_modules/.bun/@shadcn+react@0.3.0+03aa47d54245a271/node_modules/@shadcn/react/dist/message-scroller/index.js","../../../node_modules/.bun/class-variance-authority@0.7.1/node_modules/class-variance-authority/dist/index.mjs","../../../node_modules/.bun/@radix-ui+react-slot@1.3.3+0f58469d5b3bd39f/node_modules/@radix-ui/react-slot/src/slot.tsx","../../../node_modules/.bun/@radix-ui+react-compose-refs@1.1.5+0f58469d5b3bd39f/node_modules/@radix-ui/react-compose-refs/src/compose-refs.tsx","../../../apps/web/src/components/ui/button.tsx","../../../apps/web/src/lib/message-groups.ts","../../../apps/web/src/components/support-widget/use-stack-shift.ts","../../../apps/web/src/components/support-widget/motion.ts","../../../apps/web/src/components/support-widget/widget-settle.ts","../../../apps/web/src/components/support-widget/widget-composer.tsx","../src/next-image-shim.tsx","../../../apps/web/src/components/attachments/attachment-image.tsx","../../../apps/web/src/components/attachments/attachment-draft-chip.tsx","../../../apps/web/src/components/attachments/use-attachment-drafts.ts","../../../apps/web/src/components/attachments/use-attachment-target.ts","../../../apps/web/src/components/support-widget/widget-button.tsx","../../../apps/web/src/components/support-widget/widget-field.ts","../../../apps/web/src/components/ui/textarea.tsx","../../../apps/web/src/hooks/use-typing-signal.ts","../../../apps/web/src/components/support-widget/use-eased-height.ts","../../../apps/web/src/components/brand-art.ts","../../../apps/web/src/components/brand.tsx","../../../apps/web/src/components/support-widget/widget-badge.tsx","../../../apps/web/src/components/support-widget/widget-corner.ts","../../../apps/web/src/components/support-widget/widget-destination.tsx","../../../apps/web/src/components/avatar.tsx","../../../apps/web/src/components/initial-badge.tsx","../../../apps/web/src/components/support-widget/widget-avatar.tsx","../../../apps/web/src/components/support-widget/widget-empty.tsx","../../../apps/web/src/components/support-widget/widget-header.tsx","../../../apps/web/src/components/support-widget/widget-loading.tsx","../../../apps/web/src/components/support-widget/widget-locked.tsx","../../../apps/web/src/components/support-widget/widget-message.tsx","../../../apps/web/src/components/ui/badge.tsx","../../../apps/web/src/components/ui/morph-bubble.tsx","../../../apps/web/src/components/support-widget/widget-attachments.tsx","../../../apps/web/src/components/support-widget/widget-bubble.tsx","../../../apps/web/src/components/support-widget/widget-message-body.tsx","../../../apps/web/src/components/ui/line-reveal.tsx","../../../apps/web/src/hooks/use-media-query.ts","../../../apps/web/src/lib/motion.ts","../../../apps/web/src/components/support-widget/widget-typing.tsx","../../backend/convex/shared/support.ts","../../../apps/web/src/components/support-widget/widget-rating.tsx","../../../apps/web/src/components/support-widget/widget-status.tsx","../../../apps/web/src/components/support-widget/use-launcher-drag.ts","../../../apps/web/src/components/support-widget/use-teaser-placement.ts","../../../apps/web/src/components/support-widget/use-widget-sheet.ts","../../../apps/web/src/components/support-widget/use-page-lock.ts","../../../apps/web/src/components/support-widget/widget-modal.tsx","../../../apps/web/src/components/support-widget/use-hydrated.ts","../../../apps/web/src/components/support-widget/use-scroll-lock.ts","../../../apps/web/src/components/support-widget/widget-sheet-styles.ts","../../../apps/web/src/components/support-widget/widget-teaser.tsx","../../../apps/web/src/components/support-widget/plain-text.ts","../../../apps/web/src/components/support-widget/widget-spotlight.tsx","../../../apps/web/src/components/support-widget/sdk/control.ts","../../backend/convex/shared/customData.ts","../../../apps/web/src/components/support-widget/sdk/custom-data.ts","../../../apps/web/src/components/support-widget/sdk/open-store.ts","../../backend/convex/shared/mutationResult.ts","../../backend/client/mutationResult.ts","../../backend/convex/_generated/api.js","../../backend/convex/shared/crashes.ts","../../../apps/web/src/components/support-widget/sdk/report-error.ts","../../../apps/web/src/lib/upload.ts","../../../apps/web/src/components/support-widget/sdk/context.ts","../../../apps/web/src/components/support-widget/sdk/deployment.ts","../../backend/convex/shared/diagnostics.ts","../../../apps/web/src/components/support-widget/sdk/diagnostics.ts","../../../apps/web/src/components/support-widget/sdk/install.ts","../../../apps/web/src/components/support-widget/sdk/screenshot.ts","../../../apps/web/src/components/support-widget/sdk/session.ts","../../../apps/web/src/components/support-widget/sdk/use-median-thread.ts","../../../apps/web/src/components/support-widget/sdk/use-widget-mutation.ts","../../../apps/web/src/hooks/use-now.ts","../../../apps/web/src/lib/errors.ts","../../../apps/web/src/lib/time.ts","../../../apps/web/src/components/support-widget/sdk/rating-skips.ts","../../../apps/web/src/components/support-widget/sdk/read-state.ts","../../../apps/web/src/components/support-widget/sdk/use-screenshot-ask.ts","../../../apps/web/src/components/support-widget/sdk/visitor-email.ts","../../../apps/web/src/components/support-widget/sdk/signed-out.ts","../../../apps/web/src/components/support-widget/sdk/unread-store.ts","../../../apps/web/src/components/support-widget/sdk/error-boundary.tsx","../../../apps/web/src/components/support-widget/sdk/use-identity.ts","../../../apps/web/src/components/support-widget/sdk/use-median-branding.ts","../../../apps/web/src/components/support-widget/sdk/median-connection.tsx","../../../apps/web/src/components/support-widget/sdk/use-api-key-warning.ts","../../../node_modules/.bun/cuelume@0.2.2/node_modules/cuelume/dist/sounds/recipes.js","../../../node_modules/.bun/cuelume@0.2.2/node_modules/cuelume/dist/audio/engine.js","../../../apps/web/src/components/support-widget/sdk/use-message-sound.ts","../../../apps/web/src/components/support-widget/sdk/use-navigation.ts","../../../apps/web/src/components/support-widget/sdk/use-teaser.ts","../../../apps/web/src/components/support-widget/sdk/stamps.ts","../../../apps/web/src/components/support-widget/sdk/teaser-state.ts","../../../apps/web/src/components/support-widget/sdk/use-unread-title.ts","../../../apps/web/src/components/support-widget/sdk/mount-into.tsx","../../../apps/web/src/components/support-widget/sdk/mount.tsx","../../../apps/web/src/components/support-widget/sdk/median-support-modal.tsx","../../../apps/web/src/components/support-widget/sdk/mount-support-modal.tsx","../../../apps/web/src/components/support-widget/sdk/median-feedback.tsx","../../backend/convex/shared/feedback.ts","../../backend/convex/shared/reportImages.ts","../../../apps/web/src/components/support-widget/feedback/feedback-panel.tsx","../../../apps/web/src/components/support-widget/feedback/feedback-popover.tsx","../../../apps/web/src/components/support-widget/feedback/use-anchor.ts","../../../apps/web/src/components/support-widget/sdk/feedback-control.ts","../../../apps/web/src/components/support-widget/sdk/use-feedback-note.ts","../../../apps/web/src/components/support-widget/sdk/mount-feedback.tsx","../../../apps/web/src/components/support-widget/sdk/median-contact-form.tsx","../../../apps/web/src/components/support-widget/contact/contact-form.tsx","../../../apps/web/src/components/support-widget/contact/contact-field.tsx","../../../apps/web/src/components/support-widget/contact/contact-body.ts","../../../apps/web/src/components/support-widget/contact/fields.ts","../../../apps/web/src/components/support-widget/contact/sent-mark.tsx","../../../apps/web/src/components/support-widget/sdk/use-median-contact.ts","../../../apps/web/src/components/support-widget/sdk/contact.ts","../../../apps/web/src/components/support-widget/sdk/mount-contact-form.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect } from \"react\";\nimport { threadRendersMarkdown, warmMarkdownWhenIdle } from \"../markdown-chunk\";\nimport { SupportLauncher, type SupportPresentation } from \"../widget-launcher\";\nimport { WidgetSpotlight } from \"../widget-spotlight\";\nimport type { SupportLauncherKind } from \"../types\";\nimport { useMedianOpenState, useRegisterMedianSupport } from \"./control\";\nimport type { MedianDiagnostics } from \"./diagnostics\";\nimport { MedianErrorBoundary } from \"./error-boundary\";\nimport { useCustomDataSender } from \"./custom-data\";\nimport { useSignedInUser } from \"./signed-out\";\nimport { useMedianIdentity, useMissingCredentials } from \"./use-identity\";\nimport { useRememberInstall } from \"./install\";\nimport { useMedianBranding } from \"./use-median-branding\";\nimport { MedianConnection } from \"./median-connection\";\nimport { useApiKeyShapeWarning } from \"./use-api-key-warning\";\nimport { useMessageSound } from \"./use-message-sound\";\nimport { useSupportNavigation } from \"./use-navigation\";\nimport { unreadStore } from \"./unread-store\";\nimport { useTeaser } from \"./use-teaser\";\nimport { useUnreadTitle } from \"./use-unread-title\";\nimport {\n isSignedIn,\n useMedianThread,\n type MedianUser,\n} from \"./use-median-thread\";\n\n/**\n * How often the open panel says so. Well inside the server's window for\n * counting somebody as present, so one dropped beat changes nothing.\n */\nconst PRESENCE_BEAT_MS = 45_000;\n\ntype MedianSupportOptions = {\n /** Who is signed in on the host's site, if anyone. Shown to the team. */\n user?: MedianUser;\n /**\n * Whether a message carries what the browser already knows: time zone,\n * language, browser, OS, device, screen, and the page it was sent from.\n * On by default, because a team that has to ask for all six spends its\n * first reply asking. Pass `false` and none of it is read at all.\n */\n telemetry?: boolean;\n /**\n * Whether the agent may take a picture of this page when it asks to see\n * it. Drawn from the page itself rather than recorded off the screen, so\n * nobody is asked for permission to share anything, and only when the\n * agent asks: it says it is taking a look, and the picture lands in the\n * thread where the visitor can see what was sent. On by default. Pass\n * `false` on a page showing somebody else's data and the agent is told no\n * picture is coming.\n *\n * Never the panel itself or anything else Median renders.\n */\n screenshot?: boolean;\n /**\n * Take questions from signed in people only.\n *\n * Until `user` says somebody is there, the panel asks them to sign in\n * instead of offering a composer, and no conversation is opened or read.\n * Pass `user` the moment you have it and the thread takes over.\n *\n * A rule about your own page, kept on your own page: it decides what this\n * widget offers, not what the API accepts. `user.id` with a signed `hash`\n * is the part nobody can talk their way around.\n */\n requireSignIn?: boolean;\n /**\n * Where your sign in lives, for the button on that panel.\n *\n * <MedianSupport\n * apiKey=\"median_pk_...\"\n * requireSignIn\n * onSignIn={() => router.push(\"/login\")}\n * />\n *\n * Without it the panel says the same thing and offers no button, which is\n * right for a site whose sign in is already the first thing on the page.\n */\n onSignIn?: () => void;\n /**\n * Ask for an email before a visitor's first message. On by default: an\n * anonymous thread cannot be followed up, and half of support is getting\n * back to somebody. Pass `false` and the composer opens straight away.\n *\n * The composer asks once per browser; the answer rides their first message\n * and lands with their details on your side. A visitor your `user` already\n * names an email for is never asked, and `medianSupport.reset()` asks the\n * next person instead.\n *\n * Your team can turn the question off from the dashboard without a\n * redeploy, and either side saying no is enough to keep it away.\n */\n requireEmail?: boolean;\n /**\n * Show the first lines of an unread reply on the closed button. On by\n * default: the count says something arrived, and the card is what says\n * whether it is worth opening.\n *\n * <MedianSupport apiKey=\"median_pk_...\" messagePreview={false} />\n *\n * Pass `false` and the corner stays a button with a count on it. Nothing\n * else changes: the reply is still there, still counted, still announced.\n * For a page where a customer's words should never appear unprompted,\n * over somebody's shoulder or in a screen share.\n *\n * Your team can turn it off from the dashboard without a redeploy, and\n * either side saying no is enough to keep the card away.\n */\n messagePreview?: boolean;\n /**\n * Put the unread count in the tab's title, as `(1) Your page`, while a\n * reply is waiting. On by default: it is the one part of your page a\n * visitor can see from another tab. Your own title is kept and put back\n * once the reply has been read.\n *\n * <MedianSupport apiKey=\"median_pk_...\" titleCount={false} />\n *\n * Pass `false` and the title is never touched.\n */\n titleCount?: boolean;\n /**\n * Watch what goes wrong on the page, so a bug report arrives with the\n * error already attached instead of after four messages of asking.\n *\n * <MedianSupport apiKey=\"median_pk_...\" diagnostics />\n *\n * `true` listens for uncaught errors and rejected promises, which changes\n * nothing about how your page runs. The object form adds the two that wrap\n * something of yours:\n *\n * diagnostics={{ network: true, console: true }}\n *\n * `network` wraps window.fetch to note requests that failed, `console`\n * wraps console.error. Both call through to what they replaced, and both\n * are put back when the widget leaves the page.\n *\n * Off by default: this reads your program's error stream, which is yours\n * to offer rather than ours to take. Nothing is sent on its own. The\n * newest snapshot rides the next message the customer sends, and only when\n * something new is in it.\n */\n diagnostics?: MedianDiagnostics;\n /**\n * Anything about your own app worth having next to a bug report: a build,\n * a route, a flag. Read when a message is sent, so it can change as they\n * move around.\n *\n * <MedianSupport\n * apiKey=\"median_pk_...\"\n * diagnostics\n * appState={{ version: BUILD_SHA, plan: user.plan }}\n * />\n *\n * Twenty entries at most, and each one a line rather than a document.\n */\n appState?: Record<string, string | number | boolean>;\n /** Our button in the corner while it is closed, or nothing at all. */\n launcher?: SupportLauncherKind;\n /** Pass it, with `onOpenChange`, to hold the open state yourself. */\n open?: boolean;\n /** Every open and close, whoever asked for it. */\n onOpenChange?: (open: boolean) => void;\n /**\n * Take the visitor to a page, when one of your own tools offers one.\n *\n * <MedianSupport apiKey=\"median_pk_...\" onNavigate={router.push} />\n *\n * A tool that answers with a destination puts a button under the reply\n * that explains it, and this is what the button does. Nothing moves on its\n * own: the visitor presses it, or they stay where they are.\n *\n * Called with a path on your own site, already checked against this page's\n * origin. Your router decides what happens: pass `router.push` and the\n * conversation stays open across the move.\n *\n * Without it there is nowhere for a press to go, so no button is shown and\n * the console says so while you are building. Nothing else needs it.\n */\n onNavigate?: (path: string) => void;\n};\n\n/**\n * Two ways in, and every app has one of them.\n *\n * `apiKey` is the publishable id, and on its own it opens an anonymous\n * thread. `identity` is the path to a route built with `medianIdentity`,\n * which answers with the key and with whoever is signed in, already signed.\n * A framework that renders its root layout on the server cannot hand this\n * component a session any other way, so that is most of them.\n *\n * Passing both is worth it on a slow session: the button is up on the key\n * from the prop straight away, and the person arrives when the route answers.\n */\nexport type MedianSupportProps = MedianSupportOptions &\n (\n | { apiKey: string; identity?: string }\n | { apiKey?: string; identity: string }\n );\n\n/**\n * The whole SDK, as one component. Drop it on a page with your publishable API\n * key and the launcher, the thread, the AI and the inbox on the other side are\n * all wired up:\n *\n * <MedianSupport apiKey=\"median_pk_...\" />\n *\n * Optionally say who is signed in, and the team sees a person rather than\n * another anonymous visitor:\n *\n * <MedianSupport\n * apiKey=\"median_pk_...\"\n * user={{ name: \"Ada\", email: \"ada@example.com\" }}\n * />\n *\n * There are three states rather than two: open, closed as a button in the\n * corner, and not on the page at all. Take the corner back and open it from\n * your own nav, from anywhere in your app, with no state to thread through:\n *\n * <MedianSupport apiKey=\"median_pk_...\" launcher=\"hidden\" />\n *\n * const support = useMedianSupport();\n * <button onClick={support.open}>Contact us</button>\n *\n * On a site nobody uses signed out, say so and the panel asks them to sign in\n * rather than opening a conversation with a stranger:\n *\n * <MedianSupport\n * apiKey=\"median_pk_...\"\n * user={session?.user}\n * requireSignIn\n * onSignIn={() => router.push(\"/login\")}\n * />\n *\n * When somebody signs out, `medianSupport.reset()` forgets the conversation\n * along with them, so the next person at the keyboard starts fresh.\n *\n * That is the surface, on purpose. Everything else a support widget could be\n * asked to do is either our decision or a setting on the dashboard, so it can\n * change without every host having to redeploy.\n *\n * It brings its own connection, so it works on pages (and sites) that have\n * never heard of Convex.\n */\nexport function MedianSupport(props: MedianSupportProps) {\n return <MedianSupportPreview {...props} />;\n}\n\n/**\n * The same widget with the handful of knobs Median's own pages need: the\n * gallery pins it inside a frame rather than to the viewport, and `defaultOpen`\n * is there for a screenshot or a page that has room for nothing else.\n *\n * Nothing uses `defaultOpen` today, on purpose. Everywhere the widget is shown\n * it starts as the button, because that is the state a host's visitors meet it\n * in and the morph out of the corner is most of what there is to look at.\n *\n * `presentation` is the one knob a host does reach, through a component of\n * its own: `MedianSupportModal` is this with the panel drawn in the middle of\n * the screen. It is a separate component rather than a prop so the two read\n * as two things to install, not one thing with a setting.\n *\n * Not exported from the package. A host gets `MedianSupport` and nothing else,\n * which is why these can exist at all without becoming promises we have to\n * keep.\n */\nexport function MedianSupportPreview({\n defaultOpen = false,\n presentation = \"corner\",\n className,\n ...props\n}: MedianSupportProps & {\n /** Where it starts, until anything opens or closes it. */\n defaultOpen?: boolean;\n /** Out of the corner, or in the middle of the screen. */\n presentation?: SupportPresentation;\n className?: string;\n}) {\n return (\n <MedianErrorBoundary component={componentName(presentation)}>\n <MedianIdentity\n {...props}\n defaultOpen={defaultOpen}\n presentation={presentation}\n className={className}\n />\n </MedianErrorBoundary>\n );\n}\n\n/**\n * Settles who the widget is for before anything is connected.\n *\n * Both halves can come from a prop or from the host's identity route, and a\n * prop wins: a host who has the key to hand should not wait on a fetch to put\n * the button up, and one who already knows the signed in person is not told\n * otherwise by a route that answers later.\n *\n * Nothing is rendered until there is a key from one of the two. Without one\n * there is nobody to talk to, and a button that opens onto an error is worse\n * on somebody's page than no button for the moment it takes to answer.\n */\ntype GateProps = MedianSupportOptions & {\n apiKey?: string;\n identity?: string;\n defaultOpen: boolean;\n presentation: SupportPresentation;\n className?: string;\n};\n\n/** What the host wrote in their page, for anything said in the console. */\nfunction componentName(presentation: SupportPresentation): string {\n return presentation === \"modal\" ? \"MedianSupportModal\" : \"MedianSupport\";\n}\n\nfunction MedianIdentity({ apiKey, identity, user, ...rest }: GateProps) {\n const resolved = useMedianIdentity(identity);\n useMissingCredentials(\n `<${componentName(rest.presentation)}>`,\n apiKey,\n identity,\n );\n const key = apiKey ?? resolved.apiKey;\n const person = useSignedInUser(user ?? resolved.user);\n\n if (key === undefined) return null;\n\n return (\n <MedianConnection what=\"widget\">\n <MedianLauncher\n {...rest}\n apiKey={key}\n user={person}\n isResolvingIdentity={resolved.isLoading}\n />\n </MedianConnection>\n );\n}\n\ntype ConnectionProps = MedianSupportOptions & {\n apiKey: string;\n /** The host's identity route has been asked and has not answered yet. */\n isResolvingIdentity: boolean;\n defaultOpen: boolean;\n presentation: SupportPresentation;\n className?: string;\n};\n\nfunction MedianLauncher({\n apiKey,\n user,\n telemetry,\n screenshot,\n diagnostics,\n appState,\n requireSignIn = false,\n onSignIn,\n requireEmail = true,\n messagePreview = true,\n titleCount = true,\n launcher,\n presentation,\n defaultOpen,\n open,\n onOpenChange,\n onNavigate,\n isResolvingIdentity,\n className,\n}: ConnectionProps) {\n useRegisterMedianSupport(componentName(presentation));\n useCustomDataSender();\n useApiKeyShapeWarning(`<${componentName(presentation)}>`, apiKey);\n // So a crash screen can file a report from a tree this widget is no\n // longer in. See `install.ts`.\n useRememberInstall(apiKey, user);\n const showBranding = useMedianBranding(apiKey);\n\n const [isOpen, setOpen] = useMedianOpenState({\n open,\n defaultOpen,\n onOpenChange,\n });\n\n // Whoever is at the keyboard has not said who they are, and this host only\n // takes questions from people who have.\n //\n // Not while the identity route is still being read. Nobody has said there\n // is no one signed in yet, and inviting somebody who is already signed in\n // to sign in, for the half second before their own name arrives, reads as a\n // widget that has lost them.\n const isLocked = requireSignIn && !isResolvingIdentity && !isSignedIn(user);\n\n const {\n presence,\n messages,\n ask,\n conversationId,\n isLoading,\n isSending,\n askForEmail,\n setVisitorEmail,\n awaitingHuman,\n rating,\n unreadCount,\n latestUnread,\n reportPresence,\n sendError,\n send,\n setTyping,\n } = useMedianThread({\n apiKey,\n user,\n telemetry,\n screenshot,\n isLocked,\n diagnostics,\n appState,\n requireEmail,\n messagePreview,\n });\n\n // The card on the closed button, and the cross that waves it away. Held\n // per browser, so a dismissed reply stays dismissed\n // across the host's pages.\n const { teaser, dismiss: dismissTeaser } = useTeaser(apiKey, latestUnread);\n\n // Nothing here is settled yet: either the thread is on its way, or the\n // route that says whose thread it should be is. The panel shows the same\n // shimmer for both, because from the visitor's side they are one wait.\n const isSettling = isLoading || isResolvingIdentity;\n\n // Somebody who has been answered here before, with the answers still behind\n // the button. They are the likeliest person on the page to open it, so the\n // renderer is fetched once the page has finished its own work rather than\n // waited for on the click. Everybody else pays nothing until they reach for\n // it. See `markdown-chunk.ts`.\n useEffect(() => {\n if (!threadRendersMarkdown(messages)) return;\n return warmMarkdownWhenIdle();\n }, [messages]);\n\n // The same fact, told to the server as a heartbeat: on open, on coming\n // back to the tab, and every so often in between. It is what keeps a\n // reply they are watching arrive from also landing in their email.\n useEffect(() => {\n if (!isOpen) return;\n\n const beat = () => {\n if (document.visibilityState === \"visible\") reportPresence();\n };\n beat();\n const interval = setInterval(beat, PRESENCE_BEAT_MS);\n document.addEventListener(\"visibilitychange\", beat);\n window.addEventListener(\"focus\", beat);\n return () => {\n clearInterval(interval);\n document.removeEventListener(\"visibilitychange\", beat);\n window.removeEventListener(\"focus\", beat);\n };\n }, [isOpen, reportPresence]);\n\n // Whether the panel is up or not: a ring is drawn over the host's page\n // rather than in here, and an offer has to be waiting when they open it.\n const { spotlight, offer, follow } = useSupportNavigation({\n messages,\n ask,\n // Signing in is the case this exists for. A page that has been watching\n // an empty query is handed the account's whole history the moment the\n // widget can say who they are, and an instruction from a conversation\n // they had last week is not an instruction to anybody now.\n conversationId,\n // Locked, there is no thread being read at all, so the first one to\n // arrive after somebody signs in is history like any other first look.\n isLoading: isSettling || isLocked,\n onNavigate,\n });\n\n // Also whether the panel is up or not, and for the same reason as the ring:\n // a reply landing behind a closed panel is the one most worth hearing.\n useMessageSound({\n messages,\n conversationId,\n isLoading: isSettling || isLocked,\n });\n\n // And the count in the tab's title, for the reply that lands while they\n // are reading some other tab: the only part of this page they can see\n // from there.\n useUnreadTitle(titleCount ? unreadCount : 0);\n\n // And on whatever the host put in its own nav: the same count, read back\n // through `useMedianSupport()`, for a badge on a button of theirs. Taken\n // back to zero when the panel leaves the page.\n useEffect(() => {\n unreadStore.set(unreadCount);\n }, [unreadCount]);\n useEffect(() => () => unreadStore.set(0), []);\n\n // Until the config lands there is no name to put on the header, so the\n // launcher waits as a button and the panel shimmers if opened.\n const resolvedPresence = presence ?? {\n name: \"Support\",\n status: \"Connecting\",\n team: [],\n };\n\n return (\n <>\n {/* Keyed on the ask and on how many rings there have been, so a second\n one is a second ring rather than a stale one carrying on, and the\n same offer pressed twice points twice. */}\n {spotlight !== null && (\n <WidgetSpotlight key={spotlight.key} selector={spotlight.selector} />\n )}\n\n <SupportLauncher\n showBadge={showBranding}\n presence={resolvedPresence}\n messages={messages}\n // Passed through as a promise, so the composer can hold the draft on\n // a failed send and clear it on a delivered one.\n onSend={send}\n onTypingChange={setTyping}\n isLoading={isSettling}\n isLocked={isLocked}\n onSignIn={onSignIn}\n askForEmail={askForEmail}\n onSubmitEmail={setVisitorEmail}\n isSending={isSending}\n awaitingHuman={awaitingHuman}\n rating={rating}\n offer={offer}\n // Nowhere for a press to go without a handler from the host, so the\n // panel is told there is nothing to offer rather than offering\n // something that would do nothing.\n onFollow={onNavigate === undefined ? undefined : follow}\n unreadCount={unreadCount}\n teaser={teaser}\n onDismissTeaser={dismissTeaser}\n notice={sendError}\n launcher={launcher}\n presentation={presentation}\n // Always controlled from here: the panel answers to the host's own\n // prop, to their trigger anywhere on the page, and to its own button,\n // and this is where those three are settled into one answer.\n open={isOpen}\n onOpenChange={setOpen}\n className={className}\n />\n </>\n );\n}\n","\"use client\";\n\nimport { lazy, useSyncExternalStore } from \"react\";\nimport type { SupportMessage } from \"./types\";\n\n/**\n * The markdown renderer, fetched when there is a reason to and not before.\n *\n * It is the heaviest thing the widget can reach: a parser, a sanitiser and an\n * HTML tokeniser, and together they outweigh everything else in the package\n * put together. They also cannot render anything until the panel is open, and\n * most visitors on most pages never open it. Loading them with the page\n * spends that on everybody to serve the few.\n *\n * So the import is a call rather than a statement, and the bundle splits\n * where the call is. What a host's page pays for on load is the button. The\n * rest arrives on the first sign that somebody wants it, which is early\n * enough to be there before they do:\n *\n * - the pointer reaching the launcher, or a tab landing on it\n * - the panel opening, whoever opened it\n * - a page going idle with an already answered thread behind the button\n *\n * Until it lands, a reply renders as the text it was written as, which is the\n * same paragraph a visitor's own message gets. A failed fetch leaves that in\n * place rather than leaving a hole where the answer was.\n */\n\nconst load = () => import(\"@/components/ui/message-markdown\");\n\n/**\n * The renderer itself, and the only place it is rendered from.\n *\n * `lazy` rather than a component held in a variable, so this is one component\n * for the life of the page rather than a new one per render. It is only ever\n * put on screen once `useMarkdownReady` says the module is already in hand,\n * so the suspense it can throw is a guard rather than a state anybody waits\n * in.\n */\nexport const MessageMarkdown = lazy(async () => ({\n default: (await load()).MessageMarkdown,\n}));\n\nlet ready = false;\nlet inFlight = false;\nconst listeners = new Set<() => void>();\n\n/**\n * Fetch it, unless it is already here or already on its way.\n *\n * Safe to call from anywhere, as often as you like: everything after the\n * first call in a page's life is a no-op. A fetch that fails clears the flag\n * so a later trigger can try again, which covers the tunnel a phone goes\n * through between the page loading and the visitor asking a question.\n */\nexport function warmMarkdown(): void {\n if (ready || inFlight) return;\n inFlight = true;\n\n void load().then(\n () => {\n ready = true;\n for (const listener of listeners) listener();\n },\n () => {\n inFlight = false;\n },\n );\n}\n\n/** How long to wait for an idle moment that may never come. */\nconst IDLE_FALLBACK_MS = 2_000;\n\n/**\n * The same, once the page has nothing better to do. Returns the cancel, so an\n * effect torn down before the browser gets round to it does not go on to\n * fetch a megabyte for a widget that has left the page.\n *\n * `requestIdleCallback` is missing on Safari before 17, so a timeout stands\n * in. The exact delay does not matter; being after the page's own work does.\n */\nexport function warmMarkdownWhenIdle(): () => void {\n if (typeof window === \"undefined\") return () => {};\n\n if (typeof window.requestIdleCallback === \"function\") {\n const handle = window.requestIdleCallback(() => warmMarkdown());\n return () => window.cancelIdleCallback(handle);\n }\n\n const timer = setTimeout(warmMarkdown, IDLE_FALLBACK_MS);\n return () => clearTimeout(timer);\n}\n\n/**\n * Whether there is anything in this thread that markdown would render\n * differently from a plain paragraph.\n *\n * Only the house voice writes markdown. What a visitor types is quoted\n * verbatim wherever it lands, so a thread of nothing but questions never\n * needs any of this and never fetches it.\n */\nexport function threadRendersMarkdown(messages: SupportMessage[]): boolean {\n return messages.some(\n (message) => message.sender !== \"visitor\" && message.body.length > 0,\n );\n}\n\nconst subscribe = (listener: () => void) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n};\n\nconst read = () => ready;\n// False on the server and through hydration both, so the first client pass\n// renders what the server sent rather than a tree it has to throw away.\nconst onServer = () => false;\n\n/** Whether the module is in hand, so rendering it will not suspend. */\nexport function useMarkdownReady(): boolean {\n return useSyncExternalStore(subscribe, read, onServer);\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { ChatCircleDots } from \"@/components/icons\";\nimport { cn } from \"@/lib/utils\";\nimport {\n clipOrigin,\n closedClip,\n type LauncherSide,\n} from \"./launcher-position\";\nimport { warmMarkdown } from \"./markdown-chunk\";\nimport { SupportWidget } from \"./support-widget\";\nimport { BUTTON_Y, useLauncherDrag } from \"./use-launcher-drag\";\nimport { useTeaserPlacement } from \"./use-teaser-placement\";\nimport { useWidgetSheet } from \"./use-widget-sheet\";\nimport { WIDGET_RADIUS } from \"./widget-corner\";\nimport { SupportModal } from \"./widget-modal\";\nimport { SHEET_PANEL } from \"./widget-sheet-styles\";\nimport { WidgetTeaser } from \"./widget-teaser\";\nimport type {\n SupportDestination,\n SupportFollow,\n SupportLauncherKind,\n SupportMessage,\n SupportPresence,\n SupportRating,\n} from \"./types\";\n\n/**\n * Sized for 16px body text: room for a reply of a few lines at forty odd\n * characters a line, and a composer you can see from across the room.\n */\nconst DEFAULT_WIDTH = \"28rem\";\nconst DEFAULT_HEIGHT = \"40rem\";\n/** The closed button. Smaller than the panel's scale: it is a way in, not the room. */\nconst DEFAULT_BUTTON_SIZE = \"3rem\";\n\n/**\n * Bigger, for a thread you are actually reading rather than glancing at.\n *\n * More of the growth goes into the height than the width, because that is the\n * axis a conversation runs out of: room to see what was said, rather than room\n * per line. The width still moves, and moving it re-wraps every bubble as it\n * goes, so it is kept to the smaller number of the two.\n */\nconst EXPANDED_WIDTH = \"34rem\";\nconst EXPANDED_HEIGHT = \"52rem\";\n\n/**\n * The morph, as a clip rather than a resize.\n *\n * The panel is always laid out at its full size. Closed, it is clipped down to\n * a circle in the corner the size of the button; open, the clip is the panel\n * itself. Only the clip travels.\n *\n * This matters. Animating width and height means the browser lays out the whole\n * panel again on every frame, and the message scroller measures its viewport\n * every time that happens, so it fights the animation the entire way. Clipping\n * touches no layout at all: nothing inside ever moves, reflows or gets measured,\n * it is simply revealed.\n *\n * Where it grows out of is `launcher-position.ts`. The button can be dragged\n * anywhere up or down either edge, and the panel has to follow it there.\n */\n/**\n * The panel's own corner, from the one place it is written down.\n *\n * The clip used to be a hair rounder than the panel inside it, which is why\n * the panel had to give up its border: two curves that nearly agree meet in a\n * hairline, and a hairline that moves is the thing that makes an animation\n * look broken. Same number, one curve, and the border can come back. The\n * shadow layer wears this clip too, so it has to agree as well: cut squarer\n * than the panel, its fill shows past each corner as a second corner.\n */\nconst OPEN_CLIP = `inset(0px 0px 0px 0px round ${WIDGET_RADIUS})`;\n\n/**\n * The button's height inside the box.\n *\n * Measured and written by the drag before paint, because it is the difference\n * between two lengths taken against different boxes and no amount of `calc`\n * gets at that. The fallback is the resting place of a widget nobody has\n * moved, which is the one the server rendered and the only one on screen\n * before the measurement lands.\n */\nconst buttonY = (buttonSize: string) =>\n `var(${BUTTON_Y}, calc(100% - ${buttonSize}))`;\n\n/**\n * How far off the edges the whole thing sits. Close enough to read as parked\n * against the side rather than floating near it, and still clear of the corner\n * radius on a phone.\n */\nconst MARGIN = \"1rem\";\n\n/**\n * The bounce rides on a scale, not on the clip.\n *\n * A clip cannot show an overshoot: carrying past \"fully revealed\" only means\n * clipping outside the box, which looks like nothing at all. So the panel comes\n * up the last few percent of its size as it opens, and that is what carries a\n * touch too far and settles back. Around four pixels on a panel this wide,\n * which is about right for something you feel rather than watch.\n *\n * Scale is composited, so this costs the same as the clip does: nothing.\n */\nconst CLOSED_SCALE = \"scale(0.94)\";\nconst OPEN_SCALE = \"scale(1)\";\n\n/**\n * Where the panel comes from when there is no button for it to come from.\n *\n * A morph needs two shapes and one of them has gone. Clipping down to a circle\n * nobody can see is not an origin, it is a place the panel happens to vanish\n * at, and growing out of it reads as the corner deciding to produce a panel.\n * So it rises the last few pixels into place instead, which is what everything\n * that arrives out of nothing does.\n */\nconst RISEN = \"translateY(0.5rem) scale(0.98)\";\n\n/**\n * The shape, shared by the panel and the shadow layer behind it. One function\n * so the two cannot drift a frame apart.\n */\nconst shape = (\n isOpen: boolean,\n buttonSize: string,\n side: LauncherSide,\n hasBubble: boolean,\n) =>\n hasBubble\n ? {\n clipPath: isOpen\n ? OPEN_CLIP\n : closedClip(side, buttonY(buttonSize), buttonSize),\n transform: isOpen ? OPEN_SCALE : CLOSED_SCALE,\n // The point it grows out of, so the closed circle stays put under the\n // button rather than shrinking towards the middle.\n transformOrigin: clipOrigin(side, buttonY(buttonSize), buttonSize),\n }\n : { clipPath: OPEN_CLIP, transform: isOpen ? \"none\" : RISEN };\n\n/**\n * The two properties the morph is actually about, named rather than `all`.\n *\n * `transform-origin` is the one deliberately left out. It moves whenever the\n * button does, and easing a shape's origin across a panel while the shape\n * itself holds still is a swim, not a morph.\n */\nconst MORPH =\n \"transition-[clip-path,transform] duration-450 ease-spring motion-reduce:transition-none\";\n\n/**\n * The other one, for a widget with no button. Shorter and a pure deceleration:\n * a spring's overshoot is the button's swagger, and a panel that simply\n * appeared has not earned it.\n */\nconst ARRIVE =\n \"transition-transform duration-200 ease-out-quint motion-reduce:transition-none\";\n\n/**\n * The phone's version of the morph: none. A panel about to be the whole\n * screen has no corner to grow out of, so it rises into place as a sheet,\n * which is the shape every thumb has already been taught. A touch longer than\n * ARRIVE because it travels further, and the same pure deceleration.\n */\nconst SHEET =\n \"fixed transition-[opacity,transform] duration-300 ease-out-quint motion-reduce:transition-none\";\n\n/** Where the sheet waits: a little below its feet, ready to come up. */\nconst SHEET_RISEN = \"translateY(2rem)\";\n\n/**\n * Where the open panel is drawn.\n *\n * `corner` grows out of the button, which is what a widget is. `modal` keeps\n * the button and opens in the middle of the screen over a scrim instead,\n * for a site where the conversation deserves the room. Both are the same\n * panel with the same everything; only the box it is drawn in changes, and\n * on a phone the two are one sheet.\n */\nexport type SupportPresentation = \"corner\" | \"modal\";\n\n/**\n * The three things about the box itself that move: whether it is there at all,\n * and how wide and tall it is.\n *\n * One declaration rather than two named ones, because they are one property\n * list. Two classes reads better and does not work: both set\n * `transition-property`, so the second simply replaces the first.\n *\n * With no button in the corner there is nothing to show while it is closed, so\n * the whole thing fades with the morph. Quicker than the morph either way: the\n * clip still carries the panel out of the corner and back into it, but the few\n * frames where that is an empty circle are spent at zero.\n *\n * What is not in the list is the travel between corners. That is an animation\n * on the node rather than a transition on this class, so a drag can move the\n * box every frame without a transition trying to catch up with it.\n *\n * `ease-out` rather than the app's usual `ease-out-quint`, which is the one\n * exception in here and is about what is moving. Quint spends about seven\n * tenths of the distance in the first quarter of the time, which is what makes\n * it feel quick on something composited. This is a size, so every one of those\n * frames lays the panel out again and re-wraps the text in it, and thirty odd\n * pixels of that per frame reads as the panel stuttering rather than growing.\n * A gentler curve over slightly longer moves the same distance in far more\n * even steps.\n */\nconst BOX =\n \"transition-[opacity,width,height] duration-300 ease-out motion-reduce:transition-none\";\n\n/**\n * The button, and the panel it turns into.\n *\n * The shadow sits on a layer above the clip rather than on it, because a filter\n * is painted before a clip and would otherwise be cut away with everything\n * else. On the parent it reads the clipped shape's alpha instead, so the shadow\n * follows the corner round as it opens.\n *\n * Uncontrolled by default. Pass `open` and `onOpenChange` to drive it yourself,\n * for a host that opens support from its own menu. Pass `launcher=\"hidden\"`\n * too and the corner is theirs again: nothing is on the page until they ask\n * for it.\n */\nexport function SupportLauncher({\n presence,\n messages,\n onSend,\n onTypingChange,\n isLoading = false,\n isLocked = false,\n onSignIn,\n askForEmail = false,\n onSubmitEmail,\n isSending = false,\n awaitingHuman = false,\n rating,\n offer = null,\n onFollow,\n unreadCount = 0,\n teaser = null,\n onDismissTeaser,\n notice,\n showBadge = true,\n width = DEFAULT_WIDTH,\n height = DEFAULT_HEIGHT,\n expandedWidth = EXPANDED_WIDTH,\n expandedHeight = EXPANDED_HEIGHT,\n buttonSize = DEFAULT_BUTTON_SIZE,\n launcher = \"bubble\",\n presentation = \"corner\",\n defaultOpen = false,\n open,\n onOpenChange,\n className,\n}: {\n presence: SupportPresence;\n messages: SupportMessage[];\n /** Awaited by the composer, which keeps the draft if it rejects. */\n onSend?: (body: string, files: File[]) => unknown;\n /** The visitor's own typing, throttled by the composer. */\n onTypingChange?: (isTyping: boolean) => unknown;\n isLoading?: boolean;\n /** Nobody is signed in, on a host that asks people to be. */\n isLocked?: boolean;\n /** Takes the visitor to the host's own sign in. */\n onSignIn?: () => void;\n /**\n * The composer asks for an email before a first message. Off here by\n * default: only the SDK decides it, from its prop and the team's switch.\n */\n askForEmail?: boolean;\n /** What the composer's question does with a good answer. */\n onSubmitEmail?: (email: string) => void;\n /** Holds the send button while a message is on its way. */\n isSending?: boolean;\n /** The agent has called for a person and nobody has picked it up yet. */\n awaitingHuman?: boolean;\n /** The finished conversation's one question, while it is worth asking. */\n rating?: SupportRating | null;\n /** A page a tool offered with no message of its own to hang it on. */\n offer?: SupportDestination | null;\n /** What the button on an offer does. Without one, no offer is shown. */\n onFollow?: SupportFollow;\n /** Replies that arrived while the panel was closed. Sits on the button. */\n unreadCount?: number;\n /**\n * The newest of those, shown as a card on the closed button so the reply\n * can be read without opening anything. Null for no card.\n */\n teaser?: SupportMessage | null;\n /** The card's cross. Whoever owns `teaser` decides what a dismissal is. */\n onDismissTeaser?: () => void;\n /** One line above the composer, for a send that went wrong. */\n notice?: string | null;\n showBadge?: boolean;\n /** Any CSS length. The panel never grows past the viewport whatever this says. */\n width?: string;\n height?: string;\n /** What the header's own control grows it to, and back from. */\n expandedWidth?: string;\n expandedHeight?: string;\n /** The closed circle, and the corner the panel opens out of. */\n buttonSize?: string;\n /** Our button while it is closed, or nothing at all. */\n launcher?: SupportLauncherKind;\n /** Out of the corner, or in the middle of the screen. */\n presentation?: SupportPresentation;\n /** Uncontrolled starting state. Ignored when `open` is passed. */\n defaultOpen?: boolean;\n /** Pass it to control the panel yourself. */\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n /** Lands on the outer box. Override the default corner from here. */\n className?: string;\n}) {\n const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);\n const isOpen = open ?? uncontrolledOpen;\n const hasBubble = launcher === \"bubble\";\n const isModal = presentation === \"modal\";\n\n // Kept while it is closed rather than reset, so a panel that was too small\n // once is not too small again the next time it is opened. Nothing is stored\n // between visits: it is a reach for more room right now, not a setting.\n const [isExpanded, setExpanded] = useState(false);\n\n const launcherRef = useRef<HTMLButtonElement>(null);\n const panelRef = useRef<HTMLDivElement>(null);\n const boxRef = useRef<HTMLDivElement>(null);\n\n // Where it lives, and how it gets moved. The button while it is closed, the\n // panel by its own header once it is open: one position, held by whichever\n // end of the widget is on the page at the time. A modal is never dragged,\n // since it is nowhere in particular: only its button is.\n const drag = useLauncherDrag({\n boxRef,\n buttonRef: launcherRef,\n margin: MARGIN,\n enabled: isModal ? hasBubble : isOpen || hasBubble,\n subject: !isModal && isOpen ? \"panel\" : \"button\",\n });\n\n // On a phone the open panel is the screen, not a card: it leaves the box,\n // covers the viewport, and follows the keyboard. The button and the box it\n // lives in never hear about any of this. The modal keeps its own sheet,\n // measured against its own layer rather than this box, so here the\n // question is only ever asked about the corner.\n const cornerSheet = useWidgetSheet({\n boxRef,\n panelRef,\n isOpen: isOpen && !isModal,\n });\n const isSheet = !isModal && cornerSheet;\n\n // The card over the button, when there is one to show and a button to show\n // it on. Which side of the button is measured, since a button dragged to\n // the top of the window has no room over it.\n const hasTeaser = teaser !== null && hasBubble;\n const teaserPlacement = useTeaserPlacement({\n buttonRef: launcherRef,\n enabled: hasTeaser,\n isMoving: drag.isMoving,\n });\n // Whatever had focus when the panel came up, so closing can hand it back.\n // Our own button, usually; the host's own trigger when there is no button.\n const opener = useRef<HTMLElement | null>(null);\n // Focus only moves once the panel has actually changed state. Without this a\n // widget that starts open would steal focus on page load.\n const wasOpen = useRef(isOpen);\n\n const setOpen = useCallback(\n (next: boolean) => {\n if (open === undefined) setUncontrolledOpen(next);\n onOpenChange?.(next);\n },\n [open, onOpenChange],\n );\n\n const close = useCallback(() => setOpen(false), [setOpen]);\n\n // Taking somebody to a page they cannot see is not taking them anywhere.\n // As a sheet or a modal the panel is over the whole page, so it goes down\n // on the way out; as a card in the corner it was never in the way, and\n // closing it would throw away the conversation that asked the question.\n const follow = useCallback(\n (destination: SupportDestination) => {\n onFollow?.(destination);\n if (isSheet || isModal) close();\n },\n [onFollow, isSheet, isModal, close],\n );\n\n // The panel is up, so the renderer is wanted now whether or not anything\n // touched the button on the way. A host opening it from their own nav never\n // does, and neither does a phone.\n useEffect(() => {\n if (isOpen) warmMarkdown();\n }, [isOpen]);\n\n // Escape closes, wherever focus happens to be inside the panel.\n useEffect(() => {\n if (!isOpen) return;\n\n function handleKeyDown(event: KeyboardEvent) {\n if (event.key === \"Escape\") close();\n }\n\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [isOpen, close]);\n\n // Opening moves focus into the panel rather than onto the composer, so a\n // keyboard carries on from here without a phone throwing up its keyboard.\n // Closing hands it back where it came from.\n //\n // In an effect rather than in the handlers, because whichever end focus is\n // going to is still `inert` until the state that opened or closed it has\n // rendered, and focusing an inert element does nothing at all. It also has to\n // run for a host driving `open` themselves, who never touches a handler here.\n useEffect(() => {\n if (wasOpen.current === isOpen) return;\n wasOpen.current = isOpen;\n\n if (isOpen) {\n const active = document.activeElement;\n opener.current = active instanceof HTMLElement ? active : null;\n // Without `preventScroll` the browser scrolls the host's page to bring\n // the panel into view, and a panel that covers the screen is already in\n // view: all it does is drag the page out from under the sheet.\n panelRef.current?.focus({ preventScroll: true });\n return;\n }\n\n const back = launcherRef.current ?? opener.current;\n opener.current = null;\n // Only somewhere focus can actually land, and only if it is still on the\n // page: a menu that closed behind the panel is not there to take it back,\n // and leaving focus where it is beats throwing it at the body.\n if (back && back !== document.body && back.isConnected) back.focus();\n }, [isOpen]);\n\n // The panel itself, the same whichever box it is drawn in. Told whether it\n // is a sheet right now, because the corner and the modal each answer that\n // for themselves.\n const widget = (isFullScreen: boolean) => (\n <SupportWidget\n presence={presence}\n messages={messages}\n onSend={onSend}\n onTypingChange={onTypingChange}\n onClose={close}\n isExpanded={isExpanded}\n // A sheet is already every pixel there is, so a control that asks\n // for more would be a lie with an icon. A modal has its size already.\n onToggleExpanded={\n isFullScreen || isModal\n ? undefined\n : () => setExpanded((current) => !current)\n }\n isLoading={isLoading}\n isLocked={isLocked}\n onSignIn={onSignIn}\n askForEmail={askForEmail}\n onSubmitEmail={onSubmitEmail}\n isSending={isSending}\n awaitingHuman={awaitingHuman}\n rating={rating}\n offer={offer}\n // Passed on only when there is somewhere for a press to go, so a\n // panel that cannot move anybody makes no offers.\n onFollow={onFollow === undefined ? undefined : follow}\n notice={notice}\n showBadge={showBadge}\n // The header is the handle. Same four handlers the button wears,\n // and only one of the two is ever reachable at a time. A sheet has\n // nowhere to be dragged to, and neither does a modal, so there it is\n // only a header.\n grabHandlers={isFullScreen || isModal ? undefined : drag.dragHandlers}\n isMoving={drag.isMoving}\n // Its own border and its own radius, which the clip is cut to match.\n // The panel is a card sitting on somebody else's page, and a card\n // here has an edge like a card anywhere else in the app. Unless it\n // is the screen, which has neither.\n className={isFullScreen ? SHEET_PANEL : undefined}\n // A modal has room to spare, so it takes the denser scale. The corner\n // keeps the phone sized one it has always had.\n compact={isModal}\n />\n );\n\n return (\n <>\n {/* The box is panel sized whether it is open or not, so it never lets a\n click through to the host's page. Only what is actually on screen\n takes pointer events back. As a modal's home it is the button's box\n and nothing else, and stays panel sized so the card over the button\n has the same room it always had. */}\n <div\n ref={boxRef}\n // The hook the published stylesheet scopes its resets to. On a page\n // without Tailwind there is no preflight, so the package ships one that\n // reaches no further than this box and its siblings below.\n data-median-widget=\"\"\n style={{\n width: isExpanded ? expandedWidth : width,\n height: isExpanded ? expandedHeight : height,\n ...drag.style,\n }}\n className={cn(\n \"pointer-events-none fixed z-50\",\n // Twice the margin, so the panel is inset by the same gap on both\n // sides however close to the edge the button has been put.\n //\n // Against whatever is positioning it as well as against the window,\n // which are the same thing for a widget on a host's page and are not\n // for one pinned inside a frame. Expanded is what finds the\n // difference: it is bigger than some of the boxes this turns up in,\n // and a panel hanging out the side of its own preview is not a panel\n // anybody is going to trust on their site.\n \"max-h-[min(calc(100svh-2rem),calc(100%-2rem))] max-w-[min(calc(100vw-2rem),calc(100%-2rem))]\",\n BOX,\n // Closed with no button on it, there is nothing here to see. The panel\n // stays mounted and stays `inert`, so the conversation is still live\n // and nothing behind it can be tabbed into by mistake.\n !hasBubble && !isOpen && \"opacity-0\",\n // The body wears the closed hand while a drag is on, which covers the\n // host's page. It does not reach in here, where the composer asks for\n // a caret and every button asks for a pointer, so this says it again\n // for everything inside.\n drag.isMoving && \"cursor-grabbing **:cursor-grabbing\",\n className,\n )}\n >\n {/* The shadow, on an empty layer of its own behind the panel.\n\n It cannot go on the panel, because a filter is painted before a clip\n and would be cut away with everything else. It also cannot go on a\n parent of the panel, which is where it used to be: a filter anywhere\n above a scrolling area stops the browser scrolling it the cheap way\n and makes it re-paint the whole thread on every frame instead, which\n is what made the thread stutter.\n\n So it goes on a sibling with nothing inside it. Same shape, same\n timing, no children to re-paint. */}\n {/* Not when the panel is a sheet: a full screen has no edges to throw a\n shadow past, and the closed circle it would leave in the corner sits\n entirely under the button, which carries a shadow of its own. */}\n {!isSheet && !isModal && (\n <div aria-hidden className=\"absolute inset-0 drop-shadow-xl\">\n <div\n style={shape(isOpen, buttonSize, drag.side, hasBubble)}\n className={cn(\n \"size-full bg-card\",\n hasBubble ? MORPH : ARRIVE,\n drag.isMoving && \"transition-none\",\n )}\n />\n </div>\n )}\n\n {!isModal && (\n <div\n ref={panelRef}\n tabIndex={-1}\n inert={!isOpen}\n // What the parts inside read to know when to settle into place.\n // See `widget-settle.ts`.\n data-state={isOpen ? \"open\" : \"closed\"}\n // As a sheet it wears no clip at all: it fades and rises as one\n // piece, and the keyboard fit writes its edges straight onto the\n // node.\n style={\n isSheet\n ? { transform: isOpen ? \"none\" : SHEET_RISEN }\n : shape(isOpen, buttonSize, drag.side, hasBubble)\n }\n className={cn(\n \"absolute inset-0 outline-none\",\n isSheet\n ? [SHEET, !isOpen && \"opacity-0\"]\n : [\n hasBubble ? MORPH : ARRIVE,\n // While it is travelling, the clip changes corners and\n // there is nothing to animate about that. Transitioning it\n // wipes the panel across the box on the way from one\n // circle to the other.\n drag.isMoving && \"transition-none\",\n ],\n isOpen && \"pointer-events-auto\",\n )}\n >\n {widget(isSheet)}\n </div>\n )}\n\n {/* A sibling rather than a child of the clip, so its focus ring is not\n clipped away with it. Gone entirely when the host is doing the\n opening: an invisible button in the corner of somebody else's page\n would still be there to tab into. */}\n {hasBubble && (\n <button\n ref={launcherRef}\n type=\"button\"\n {...drag.dragHandlers}\n draggable={false}\n // The first sign anybody wants the panel. Fetching the renderer\n // here rather than on the click buys it the time between reaching\n // for the button and pressing it, which is usually all it needs.\n onPointerEnter={warmMarkdown}\n onFocus={warmMarkdown}\n onClick={() => {\n // The pointerup that ended a drag still arrives here as a click.\n // Opening on it would mean the panel came up every time somebody\n // moved the button, which is the one thing they did not ask for.\n if (drag.swallowClick()) return;\n setOpen(true);\n }}\n aria-expanded={isOpen}\n aria-label={\n unreadCount > 0\n ? `Chat with ${presence.name} support, ${unreadCount} unread`\n : `Chat with ${presence.name} support`\n }\n inert={isOpen}\n style={{\n width: buttonSize,\n height: buttonSize,\n // The same height the clip is cut at, from the same variable, so\n // the button and the circle under it cannot end up a pixel apart.\n top: buttonY(buttonSize),\n [drag.side]: 0,\n }}\n className={cn(\n \"absolute grid place-items-center rounded-full\",\n \"bg-primary text-primary-foreground shadow-lg outline-none\",\n // Struck off its own foreground rather than the border token. The\n // button is the inverse of the page it sits on, so `border` is a\n // colour picked to separate a card from the background and lands\n // invisible here: white at a tenth, over white. This reads as a\n // rim in both, because it is a tenth of whatever the icon is.\n \"border border-primary-foreground/15\",\n // Named properties rather than `all`, and the names matter.\n //\n // Its height inside the box is a real length now, and it changes\n // the instant a drag is committed. Under `transition-all` that is\n // an animatable property, so the button eased to its new height\n // over three hundred milliseconds after a two hundred millisecond\n // delay, while the clipped circle it is supposed to be sitting on\n // moved immediately. Half a second of a button beside its own\n // shadow rather than on it.\n //\n // Nothing about where it is should ever animate. The box carries\n // it, in one animation, and that is the only travel there is.\n \"transition-[opacity,transform,filter] motion-reduce:transition-none\",\n \"hover:brightness-125 focus-visible:ring-3 focus-visible:ring-ring/50\",\n // The browser must not claim the gesture for scrolling, or a drag\n // on a phone moves the page instead of the button.\n \"touch-none\",\n isOpen\n ? // Gone before the clip has opened far enough to show what is\n // underneath it.\n \"scale-90 opacity-0 duration-150 ease-out-quint\"\n : \"pointer-events-auto scale-100 opacity-100 delay-200 duration-300 ease-spring\",\n // Nothing on it is selectable, and it is not a thing the browser\n // may pick up and drag as an image either. Both of those end the\n // same way: the gesture becomes the browser's instead of ours.\n \"select-none\",\n // No scale while it is being dragged, and not only because the\n // press-in belongs to a tap. A scaled button measures scaled, and\n // every sum here starts from where its edges are.\n // A pointer at rest, not a grab hand. The button is a button first\n // and a thing you can move second, and hovering it should say the\n // first of those. The grab shows up once one is actually underway.\n !isOpen &&\n (drag.isMoving ? \"cursor-grabbing\" : \"cursor-pointer active:scale-95\"),\n )}\n >\n <ChatCircleDots className=\"size-7\" />\n\n {/* What arrived while they were not looking. The count is already in\n the button's own label, so this is only the picture of it.\n\n Struck off `background` rather than a fixed white, so it inverts\n with the theme and stays legible on the red either way. */}\n {unreadCount > 0 && (\n <span\n aria-hidden\n className={cn(\n \"absolute -top-0.5 -right-0.5 grid min-w-5 place-items-center rounded-full px-1\",\n \"bg-destructive text-xs font-semibold text-background tabular-nums\",\n \"ring-2 ring-card\",\n \"animate-in duration-300 ease-spring fade-in-0 zoom-in-50 motion-reduce:animate-none\",\n )}\n >\n {unreadCount > 9 ? \"9+\" : unreadCount}\n </span>\n )}\n </button>\n )}\n\n {/* The newest unread reply, as a card on the button. A sibling of the\n clip for the same reason the button is, and after it so it sits\n over everything else in the box. It hides itself the moment the\n panel is opening, since the panel is about to show the same words\n in full. */}\n {hasBubble && (\n <WidgetTeaser\n message={teaser}\n isShown={hasTeaser && !isOpen}\n side={drag.side}\n placement={teaserPlacement}\n buttonY={buttonY(buttonSize)}\n buttonSize={buttonSize}\n onOpen={() => setOpen(true)}\n onDismiss={() => onDismissTeaser?.()}\n />\n )}\n </div>\n\n {/* The panel, in the middle of the screen. A sibling of the box rather\n than a child, since it answers to the window and the box answers to\n wherever the host put the button. The focus and escape handling\n above reach it through the same ref the corner's panel wears. */}\n {isModal && (\n <SupportModal\n isOpen={isOpen}\n onClose={close}\n panelRef={panelRef}\n label={`${presence.name} support`}\n >\n {widget}\n </SupportModal>\n )}\n </>\n );\n}\n","import * as e from \"react\";\nconst w = e.forwardRef((l, s) => {\n const {\n alt: r,\n color: a = \"currentColor\",\n size: t = \"1em\",\n weight: o = \"regular\",\n mirrored: i = !1,\n children: n,\n weights: c,\n ...m\n } = l;\n return /* @__PURE__ */ e.createElement(\n \"svg\",\n {\n ref: s,\n xmlns: \"http://www.w3.org/2000/svg\",\n width: t,\n height: t,\n fill: a,\n viewBox: \"0 0 256 256\",\n transform: i ? \"scale(-1, 1)\" : void 0,\n ...m\n },\n !!r && /* @__PURE__ */ e.createElement(\"title\", null, r),\n n,\n c.get(o)\n );\n});\nw.displayName = \"SSRBase\";\nexport {\n w as default\n};\n","import * as o from \"react\";\nimport a from \"../lib/SSRBase.es.js\";\nimport m from \"../defs/ArrowDown.es.js\";\nconst r = o.forwardRef((e, t) => /* @__PURE__ */ o.createElement(a, { ref: t, ...e, weights: m }));\nr.displayName = \"ArrowDownIcon\";\nconst s = r;\nexport {\n s as ArrowDown,\n r as ArrowDownIcon\n};\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M208.49,152.49l-72,72a12,12,0,0,1-17,0l-72-72a12,12,0,0,1,17-17L116,187V40a12,12,0,0,1,24,0V187l51.51-51.52a12,12,0,0,1,17,17Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M200,144l-72,72L56,144Z\", opacity: \"0.2\" }), /* @__PURE__ */ e.createElement(\"path\", { d: \"M207.39,140.94A8,8,0,0,0,200,136H136V40a8,8,0,0,0-16,0v96H56a8,8,0,0,0-5.66,13.66l72,72a8,8,0,0,0,11.32,0l72-72A8,8,0,0,0,207.39,140.94ZM128,204.69,75.31,152H180.69Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M205.66,149.66l-72,72a8,8,0,0,1-11.32,0l-72-72A8,8,0,0,1,56,136h64V40a8,8,0,0,1,16,0v96h64a8,8,0,0,1,5.66,13.66Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M204.24,148.24l-72,72a6,6,0,0,1-8.48,0l-72-72a6,6,0,0,1,8.48-8.48L122,201.51V40a6,6,0,0,1,12,0V201.51l61.76-61.75a6,6,0,0,1,8.48,8.48Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M205.66,149.66l-72,72a8,8,0,0,1-11.32,0l-72-72a8,8,0,0,1,11.32-11.32L120,196.69V40a8,8,0,0,1,16,0V196.69l58.34-58.35a8,8,0,0,1,11.32,11.32Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M202.83,146.83l-72,72a4,4,0,0,1-5.66,0l-72-72a4,4,0,0,1,5.66-5.66L124,206.34V40a4,4,0,0,1,8,0V206.34l65.17-65.17a4,4,0,0,1,5.66,5.66Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as r from \"react\";\nimport a from \"../lib/SSRBase.es.js\";\nimport i from \"../defs/ArrowRight.es.js\";\nconst o = r.forwardRef((t, e) => /* @__PURE__ */ r.createElement(a, { ref: e, ...t, weights: i }));\no.displayName = \"ArrowRightIcon\";\nconst c = o;\nexport {\n c as ArrowRight,\n o as ArrowRightIcon\n};\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M224.49,136.49l-72,72a12,12,0,0,1-17-17L187,140H40a12,12,0,0,1,0-24H187L135.51,64.48a12,12,0,0,1,17-17l72,72A12,12,0,0,1,224.49,136.49Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M216,128l-72,72V56Z\", opacity: \"0.2\" }), /* @__PURE__ */ e.createElement(\"path\", { d: \"M221.66,122.34l-72-72A8,8,0,0,0,136,56v64H40a8,8,0,0,0,0,16h96v64a8,8,0,0,0,13.66,5.66l72-72A8,8,0,0,0,221.66,122.34ZM152,180.69V75.31L204.69,128Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M221.66,133.66l-72,72A8,8,0,0,1,136,200V136H40a8,8,0,0,1,0-16h96V56a8,8,0,0,1,13.66-5.66l72,72A8,8,0,0,1,221.66,133.66Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M220.24,132.24l-72,72a6,6,0,0,1-8.48-8.48L201.51,134H40a6,6,0,0,1,0-12H201.51L139.76,60.24a6,6,0,0,1,8.48-8.48l72,72A6,6,0,0,1,220.24,132.24Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M221.66,133.66l-72,72a8,8,0,0,1-11.32-11.32L196.69,136H40a8,8,0,0,1,0-16H196.69L138.34,61.66a8,8,0,0,1,11.32-11.32l72,72A8,8,0,0,1,221.66,133.66Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M218.83,130.83l-72,72a4,4,0,0,1-5.66-5.66L206.34,132H40a4,4,0,0,1,0-8H206.34L141.17,58.83a4,4,0,0,1,5.66-5.66l72,72A4,4,0,0,1,218.83,130.83Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as r from \"react\";\nimport a from \"../lib/SSRBase.es.js\";\nimport m from \"../defs/ArrowUp.es.js\";\nconst o = r.forwardRef((e, t) => /* @__PURE__ */ r.createElement(a, { ref: t, ...e, weights: m }));\no.displayName = \"ArrowUpIcon\";\nconst c = o;\nexport {\n c as ArrowUp,\n o as ArrowUpIcon\n};\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M208.49,120.49a12,12,0,0,1-17,0L140,69V216a12,12,0,0,1-24,0V69L64.49,120.49a12,12,0,0,1-17-17l72-72a12,12,0,0,1,17,0l72,72A12,12,0,0,1,208.49,120.49Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M200,112H56l72-72Z\", opacity: \"0.2\" }), /* @__PURE__ */ e.createElement(\"path\", { d: \"M205.66,106.34l-72-72a8,8,0,0,0-11.32,0l-72,72A8,8,0,0,0,56,120h64v96a8,8,0,0,0,16,0V120h64a8,8,0,0,0,5.66-13.66ZM75.31,104,128,51.31,180.69,104Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M207.39,115.06A8,8,0,0,1,200,120H136v96a8,8,0,0,1-16,0V120H56a8,8,0,0,1-5.66-13.66l72-72a8,8,0,0,1,11.32,0l72,72A8,8,0,0,1,207.39,115.06Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M204.24,116.24a6,6,0,0,1-8.48,0L134,54.49V216a6,6,0,0,1-12,0V54.49L60.24,116.24a6,6,0,0,1-8.48-8.48l72-72a6,6,0,0,1,8.48,0l72,72A6,6,0,0,1,204.24,116.24Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M205.66,117.66a8,8,0,0,1-11.32,0L136,59.31V216a8,8,0,0,1-16,0V59.31L61.66,117.66a8,8,0,0,1-11.32-11.32l72-72a8,8,0,0,1,11.32,0l72,72A8,8,0,0,1,205.66,117.66Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M202.83,114.83a4,4,0,0,1-5.66,0L132,49.66V216a4,4,0,0,1-8,0V49.66L58.83,114.83a4,4,0,0,1-5.66-5.66l72-72a4,4,0,0,1,5.66,0l72,72A4,4,0,0,1,202.83,114.83Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as o from \"react\";\nimport t from \"../lib/SSRBase.es.js\";\nimport c from \"../defs/ArrowsClockwise.es.js\";\nconst r = o.forwardRef((e, s) => /* @__PURE__ */ o.createElement(t, { ref: s, ...e, weights: c }));\nr.displayName = \"ArrowsClockwiseIcon\";\nconst m = r;\nexport {\n m as ArrowsClockwise,\n r as ArrowsClockwiseIcon\n};\n","import * as a from \"react\";\nconst e = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M228,48V96a12,12,0,0,1-12,12H168a12,12,0,0,1,0-24h19l-7.8-7.8a75.55,75.55,0,0,0-53.32-22.26h-.43A75.49,75.49,0,0,0,72.39,75.57,12,12,0,1,1,55.61,58.41a99.38,99.38,0,0,1,69.87-28.47H126A99.42,99.42,0,0,1,196.2,59.23L204,67V48a12,12,0,0,1,24,0ZM183.61,180.43a75.49,75.49,0,0,1-53.09,21.63h-.43A75.55,75.55,0,0,1,76.77,179.8L69,172H88a12,12,0,0,0,0-24H40a12,12,0,0,0-12,12v48a12,12,0,0,0,24,0V189l7.8,7.8A99.42,99.42,0,0,0,130,226.06h.56a99.38,99.38,0,0,0,69.87-28.47,12,12,0,0,0-16.78-17.16Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M216,128a88,88,0,1,1-88-88A88,88,0,0,1,216,128Z\", opacity: \"0.2\" }), /* @__PURE__ */ a.createElement(\"path\", { d: \"M224,48V96a8,8,0,0,1-8,8H168a8,8,0,0,1,0-16h28.69L182.06,73.37a79.56,79.56,0,0,0-56.13-23.43h-.45A79.52,79.52,0,0,0,69.59,72.71,8,8,0,0,1,58.41,61.27a96,96,0,0,1,135,.79L208,76.69V48a8,8,0,0,1,16,0ZM186.41,183.29a80,80,0,0,1-112.47-.66L59.31,168H88a8,8,0,0,0,0-16H40a8,8,0,0,0-8,8v48a8,8,0,0,0,16,0V179.31l14.63,14.63A95.43,95.43,0,0,0,130,222.06h.53a95.36,95.36,0,0,0,67.07-27.33,8,8,0,0,0-11.18-11.44Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M224,48V96a8,8,0,0,1-8,8H168a8,8,0,0,1-5.66-13.66L180.65,72a79.48,79.48,0,0,0-54.72-22.09h-.45A79.52,79.52,0,0,0,69.59,72.71,8,8,0,0,1,58.41,61.27,96,96,0,0,1,192,60.7l18.36-18.36A8,8,0,0,1,224,48ZM186.41,183.29A80,80,0,0,1,75.35,184l18.31-18.31A8,8,0,0,0,88,152H40a8,8,0,0,0-8,8v48a8,8,0,0,0,13.66,5.66L64,195.3a95.42,95.42,0,0,0,66,26.76h.53a95.36,95.36,0,0,0,67.07-27.33,8,8,0,0,0-11.18-11.44Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M222,48V96a6,6,0,0,1-6,6H168a6,6,0,0,1,0-12h33.52L183.47,72a81.51,81.51,0,0,0-57.53-24h-.46A81.5,81.5,0,0,0,68.19,71.28a6,6,0,1,1-8.38-8.58,93.38,93.38,0,0,1,65.67-26.76H126a93.45,93.45,0,0,1,66,27.53l18,18V48a6,6,0,0,1,12,0ZM187.81,184.72a81.5,81.5,0,0,1-57.29,23.34h-.46a81.51,81.51,0,0,1-57.53-24L54.48,166H88a6,6,0,0,0,0-12H40a6,6,0,0,0-6,6v48a6,6,0,0,0,12,0V174.48l18,18.05a93.45,93.45,0,0,0,66,27.53h.52a93.38,93.38,0,0,0,65.67-26.76,6,6,0,1,0-8.38-8.58Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M224,48V96a8,8,0,0,1-8,8H168a8,8,0,0,1,0-16h28.69L182.06,73.37a79.56,79.56,0,0,0-56.13-23.43h-.45A79.52,79.52,0,0,0,69.59,72.71,8,8,0,0,1,58.41,61.27a96,96,0,0,1,135,.79L208,76.69V48a8,8,0,0,1,16,0ZM186.41,183.29a80,80,0,0,1-112.47-.66L59.31,168H88a8,8,0,0,0,0-16H40a8,8,0,0,0-8,8v48a8,8,0,0,0,16,0V179.31l14.63,14.63A95.43,95.43,0,0,0,130,222.06h.53a95.36,95.36,0,0,0,67.07-27.33,8,8,0,0,0-11.18-11.44Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M220,48V96a4,4,0,0,1-4,4H168a4,4,0,0,1,0-8h38.34L184.89,70.54A84,84,0,0,0,66.8,69.85a4,4,0,1,1-5.6-5.72,92,92,0,0,1,129.34.76L212,86.34V48a4,4,0,0,1,8,0ZM189.2,186.15a83.44,83.44,0,0,1-58.68,23.91h-.47a83.52,83.52,0,0,1-58.94-24.6L49.66,164H88a4,4,0,0,0,0-8H40a4,4,0,0,0-4,4v48a4,4,0,0,0,8,0V169.66l21.46,21.45A91.43,91.43,0,0,0,130,218.06h.51a91.45,91.45,0,0,0,64.28-26.19,4,4,0,1,0-5.6-5.72Z\" }))\n ]\n]);\nexport {\n e as default\n};\n","import * as r from \"react\";\nimport t from \"../lib/SSRBase.es.js\";\nimport a from \"../defs/ArrowsIn.es.js\";\nconst o = r.forwardRef((e, s) => /* @__PURE__ */ r.createElement(t, { ref: s, ...e, weights: a }));\no.displayName = \"ArrowsInIcon\";\nconst c = o;\nexport {\n c as ArrowsIn,\n o as ArrowsInIcon\n};\n","import * as a from \"react\";\nconst e = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M140,104V64a12,12,0,0,1,24,0V75l35.51-35.52a12,12,0,0,1,17,17L181,92h11a12,12,0,0,1,0,24H152A12,12,0,0,1,140,104Zm-36,36H64a12,12,0,0,0,0,24H75L39.51,199.51a12,12,0,0,0,17,17L92,181v11a12,12,0,0,0,24,0V152A12,12,0,0,0,104,140Zm77,24h11a12,12,0,0,0,0-24H152a12,12,0,0,0-12,12v40a12,12,0,0,0,24,0V181l35.51,35.52a12,12,0,0,0,17-17ZM104,52A12,12,0,0,0,92,64V75L56.49,39.51a12,12,0,0,0-17,17L75,92H64a12,12,0,0,0,0,24h40a12,12,0,0,0,12-12V64A12,12,0,0,0,104,52Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\n \"path\",\n {\n d: \"M224,48V208a16,16,0,0,1-16,16H48a16,16,0,0,1-16-16V48A16,16,0,0,1,48,32H208A16,16,0,0,1,224,48Z\",\n opacity: \"0.2\"\n }\n ), /* @__PURE__ */ a.createElement(\"path\", { d: \"M144,104V64a8,8,0,0,1,16,0V84.69l42.34-42.35a8,8,0,0,1,11.32,11.32L171.31,96H192a8,8,0,0,1,0,16H152A8,8,0,0,1,144,104Zm-40,40H64a8,8,0,0,0,0,16H84.69L42.34,202.34a8,8,0,0,0,11.32,11.32L96,171.31V192a8,8,0,0,0,16,0V152A8,8,0,0,0,104,144Zm67.31,16H192a8,8,0,0,0,0-16H152a8,8,0,0,0-8,8v40a8,8,0,0,0,16,0V171.31l42.34,42.35a8,8,0,0,0,11.32-11.32ZM104,56a8,8,0,0,0-8,8V84.69L53.66,42.34A8,8,0,0,0,42.34,53.66L84.69,96H64a8,8,0,0,0,0,16h40a8,8,0,0,0,8-8V64A8,8,0,0,0,104,56Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M144,104V64a8,8,0,0,1,13.66-5.66L172,72.69l30.34-30.35a8,8,0,0,1,11.32,11.32L183.31,84l14.35,14.34A8,8,0,0,1,192,112H152A8,8,0,0,1,144,104Zm-40,40H64a8,8,0,0,0-5.66,13.66L72.69,172,42.34,202.34a8,8,0,0,0,11.32,11.32L84,183.31l14.34,14.35A8,8,0,0,0,112,192V152A8,8,0,0,0,104,144Zm3.06-87.39a8,8,0,0,0-8.72,1.73L84,72.69,53.66,42.34A8,8,0,0,0,42.34,53.66L72.69,84,58.34,98.34A8,8,0,0,0,64,112h40a8,8,0,0,0,8-8V64A8,8,0,0,0,107.06,56.61ZM183.31,172l14.35-14.34A8,8,0,0,0,192,144H152a8,8,0,0,0-8,8v40a8,8,0,0,0,13.66,5.66L172,183.31l30.34,30.35a8,8,0,0,0,11.32-11.32Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M146,104V64a6,6,0,0,1,12,0V89.51l45.76-45.75a6,6,0,0,1,8.48,8.48L166.48,98H192a6,6,0,0,1,0,12H152A6,6,0,0,1,146,104Zm-42,42H64a6,6,0,0,0,0,12H89.52L43.76,203.76a6,6,0,1,0,8.48,8.48L98,166.48V192a6,6,0,0,0,12,0V152A6,6,0,0,0,104,146Zm62.48,12H192a6,6,0,0,0,0-12H152a6,6,0,0,0-6,6v40a6,6,0,0,0,12,0V166.48l45.76,45.76a6,6,0,0,0,8.48-8.48ZM104,58a6,6,0,0,0-6,6V89.51L52.24,43.76a6,6,0,0,0-8.48,8.48L89.52,98H64a6,6,0,0,0,0,12h40a6,6,0,0,0,6-6V64A6,6,0,0,0,104,58Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M144,104V64a8,8,0,0,1,16,0V84.69l42.34-42.35a8,8,0,0,1,11.32,11.32L171.31,96H192a8,8,0,0,1,0,16H152A8,8,0,0,1,144,104Zm-40,40H64a8,8,0,0,0,0,16H84.69L42.34,202.34a8,8,0,0,0,11.32,11.32L96,171.31V192a8,8,0,0,0,16,0V152A8,8,0,0,0,104,144Zm67.31,16H192a8,8,0,0,0,0-16H152a8,8,0,0,0-8,8v40a8,8,0,0,0,16,0V171.31l42.34,42.35a8,8,0,0,0,11.32-11.32ZM104,56a8,8,0,0,0-8,8V84.69L53.66,42.34A8,8,0,0,0,42.34,53.66L84.69,96H64a8,8,0,0,0,0,16h40a8,8,0,0,0,8-8V64A8,8,0,0,0,104,56Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M148,104V64a4,4,0,0,1,8,0V94.34l49.17-49.17a4,4,0,1,1,5.66,5.66L161.66,100H192a4,4,0,0,1,0,8H152A4,4,0,0,1,148,104Zm-44,44H64a4,4,0,0,0,0,8H94.34L45.17,205.17a4,4,0,0,0,5.66,5.66L100,161.66V192a4,4,0,0,0,8,0V152A4,4,0,0,0,104,148Zm57.66,8H192a4,4,0,0,0,0-8H152a4,4,0,0,0-4,4v40a4,4,0,0,0,8,0V161.66l49.17,49.17a4,4,0,0,0,5.66-5.66ZM104,60a4,4,0,0,0-4,4V94.34L50.83,45.17a4,4,0,0,0-5.66,5.66L94.34,100H64a4,4,0,0,0,0,8h40a4,4,0,0,0,4-4V64A4,4,0,0,0,104,60Z\" }))\n ]\n]);\nexport {\n e as default\n};\n","import * as r from \"react\";\nimport s from \"../lib/SSRBase.es.js\";\nimport a from \"../defs/ArrowsOut.es.js\";\nconst o = r.forwardRef((t, e) => /* @__PURE__ */ r.createElement(s, { ref: e, ...t, weights: a }));\no.displayName = \"ArrowsOutIcon\";\nconst f = o;\nexport {\n f as ArrowsOut,\n o as ArrowsOutIcon\n};\n","import * as a from \"react\";\nconst e = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M220,48V96a12,12,0,0,1-24,0V77l-35.51,35.52a12,12,0,0,1-17-17L179,60H160a12,12,0,0,1,0-24h48A12,12,0,0,1,220,48ZM95.51,143.51,60,179V160a12,12,0,0,0-24,0v48a12,12,0,0,0,12,12H96a12,12,0,0,0,0-24H77l35.52-35.51a12,12,0,0,0-17-17ZM208,148a12,12,0,0,0-12,12v19l-35.51-35.52a12,12,0,0,0-17,17L179,196H160a12,12,0,0,0,0,24h48a12,12,0,0,0,12-12V160A12,12,0,0,0,208,148ZM77,60H96a12,12,0,0,0,0-24H48A12,12,0,0,0,36,48V96a12,12,0,0,0,24,0V77l35.51,35.52a12,12,0,0,0,17-17Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M208,48V208H48V48Z\", opacity: \"0.2\" }), /* @__PURE__ */ a.createElement(\"path\", { d: \"M216,48V96a8,8,0,0,1-16,0V67.31l-42.34,42.35a8,8,0,0,1-11.32-11.32L188.69,56H160a8,8,0,0,1,0-16h48A8,8,0,0,1,216,48ZM98.34,146.34,56,188.69V160a8,8,0,0,0-16,0v48a8,8,0,0,0,8,8H96a8,8,0,0,0,0-16H67.31l42.35-42.34a8,8,0,0,0-11.32-11.32ZM208,152a8,8,0,0,0-8,8v28.69l-42.34-42.35a8,8,0,0,0-11.32,11.32L188.69,200H160a8,8,0,0,0,0,16h48a8,8,0,0,0,8-8V160A8,8,0,0,0,208,152ZM67.31,56H96a8,8,0,0,0,0-16H48a8,8,0,0,0-8,8V96a8,8,0,0,0,16,0V67.31l42.34,42.35a8,8,0,0,0,11.32-11.32Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M109.66,146.34a8,8,0,0,1,0,11.32L83.31,184l18.35,18.34A8,8,0,0,1,96,216H48a8,8,0,0,1-8-8V160a8,8,0,0,1,13.66-5.66L72,172.69l26.34-26.35A8,8,0,0,1,109.66,146.34ZM83.31,72l18.35-18.34A8,8,0,0,0,96,40H48a8,8,0,0,0-8,8V96a8,8,0,0,0,13.66,5.66L72,83.31l26.34,26.35a8,8,0,0,0,11.32-11.32ZM208,40H160a8,8,0,0,0-5.66,13.66L172.69,72,146.34,98.34a8,8,0,0,0,11.32,11.32L184,83.31l18.34,18.35A8,8,0,0,0,216,96V48A8,8,0,0,0,208,40Zm3.06,112.61a8,8,0,0,0-8.72,1.73L184,172.69l-26.34-26.35a8,8,0,0,0-11.32,11.32L172.69,184l-18.35,18.34A8,8,0,0,0,160,216h48a8,8,0,0,0,8-8V160A8,8,0,0,0,211.06,152.61Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M214,48V96a6,6,0,0,1-12,0V62.48l-45.76,45.76a6,6,0,0,1-8.48-8.48L193.52,54H160a6,6,0,0,1,0-12h48A6,6,0,0,1,214,48ZM99.76,147.76,54,193.52V160a6,6,0,0,0-12,0v48a6,6,0,0,0,6,6H96a6,6,0,0,0,0-12H62.48l45.76-45.76a6,6,0,0,0-8.48-8.48ZM208,154a6,6,0,0,0-6,6v33.52l-45.76-45.76a6,6,0,0,0-8.48,8.48L193.52,202H160a6,6,0,0,0,0,12h48a6,6,0,0,0,6-6V160A6,6,0,0,0,208,154ZM62.48,54H96a6,6,0,0,0,0-12H48a6,6,0,0,0-6,6V96a6,6,0,0,0,12,0V62.48l45.76,45.76a6,6,0,0,0,8.48-8.48Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M216,48V96a8,8,0,0,1-16,0V67.31l-42.34,42.35a8,8,0,0,1-11.32-11.32L188.69,56H160a8,8,0,0,1,0-16h48A8,8,0,0,1,216,48ZM98.34,146.34,56,188.69V160a8,8,0,0,0-16,0v48a8,8,0,0,0,8,8H96a8,8,0,0,0,0-16H67.31l42.35-42.34a8,8,0,0,0-11.32-11.32ZM208,152a8,8,0,0,0-8,8v28.69l-42.34-42.35a8,8,0,0,0-11.32,11.32L188.69,200H160a8,8,0,0,0,0,16h48a8,8,0,0,0,8-8V160A8,8,0,0,0,208,152ZM67.31,56H96a8,8,0,0,0,0-16H48a8,8,0,0,0-8,8V96a8,8,0,0,0,16,0V67.31l42.34,42.35a8,8,0,0,0,11.32-11.32Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M212,48V96a4,4,0,0,1-8,0V57.66l-49.17,49.17a4,4,0,0,1-5.66-5.66L198.34,52H160a4,4,0,0,1,0-8h48A4,4,0,0,1,212,48ZM101.17,149.17,52,198.34V160a4,4,0,0,0-8,0v48a4,4,0,0,0,4,4H96a4,4,0,0,0,0-8H57.66l49.17-49.17a4,4,0,0,0-5.66-5.66ZM208,156a4,4,0,0,0-4,4v38.34l-49.17-49.17a4,4,0,0,0-5.66,5.66L198.34,204H160a4,4,0,0,0,0,8h48a4,4,0,0,0,4-4V160A4,4,0,0,0,208,156ZM57.66,52H96a4,4,0,0,0,0-8H48a4,4,0,0,0-4,4V96a4,4,0,0,0,8,0V57.66l49.17,49.17a4,4,0,0,0,5.66-5.66Z\" }))\n ]\n]);\nexport {\n e as default\n};\n","import * as o from \"react\";\nimport a from \"../lib/SSRBase.es.js\";\nimport m from \"../defs/Bug.es.js\";\nconst e = o.forwardRef((r, t) => /* @__PURE__ */ o.createElement(a, { ref: t, ...r, weights: m }));\ne.displayName = \"BugIcon\";\nconst f = e;\nexport {\n f as Bug,\n e as BugIcon\n};\n","import * as a from \"react\";\nconst e = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M140,88a16,16,0,1,1,16,16A16,16,0,0,1,140,88ZM100,72a16,16,0,1,0,16,16A16,16,0,0,0,100,72Zm120,72a91.84,91.84,0,0,1-2.34,20.64L236.81,173a12,12,0,0,1-9.62,22l-18-7.85a92,92,0,0,1-162.46,0l-18,7.85a12,12,0,1,1-9.62-22l19.15-8.36A91.84,91.84,0,0,1,36,144v-4H16a12,12,0,0,1,0-24H36v-4a91.84,91.84,0,0,1,2.34-20.64L19.19,83a12,12,0,0,1,9.62-22l18,7.85a92,92,0,0,1,162.46,0l18-7.85a12,12,0,1,1,9.62,22l-19.15,8.36A91.84,91.84,0,0,1,220,112v4h20a12,12,0,0,1,0,24H220ZM60,116H196v-4a68,68,0,0,0-136,0Zm56,94.92V140H60v4A68.1,68.1,0,0,0,116,210.92ZM196,144v-4H140v70.92A68.1,68.1,0,0,0,196,144Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M208,128v16a80,80,0,0,1-160,0V128Z\", opacity: \"0.2\" }), /* @__PURE__ */ a.createElement(\"path\", { d: \"M144,92a12,12,0,1,1,12,12A12,12,0,0,1,144,92ZM100,80a12,12,0,1,0,12,12A12,12,0,0,0,100,80Zm116,64A87.76,87.76,0,0,1,213,167l22.24,9.72A8,8,0,0,1,232,192a7.89,7.89,0,0,1-3.2-.67L207.38,182a88,88,0,0,1-158.76,0L27.2,191.33A7.89,7.89,0,0,1,24,192a8,8,0,0,1-3.2-15.33L43,167A87.76,87.76,0,0,1,40,144v-8H16a8,8,0,0,1,0-16H40v-8a87.76,87.76,0,0,1,3-23L20.8,79.33a8,8,0,1,1,6.4-14.66L48.62,74a88,88,0,0,1,158.76,0l21.42-9.36a8,8,0,0,1,6.4,14.66L213,89.05a87.76,87.76,0,0,1,3,23v8h24a8,8,0,0,1,0,16H216ZM56,120H200v-8a72,72,0,0,0-144,0Zm64,95.54V136H56v8A72.08,72.08,0,0,0,120,215.54ZM200,144v-8H136v79.54A72.08,72.08,0,0,0,200,144Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M168,92a12,12,0,1,1-12-12A12,12,0,0,1,168,92ZM100,80a12,12,0,1,0,12,12A12,12,0,0,0,100,80Zm116,64A87.76,87.76,0,0,1,213,167l22.24,9.72A8,8,0,0,1,232,192a7.89,7.89,0,0,1-3.2-.67L207.38,182a88,88,0,0,1-158.76,0L27.2,191.33A7.89,7.89,0,0,1,24,192a8,8,0,0,1-3.2-15.33L43,167A87.76,87.76,0,0,1,40,144v-8H16a8,8,0,0,1,0-16H40v-8a87.76,87.76,0,0,1,3-23L20.8,79.33a8,8,0,1,1,6.4-14.66L48.62,74a88,88,0,0,1,158.76,0l21.42-9.36a8,8,0,0,1,6.4,14.66L213,89.05a87.76,87.76,0,0,1,3,23v8h24a8,8,0,0,1,0,16H216Zm-80,0a8,8,0,0,0-16,0v64a8,8,0,0,0,16,0Zm64-32a72,72,0,0,0-144,0v8H200Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M146,92a10,10,0,1,1,10,10A10,10,0,0,1,146,92ZM100,82a10,10,0,1,0,10,10A10,10,0,0,0,100,82Zm146,46a6,6,0,0,1-6,6H214v10a85.88,85.88,0,0,1-3.45,24.08L234.4,178.5a6,6,0,0,1-4.8,11l-23.23-10.15a86,86,0,0,1-156.74,0L26.4,189.5a6,6,0,1,1-4.8-11l23.85-10.42A85.88,85.88,0,0,1,42,144V134H16a6,6,0,0,1,0-12H42V112a85.88,85.88,0,0,1,3.45-24.08L21.6,77.5a6,6,0,0,1,4.8-11L49.63,76.65a86,86,0,0,1,156.74,0L229.6,66.5a6,6,0,1,1,4.8,11L210.55,87.92A85.88,85.88,0,0,1,214,112v10h26A6,6,0,0,1,246,128ZM54,122H202V112a74,74,0,0,0-148,0Zm68,95.74V134H54v10A74.09,74.09,0,0,0,122,217.74ZM202,134H134v83.74A74.09,74.09,0,0,0,202,144Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M144,92a12,12,0,1,1,12,12A12,12,0,0,1,144,92ZM100,80a12,12,0,1,0,12,12A12,12,0,0,0,100,80Zm116,64A87.76,87.76,0,0,1,213,167l22.24,9.72A8,8,0,0,1,232,192a7.89,7.89,0,0,1-3.2-.67L207.38,182a88,88,0,0,1-158.76,0L27.2,191.33A7.89,7.89,0,0,1,24,192a8,8,0,0,1-3.2-15.33L43,167A87.76,87.76,0,0,1,40,144v-8H16a8,8,0,0,1,0-16H40v-8a87.76,87.76,0,0,1,3-23L20.8,79.33a8,8,0,1,1,6.4-14.66L48.62,74a88,88,0,0,1,158.76,0l21.42-9.36a8,8,0,0,1,6.4,14.66L213,89.05a87.76,87.76,0,0,1,3,23v8h24a8,8,0,0,1,0,16H216ZM56,120H200v-8a72,72,0,0,0-144,0Zm64,95.54V136H56v8A72.08,72.08,0,0,0,120,215.54ZM200,144v-8H136v79.54A72.08,72.08,0,0,0,200,144Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M148,92a8,8,0,1,1,8,8A8,8,0,0,1,148,92Zm-48-8a8,8,0,1,0,8,8A8,8,0,0,0,100,84Zm144,44a4,4,0,0,1-4,4H212v12a83.64,83.64,0,0,1-3.87,25.2l25.47,11.13A4,4,0,0,1,232,188a4.09,4.09,0,0,1-1.6-.33l-25-10.95a84,84,0,0,1-154.72,0l-25,10.95A4.09,4.09,0,0,1,24,188a4,4,0,0,1-1.6-7.67L47.87,169.2A83.64,83.64,0,0,1,44,144V132H16a4,4,0,0,1,0-8H44V112a83.64,83.64,0,0,1,3.87-25.2L22.4,75.67a4,4,0,0,1,3.2-7.34l25,11a84,84,0,0,1,154.72,0l25-11a4,4,0,1,1,3.2,7.34L208.13,86.8A83.64,83.64,0,0,1,212,112v12h28A4,4,0,0,1,244,128ZM52,124H204V112a76,76,0,0,0-152,0Zm72,95.89V132H52v12A76.09,76.09,0,0,0,124,219.89ZM204,132H132v87.89A76.09,76.09,0,0,0,204,144Z\" }))\n ]\n]);\nexport {\n e as default\n};\n","import * as o from \"react\";\nimport a from \"../lib/SSRBase.es.js\";\nimport m from \"../defs/CaretDown.es.js\";\nconst e = o.forwardRef((r, t) => /* @__PURE__ */ o.createElement(a, { ref: t, ...r, weights: m }));\ne.displayName = \"CaretDownIcon\";\nconst c = e;\nexport {\n c as CaretDown,\n e as CaretDownIcon\n};\n","import * as e from \"react\";\nconst t = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M216.49,104.49l-80,80a12,12,0,0,1-17,0l-80-80a12,12,0,0,1,17-17L128,159l71.51-71.52a12,12,0,0,1,17,17Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M208,96l-80,80L48,96Z\", opacity: \"0.2\" }), /* @__PURE__ */ e.createElement(\"path\", { d: \"M215.39,92.94A8,8,0,0,0,208,88H48a8,8,0,0,0-5.66,13.66l80,80a8,8,0,0,0,11.32,0l80-80A8,8,0,0,0,215.39,92.94ZM128,164.69,67.31,104H188.69Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,48,88H208a8,8,0,0,1,5.66,13.66Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M212.24,100.24l-80,80a6,6,0,0,1-8.48,0l-80-80a6,6,0,0,1,8.48-8.48L128,167.51l75.76-75.75a6,6,0,0,1,8.48,8.48Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M210.83,98.83l-80,80a4,4,0,0,1-5.66,0l-80-80a4,4,0,0,1,5.66-5.66L128,170.34l77.17-77.17a4,4,0,1,1,5.66,5.66Z\" }))\n ]\n]);\nexport {\n t as default\n};\n","import * as t from \"react\";\nimport a from \"../lib/SSRBase.es.js\";\nimport i from \"../defs/CaretRight.es.js\";\nconst e = t.forwardRef((r, o) => /* @__PURE__ */ t.createElement(a, { ref: o, ...r, weights: i }));\ne.displayName = \"CaretRightIcon\";\nconst c = e;\nexport {\n c as CaretRight,\n e as CaretRightIcon\n};\n","import * as e from \"react\";\nconst t = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M184.49,136.49l-80,80a12,12,0,0,1-17-17L159,128,87.51,56.49a12,12,0,1,1,17-17l80,80A12,12,0,0,1,184.49,136.49Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M176,128,96,208V48Z\", opacity: \"0.2\" }), /* @__PURE__ */ e.createElement(\"path\", { d: \"M181.66,122.34l-80-80A8,8,0,0,0,88,48V208a8,8,0,0,0,13.66,5.66l80-80A8,8,0,0,0,181.66,122.34ZM104,188.69V67.31L164.69,128Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M181.66,133.66l-80,80A8,8,0,0,1,88,208V48a8,8,0,0,1,13.66-5.66l80,80A8,8,0,0,1,181.66,133.66Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M180.24,132.24l-80,80a6,6,0,0,1-8.48-8.48L167.51,128,91.76,52.24a6,6,0,0,1,8.48-8.48l80,80A6,6,0,0,1,180.24,132.24Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M181.66,133.66l-80,80a8,8,0,0,1-11.32-11.32L164.69,128,90.34,53.66a8,8,0,0,1,11.32-11.32l80,80A8,8,0,0,1,181.66,133.66Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M178.83,130.83l-80,80a4,4,0,0,1-5.66-5.66L170.34,128,93.17,50.83a4,4,0,0,1,5.66-5.66l80,80A4,4,0,0,1,178.83,130.83Z\" }))\n ]\n]);\nexport {\n t as default\n};\n","import * as t from \"react\";\nimport a from \"../lib/SSRBase.es.js\";\nimport s from \"../defs/ChatCircleDots.es.js\";\nconst o = t.forwardRef((e, r) => /* @__PURE__ */ t.createElement(a, { ref: r, ...e, weights: s }));\no.displayName = \"ChatCircleDotsIcon\";\nconst m = o;\nexport {\n m as ChatCircleDots,\n o as ChatCircleDotsIcon\n};\n","import * as a from \"react\";\nconst e = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M120,128a16,16,0,1,1-16-16A16,16,0,0,1,120,128Zm32-16a16,16,0,1,0,16,16A16,16,0,0,0,152,112Zm84,16A108,108,0,0,1,78.77,224.15L46.34,235A20,20,0,0,1,21,209.66l10.81-32.43A108,108,0,1,1,236,128Zm-24,0A84,84,0,1,0,55.27,170.06a12,12,0,0,1,1,9.81l-9.93,29.79,29.79-9.93a12.1,12.1,0,0,1,3.8-.62,12,12,0,0,1,6,1.62A84,84,0,0,0,212,128Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\n \"path\",\n {\n d: \"M224,128A96,96,0,0,1,79.93,211.11h0L42.54,223.58a8,8,0,0,1-10.12-10.12l12.47-37.39h0A96,96,0,1,1,224,128Z\",\n opacity: \"0.2\"\n }\n ), /* @__PURE__ */ a.createElement(\"path\", { d: \"M128,24A104,104,0,0,0,36.18,176.88L24.83,210.93a16,16,0,0,0,20.24,20.24l34.05-11.35A104,104,0,1,0,128,24Zm0,192a87.87,87.87,0,0,1-44.06-11.81,8,8,0,0,0-4-1.08,7.85,7.85,0,0,0-2.53.42L40,216,52.47,178.6a8,8,0,0,0-.66-6.54A88,88,0,1,1,128,216Zm12-88a12,12,0,1,1-12-12A12,12,0,0,1,140,128Zm-44,0a12,12,0,1,1-12-12A12,12,0,0,1,96,128Zm88,0a12,12,0,1,1-12-12A12,12,0,0,1,184,128Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M128,24A104,104,0,0,0,36.18,176.88L24.83,210.93a16,16,0,0,0,20.24,20.24l34.05-11.35A104,104,0,1,0,128,24ZM84,140a12,12,0,1,1,12-12A12,12,0,0,1,84,140Zm44,0a12,12,0,1,1,12-12A12,12,0,0,1,128,140Zm44,0a12,12,0,1,1,12-12A12,12,0,0,1,172,140Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M138,128a10,10,0,1,1-10-10A10,10,0,0,1,138,128ZM84,118a10,10,0,1,0,10,10A10,10,0,0,0,84,118Zm88,0a10,10,0,1,0,10,10A10,10,0,0,0,172,118Zm58,10A102,102,0,0,1,79.31,217.65L44.44,229.27a14,14,0,0,1-17.71-17.71l11.62-34.87A102,102,0,1,1,230,128Zm-12,0A90,90,0,1,0,50.08,173.06a6,6,0,0,1,.5,4.91L38.12,215.35a2,2,0,0,0,2.53,2.53L78,205.42a6.2,6.2,0,0,1,1.9-.31,6.09,6.09,0,0,1,3,.81A90,90,0,0,0,218,128Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M140,128a12,12,0,1,1-12-12A12,12,0,0,1,140,128ZM84,116a12,12,0,1,0,12,12A12,12,0,0,0,84,116Zm88,0a12,12,0,1,0,12,12A12,12,0,0,0,172,116Zm60,12A104,104,0,0,1,79.12,219.82L45.07,231.17a16,16,0,0,1-20.24-20.24l11.35-34.05A104,104,0,1,1,232,128Zm-16,0A88,88,0,1,0,51.81,172.06a8,8,0,0,1,.66,6.54L40,216,77.4,203.53a7.85,7.85,0,0,1,2.53-.42,8,8,0,0,1,4,1.08A88,88,0,0,0,216,128Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M136,128a8,8,0,1,1-8-8A8,8,0,0,1,136,128Zm-52-8a8,8,0,1,0,8,8A8,8,0,0,0,84,120Zm88,0a8,8,0,1,0,8,8A8,8,0,0,0,172,120Zm56,8A100,100,0,0,1,79.5,215.47l-35.69,11.9a12,12,0,0,1-15.18-15.18l11.9-35.69A100,100,0,1,1,228,128Zm-8,0A92,92,0,1,0,48.35,174.07a4,4,0,0,1,.33,3.27L36.22,214.72a4,4,0,0,0,5.06,5.06l37.38-12.46a3.93,3.93,0,0,1,1.27-.21,4.05,4.05,0,0,1,2,.54A92,92,0,0,0,220,128Z\" }))\n ]\n]);\nexport {\n e as default\n};\n","import * as e from \"react\";\nimport c from \"../lib/SSRBase.es.js\";\nimport a from \"../defs/Check.es.js\";\nconst o = e.forwardRef((r, t) => /* @__PURE__ */ e.createElement(c, { ref: t, ...r, weights: a }));\no.displayName = \"CheckIcon\";\nconst f = o;\nexport {\n f as Check,\n o as CheckIcon\n};\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\n \"path\",\n {\n d: \"M232,56V200a16,16,0,0,1-16,16H40a16,16,0,0,1-16-16V56A16,16,0,0,1,40,40H216A16,16,0,0,1,232,56Z\",\n opacity: \"0.2\"\n }\n ), /* @__PURE__ */ e.createElement(\"path\", { d: \"M205.66,85.66l-96,96a8,8,0,0,1-11.32,0l-40-40a8,8,0,0,1,11.32-11.32L104,164.69l90.34-90.35a8,8,0,0,1,11.32,11.32Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M216,40H40A16,16,0,0,0,24,56V200a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40ZM205.66,85.66l-96,96a8,8,0,0,1-11.32,0l-40-40a8,8,0,0,1,11.32-11.32L104,164.69l90.34-90.35a8,8,0,0,1,11.32,11.32Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M228.24,76.24l-128,128a6,6,0,0,1-8.48,0l-56-56a6,6,0,0,1,8.48-8.48L96,191.51,219.76,67.76a6,6,0,0,1,8.48,8.48Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M226.83,74.83l-128,128a4,4,0,0,1-5.66,0l-56-56a4,4,0,0,1,5.66-5.66L96,194.34,221.17,69.17a4,4,0,1,1,5.66,5.66Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as e from \"react\";\nimport r from \"../lib/SSRBase.es.js\";\nimport t from \"../defs/EnvelopeSimple.es.js\";\nconst o = e.forwardRef((m, p) => /* @__PURE__ */ e.createElement(r, { ref: p, ...m, weights: t }));\no.displayName = \"EnvelopeSimpleIcon\";\nconst l = o;\nexport {\n l as EnvelopeSimple,\n o as EnvelopeSimpleIcon\n};\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M224,44H32A12,12,0,0,0,20,56V192a20,20,0,0,0,20,20H216a20,20,0,0,0,20-20V56A12,12,0,0,0,224,44ZM193.15,68,128,127.72,62.85,68ZM44,188V83.28l75.89,69.57a12,12,0,0,0,16.22,0L212,83.28V188Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M224,56l-96,88L32,56Z\", opacity: \"0.2\" }), /* @__PURE__ */ e.createElement(\"path\", { d: \"M224,48H32a8,8,0,0,0-8,8V192a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A8,8,0,0,0,224,48ZM203.43,64,128,133.15,52.57,64ZM216,192H40V74.19l82.59,75.71a8,8,0,0,0,10.82,0L216,74.19V192Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M224,48H32a8,8,0,0,0-8,8V192a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A8,8,0,0,0,224,48Zm-8,144H40V74.19l82.59,75.71a8,8,0,0,0,10.82,0L216,74.19V192Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M224,50H32a6,6,0,0,0-6,6V192a14,14,0,0,0,14,14H216a14,14,0,0,0,14-14V56A6,6,0,0,0,224,50ZM208.58,62,128,135.86,47.42,62ZM216,194H40a2,2,0,0,1-2-2V69.64l86,78.78a6,6,0,0,0,8.1,0L218,69.64V192A2,2,0,0,1,216,194Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M224,48H32a8,8,0,0,0-8,8V192a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A8,8,0,0,0,224,48ZM203.43,64,128,133.15,52.57,64ZM216,192H40V74.19l82.59,75.71a8,8,0,0,0,10.82,0L216,74.19V192Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M224,52H32a4,4,0,0,0-4,4V192a12,12,0,0,0,12,12H216a12,12,0,0,0,12-12V56A4,4,0,0,0,224,52Zm-10.28,8L128,138.57,42.28,60ZM216,196H40a4,4,0,0,1-4-4V65.09L125.3,147a4,4,0,0,0,5.4,0L220,65.09V192A4,4,0,0,1,216,196Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as e from \"react\";\nimport a from \"../lib/SSRBase.es.js\";\nimport i from \"../defs/File.es.js\";\nconst o = e.forwardRef((r, t) => /* @__PURE__ */ e.createElement(a, { ref: t, ...r, weights: i }));\no.displayName = \"FileIcon\";\nconst c = o;\nexport {\n c as File,\n o as FileIcon\n};\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M216.49,79.52l-56-56A12,12,0,0,0,152,20H56A20,20,0,0,0,36,40V216a20,20,0,0,0,20,20H200a20,20,0,0,0,20-20V88A12,12,0,0,0,216.49,79.52ZM160,57l23,23H160ZM60,212V44h76V92a12,12,0,0,0,12,12h48V212Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M208,88H152V32Z\", opacity: \"0.2\" }), /* @__PURE__ */ e.createElement(\"path\", { d: \"M213.66,82.34l-56-56A8,8,0,0,0,152,24H56A16,16,0,0,0,40,40V216a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V88A8,8,0,0,0,213.66,82.34ZM160,51.31,188.69,80H160ZM200,216H56V40h88V88a8,8,0,0,0,8,8h48V216Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M213.66,82.34l-56-56A8,8,0,0,0,152,24H56A16,16,0,0,0,40,40V216a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V88A8,8,0,0,0,213.66,82.34ZM152,88V44l44,44Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M212.24,83.76l-56-56A6,6,0,0,0,152,26H56A14,14,0,0,0,42,40V216a14,14,0,0,0,14,14H200a14,14,0,0,0,14-14V88A6,6,0,0,0,212.24,83.76ZM158,46.48,193.52,82H158ZM200,218H56a2,2,0,0,1-2-2V40a2,2,0,0,1,2-2h90V88a6,6,0,0,0,6,6h50V216A2,2,0,0,1,200,218Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M213.66,82.34l-56-56A8,8,0,0,0,152,24H56A16,16,0,0,0,40,40V216a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V88A8,8,0,0,0,213.66,82.34ZM160,51.31,188.69,80H160ZM200,216H56V40h88V88a8,8,0,0,0,8,8h48V216Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M210.83,85.17l-56-56A4,4,0,0,0,152,28H56A12,12,0,0,0,44,40V216a12,12,0,0,0,12,12H200a12,12,0,0,0,12-12V88A4,4,0,0,0,210.83,85.17ZM156,41.65,198.34,84H156ZM200,220H56a4,4,0,0,1-4-4V40a4,4,0,0,1,4-4h92V88a4,4,0,0,0,4,4h52V216A4,4,0,0,1,200,220Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as e from \"react\";\nimport m from \"../lib/SSRBase.es.js\";\nimport t from \"../defs/ImageSquare.es.js\";\nconst a = e.forwardRef((r, o) => /* @__PURE__ */ e.createElement(m, { ref: o, ...r, weights: t }));\na.displayName = \"ImageSquareIcon\";\nconst I = a;\nexport {\n I as ImageSquare,\n a as ImageSquareIcon\n};\n","import * as a from \"react\";\nconst e = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M208,28H48A20,20,0,0,0,28,48V208a20,20,0,0,0,20,20H208a20,20,0,0,0,20-20V48A20,20,0,0,0,208,28Zm-4,24v63.72L186.14,97.86a20,20,0,0,0-28.28,0L52,203.72V52ZM85.66,204,172,117.66l32,32V204ZM76,96a20,20,0,1,1,20,20A20,20,0,0,1,76,96Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\n \"path\",\n {\n d: \"M208,40H48a8,8,0,0,0-8,8V208a8,8,0,0,0,8,8h8.69L166.34,106.34a8,8,0,0,1,11.32,0L216,144.69V48A8,8,0,0,0,208,40ZM96,112a16,16,0,1,1,16-16A16,16,0,0,1,96,112Z\",\n opacity: \"0.2\"\n }\n ), /* @__PURE__ */ a.createElement(\"path\", { d: \"M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM48,48H208v77.38l-24.69-24.7a16,16,0,0,0-22.62,0L53.37,208H48ZM208,208H76l96-96,36,36v60ZM96,120A24,24,0,1,0,72,96,24,24,0,0,0,96,120Zm0-32a8,8,0,1,1-8,8A8,8,0,0,1,96,88Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM48,48H208v77.38l-24.69-24.7a16,16,0,0,0-22.62,0L53.37,208H48ZM80,96a16,16,0,1,1,16,16A16,16,0,0,1,80,96Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M208,34H48A14,14,0,0,0,34,48V208a14,14,0,0,0,14,14H208a14,14,0,0,0,14-14V48A14,14,0,0,0,208,34ZM46,208V48a2,2,0,0,1,2-2H208a2,2,0,0,1,2,2v82.2l-28.1-28.1a14,14,0,0,0-19.8,0L54.2,210H48A2,2,0,0,1,46,208Zm162,2H71.17l99.41-99.41a2,2,0,0,1,2.83,0L210,147.17V208A2,2,0,0,1,208,210ZM96,118A22,22,0,1,0,74,96,22,22,0,0,0,96,118Zm0-32A10,10,0,1,1,86,96,10,10,0,0,1,96,86Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM48,48H208v77.38l-24.69-24.7a16,16,0,0,0-22.62,0L53.37,208H48ZM208,208H76l96-96,36,36v60ZM96,120A24,24,0,1,0,72,96,24,24,0,0,0,96,120Zm0-32a8,8,0,1,1-8,8A8,8,0,0,1,96,88Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M208,36H48A12,12,0,0,0,36,48V208a12,12,0,0,0,12,12H208a12,12,0,0,0,12-12V48A12,12,0,0,0,208,36ZM44,208V48a4,4,0,0,1,4-4H208a4,4,0,0,1,4,4v87l-31.51-31.52a12,12,0,0,0-17,0L55,212H48A4,4,0,0,1,44,208Zm164,4H66.34L169.17,109.17a4,4,0,0,1,5.66,0L212,146.34V208A4,4,0,0,1,208,212ZM96,116A20,20,0,1,0,76,96,20,20,0,0,0,96,116Zm0-32A12,12,0,1,1,84,96,12,12,0,0,1,96,84Z\" }))\n ]\n]);\nexport {\n e as default\n};\n","import * as t from \"react\";\nimport a from \"../lib/SSRBase.es.js\";\nimport i from \"../defs/Lightbulb.es.js\";\nconst o = t.forwardRef((e, r) => /* @__PURE__ */ t.createElement(a, { ref: r, ...e, weights: i }));\no.displayName = \"LightbulbIcon\";\nconst b = o;\nexport {\n b as Lightbulb,\n o as LightbulbIcon\n};\n","import * as a from \"react\";\nconst e = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M180,232a12,12,0,0,1-12,12H88a12,12,0,0,1,0-24h80A12,12,0,0,1,180,232Zm40-128a91.51,91.51,0,0,1-35.17,72.35A12.26,12.26,0,0,0,180,186v2a20,20,0,0,1-20,20H96a20,20,0,0,1-20-20v-2a12,12,0,0,0-4.7-9.51A91.57,91.57,0,0,1,36,104.52C35.73,54.69,76,13.2,125.79,12A92,92,0,0,1,220,104Zm-24,0a68,68,0,0,0-69.65-68C89.56,36.88,59.8,67.55,60,104.38a67.71,67.71,0,0,0,26.1,53.19A35.87,35.87,0,0,1,100,184h56.1A36.13,36.13,0,0,1,170,157.49,67.68,67.68,0,0,0,196,104Zm-20.07-5.32a48.5,48.5,0,0,0-31.91-40,12,12,0,0,0-8,22.62,24.31,24.31,0,0,1,16.09,20,12,12,0,0,0,23.86-2.64Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\n \"path\",\n {\n d: \"M208,104a79.86,79.86,0,0,1-30.59,62.92A24.29,24.29,0,0,0,168,186v6a8,8,0,0,1-8,8H96a8,8,0,0,1-8-8v-6a24.11,24.11,0,0,0-9.3-19A79.87,79.87,0,0,1,48,104.45C47.76,61.09,82.72,25,126.07,24A80,80,0,0,1,208,104Z\",\n opacity: \"0.2\"\n }\n ), /* @__PURE__ */ a.createElement(\"path\", { d: \"M176,232a8,8,0,0,1-8,8H88a8,8,0,0,1,0-16h80A8,8,0,0,1,176,232Zm40-128a87.55,87.55,0,0,1-33.64,69.21A16.24,16.24,0,0,0,176,186v6a16,16,0,0,1-16,16H96a16,16,0,0,1-16-16v-6a16,16,0,0,0-6.23-12.66A87.59,87.59,0,0,1,40,104.49C39.74,56.83,78.26,17.14,125.88,16A88,88,0,0,1,216,104Zm-16,0a72,72,0,0,0-73.74-72c-39,.92-70.47,33.39-70.26,72.39a71.65,71.65,0,0,0,27.64,56.3A32,32,0,0,1,96,186v6h64v-6a32.15,32.15,0,0,1,12.47-25.35A71.65,71.65,0,0,0,200,104Zm-16.11-9.34a57.6,57.6,0,0,0-46.56-46.55,8,8,0,0,0-2.66,15.78c16.57,2.79,30.63,16.85,33.44,33.45A8,8,0,0,0,176,104a9,9,0,0,0,1.35-.11A8,8,0,0,0,183.89,94.66Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M176,232a8,8,0,0,1-8,8H88a8,8,0,0,1,0-16h80A8,8,0,0,1,176,232Zm40-128a87.55,87.55,0,0,1-33.64,69.21A16.24,16.24,0,0,0,176,186v6a16,16,0,0,1-16,16H96a16,16,0,0,1-16-16v-6a16,16,0,0,0-6.23-12.66A87.59,87.59,0,0,1,40,104.49C39.74,56.83,78.26,17.14,125.88,16A88,88,0,0,1,216,104Zm-32.11-9.34a57.6,57.6,0,0,0-46.56-46.55,8,8,0,0,0-2.66,15.78c16.57,2.79,30.63,16.85,33.44,33.45A8,8,0,0,0,176,104a9,9,0,0,0,1.35-.11A8,8,0,0,0,183.89,94.66Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M174,232a6,6,0,0,1-6,6H88a6,6,0,0,1,0-12h80A6,6,0,0,1,174,232Zm40-128a85.56,85.56,0,0,1-32.88,67.64A18.23,18.23,0,0,0,174,186v6a14,14,0,0,1-14,14H96a14,14,0,0,1-14-14v-6a18,18,0,0,0-7-14.23h0a85.59,85.59,0,0,1-33-67.24C41.74,57.91,79.39,19.12,125.93,18A86,86,0,0,1,214,104Zm-12,0a74,74,0,0,0-75.79-74C86.17,31,53.78,64.34,54,104.42a73.67,73.67,0,0,0,28.4,57.87A29.92,29.92,0,0,1,94,186v6a2,2,0,0,0,2,2h64a2,2,0,0,0,2-2v-6a30.18,30.18,0,0,1,11.7-23.78A73.59,73.59,0,0,0,202,104Zm-20.08-9A55.58,55.58,0,0,0,137,50.08a6,6,0,1,0-2,11.84C152.38,64.84,167.13,79.6,170.08,97a6,6,0,0,0,5.91,5,6.87,6.87,0,0,0,1-.08A6,6,0,0,0,181.92,95Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M176,232a8,8,0,0,1-8,8H88a8,8,0,0,1,0-16h80A8,8,0,0,1,176,232Zm40-128a87.55,87.55,0,0,1-33.64,69.21A16.24,16.24,0,0,0,176,186v6a16,16,0,0,1-16,16H96a16,16,0,0,1-16-16v-6a16,16,0,0,0-6.23-12.66A87.59,87.59,0,0,1,40,104.49C39.74,56.83,78.26,17.14,125.88,16A88,88,0,0,1,216,104Zm-16,0a72,72,0,0,0-73.74-72c-39,.92-70.47,33.39-70.26,72.39a71.65,71.65,0,0,0,27.64,56.3A32,32,0,0,1,96,186v6h64v-6a32.15,32.15,0,0,1,12.47-25.35A71.65,71.65,0,0,0,200,104Zm-16.11-9.34a57.6,57.6,0,0,0-46.56-46.55,8,8,0,0,0-2.66,15.78c16.57,2.79,30.63,16.85,33.44,33.45A8,8,0,0,0,176,104a9,9,0,0,0,1.35-.11A8,8,0,0,0,183.89,94.66Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M172,232a4,4,0,0,1-4,4H88a4,4,0,0,1,0-8h80A4,4,0,0,1,172,232Zm40-128a83.59,83.59,0,0,1-32.11,66.06A20.2,20.2,0,0,0,172,186v6a12,12,0,0,1-12,12H96a12,12,0,0,1-12-12v-6a20,20,0,0,0-7.76-15.81A83.58,83.58,0,0,1,44,104.47C43.75,59,80.52,21.09,126,20a84,84,0,0,1,86,84Zm-8,0a76,76,0,0,0-77.83-76C85,29,51.77,63.27,52,104.43a75.62,75.62,0,0,0,29.17,59.43A28,28,0,0,1,92,186v6a4,4,0,0,0,4,4h64a4,4,0,0,0,4-4v-6a28.14,28.14,0,0,1,10.94-22.2A75.62,75.62,0,0,0,204,104ZM136.66,52.06a4,4,0,0,0-1.32,7.88C153.53,63,169,78.45,172.06,96.67A4,4,0,0,0,176,100a3.88,3.88,0,0,0,.67-.06,4,4,0,0,0,3.27-4.61A53.51,53.51,0,0,0,136.66,52.06Z\" }))\n ]\n]);\nexport {\n e as default\n};\n","import * as o from \"react\";\nimport t from \"../lib/SSRBase.es.js\";\nimport c from \"../defs/LockSimple.es.js\";\nconst e = o.forwardRef((m, r) => /* @__PURE__ */ o.createElement(t, { ref: r, ...m, weights: c }));\ne.displayName = \"LockSimpleIcon\";\nconst p = e;\nexport {\n p as LockSimple,\n e as LockSimpleIcon\n};\n","import * as a from \"react\";\nconst e = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M208,76H180V56A52,52,0,0,0,76,56V76H48A20,20,0,0,0,28,96V208a20,20,0,0,0,20,20H208a20,20,0,0,0,20-20V96A20,20,0,0,0,208,76ZM100,56a28,28,0,0,1,56,0V76H100ZM204,204H52V100H204Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\n \"path\",\n {\n d: \"M216,96V208a8,8,0,0,1-8,8H48a8,8,0,0,1-8-8V96a8,8,0,0,1,8-8H208A8,8,0,0,1,216,96Z\",\n opacity: \"0.2\"\n }\n ), /* @__PURE__ */ a.createElement(\"path\", { d: \"M208,80H176V56a48,48,0,0,0-96,0V80H48A16,16,0,0,0,32,96V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V96A16,16,0,0,0,208,80ZM96,56a32,32,0,0,1,64,0V80H96ZM208,208H48V96H208V208Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M208,80H176V56a48,48,0,0,0-96,0V80H48A16,16,0,0,0,32,96V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V96A16,16,0,0,0,208,80ZM96,56a32,32,0,0,1,64,0V80H96Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M208,82H174V56a46,46,0,0,0-92,0V82H48A14,14,0,0,0,34,96V208a14,14,0,0,0,14,14H208a14,14,0,0,0,14-14V96A14,14,0,0,0,208,82ZM94,56a34,34,0,0,1,68,0V82H94ZM210,208a2,2,0,0,1-2,2H48a2,2,0,0,1-2-2V96a2,2,0,0,1,2-2H208a2,2,0,0,1,2,2Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M208,80H176V56a48,48,0,0,0-96,0V80H48A16,16,0,0,0,32,96V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V96A16,16,0,0,0,208,80ZM96,56a32,32,0,0,1,64,0V80H96ZM208,208H48V96H208V208Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M208,84H172V56a44,44,0,0,0-88,0V84H48A12,12,0,0,0,36,96V208a12,12,0,0,0,12,12H208a12,12,0,0,0,12-12V96A12,12,0,0,0,208,84ZM92,56a36,36,0,0,1,72,0V84H92ZM212,208a4,4,0,0,1-4,4H48a4,4,0,0,1-4-4V96a4,4,0,0,1,4-4H208a4,4,0,0,1,4,4Z\" }))\n ]\n]);\nexport {\n e as default\n};\n","import * as o from \"react\";\nimport t from \"../lib/SSRBase.es.js\";\nimport m from \"../defs/Play.es.js\";\nconst a = o.forwardRef((e, r) => /* @__PURE__ */ o.createElement(t, { ref: r, ...e, weights: m }));\na.displayName = \"PlayIcon\";\nconst f = a;\nexport {\n f as Play,\n a as PlayIcon\n};\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M234.49,111.07,90.41,22.94A20,20,0,0,0,60,39.87V216.13a20,20,0,0,0,30.41,16.93l144.08-88.13a19.82,19.82,0,0,0,0-33.86ZM84,208.85V47.15L216.16,128Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\n \"path\",\n {\n d: \"M228.23,134.69,84.15,222.81A8,8,0,0,1,72,216.12V39.88a8,8,0,0,1,12.15-6.69l144.08,88.12A7.82,7.82,0,0,1,228.23,134.69Z\",\n opacity: \"0.2\"\n }\n ), /* @__PURE__ */ e.createElement(\"path\", { d: \"M232.4,114.49,88.32,26.35a16,16,0,0,0-16.2-.3A15.86,15.86,0,0,0,64,39.87V216.13A15.94,15.94,0,0,0,80,232a16.07,16.07,0,0,0,8.36-2.35L232.4,141.51a15.81,15.81,0,0,0,0-27ZM80,215.94V40l143.83,88Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M240,128a15.74,15.74,0,0,1-7.6,13.51L88.32,229.65a16,16,0,0,1-16.2.3A15.86,15.86,0,0,1,64,216.13V39.87a15.86,15.86,0,0,1,8.12-13.82,16,16,0,0,1,16.2.3L232.4,114.49A15.74,15.74,0,0,1,240,128Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M231.36,116.19,87.28,28.06a14,14,0,0,0-14.18-.27A13.69,13.69,0,0,0,66,39.87V216.13a13.69,13.69,0,0,0,7.1,12.08,14,14,0,0,0,14.18-.27l144.08-88.13a13.82,13.82,0,0,0,0-23.62Zm-6.26,13.38L81,217.7a2,2,0,0,1-2.06,0,1.78,1.78,0,0,1-1-1.61V39.87a1.78,1.78,0,0,1,1-1.61A2.06,2.06,0,0,1,80,38a2,2,0,0,1,1,.31L225.1,126.43a1.82,1.82,0,0,1,0,3.14Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M232.4,114.49,88.32,26.35a16,16,0,0,0-16.2-.3A15.86,15.86,0,0,0,64,39.87V216.13A15.94,15.94,0,0,0,80,232a16.07,16.07,0,0,0,8.36-2.35L232.4,141.51a15.81,15.81,0,0,0,0-27ZM80,215.94V40l143.83,88Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M230.32,117.9,86.24,29.79a11.91,11.91,0,0,0-12.17-.23A11.71,11.71,0,0,0,68,39.89V216.11a11.71,11.71,0,0,0,6.07,10.33,11.91,11.91,0,0,0,12.17-.23L230.32,138.1a11.82,11.82,0,0,0,0-20.2Zm-4.18,13.37L82.06,219.39a4,4,0,0,1-4.07.07,3.77,3.77,0,0,1-2-3.35V39.89a3.77,3.77,0,0,1,2-3.35,4,4,0,0,1,4.07.07l144.08,88.12a3.8,3.8,0,0,1,0,6.54Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as o from \"react\";\nimport t from \"../lib/SSRBase.es.js\";\nimport a from \"../defs/Plus.es.js\";\nconst e = o.forwardRef((r, s) => /* @__PURE__ */ o.createElement(t, { ref: s, ...r, weights: a }));\ne.displayName = \"PlusIcon\";\nconst f = e;\nexport {\n f as Plus,\n e as PlusIcon\n};\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M228,128a12,12,0,0,1-12,12H140v76a12,12,0,0,1-24,0V140H40a12,12,0,0,1,0-24h76V40a12,12,0,0,1,24,0v76h76A12,12,0,0,1,228,128Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\n \"path\",\n {\n d: \"M216,56V200a16,16,0,0,1-16,16H56a16,16,0,0,1-16-16V56A16,16,0,0,1,56,40H200A16,16,0,0,1,216,56Z\",\n opacity: \"0.2\"\n }\n ), /* @__PURE__ */ e.createElement(\"path\", { d: \"M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM184,136H136v48a8,8,0,0,1-16,0V136H72a8,8,0,0,1,0-16h48V72a8,8,0,0,1,16,0v48h48a8,8,0,0,1,0,16Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M222,128a6,6,0,0,1-6,6H134v82a6,6,0,0,1-12,0V134H40a6,6,0,0,1,0-12h82V40a6,6,0,0,1,12,0v82h82A6,6,0,0,1,222,128Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M220,128a4,4,0,0,1-4,4H132v84a4,4,0,0,1-8,0V132H40a4,4,0,0,1,0-8h84V40a4,4,0,0,1,8,0v84h84A4,4,0,0,1,220,128Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as e from \"react\";\nimport t from \"../lib/SSRBase.es.js\";\nimport m from \"../defs/Sparkle.es.js\";\nconst r = e.forwardRef((o, a) => /* @__PURE__ */ e.createElement(t, { ref: a, ...o, weights: m }));\nr.displayName = \"SparkleIcon\";\nconst c = r;\nexport {\n c as Sparkle,\n r as SparkleIcon\n};\n","import * as a from \"react\";\nconst l = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M199,125.31l-49.88-18.39L130.69,57a19.92,19.92,0,0,0-37.38,0L74.92,106.92,25,125.31a19.92,19.92,0,0,0,0,37.38l49.88,18.39L93.31,231a19.92,19.92,0,0,0,37.38,0l18.39-49.88L199,162.69a19.92,19.92,0,0,0,0-37.38Zm-63.38,35.16a12,12,0,0,0-7.11,7.11L112,212.28l-16.47-44.7a12,12,0,0,0-7.11-7.11L43.72,144l44.7-16.47a12,12,0,0,0,7.11-7.11L112,75.72l16.47,44.7a12,12,0,0,0,7.11,7.11L180.28,144ZM140,40a12,12,0,0,1,12-12h12V16a12,12,0,0,1,24,0V28h12a12,12,0,0,1,0,24H188V64a12,12,0,0,1-24,0V52H152A12,12,0,0,1,140,40ZM252,88a12,12,0,0,1-12,12h-4v4a12,12,0,0,1-24,0v-4h-4a12,12,0,0,1,0-24h4V72a12,12,0,0,1,24,0v4h4A12,12,0,0,1,252,88Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\n \"path\",\n {\n d: \"M194.82,151.43l-55.09,20.3-20.3,55.09a7.92,7.92,0,0,1-14.86,0l-20.3-55.09-55.09-20.3a7.92,7.92,0,0,1,0-14.86l55.09-20.3,20.3-55.09a7.92,7.92,0,0,1,14.86,0l20.3,55.09,55.09,20.3A7.92,7.92,0,0,1,194.82,151.43Z\",\n opacity: \"0.2\"\n }\n ), /* @__PURE__ */ a.createElement(\"path\", { d: \"M197.58,129.06,146,110l-19-51.62a15.92,15.92,0,0,0-29.88,0L78,110l-51.62,19a15.92,15.92,0,0,0,0,29.88L78,178l19,51.62a15.92,15.92,0,0,0,29.88,0L146,178l51.62-19a15.92,15.92,0,0,0,0-29.88ZM137,164.22a8,8,0,0,0-4.74,4.74L112,223.85,91.78,169A8,8,0,0,0,87,164.22L32.15,144,87,123.78A8,8,0,0,0,91.78,119L112,64.15,132.22,119a8,8,0,0,0,4.74,4.74L191.85,144ZM144,40a8,8,0,0,1,8-8h16V16a8,8,0,0,1,16,0V32h16a8,8,0,0,1,0,16H184V64a8,8,0,0,1-16,0V48H152A8,8,0,0,1,144,40ZM248,88a8,8,0,0,1-8,8h-8v8a8,8,0,0,1-16,0V96h-8a8,8,0,0,1,0-16h8V72a8,8,0,0,1,16,0v8h8A8,8,0,0,1,248,88Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M208,144a15.78,15.78,0,0,1-10.42,14.94L146,178l-19,51.62a15.92,15.92,0,0,1-29.88,0L78,178l-51.62-19a15.92,15.92,0,0,1,0-29.88L78,110l19-51.62a15.92,15.92,0,0,1,29.88,0L146,110l51.62,19A15.78,15.78,0,0,1,208,144ZM152,48h16V64a8,8,0,0,0,16,0V48h16a8,8,0,0,0,0-16H184V16a8,8,0,0,0-16,0V32H152a8,8,0,0,0,0,16Zm88,32h-8V72a8,8,0,0,0-16,0v8h-8a8,8,0,0,0,0,16h8v8a8,8,0,0,0,16,0V96h8a8,8,0,0,0,0-16Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M196.89,130.94,144.4,111.6,125.06,59.11a13.92,13.92,0,0,0-26.12,0L79.6,111.6,27.11,130.94a13.92,13.92,0,0,0,0,26.12L79.6,176.4l19.34,52.49a13.92,13.92,0,0,0,26.12,0L144.4,176.4l52.49-19.34a13.92,13.92,0,0,0,0-26.12Zm-4.15,14.86-55.08,20.3a6,6,0,0,0-3.56,3.56l-20.3,55.08a1.92,1.92,0,0,1-3.6,0L89.9,169.66a6,6,0,0,0-3.56-3.56L31.26,145.8a1.92,1.92,0,0,1,0-3.6l55.08-20.3a6,6,0,0,0,3.56-3.56l20.3-55.08a1.92,1.92,0,0,1,3.6,0l20.3,55.08a6,6,0,0,0,3.56,3.56l55.08,20.3a1.92,1.92,0,0,1,0,3.6ZM146,40a6,6,0,0,1,6-6h18V16a6,6,0,0,1,12,0V34h18a6,6,0,0,1,0,12H182V64a6,6,0,0,1-12,0V46H152A6,6,0,0,1,146,40ZM246,88a6,6,0,0,1-6,6H230v10a6,6,0,0,1-12,0V94H208a6,6,0,0,1,0-12h10V72a6,6,0,0,1,12,0V82h10A6,6,0,0,1,246,88Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M197.58,129.06,146,110l-19-51.62a15.92,15.92,0,0,0-29.88,0L78,110l-51.62,19a15.92,15.92,0,0,0,0,29.88L78,178l19,51.62a15.92,15.92,0,0,0,29.88,0L146,178l51.62-19a15.92,15.92,0,0,0,0-29.88ZM137,164.22a8,8,0,0,0-4.74,4.74L112,223.85,91.78,169A8,8,0,0,0,87,164.22L32.15,144,87,123.78A8,8,0,0,0,91.78,119L112,64.15,132.22,119a8,8,0,0,0,4.74,4.74L191.85,144ZM144,40a8,8,0,0,1,8-8h16V16a8,8,0,0,1,16,0V32h16a8,8,0,0,1,0,16H184V64a8,8,0,0,1-16,0V48H152A8,8,0,0,1,144,40ZM248,88a8,8,0,0,1-8,8h-8v8a8,8,0,0,1-16,0V96h-8a8,8,0,0,1,0-16h8V72a8,8,0,0,1,16,0v8h8A8,8,0,0,1,248,88Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M196.2,132.81l-53.36-19.65L123.19,59.8a11.93,11.93,0,0,0-22.38,0L81.16,113.16,27.8,132.81a11.93,11.93,0,0,0,0,22.38l53.36,19.65,19.65,53.36a11.93,11.93,0,0,0,22.38,0l19.65-53.36,53.36-19.65a11.93,11.93,0,0,0,0-22.38Zm-2.77,14.87L138.35,168a4,4,0,0,0-2.37,2.37l-20.3,55.08a3.92,3.92,0,0,1-7.36,0L88,170.35A4,4,0,0,0,85.65,168l-55.08-20.3a3.92,3.92,0,0,1,0-7.36L85.65,120A4,4,0,0,0,88,117.65l20.3-55.08a3.92,3.92,0,0,1,7.36,0L136,117.65a4,4,0,0,0,2.37,2.37l55.08,20.3a3.92,3.92,0,0,1,0,7.36ZM148,40a4,4,0,0,1,4-4h20V16a4,4,0,0,1,8,0V36h20a4,4,0,0,1,0,8H180V64a4,4,0,0,1-8,0V44H152A4,4,0,0,1,148,40Zm96,48a4,4,0,0,1-4,4H228v12a4,4,0,0,1-8,0V92H208a4,4,0,0,1,0-8h12V72a4,4,0,0,1,8,0V84h12A4,4,0,0,1,244,88Z\" }))\n ]\n]);\nexport {\n l as default\n};\n","import * as r from \"react\";\nimport e from \"../lib/SSRBase.es.js\";\nimport m from \"../defs/Star.es.js\";\nconst t = r.forwardRef((o, a) => /* @__PURE__ */ r.createElement(e, { ref: a, ...o, weights: m }));\nt.displayName = \"StarIcon\";\nconst f = t;\nexport {\n f as Star,\n t as StarIcon\n};\n","import * as a from \"react\";\nconst l = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M243,96a20.33,20.33,0,0,0-17.74-14l-56.59-4.57L146.83,24.62a20.36,20.36,0,0,0-37.66,0L87.35,77.44,30.76,82A20.45,20.45,0,0,0,19.1,117.88l43.18,37.24-13.2,55.7A20.37,20.37,0,0,0,79.57,233L128,203.19,176.43,233a20.39,20.39,0,0,0,30.49-22.15l-13.2-55.7,43.18-37.24A20.43,20.43,0,0,0,243,96ZM172.53,141.7a12,12,0,0,0-3.84,11.86L181.58,208l-47.29-29.08a12,12,0,0,0-12.58,0L74.42,208l12.89-54.4a12,12,0,0,0-3.84-11.86L41.2,105.24l55.4-4.47a12,12,0,0,0,10.13-7.38L128,41.89l21.27,51.5a12,12,0,0,0,10.13,7.38l55.4,4.47Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\n \"path\",\n {\n d: \"M229.06,108.79l-48.7,42,14.88,62.79a8.4,8.4,0,0,1-12.52,9.17L128,189.09,73.28,222.74a8.4,8.4,0,0,1-12.52-9.17l14.88-62.79-48.7-42A8.46,8.46,0,0,1,31.73,94L95.64,88.8l24.62-59.6a8.36,8.36,0,0,1,15.48,0l24.62,59.6L224.27,94A8.46,8.46,0,0,1,229.06,108.79Z\",\n opacity: \"0.2\"\n }\n ), /* @__PURE__ */ a.createElement(\"path\", { d: \"M239.18,97.26A16.38,16.38,0,0,0,224.92,86l-59-4.76L143.14,26.15a16.36,16.36,0,0,0-30.27,0L90.11,81.23,31.08,86a16.46,16.46,0,0,0-9.37,28.86l45,38.83L53,211.75a16.38,16.38,0,0,0,24.5,17.82L128,198.49l50.53,31.08A16.4,16.4,0,0,0,203,211.75l-13.76-58.07,45-38.83A16.43,16.43,0,0,0,239.18,97.26Zm-15.34,5.47-48.7,42a8,8,0,0,0-2.56,7.91l14.88,62.8a.37.37,0,0,1-.17.48c-.18.14-.23.11-.38,0l-54.72-33.65a8,8,0,0,0-8.38,0L69.09,215.94c-.15.09-.19.12-.38,0a.37.37,0,0,1-.17-.48l14.88-62.8a8,8,0,0,0-2.56-7.91l-48.7-42c-.12-.1-.23-.19-.13-.5s.18-.27.33-.29l63.92-5.16A8,8,0,0,0,103,91.86l24.62-59.61c.08-.17.11-.25.35-.25s.27.08.35.25L153,91.86a8,8,0,0,0,6.75,4.92l63.92,5.16c.15,0,.24,0,.33.29S224,102.63,223.84,102.73Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M234.29,114.85l-45,38.83L203,211.75a16.4,16.4,0,0,1-24.5,17.82L128,198.49,77.47,229.57A16.4,16.4,0,0,1,53,211.75l13.76-58.07-45-38.83A16.46,16.46,0,0,1,31.08,86l59-4.76,22.76-55.08a16.36,16.36,0,0,1,30.27,0l22.75,55.08,59,4.76a16.46,16.46,0,0,1,9.37,28.86Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M237.28,97.87A14.18,14.18,0,0,0,224.76,88l-60.25-4.87-23.22-56.2a14.37,14.37,0,0,0-26.58,0L91.49,83.11,31.24,88a14.18,14.18,0,0,0-12.52,9.89A14.43,14.43,0,0,0,23,113.32L69,152.93l-14,59.25a14.4,14.4,0,0,0,5.59,15,14.1,14.1,0,0,0,15.91.6L128,196.12l51.58,31.71a14.1,14.1,0,0,0,15.91-.6,14.4,14.4,0,0,0,5.59-15l-14-59.25L233,113.32A14.43,14.43,0,0,0,237.28,97.87Zm-12.14,6.37-48.69,42a6,6,0,0,0-1.92,5.92l14.88,62.79a2.35,2.35,0,0,1-.95,2.57,2.24,2.24,0,0,1-2.6.1L131.14,184a6,6,0,0,0-6.28,0L70.14,217.61a2.24,2.24,0,0,1-2.6-.1,2.35,2.35,0,0,1-1-2.57l14.88-62.79a6,6,0,0,0-1.92-5.92l-48.69-42a2.37,2.37,0,0,1-.73-2.65,2.28,2.28,0,0,1,2.07-1.65l63.92-5.16a6,6,0,0,0,5.06-3.69l24.63-59.6a2.35,2.35,0,0,1,4.38,0l24.63,59.6a6,6,0,0,0,5.06,3.69l63.92,5.16a2.28,2.28,0,0,1,2.07,1.65A2.37,2.37,0,0,1,225.14,104.24Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M239.18,97.26A16.38,16.38,0,0,0,224.92,86l-59-4.76L143.14,26.15a16.36,16.36,0,0,0-30.27,0L90.11,81.23,31.08,86a16.46,16.46,0,0,0-9.37,28.86l45,38.83L53,211.75a16.38,16.38,0,0,0,24.5,17.82L128,198.49l50.53,31.08A16.4,16.4,0,0,0,203,211.75l-13.76-58.07,45-38.83A16.43,16.43,0,0,0,239.18,97.26Zm-15.34,5.47-48.7,42a8,8,0,0,0-2.56,7.91l14.88,62.8a.37.37,0,0,1-.17.48c-.18.14-.23.11-.38,0l-54.72-33.65a8,8,0,0,0-8.38,0L69.09,215.94c-.15.09-.19.12-.38,0a.37.37,0,0,1-.17-.48l14.88-62.8a8,8,0,0,0-2.56-7.91l-48.7-42c-.12-.1-.23-.19-.13-.5s.18-.27.33-.29l63.92-5.16A8,8,0,0,0,103,91.86l24.62-59.61c.08-.17.11-.25.35-.25s.27.08.35.25L153,91.86a8,8,0,0,0,6.75,4.92l63.92,5.16c.15,0,.24,0,.33.29S224,102.63,223.84,102.73Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement(\"path\", { d: \"M235.36,98.49A12.21,12.21,0,0,0,224.59,90l-61.47-5L139.44,27.67a12.37,12.37,0,0,0-22.88,0L92.88,85,31.41,90a12.45,12.45,0,0,0-7.07,21.84l46.85,40.41L56.87,212.64a12.35,12.35,0,0,0,18.51,13.49L128,193.77l52.62,32.36a12.12,12.12,0,0,0,13.69-.51,12.28,12.28,0,0,0,4.82-13l-14.32-60.42,46.85-40.41A12.29,12.29,0,0,0,235.36,98.49Zm-8.93,7.26-48.68,42a4,4,0,0,0-1.28,3.95l14.87,62.79a4.37,4.37,0,0,1-1.72,4.65,4.24,4.24,0,0,1-4.81.18L130.1,185.67a4,4,0,0,0-4.2,0L71.19,219.32a4.24,4.24,0,0,1-4.81-.18,4.37,4.37,0,0,1-1.72-4.65L79.53,151.7a4,4,0,0,0-1.28-3.95l-48.68-42A4.37,4.37,0,0,1,28.25,101a4.31,4.31,0,0,1,3.81-3L96,92.79a4,4,0,0,0,3.38-2.46L124,30.73a4.35,4.35,0,0,1,8.08,0l24.62,59.6A4,4,0,0,0,160,92.79l63.9,5.15a4.31,4.31,0,0,1,3.81,3A4.37,4.37,0,0,1,226.43,105.75Z\" }))\n ]\n]);\nexport {\n l as default\n};\n","import * as e from \"react\";\nimport t from \"../lib/SSRBase.es.js\";\nimport c from \"../defs/UserCircle.es.js\";\nconst r = e.forwardRef((o, s) => /* @__PURE__ */ e.createElement(t, { ref: s, ...o, weights: c }));\nr.displayName = \"UserCircleIcon\";\nconst m = r;\nexport {\n m as UserCircle,\n r as UserCircleIcon\n};\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,20A108,108,0,1,0,236,128,108.12,108.12,0,0,0,128,20ZM79.57,196.57a60,60,0,0,1,96.86,0,83.72,83.72,0,0,1-96.86,0ZM100,120a28,28,0,1,1,28,28A28,28,0,0,1,100,120ZM194,179.94a83.48,83.48,0,0,0-29-23.42,52,52,0,1,0-74,0,83.48,83.48,0,0,0-29,23.42,84,84,0,1,1,131.9,0Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\n \"path\",\n {\n d: \"M224,128a95.76,95.76,0,0,1-31.8,71.37A72,72,0,0,0,128,160a40,40,0,1,0-40-40,40,40,0,0,0,40,40,72,72,0,0,0-64.2,39.37h0A96,96,0,1,1,224,128Z\",\n opacity: \"0.2\"\n }\n ), /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24ZM74.08,197.5a64,64,0,0,1,107.84,0,87.83,87.83,0,0,1-107.84,0ZM96,120a32,32,0,1,1,32,32A32,32,0,0,1,96,120Zm97.76,66.41a79.66,79.66,0,0,0-36.06-28.75,48,48,0,1,0-59.4,0,79.66,79.66,0,0,0-36.06,28.75,88,88,0,1,1,131.52,0Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M172,120a44,44,0,1,1-44-44A44.05,44.05,0,0,1,172,120Zm60,8A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88.09,88.09,0,0,0-91.47-87.93C77.43,41.89,39.87,81.12,40,128.25a87.65,87.65,0,0,0,22.24,58.16A79.71,79.71,0,0,1,84,165.1a4,4,0,0,1,4.83.32,59.83,59.83,0,0,0,78.28,0,4,4,0,0,1,4.83-.32,79.71,79.71,0,0,1,21.79,21.31A87.62,87.62,0,0,0,216,128Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,26A102,102,0,1,0,230,128,102.12,102.12,0,0,0,128,26ZM71.44,198a66,66,0,0,1,113.12,0,89.8,89.8,0,0,1-113.12,0ZM94,120a34,34,0,1,1,34,34A34,34,0,0,1,94,120Zm99.51,69.64a77.53,77.53,0,0,0-40-31.38,46,46,0,1,0-51,0,77.53,77.53,0,0,0-40,31.38,90,90,0,1,1,131,0Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24ZM74.08,197.5a64,64,0,0,1,107.84,0,87.83,87.83,0,0,1-107.84,0ZM96,120a32,32,0,1,1,32,32A32,32,0,0,1,96,120Zm97.76,66.41a79.66,79.66,0,0,0-36.06-28.75,48,48,0,1,0-59.4,0,79.66,79.66,0,0,0-36.06,28.75,88,88,0,1,1,131.52,0Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,28A100,100,0,1,0,228,128,100.11,100.11,0,0,0,128,28ZM68.87,198.42a68,68,0,0,1,118.26,0,91.8,91.8,0,0,1-118.26,0Zm124.3-5.55a75.61,75.61,0,0,0-44.51-34,44,44,0,1,0-41.32,0,75.61,75.61,0,0,0-44.51,34,92,92,0,1,1,130.34,0ZM128,156a36,36,0,1,1,36-36A36,36,0,0,1,128,156Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as r from \"react\";\nimport i from \"../lib/SSRBase.es.js\";\nimport n from \"../defs/WarningCircle.es.js\";\nconst e = r.forwardRef((o, a) => /* @__PURE__ */ r.createElement(i, { ref: a, ...o, weights: n }));\ne.displayName = \"WarningCircleIcon\";\nconst m = e;\nexport {\n m as WarningCircle,\n e as WarningCircleIcon\n};\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,20A108,108,0,1,0,236,128,108.12,108.12,0,0,0,128,20Zm0,192a84,84,0,1,1,84-84A84.09,84.09,0,0,1,128,212Zm-12-80V80a12,12,0,0,1,24,0v52a12,12,0,0,1-24,0Zm28,40a16,16,0,1,1-16-16A16,16,0,0,1,144,172Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M224,128a96,96,0,1,1-96-96A96,96,0,0,1,224,128Z\", opacity: \"0.2\" }), /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm-8-80V80a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,172Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm-8,56a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm8,104a12,12,0,1,1,12-12A12,12,0,0,1,128,184Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,26A102,102,0,1,0,230,128,102.12,102.12,0,0,0,128,26Zm0,192a90,90,0,1,1,90-90A90.1,90.1,0,0,1,128,218Zm-6-82V80a6,6,0,0,1,12,0v56a6,6,0,0,1-12,0Zm16,36a10,10,0,1,1-10-10A10,10,0,0,1,138,172Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm-8-80V80a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,172Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M128,28A100,100,0,1,0,228,128,100.11,100.11,0,0,0,128,28Zm0,192a92,92,0,1,1,92-92A92.1,92.1,0,0,1,128,220Zm-4-84V80a4,4,0,0,1,8,0v56a4,4,0,0,1-8,0Zm12,36a8,8,0,1,1-8-8A8,8,0,0,1,136,172Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import * as o from \"react\";\nimport a from \"../lib/SSRBase.es.js\";\nimport m from \"../defs/X.es.js\";\nconst e = o.forwardRef((r, t) => /* @__PURE__ */ o.createElement(a, { ref: t, ...r, weights: m }));\ne.displayName = \"XIcon\";\nconst f = e;\nexport {\n f as X,\n e as XIcon\n};\n","import * as e from \"react\";\nconst a = /* @__PURE__ */ new Map([\n [\n \"bold\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M208.49,191.51a12,12,0,0,1-17,17L128,145,64.49,208.49a12,12,0,0,1-17-17L111,128,47.51,64.49a12,12,0,0,1,17-17L128,111l63.51-63.52a12,12,0,0,1,17,17L145,128Z\" }))\n ],\n [\n \"duotone\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\n \"path\",\n {\n d: \"M216,56V200a16,16,0,0,1-16,16H56a16,16,0,0,1-16-16V56A16,16,0,0,1,56,40H200A16,16,0,0,1,216,56Z\",\n opacity: \"0.2\"\n }\n ), /* @__PURE__ */ e.createElement(\"path\", { d: \"M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z\" }))\n ],\n [\n \"fill\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM181.66,170.34a8,8,0,0,1-11.32,11.32L128,139.31,85.66,181.66a8,8,0,0,1-11.32-11.32L116.69,128,74.34,85.66A8,8,0,0,1,85.66,74.34L128,116.69l42.34-42.35a8,8,0,0,1,11.32,11.32L139.31,128Z\" }))\n ],\n [\n \"light\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M204.24,195.76a6,6,0,1,1-8.48,8.48L128,136.49,60.24,204.24a6,6,0,0,1-8.48-8.48L119.51,128,51.76,60.24a6,6,0,0,1,8.48-8.48L128,119.51l67.76-67.75a6,6,0,0,1,8.48,8.48L136.49,128Z\" }))\n ],\n [\n \"regular\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z\" }))\n ],\n [\n \"thin\",\n /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(\"path\", { d: \"M202.83,197.17a4,4,0,0,1-5.66,5.66L128,133.66,58.83,202.83a4,4,0,0,1-5.66-5.66L122.34,128,53.17,58.83a4,4,0,0,1,5.66-5.66L128,122.34l69.17-69.17a4,4,0,1,1,5.66,5.66L133.66,128Z\" }))\n ]\n]);\nexport {\n a as default\n};\n","import type { Icon, IconProps, IconWeight } from \"@phosphor-icons/react\";\nimport {\n AlarmIcon,\n ArchiveIcon,\n ArrowBendUpLeftIcon,\n ArrowCounterClockwiseIcon,\n ArrowDownIcon,\n ArrowElbowDownLeftIcon,\n ArrowRightIcon,\n ArrowUpIcon,\n ArrowsClockwiseIcon,\n ArrowSquareOutIcon,\n ArrowsInIcon,\n ArrowsOutIcon,\n ArrowUpRightIcon,\n BinocularsIcon,\n BookOpenTextIcon,\n BroomIcon,\n BugIcon,\n BuildingsIcon,\n CoinsIcon,\n CopyIcon,\n CalendarBlankIcon,\n CaretDownIcon,\n CaretLeftIcon,\n CaretRightIcon,\n CellSignalHighIcon,\n CellSignalLowIcon,\n CellSignalMediumIcon,\n ChartBarIcon,\n ChartBarHorizontalIcon,\n ChartDonutIcon,\n ChartLineIcon,\n ChartLineUpIcon,\n ChatCircleDotsIcon,\n ChatsCircleIcon,\n ChatTeardropDotsIcon,\n CheckIcon,\n CheckCircleIcon,\n CircleDashedIcon,\n CircleHalfIcon,\n CircleIcon,\n ClockIcon,\n ClockCounterClockwiseIcon,\n CodeIcon,\n CodeBlockIcon,\n CreditCardIcon,\n DatabaseIcon,\n DeviceMobileIcon,\n DotsThreeIcon,\n DownloadSimpleIcon,\n EnvelopeSimpleIcon,\n EyeIcon,\n FileIcon,\n FileTextIcon,\n FilesIcon,\n FlaskIcon,\n FolderIcon,\n FolderOpenIcon,\n FolderPlusIcon,\n FunnelIcon,\n GaugeIcon,\n GearSixIcon,\n GitBranchIcon,\n GitCommitIcon,\n GlobeSimpleIcon,\n GridNineIcon,\n HashIcon,\n HourglassIcon,\n IdentificationCardIcon,\n ImageSquareIcon,\n InfinityIcon,\n InfoIcon,\n KanbanIcon,\n KeyIcon,\n LightbulbIcon,\n LinkSimpleIcon,\n ListIcon,\n ListBulletsIcon,\n ListChecksIcon,\n ListNumbersIcon,\n LockSimpleIcon,\n MagicWandIcon,\n MagnifyingGlassIcon,\n MapPinIcon,\n MegaphoneIcon,\n MinusIcon,\n MonitorIcon,\n MoonIcon,\n NoteIcon,\n NotionLogoIcon,\n PaletteIcon,\n PaperclipIcon,\n PaperPlaneRightIcon,\n PauseIcon,\n PencilSimpleIcon,\n PlayIcon,\n PlugsConnectedIcon,\n PlusIcon,\n ProhibitIcon,\n PulseIcon,\n QueueIcon,\n QuotesIcon,\n RobotIcon,\n ShieldCheckIcon,\n ShieldWarningIcon,\n SidebarSimpleIcon,\n SignOutIcon,\n SlidersHorizontalIcon,\n SparkleIcon,\n SquareIcon,\n SquaresFourIcon,\n StarIcon,\n SunIcon,\n TableIcon,\n TagIcon,\n TerminalWindowIcon,\n TextBIcon,\n TextItalicIcon,\n TextStrikethroughIcon,\n TimerIcon,\n TrashIcon,\n TrayIcon,\n TrendUpIcon,\n UploadSimpleIcon,\n UserCircleIcon,\n UserPlusIcon,\n UsersIcon,\n WalletIcon,\n WarningCircleIcon,\n WarningIcon,\n WebhooksLogoIcon,\n WrenchIcon,\n XCircleIcon,\n XIcon,\n} from \"@phosphor-icons/react/dist/ssr\";\n\n/**\n * Icons are the filled weight in this app.\n *\n * Phosphor sets weight per icon and the SSR entry does not read IconContext,\n * so the rule is applied here once instead of at every call site. Import from\n * this file rather than from @phosphor-icons/react directly.\n *\n * Props spread after the weight, so a caller can still override it.\n *\n * Every export below is annotated `@__PURE__`. A bundler cannot know that a\n * call at the top level of a module does nothing else, so without the\n * annotation one import from this file drags in all eighty-seven. That is\n * invisible in the app, which loads the whole set anyway, and was a fifth of\n * the published widget: it shipped Notion's logo and a signal meter to answer\n * a support question. Keep the annotation on anything added here.\n */\nfunction icon(Base: Icon, name: string, weight: IconWeight = \"fill\") {\n function Wrapped(props: IconProps) {\n return <Base weight={weight} {...props} />;\n }\n Wrapped.displayName = `${name}Icon`;\n return Wrapped;\n}\n\nexport const Alarm = /* @__PURE__ */ icon(AlarmIcon, \"Alarm\");\nexport const Archive = /* @__PURE__ */ icon(ArchiveIcon, \"Archive\");\nexport const Binoculars = /* @__PURE__ */ icon(BinocularsIcon, \"Binoculars\");\nexport const BookOpenText = /* @__PURE__ */ icon(BookOpenTextIcon, \"BookOpenText\");\nexport const Bug = /* @__PURE__ */ icon(BugIcon, \"Bug\");\nexport const Buildings = /* @__PURE__ */ icon(BuildingsIcon, \"Buildings\");\nexport const CalendarBlank = /* @__PURE__ */ icon(CalendarBlankIcon, \"CalendarBlank\");\nexport const ChatCircleDots = /* @__PURE__ */ icon(ChatCircleDotsIcon, \"ChatCircleDots\");\nexport const ChatsCircle = /* @__PURE__ */ icon(ChatsCircleIcon, \"ChatsCircle\");\nexport const ChatTeardropDots = /* @__PURE__ */ icon(ChatTeardropDotsIcon, \"ChatTeardropDots\");\nexport const CellSignalHigh = /* @__PURE__ */ icon(CellSignalHighIcon, \"CellSignalHigh\");\nexport const CellSignalLow = /* @__PURE__ */ icon(CellSignalLowIcon, \"CellSignalLow\");\nexport const CellSignalMedium = /* @__PURE__ */ icon(CellSignalMediumIcon, \"CellSignalMedium\");\nexport const ChartBar = /* @__PURE__ */ icon(ChartBarIcon, \"ChartBar\");\nexport const ChartBarHorizontal = /* @__PURE__ */ icon(\n ChartBarHorizontalIcon,\n \"ChartBarHorizontal\",\n);\nexport const ChartDonut = /* @__PURE__ */ icon(ChartDonutIcon, \"ChartDonut\");\nexport const ChartLine = /* @__PURE__ */ icon(ChartLineIcon, \"ChartLine\");\nexport const ChartLineUp = /* @__PURE__ */ icon(ChartLineUpIcon, \"ChartLineUp\");\nexport const CheckCircle = /* @__PURE__ */ icon(CheckCircleIcon, \"CheckCircle\");\n// Half a body by design; the fill weight is the half that shows.\nexport const CircleHalf = /* @__PURE__ */ icon(CircleHalfIcon, \"CircleHalf\");\nexport function CircleThreeQuarter(props: IconProps) {\n return (\n <CircleIcon\n {...props}\n weight=\"fill\"\n style={{\n ...props.style,\n clipPath: \"polygon(50% 0, 100% 0, 100% 100%, 0 100%, 0 50%, 50% 50%)\",\n }}\n />\n );\n}\nexport const Clock = /* @__PURE__ */ icon(ClockIcon, \"Clock\");\nexport const ClockCounterClockwise = /* @__PURE__ */ icon(\n ClockCounterClockwiseIcon,\n \"ClockCounterClockwise\",\n);\nexport const CodeBlock = /* @__PURE__ */ icon(CodeBlockIcon, \"CodeBlock\");\nexport const Copy = /* @__PURE__ */ icon(CopyIcon, \"Copy\");\nexport const CreditCard = /* @__PURE__ */ icon(CreditCardIcon, \"CreditCard\");\nexport const Database = /* @__PURE__ */ icon(DatabaseIcon, \"Database\");\nexport const DeviceMobile = /* @__PURE__ */ icon(DeviceMobileIcon, \"DeviceMobile\");\nexport const EnvelopeSimple = /* @__PURE__ */ icon(EnvelopeSimpleIcon, \"EnvelopeSimple\");\nexport const File = /* @__PURE__ */ icon(FileIcon, \"File\");\nexport const Files = /* @__PURE__ */ icon(FilesIcon, \"Files\");\nexport const Folder = /* @__PURE__ */ icon(FolderIcon, \"Folder\");\nexport const FolderOpen = /* @__PURE__ */ icon(FolderOpenIcon, \"FolderOpen\");\nexport const FolderPlus = /* @__PURE__ */ icon(FolderPlusIcon, \"FolderPlus\");\nexport const Funnel = /* @__PURE__ */ icon(FunnelIcon, \"Funnel\");\nexport const GearSix = /* @__PURE__ */ icon(GearSixIcon, \"GearSix\");\nexport const GlobeSimple = /* @__PURE__ */ icon(GlobeSimpleIcon, \"GlobeSimple\");\nexport const GridNine = /* @__PURE__ */ icon(GridNineIcon, \"GridNine\");\nexport const Hash = /* @__PURE__ */ icon(HashIcon, \"Hash\");\n// Linear has no logo in this set, and a board is what the integration is\n// about anyway.\nexport const ImageSquare = /* @__PURE__ */ icon(ImageSquareIcon, \"ImageSquare\");\nexport const Kanban = /* @__PURE__ */ icon(KanbanIcon, \"Kanban\");\nexport const Key = /* @__PURE__ */ icon(KeyIcon, \"Key\");\n/**\n * A solid lifebuoy, drawn here. Phosphor's fill weight only takes the top\n * and bottom out of the ring and leaves a hole in the middle, which at\n * sixteen pixels reads as a disc with a band across it. This one is\n * Phosphor's disc with a ring cut out of it between the radii 46 and 70,\n * and four spokes 28 wide left across the cut on the diagonals. Heavier\n * than Phosphor's proportions on purpose: at sixteen pixels the thin band\n * and wide ring turned into an outline.\n */\nconst LIFEBUOY = [\n \"M128,24A104,104,0,1,0,232,128,104,104,0,0,0,128,24Z\",\n \"M166.6,69.6L149.08,87.12A46,46,0,0,0,106.92,87.12L89.4,69.6A70,70,0,0,1,166.6,69.6Z\",\n \"M186.4,166.6L168.88,149.08A46,46,0,0,0,168.88,106.92L186.4,89.4A70,70,0,0,1,186.4,166.6Z\",\n \"M89.4,186.4L106.92,168.88A46,46,0,0,0,149.08,168.88L166.6,186.4A70,70,0,0,1,89.4,186.4Z\",\n \"M69.6,89.4L87.12,106.92A46,46,0,0,0,87.12,149.08L69.6,166.6A70,70,0,0,1,69.6,89.4Z\",\n].join(\"\");\nexport function Lifebuoy({\n size = \"1em\",\n color = \"currentColor\",\n ...props\n}: Omit<IconProps, \"weight\" | \"mirrored\" | \"alt\">) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 256 256\"\n width={size}\n height={size}\n fill={color}\n {...props}\n >\n <path d={LIFEBUOY} />\n </svg>\n );\n}\nexport const Lightbulb = /* @__PURE__ */ icon(LightbulbIcon, \"Lightbulb\");\nexport const LockSimple = /* @__PURE__ */ icon(LockSimpleIcon, \"LockSimple\");\nexport const MagicWand = /* @__PURE__ */ icon(MagicWandIcon, \"MagicWand\");\nexport const MapPin = /* @__PURE__ */ icon(MapPinIcon, \"MapPin\");\nexport const Megaphone = /* @__PURE__ */ icon(MegaphoneIcon, \"Megaphone\");\nexport const Monitor = /* @__PURE__ */ icon(MonitorIcon, \"Monitor\");\nexport const Moon = /* @__PURE__ */ icon(MoonIcon, \"Moon\");\nexport const Note = /* @__PURE__ */ icon(NoteIcon, \"Note\");\nexport const NotionLogo = /* @__PURE__ */ icon(NotionLogoIcon, \"NotionLogo\");\nexport const Paperclip = /* @__PURE__ */ icon(PaperclipIcon, \"Paperclip\");\nexport const PaperPlaneRight = /* @__PURE__ */ icon(PaperPlaneRightIcon, \"PaperPlaneRight\");\nexport const PencilSimple = /* @__PURE__ */ icon(PencilSimpleIcon, \"PencilSimple\");\nexport const Play = /* @__PURE__ */ icon(PlayIcon, \"Play\");\nexport const PlugsConnected = /* @__PURE__ */ icon(PlugsConnectedIcon, \"PlugsConnected\");\nexport const Quotes = /* @__PURE__ */ icon(QuotesIcon, \"Quotes\");\nexport const Reply = /* @__PURE__ */ icon(ArrowBendUpLeftIcon, \"Reply\");\nexport const Robot = /* @__PURE__ */ icon(RobotIcon, \"Robot\");\nexport const ShieldCheck = /* @__PURE__ */ icon(ShieldCheckIcon, \"ShieldCheck\");\nexport const Prohibit = /* @__PURE__ */ icon(ProhibitIcon, \"Prohibit\");\nexport const ShieldWarning = /* @__PURE__ */ icon(ShieldWarningIcon, \"ShieldWarning\");\nexport const SidebarSimple = /* @__PURE__ */ icon(SidebarSimpleIcon, \"SidebarSimple\");\nexport const SignOut = /* @__PURE__ */ icon(SignOutIcon, \"SignOut\");\nexport const SlidersHorizontal = /* @__PURE__ */ icon(\n SlidersHorizontalIcon,\n \"SlidersHorizontal\",\n);\nexport const Sparkle = /* @__PURE__ */ icon(SparkleIcon, \"Sparkle\");\n// The stop button's own glyph. Filled, because a stop is a solid block and\n// an outline of one reads as a checkbox.\nexport const Square = /* @__PURE__ */ icon(SquareIcon, \"Square\");\nexport const SquaresFour = /* @__PURE__ */ icon(SquaresFourIcon, \"SquaresFour\");\n// Filled either way: an empty star is this one wearing a quieter colour, so\n// the row cannot change shape as it fills.\nexport const Star = /* @__PURE__ */ icon(StarIcon, \"Star\");\nexport const Sun = /* @__PURE__ */ icon(SunIcon, \"Sun\");\nexport const Table = /* @__PURE__ */ icon(TableIcon, \"Table\");\nexport const Tag = /* @__PURE__ */ icon(TagIcon, \"Tag\");\nexport const Timer = /* @__PURE__ */ icon(TimerIcon, \"Timer\");\nexport const Trash = /* @__PURE__ */ icon(TrashIcon, \"Trash\");\nexport const Tray = /* @__PURE__ */ icon(TrayIcon, \"Tray\");\nexport const TrendUp = /* @__PURE__ */ icon(TrendUpIcon, \"TrendUp\");\nexport const UserCircle = /* @__PURE__ */ icon(UserCircleIcon, \"UserCircle\");\nexport const Users = /* @__PURE__ */ icon(UsersIcon, \"Users\");\nexport const WarningCircle = /* @__PURE__ */ icon(WarningCircleIcon, \"WarningCircle\");\nexport const Wrench = /* @__PURE__ */ icon(WrenchIcon, \"Wrench\");\nexport const XCircle = /* @__PURE__ */ icon(XCircleIcon, \"XCircle\");\n\n// Logs and billing: categories, money and status all have a body to fill.\nexport const Broom = /* @__PURE__ */ icon(BroomIcon, \"Broom\");\nexport const Coins = /* @__PURE__ */ icon(CoinsIcon, \"Coins\");\nexport const Eye = /* @__PURE__ */ icon(EyeIcon, \"Eye\");\nexport const FileText = /* @__PURE__ */ icon(FileTextIcon, \"FileText\");\nexport const Flask = /* @__PURE__ */ icon(FlaskIcon, \"Flask\");\nexport const Gauge = /* @__PURE__ */ icon(GaugeIcon, \"Gauge\");\nexport const Hourglass = /* @__PURE__ */ icon(HourglassIcon, \"Hourglass\");\nexport const IdentificationCard = /* @__PURE__ */ icon(\n IdentificationCardIcon,\n \"IdentificationCard\",\n);\nexport const Info = /* @__PURE__ */ icon(InfoIcon, \"Info\");\nexport const Palette = /* @__PURE__ */ icon(PaletteIcon, \"Palette\");\nexport const Pause = /* @__PURE__ */ icon(PauseIcon, \"Pause\");\nexport const Queue = /* @__PURE__ */ icon(QueueIcon, \"Queue\");\nexport const TerminalWindow = /* @__PURE__ */ icon(\n TerminalWindowIcon,\n \"TerminalWindow\",\n);\nexport const UserPlus = /* @__PURE__ */ icon(UserPlusIcon, \"UserPlus\");\nexport const Wallet = /* @__PURE__ */ icon(WalletIcon, \"Wallet\");\nexport const Warning = /* @__PURE__ */ icon(WarningIcon, \"Warning\");\nexport const WebhooksLogo = /* @__PURE__ */ icon(WebhooksLogoIcon, \"WebhooksLogo\");\n\n// Exceptions. These are line glyphs with nothing to fill in, so Phosphor's\n// fill weight gives them a shape instead of a body: Check becomes a filled\n// box, the carets become solid triangles, the magnifier becomes a solid disc,\n// the three dots swell into blobs, and the arrow turns into a slab. Bold is\n// the heavier version of the same icon, which is what fill is meant to look\n// like here.\nexport const ArrowDown = /* @__PURE__ */ icon(ArrowDownIcon, \"ArrowDown\", \"bold\");\n// The return key, drawn as the arrow printed on it.\nexport const EnterKey = /* @__PURE__ */ icon(ArrowElbowDownLeftIcon, \"EnterKey\", \"bold\");\nexport const ArrowRight = /* @__PURE__ */ icon(ArrowRightIcon, \"ArrowRight\", \"bold\");\nexport const ArrowUp = /* @__PURE__ */ icon(ArrowUpIcon, \"ArrowUp\", \"bold\");\nexport const ArrowsClockwise = /* @__PURE__ */ icon(\n ArrowsClockwiseIcon,\n \"ArrowsClockwise\",\n \"bold\",\n);\nexport const ArrowSquareOut = /* @__PURE__ */ icon(ArrowSquareOutIcon, \"ArrowSquareOut\", \"bold\");\nexport const ArrowsIn = /* @__PURE__ */ icon(ArrowsInIcon, \"ArrowsIn\", \"bold\");\nexport const ArrowsOut = /* @__PURE__ */ icon(ArrowsOutIcon, \"ArrowsOut\", \"bold\");\nexport const CaretDown = /* @__PURE__ */ icon(CaretDownIcon, \"CaretDown\", \"bold\");\nexport const CaretLeft = /* @__PURE__ */ icon(CaretLeftIcon, \"CaretLeft\", \"bold\");\nexport const CaretRight = /* @__PURE__ */ icon(CaretRightIcon, \"CaretRight\", \"bold\");\n// Rings, not discs: filling them leaves a dot where a status should be.\nexport const Circle = /* @__PURE__ */ icon(CircleIcon, \"Circle\", \"bold\");\nexport const CircleDashed = /* @__PURE__ */ icon(CircleDashedIcon, \"CircleDashed\", \"bold\");\nexport const Check = /* @__PURE__ */ icon(CheckIcon, \"Check\", \"bold\");\n// A line drawing of a node on a line, like the carets: filling it makes a\n// blob rather than a heavier commit.\nexport const GitCommit = /* @__PURE__ */ icon(GitCommitIcon, \"GitCommit\", \"bold\");\n// Two lines and three dots: a line drawing, like the commit.\nexport const GitBranch = /* @__PURE__ */ icon(GitBranchIcon, \"GitBranch\", \"bold\");\nexport const DotsThree = /* @__PURE__ */ icon(DotsThreeIcon, \"DotsThree\", \"bold\");\nexport const X = /* @__PURE__ */ icon(XIcon, \"X\", \"bold\");\nexport const Plus = /* @__PURE__ */ icon(PlusIcon, \"Plus\", \"bold\");\nexport const UploadSimple = /* @__PURE__ */ icon(UploadSimpleIcon, \"UploadSimple\", \"bold\");\nexport const MagnifyingGlass = /* @__PURE__ */ icon(\n MagnifyingGlassIcon,\n \"MagnifyingGlass\",\n \"bold\",\n);\n\n// The editor toolbar's glyphs live here too: letterforms, list markers, and\n// brackets are all line drawings, so they take the bold weight for the same\n// reason the carets do. CodeBlock and Quotes are up in the filled group,\n// having actual bodies to fill.\nexport const Code = /* @__PURE__ */ icon(CodeIcon, \"Code\", \"bold\");\nexport const LinkSimple = /* @__PURE__ */ icon(LinkSimpleIcon, \"LinkSimple\", \"bold\");\n// Three bars: fill would turn them into slabs.\nexport const List = /* @__PURE__ */ icon(ListIcon, \"List\", \"bold\");\nexport const ListBullets = /* @__PURE__ */ icon(ListBulletsIcon, \"ListBullets\", \"bold\");\nexport const ListChecks = /* @__PURE__ */ icon(ListChecksIcon, \"ListChecks\", \"bold\");\nexport const ListNumbers = /* @__PURE__ */ icon(ListNumbersIcon, \"ListNumbers\", \"bold\");\nexport const TextB = /* @__PURE__ */ icon(TextBIcon, \"TextB\", \"bold\");\nexport const TextItalic = /* @__PURE__ */ icon(TextItalicIcon, \"TextItalic\", \"bold\");\nexport const TextStrikethrough = /* @__PURE__ */ icon(\n TextStrikethroughIcon,\n \"TextStrikethrough\",\n \"bold\",\n);\n\n// Logs and billing, line glyphs: arrows, a download tray, a dash, a loop and\n// a heartbeat trace, which fill would thicken into slabs.\nexport const ArrowCounterClockwise = /* @__PURE__ */ icon(\n ArrowCounterClockwiseIcon,\n \"ArrowCounterClockwise\",\n \"bold\",\n);\nexport const ArrowUpRight = /* @__PURE__ */ icon(ArrowUpRightIcon, \"ArrowUpRight\", \"bold\");\nexport const DownloadSimple = /* @__PURE__ */ icon(\n DownloadSimpleIcon,\n \"DownloadSimple\",\n \"bold\",\n);\nexport const Infinity = /* @__PURE__ */ icon(InfinityIcon, \"Infinity\", \"bold\");\nexport const Minus = /* @__PURE__ */ icon(MinusIcon, \"Minus\", \"bold\");\nexport const Pulse = /* @__PURE__ */ icon(PulseIcon, \"Pulse\", \"bold\");\n","/**\n * Where the widget lives, and everything that follows from it.\n *\n * A side and a height. The button snaps to the left or the right edge, because\n * an edge is where a thing parked on somebody's page belongs, and keeps\n * whatever height it was let go at, because that is the half nobody can guess\n * for them: the button is in the way of a cart, a banner or a footer, and only\n * the person looking at it knows which.\n *\n * The height is kept as a fraction of the travel rather than a number of\n * pixels, so a window that changes size moves it proportionally instead of\n * stranding it off the bottom.\n */\n\nexport type LauncherSide = \"left\" | \"right\";\n\n/** `y` runs 0 at the top of its travel to 1 at the bottom. */\nexport type LauncherSpot = { side: LauncherSide; y: number };\n\nexport const DEFAULT_SPOT: LauncherSpot = { side: \"right\", y: 1 };\n\nexport type Bounds = { left: number; top: number; width: number; height: number };\nexport type Size = { width: number; height: number };\n\nfunction clamp(value: number, low: number, high: number): number {\n return Math.min(Math.max(value, low), Math.max(low, high));\n}\n\n/** How far the button can travel between the top margin and the bottom one. */\nexport function travelOf(bounds: Bounds, button: Size, margin: number): number {\n return Math.max(0, bounds.height - 2 * margin - button.height);\n}\n\n/** The spot a button that landed here belongs to. */\nexport function spotFrom(\n at: { x: number; y: number },\n bounds: Bounds,\n button: Size,\n margin: number,\n): LauncherSpot {\n const travel = travelOf(bounds, button, margin);\n\n return {\n side:\n at.x + button.width / 2 < bounds.left + bounds.width / 2\n ? \"left\"\n : \"right\",\n y: travel === 0 ? 0 : clamp((at.y - bounds.top - margin) / travel, 0, 1),\n };\n}\n\n/**\n * The spot a panel dropped here belongs to.\n *\n * The other way round from the one above: an open panel is dragged by its own\n * header, so what landed somewhere is the box, not the button. The box hangs\n * upward from the button, so the button is at the box's bottom edge, and that\n * is the height the spot is a fraction of.\n *\n * The two do not cover the same range, and should not. A panel dragged to the\n * top of a short window still leaves its button at the panel's bottom edge,\n * which is exactly where the bubble should be waiting when it closes.\n */\nexport function spotFromPanel(\n at: { x: number; y: number },\n bounds: Bounds,\n button: Size,\n box: Size,\n margin: number,\n): LauncherSpot {\n const travel = travelOf(bounds, button, margin);\n const buttonY = at.y + box.height - button.height;\n\n return {\n side:\n at.x + box.width / 2 < bounds.left + bounds.width / 2 ? \"left\" : \"right\",\n y: travel === 0 ? 0 : clamp((buttonY - bounds.top - margin) / travel, 0, 1),\n };\n}\n\n/**\n * Everything the widget is positioned by, worked out from a spot.\n *\n * The box is panel sized and hangs upward from the button, which is where it\n * hung when this was a corner. Clamped so it never leaves the space it is in:\n * a button near the top drags the panel down below it instead, and the button\n * simply sits further up inside the same box.\n */\nexport function metricsFor(\n spot: LauncherSpot,\n bounds: Bounds,\n button: Size,\n boxHeight: number,\n margin: number,\n): { buttonY: number; boxTop: number; buttonInBox: number } {\n const buttonY = bounds.top + margin + spot.y * travelOf(bounds, button, margin);\n\n const highest = bounds.top + margin;\n const lowest = bounds.top + bounds.height - margin - boxHeight;\n const boxTop = clamp(buttonY + button.height - boxHeight, highest, lowest);\n\n return { buttonY, boxTop, buttonInBox: buttonY - boxTop };\n}\n\n/**\n * The closed shape: a circle the size of the button, at the button's own\n * height on the side it is parked against.\n *\n * `buttonY` is handed in as a CSS length rather than a number, so the same\n * function writes the resting shape from a variable the layout has filled in\n * and the fallback shape from a plain calc.\n */\nexport function closedClip(\n side: LauncherSide,\n buttonY: string,\n buttonSize: string,\n): string {\n const far = `calc(100% - ${buttonSize})`;\n\n return [\n `inset(${buttonY}`,\n side === \"left\" ? far : \"0px\",\n `calc(100% - ${buttonY} - ${buttonSize})`,\n side === \"left\" ? \"0px\" : far,\n `round calc(${buttonSize} / 2))`,\n ].join(\" \");\n}\n\n/** The point the scale grows from, so the circle stays under the button. */\nexport function clipOrigin(\n side: LauncherSide,\n buttonY: string,\n buttonSize: string,\n): string {\n return `${side === \"left\" ? \"0%\" : \"100%\"} calc(${buttonY} + ${buttonSize} / 2)`;\n}\n\n/**\n * Where this browser last left it.\n *\n * A small store rather than a bare read, the same shape `session.ts` uses and\n * for the same two reasons. The server cannot know, so it renders the default\n * and the store hands the real answer over on the client without a mismatched\n * attribute in between; and a snapshot has to be stable across calls, which is\n * what the cache is for.\n */\nconst STORAGE_KEY = \"median:launcher-spot\";\n\nconst listeners = new Set<() => void>();\nlet current: LauncherSpot | null = null;\n\nexport function subscribeSpot(listener: () => void) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\nexport function getSpot(): LauncherSpot {\n if (current !== null) return current;\n\n current = DEFAULT_SPOT;\n try {\n const stored = window.localStorage.getItem(STORAGE_KEY);\n const parsed: unknown = stored === null ? null : JSON.parse(stored);\n if (\n typeof parsed === \"object\" &&\n parsed !== null &&\n \"side\" in parsed &&\n \"y\" in parsed\n ) {\n const { side, y } = parsed as { side: unknown; y: unknown };\n if ((side === \"left\" || side === \"right\") && typeof y === \"number\") {\n current = { side, y: clamp(y, 0, 1) };\n }\n }\n } catch {\n // Blocked storage, or something else's key with our name on it. Either\n // way the default is a perfectly good place for a support button.\n }\n return current;\n}\n\nexport function getDefaultSpot(): LauncherSpot {\n return DEFAULT_SPOT;\n}\n\nexport function setSpot(spot: LauncherSpot): void {\n current = spot;\n\n try {\n window.localStorage.setItem(STORAGE_KEY, JSON.stringify(spot));\n } catch {\n // It stays where they put it until the page goes away, which is the same\n // deal the session token gets.\n }\n\n for (const listener of listeners) listener();\n}\n","\"use client\";\n\nimport { useState } from \"react\";\nimport { Collapse } from \"@/components/ui/collapse\";\nimport {\n MessageScroller,\n MessageScrollerButton,\n MessageScrollerContent,\n MessageScrollerItem,\n MessageScrollerProvider,\n MessageScrollerViewport,\n} from \"@/components/ui/message-scroller\";\nimport { groupMessages, withinRun } from \"@/lib/message-groups\";\nimport { cn } from \"@/lib/utils\";\nimport type { DragHandlers } from \"./use-launcher-drag\";\nimport { useStackShift } from \"./use-stack-shift\";\nimport { SETTLE_BODY, SETTLE_HEADER } from \"./widget-settle\";\nimport { WidgetComposer } from \"./widget-composer\";\nimport { WIDGET_CORNER } from \"./widget-corner\";\nimport { WidgetDestination } from \"./widget-destination\";\nimport { WidgetEmpty } from \"./widget-empty\";\nimport { WidgetHeader } from \"./widget-header\";\nimport { WidgetLoading } from \"./widget-loading\";\nimport { WidgetLocked } from \"./widget-locked\";\nimport { WidgetDivider, WidgetMessage } from \"./widget-message\";\nimport { WidgetRating } from \"./widget-rating\";\nimport { WidgetAwaitingHuman } from \"./widget-status\";\nimport type {\n SupportDestination,\n SupportFollow,\n SupportMessage,\n SupportPresence,\n SupportRating,\n} from \"./types\";\n\n/**\n * What keeps a run going: the same voice, close together, and no divider\n * cutting in. The agent is compared by id rather than by side, so a handover\n * from the AI to a person always starts a fresh run and says so. A new\n * conversation always does too, whoever is speaking either side of it.\n */\nfunction joinsPreviousTurn(message: SupportMessage, previous: SupportMessage) {\n return (\n !message.dayLabel &&\n !message.startsConversation &&\n message.sender === previous.sender &&\n message.agent?.id === previous.agent?.id &&\n withinRun(previous.sentAt, message.sentAt)\n );\n}\n\n/**\n * The support panel a visitor sees on a host's site: who is around, the\n * thread, and a box to write in.\n *\n * It fills whatever it is given rather than picking its own size, so the same\n * component works as a floating bubble on desktop, a full screen sheet on a\n * phone, and a framed preview in the gallery. Whoever mounts it decides the\n * box; this decides what goes in it.\n *\n * Uncontrolled on purpose for now: `messages` come in, `onSend` goes out, and\n * nothing here holds a thread of its own. That is the seam the SDK will sit on\n * once this is wired to a real conversation.\n */\nexport function SupportWidget({\n presence,\n messages,\n onSend,\n onTypingChange,\n onClose,\n isExpanded = false,\n onToggleExpanded,\n isLoading = false,\n isLocked = false,\n onSignIn,\n askForEmail = false,\n onSubmitEmail,\n isSending = false,\n awaitingHuman = false,\n rating,\n offer = null,\n onFollow,\n notice,\n showBadge = true,\n grabHandlers,\n isMoving = false,\n compact = false,\n className,\n}: {\n presence: SupportPresence;\n messages: SupportMessage[];\n /** Awaited by the composer, which keeps the draft if it rejects. */\n onSend?: (body: string, files: File[]) => unknown;\n /** The visitor's own typing, throttled by the composer. */\n onTypingChange?: (isTyping: boolean) => unknown;\n /** Omit to hide the close button, for a widget that is always on the page. */\n onClose?: () => void;\n /** Standing taller than it normally does. */\n isExpanded?: boolean;\n /**\n * Omit to hide the control. Only whoever owns the box the panel is drawn in\n * can grow it, so a widget that fills a frame it was given does not offer it.\n */\n onToggleExpanded?: () => void;\n /** Holds the mark while the conversation is on its way. */\n isLoading?: boolean;\n /**\n * Nobody is signed in, on a host that only takes questions from people who\n * are. The thread and the composer make way for the invitation to sign in.\n */\n isLocked?: boolean;\n /** Takes the visitor to the host's own sign in, and gives it its button. */\n onSignIn?: () => void;\n /**\n * The composer asks for an email before a first message, and the starter\n * rows stand down while it does: a pick would send before the question\n * was answered.\n */\n askForEmail?: boolean;\n /** What the composer's question does with a good answer. */\n onSubmitEmail?: (email: string) => void;\n /** Holds the send button while a message is. */\n isSending?: boolean;\n /** The agent has called for a person and nobody has picked it up yet. */\n awaitingHuman?: boolean;\n /**\n * The conversation is over and nobody has asked how it went yet. Null the\n * rest of the time, which is most of it.\n */\n rating?: SupportRating | null;\n /**\n * A page one of the team's tools offered with no message to hang it on,\n * which is a teammate running a tool themselves. It stands over the\n * composer while the offer is worth making.\n */\n offer?: SupportDestination | null;\n /**\n * What the buttons on those offers do. Omit it and none of them are shown:\n * this is the only way the panel can move anybody.\n */\n onFollow?: SupportFollow;\n /** One line above the composer, for a send that went wrong. */\n notice?: string | null;\n showBadge?: boolean;\n /**\n * Makes the header the thing the panel is dragged around by. Omitted by\n * anything that frames the widget rather than floats it, which is most of\n * the places it turns up outside a host's page.\n */\n grabHandlers?: DragHandlers;\n /** Being dragged right now, so the header can hold the closed hand. */\n isMoving?: boolean;\n /**\n * A denser scale: smaller type, tighter rows, the composer as a box. For\n * a panel with room to spare, where the corner's phone sized type reads\n * as oversized. The corner never sets it.\n */\n compact?: boolean;\n className?: string;\n}) {\n const stack = useStackShift(messages.length);\n return (\n <section\n aria-label={`${presence.name} support`}\n className={cn(\n // The widget keeps its own radius scale. The app's token can climb as\n // the chrome gets rounder without the widget changing shape under the\n // hosts embedding it.\n `flex h-full w-full flex-col overflow-hidden ${WIDGET_CORNER} border bg-card text-card-foreground [--radius:0.625rem]`,\n // And its own pace for a reply arriving a line at a time: a step\n // quicker than the palette's ladder, since a support answer is a\n // few short lines in a bubble rather than an essay in a column.\n \"[--duration-line-rise:360ms] [--duration-line-beat:45ms]\",\n className,\n )}\n >\n <WidgetHeader\n presence={presence}\n onClose={onClose}\n isExpanded={isExpanded}\n onToggleExpanded={onToggleExpanded}\n grabHandlers={grabHandlers}\n isMoving={isMoving}\n compact={compact}\n className={SETTLE_HEADER}\n />\n\n {isLoading ? (\n <WidgetLoading />\n ) : isLocked ? (\n /* After the loading rather than before it: the line names the host,\n so it waits for the name the same as everything else does. */\n <WidgetLocked\n orgName={presence.name}\n onSignIn={onSignIn}\n showBadge={showBadge}\n />\n ) : (\n <Conversation\n presence={presence}\n messages={messages}\n onSend={onSend}\n onTypingChange={onTypingChange}\n askForEmail={askForEmail}\n onSubmitEmail={onSubmitEmail}\n isSending={isSending}\n awaitingHuman={awaitingHuman}\n rating={rating}\n offer={offer}\n onFollow={onFollow}\n notice={notice}\n showBadge={showBadge}\n compact={compact}\n stackRef={stack}\n />\n )}\n </section>\n );\n}\n\n/**\n * Everything under the header. Split out so it mounts as one thing when the\n * conversation lands, which is what gives the whole lot a single fade in\n * rather than a thread and a composer arriving separately.\n */\nfunction Conversation({\n presence,\n messages,\n onSend,\n onTypingChange,\n askForEmail = false,\n onSubmitEmail,\n isSending = false,\n awaitingHuman = false,\n rating,\n offer = null,\n onFollow,\n notice,\n showBadge,\n compact,\n stackRef,\n}: {\n presence: SupportPresence;\n messages: SupportMessage[];\n onSend?: (body: string, files: File[]) => unknown;\n onTypingChange?: (isTyping: boolean) => unknown;\n /** The composer asks for an email before a first message. */\n askForEmail?: boolean;\n /** What the composer's question does with a good answer. */\n onSubmitEmail?: (email: string) => void;\n isSending?: boolean;\n awaitingHuman?: boolean;\n rating?: SupportRating | null;\n /** A page offered with no message to hang it on. */\n offer?: SupportDestination | null;\n /** What every offer's button does, if the panel can move anybody at all. */\n onFollow?: SupportFollow;\n notice?: string | null;\n showBadge: boolean;\n compact: boolean;\n stackRef: (node: HTMLDivElement | null) => void;\n}) {\n const groups = groupMessages(messages, joinsPreviousTurn);\n\n // Which messages were already here when the thread was first drawn. Those\n // sit still; anything after them landed in front of somebody, and arrives\n // like it.\n const [openedWith] = useState<ReadonlySet<string>>(\n () => new Set(messages.map((message) => message.id)),\n );\n\n // The last offer and the last notice, held through their own fold: by the\n // time either is closing, whoever passed it has stopped, and a section\n // folding shut around nothing has no height to fold.\n const [heldOffer, setHeldOffer] = useState(offer);\n if (offer !== null && offer.key !== heldOffer?.key) setHeldOffer(offer);\n const [heldNotice, setHeldNotice] = useState(notice ?? null);\n if (notice && notice !== heldNotice) setHeldNotice(notice);\n\n return (\n <div\n className={cn(\n \"flex min-h-0 flex-1 flex-col animate-in fade-in-0 duration-500 ease-out motion-reduce:animate-none\",\n SETTLE_BODY,\n )}\n >\n {messages.length === 0 ? (\n <WidgetEmpty\n team={presence.team}\n orgName={presence.name}\n onPick={\n onSend === undefined\n ? undefined\n : (message) => void onSend(message, [])\n }\n isSending={isSending}\n // Held rather than taken away while the email is asked for: a row\n // would send before the question above it was answered, and rows\n // that leave and come back move everything above them twice.\n isWaiting={askForEmail}\n compact={compact}\n />\n ) : (\n /* The same scroller the inbox thread uses, so both sides of a\n conversation stick to the newest message the same way, and both grow\n the same button when you scroll back up.\n\n It keeps following the bottom while the panel is resizing, and it\n has to. A panel shrinking takes its top edge down past the thread,\n so a scroller left alone slides the newest message under the\n composer and has to catch up afterwards. Following it is what the\n scroller stops easing for, so this tracks frame by frame. */\n <MessageScrollerProvider>\n {/* h-auto so the scroller takes the space the header and composer\n leave, rather than the full column its own `size-full` asks for. */}\n <MessageScroller className=\"h-auto min-h-0 flex-1\">\n <MessageScrollerViewport\n // A log with a polite live region, so a reply is announced as it\n // lands without pulling focus out of the box being typed in.\n role=\"log\"\n aria-live=\"polite\"\n aria-relevant=\"additions\"\n aria-label=\"Conversation\"\n className={compact ? \"px-5 py-3\" : \"px-4 py-3\"}\n >\n {/* justify-end so a short thread sits on the composer rather than\n hanging from the header with a field of nothing under it. The\n content is already at least a viewport tall, so this only\n decides where the slack goes: above the first message, not\n below the last.\n\n The space between messages is on the items rather than here,\n since a message carrying on the one above it sits closer to\n it than one starting a new turn. */}\n <MessageScrollerContent\n ref={stackRef}\n className=\"justify-end gap-0\"\n >\n {messages.map((message, index) => (\n <MessageScrollerItem\n key={message.id}\n messageId={message.id}\n // The scroller skips rendering off screen items and holds\n // 10rem open for each one until it arrives. That is a fair\n // guess in the inbox, where the column is wide and a\n // message is tall. Here it is roughly double: a bubble in a\n // panel this narrow is nearer 4rem, so every item resized\n // the moment it scrolled into view and the whole thread\n // jumped. A support thread is short enough that skipping\n // any of it buys nothing anyway.\n className={cn(\n \"flex flex-col gap-4 [content-visibility:visible] first:pt-0\",\n groups[index].isFirst\n ? compact\n ? \"pt-4\"\n : \"pt-5\"\n : compact\n ? \"pt-1\"\n : \"pt-1.5\",\n )}\n >\n <WidgetDivider message={message} />\n <WidgetMessage\n message={message}\n group={groups[index]}\n onFollow={onFollow}\n compact={compact}\n isLive={!openedWith.has(message.id)}\n />\n </MessageScrollerItem>\n ))}\n </MessageScrollerContent>\n </MessageScrollerViewport>\n\n {/* Outside the viewport: it is positioned against the scroller, so\n inside it would scroll away with the messages. */}\n <MessageScrollerButton />\n </MessageScroller>\n </MessageScrollerProvider>\n )}\n\n {/* The question a finished thread asks. Never at the same time as the\n line below it: one is a conversation that is over and the other is\n one waiting on a person. */}\n {rating && <WidgetRating rating={rating} />}\n\n {/* A page a teammate's own tool offered. It sits here rather than in\n the thread because there is no message it belongs under, and this\n is where the panel puts anything about where the conversation\n stands rather than what was said in it.\n\n Each of the three lines below opens out of the composer and folds\n back into it, so the thread above them moves with the fold rather\n than jumping the height of a line. Always mounted, for that: a\n section that comes and goes cannot fold. */}\n <Collapse\n isOpen={offer !== null && onFollow !== undefined}\n className=\"shrink-0\"\n >\n {heldOffer !== null && onFollow !== undefined && (\n <div className=\"px-4 pb-1.5\">\n <WidgetDestination destination={heldOffer} onFollow={onFollow} />\n </div>\n )}\n </Collapse>\n\n {/* Above the failed send, since it is the older of the two facts and\n the one that is still true after a retry works. */}\n <Collapse isOpen={awaitingHuman} className=\"shrink-0\">\n <WidgetAwaitingHuman />\n </Collapse>\n\n {/* A send that failed says so right where the retry happens. */}\n <Collapse isOpen={Boolean(notice)} className=\"shrink-0\">\n <p className=\"px-4 pb-1.5 text-center text-sm text-destructive\">\n {notice ?? heldNotice}\n </p>\n </Collapse>\n\n <WidgetComposer\n onSend={onSend}\n onTypingChange={onTypingChange}\n askForEmail={askForEmail}\n onSubmitEmail={onSubmitEmail}\n isSending={isSending}\n showBadge={showBadge}\n variant={compact ? \"stacked\" : \"pill\"}\n compact={compact}\n />\n </div>\n );\n}\n","import type { ReactNode } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * How long one takes to open or close. Anything that has to act after a\n * collapse has finished reads this rather than guessing, so the two cannot\n * drift; it matches the `duration-200` below.\n */\nexport const COLLAPSE_MS = 200;\n\n/**\n * A section that opens and closes rather than appearing and disappearing.\n *\n * The height is a grid row travelling between nothing and whatever the\n * contents want to be, which is the one way to animate to `auto` without\n * measuring anything on every frame. Sideways it is the width, a grid column\n * doing the same, for something that joins a row: its neighbours glide over\n * to make room instead of jumping. The contents keep their own size\n * throughout and are clipped rather than squashed, so words never reflow\n * mid animation.\n *\n * Closed, it is inert: a keyboard cannot land in a section that is not on\n * screen, and nothing inside one can be clicked through the clip.\n *\n * No gap between this and its neighbours. A closed section is zero pixels\n * tall but a flex gap around it is not, so spacing belongs inside the\n * contents; otherwise closing one leaves a hole where it used to be.\n */\nexport function Collapse({\n isOpen,\n id,\n direction = \"vertical\",\n children,\n className,\n}: {\n isOpen: boolean;\n /** For whatever toggles it to point at with `aria-controls`. */\n id?: string;\n /** Which way it opens. Sideways for something joining a row. */\n direction?: \"vertical\" | \"horizontal\";\n children: ReactNode;\n className?: string;\n}) {\n const isSideways = direction === \"horizontal\";\n\n return (\n <div\n id={id}\n inert={isOpen ? undefined : true}\n className={cn(\n \"grid duration-200 ease-out motion-reduce:transition-none\",\n isSideways\n ? \"transition-[grid-template-columns,opacity]\"\n : \"transition-[grid-template-rows,opacity]\",\n isSideways\n ? isOpen\n ? \"grid-cols-[1fr]\"\n : \"grid-cols-[0fr]\"\n : isOpen\n ? \"grid-rows-[1fr]\"\n : \"grid-rows-[0fr]\",\n isOpen ? \"opacity-100\" : \"opacity-0\",\n className,\n )}\n >\n <div className=\"overflow-hidden\">{children}</div>\n </div>\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport {\n MessageScroller as MessageScrollerPrimitive,\n useMessageScroller,\n useMessageScrollerScrollable,\n useMessageScrollerVisibility,\n} from \"@shadcn/react/message-scroller\";\n\n// Phosphor instead of the registry default, per the project icon rule.\nimport { ArrowDown } from \"@/components/icons\";\nimport { Button } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * From the shadcn registry (@shadcn/message-scroller, radix-nova style), with\n * two changes: the icon comes from our own set, and the registry's\n * `scrollbar-*` and `inset-s-*` utilities are written as plain properties,\n * since neither the scrollbar plugin nor logical inset utilities are here.\n *\n * `scrollbar-width` is said here as well as on the app's own `html`, and the\n * repetition is the point: this is the one scroller that also runs inside the\n * widget on somebody else's page, where there is no `html` of ours to inherit\n * from and a thread would otherwise wear whatever their site wears.\n */\n\nfunction MessageScrollerProvider({\n autoScroll = true,\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Provider>) {\n // Every thread here is a chat, so the chat behavior is the default: stay\n // pinned to the newest message while at the bottom, stay put while reading\n // older ones, and let the button offer the way back down. The primitive's\n // own default (off, with per-item scroll anchors) is built for pin-the-\n // question streaming UIs and yanks the viewport around in a live thread.\n return <MessageScrollerPrimitive.Provider autoScroll={autoScroll} {...props} />;\n}\n\nfunction MessageScroller({\n className,\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Root>) {\n return (\n <MessageScrollerPrimitive.Root\n data-slot=\"message-scroller\"\n className={cn(\n \"group/message-scroller relative flex size-full min-h-0 flex-col overflow-hidden\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/** Within this of the foot of the thread counts as being at it. */\nconst AT_END_PX = 8;\n/** Less than a pixel adrift is not worth correcting. */\nconst DRIFT_PX = 1;\n\n/**\n * Holds the newest message against the foot of the viewport while the viewport\n * is changing size.\n *\n * The scroller already does this, one frame late. Both of its resize observers\n * hand their work to `requestAnimationFrame`, so every frame of a resize is\n * painted with the scroll position the frame before it asked for.\n *\n * Growing hides that. A viewport that gets taller leaves its scroll offset out\n * of range, and the browser has to pull it back in as part of the same layout,\n * which lands the thread exactly where the late correction was going to put it\n * anyway. Shrinking has nothing doing that: the offset stays valid, the foot of\n * the viewport comes down past the thread, and the newest message drops a dozen\n * pixels behind and is dragged back a frame later. Every frame. That is the\n * shiver, and it is why only shrinking has it, and why it is worst on a panel\n * parked at the bottom, where the whole of the change comes off the top.\n *\n * So the same correction is made here first, in the observer itself, which is\n * before the frame is painted rather than after.\n */\nfunction usePinnedToEnd(viewport: React.RefObject<HTMLDivElement | null>) {\n React.useEffect(() => {\n const node = viewport.current;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n\n // Whether the thread was against the foot of the viewport, written down as\n // it happens: by the time a resize is reported the new size is already on\n // and the answer to that has changed.\n let wasAtEnd = true;\n const remember = () => {\n wasAtEnd =\n node.scrollHeight - node.scrollTop - node.clientHeight <= AT_END_PX;\n };\n // And how tall it was, since only a viewport that got shorter is one\n // this has to catch up.\n let height = node.clientHeight;\n\n const observer = new ResizeObserver(() => {\n const drift = node.scrollHeight - node.scrollTop - node.clientHeight;\n const shrank = node.clientHeight < height;\n height = node.clientHeight;\n\n // Only ever catching up, never restating. A viewport that grew has had\n // its offset put back in range by the browser already, to a precision\n // this cannot match: `scrollHeight` and `clientHeight` are whole pixels\n // and the box between them is not, so saying it again would replace an\n // exact answer with one up to a pixel out, differently on every frame.\n // And when it grew because the composer gave back its lines as a\n // message landed, the drift is the message, not the box: the scroller\n // is already gliding to it, and a correction here would snap instead.\n if (wasAtEnd && shrank && drift > DRIFT_PX) {\n // Not `auto`, which would take the smooth scrolling below and ease a\n // correction that is meant to be invisible.\n node.scrollTo({\n top: node.scrollHeight - node.clientHeight,\n behavior: \"instant\",\n });\n }\n\n remember();\n });\n\n node.addEventListener(\"scroll\", remember, { passive: true });\n observer.observe(node);\n return () => {\n node.removeEventListener(\"scroll\", remember);\n observer.disconnect();\n };\n }, [viewport]);\n}\n\n/**\n * How quickly the glide closes on where it is going, as a time constant:\n * every millisecond covers the same share of what is left. Short enough to\n * arrive promptly, and the tail is what makes it read as settling rather\n * than stopping. Half a screen is within a pixel in about a third of a\n * second.\n */\nconst GLIDE_MS = 70;\n\n/** A frame that took longer than this is a tab that was asleep, not a long step. */\nconst MAX_STEP_MS = 64;\n\nfunction prefersReducedMotion(): boolean {\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n}\n\n/**\n * The scroller's own scrolls, on the thread's own curve.\n *\n * The primitive pins the thread by calling `scrollTo` on the viewport, and\n * `scroll-behavior: smooth` used to answer that with the browser's animation:\n * a fixed curve that is not ours to time, restarted from rest every time the\n * target moved, which it does on every frame of a bubble growing into a\n * reply. So the method is taken over on the node. Each call says where the\n * thread should be, and one loop carries the viewport there, closing a fixed\n * share of what is left on every frame. A target that moves mid glide is\n * followed from wherever the thread has got to; the speed never resets.\n *\n * Two scrolls go straight to the browser, and neither is worth watching. The\n * first one, since a thread opens at the top and lands on the newest\n * message, and easing that drags the whole history past before you can read\n * any of it. And any one that finds the box narrower or shorter than the\n * last one did: a window dragged, a keyboard coming up, a composer growing a\n * line all re-pin the thread on every frame, and gliding towards a target\n * that moves every frame leaves the messages trailing the box they are in.\n * A box that got taller is different. The browser keeps the thread on the\n * bottom of a growing box by itself, so a request then is about something\n * else, a message landing as the composer gives its lines back, and that\n * one is worth gliding. `instant`, and anyone who asked for less motion, go\n * straight through too.\n *\n * A wheel, a touch or a key under the glide ends it: the reader has taken\n * over where the thread is, and the primitive stops asking on its own.\n */\nfunction useGlidingScroll(\n viewport: React.RefObject<HTMLDivElement | null>,\n hasLanded: boolean,\n) {\n // Read at the moment a scroll is asked for rather than captured, since the\n // first one can come on any frame.\n const landed = React.useRef(hasLanded);\n React.useLayoutEffect(() => {\n landed.current = hasLanded;\n }, [hasLanded]);\n\n React.useEffect(() => {\n const node = viewport.current;\n if (!node) return;\n\n // The prototype's, under one signature, so it can be called back\n // without arguing with the overloads.\n const scrollNatively = Element.prototype.scrollTo as (\n this: Element,\n options?: ScrollToOptions,\n ) => void;\n\n let target: number | null = null;\n let frame = 0;\n let last = 0;\n // The box as the last request found it. See `glide`.\n let box = { width: node.clientWidth, height: node.clientHeight };\n\n const stop = () => {\n if (frame !== 0) cancelAnimationFrame(frame);\n frame = 0;\n target = null;\n };\n\n const step = (now: number) => {\n frame = 0;\n if (target === null) return;\n\n // Never below zero. A frame's timestamp is the frame's start, and a\n // request made partway through that frame is stamped later than it,\n // which would make the first step a step backwards.\n const elapsed = Math.max(0, Math.min(now - last, MAX_STEP_MS));\n last = now;\n // Against the box as it is now, not as it was when asked: a box that\n // grew since then has less room to scroll than the request thought,\n // and a goal past the end is a goal that is never reached.\n const goal = Math.min(target, node.scrollHeight - node.clientHeight);\n const left = goal - node.scrollTop;\n if (Math.abs(left) < 0.5) {\n node.scrollTop = goal;\n target = null;\n return;\n }\n node.scrollTop += left * (1 - Math.exp(-elapsed / GLIDE_MS));\n frame = requestAnimationFrame(step);\n };\n\n const glide = (options?: ScrollToOptions | number, y?: number) => {\n const asked: ScrollToOptions | undefined =\n typeof options === \"number\" ? { left: options, top: y } : options;\n const size = { width: node.clientWidth, height: node.clientHeight };\n const shrank = size.width !== box.width || size.height < box.height;\n box = size;\n const glides =\n asked !== undefined &&\n typeof asked.top === \"number\" &&\n asked.behavior !== \"instant\" &&\n landed.current &&\n !shrank &&\n !prefersReducedMotion();\n\n if (!glides) {\n stop();\n scrollNatively.call(node, asked);\n return;\n }\n\n const furthest = node.scrollHeight - node.clientHeight;\n target = Math.min(Math.max(0, asked.top as number), furthest);\n if (frame === 0) {\n last = performance.now();\n frame = requestAnimationFrame(step);\n }\n };\n\n // On the node itself, in front of the prototype's, and taken off again\n // below.\n Object.defineProperty(node, \"scrollTo\", {\n value: glide,\n configurable: true,\n writable: true,\n });\n\n node.addEventListener(\"wheel\", stop, { passive: true });\n node.addEventListener(\"touchstart\", stop, { passive: true });\n node.addEventListener(\"keydown\", stop);\n return () => {\n stop();\n node.removeEventListener(\"wheel\", stop);\n node.removeEventListener(\"touchstart\", stop);\n node.removeEventListener(\"keydown\", stop);\n // Back to the prototype's, for a node that outlives this.\n Reflect.deleteProperty(node, \"scrollTo\");\n };\n }, [viewport]);\n}\n\n/**\n * True from the frame after the viewport mounts, which is a frame after the\n * scroller has put itself at the newest message.\n */\nfunction useHasLanded() {\n const [hasLanded, setHasLanded] = React.useState(false);\n\n React.useEffect(() => {\n const frame = requestAnimationFrame(() => setHasLanded(true));\n\n return () => cancelAnimationFrame(frame);\n }, []);\n\n return hasLanded;\n}\n\nfunction MessageScrollerViewport({\n className,\n ref,\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Viewport>) {\n const viewport = React.useRef<HTMLDivElement | null>(null);\n\n // Whether the first scroll has happened, which is the one that lands. See\n // `useGlidingScroll` for the rest.\n const hasLanded = useHasLanded();\n\n usePinnedToEnd(viewport);\n useGlidingScroll(viewport, hasLanded);\n\n // Ours as well as whoever else asked for it. The primitive keeps its own\n // separately, so this is only the two of us.\n const holdViewport = React.useCallback(\n (node: HTMLDivElement | null) => {\n viewport.current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref) ref.current = node;\n },\n [ref],\n );\n\n return (\n <MessageScrollerPrimitive.Viewport\n ref={holdViewport}\n data-slot=\"message-scroller-viewport\"\n // Every scroll the scroller makes for itself asks for `auto`, and the\n // glide is what answers: a reply landing while you are at the bottom\n // carries you down rather than teleporting you, and the button travels\n // the distance it is offering to cover.\n //\n // All of them are a pin to the newest message today. The one to watch\n // for is the correction that holds your place when older messages are\n // loaded in above: that one wants to be instant, and it would have to\n // ask for it by name to stay that way.\n className={cn(\n \"size-full min-h-0 min-w-0 overflow-y-auto overscroll-contain contain-content [scrollbar-gutter:stable] [scrollbar-width:thin]\",\n // The fade says \"there is more below\", so it only belongs to a\n // viewport that has more below.\n //\n // The registry hangs `scroll-fade-b` on unconditionally and retracts\n // it with a scroll driven animation. That leaves the newest message\n // dimmed at the foot of every thread: a browser without scroll\n // timelines takes the utility's own fallback and pins the fade on\n // forever, and even where they work the mask is still painted over a\n // thread too short to scroll at all. `data-scrollable` is the same\n // question answered by measurement, and the primitive already writes\n // it here. It drops \"end\" as soon as you are within a few pixels of\n // the bottom, and while it is following the bottom it never claims it.\n \"data-[scrollable~=end]:scroll-fade-b\",\n // The registry also blanks the scrollbar while the scroller is moving\n // on its own. That was covering for the jump: the bar flicked from one\n // place to another with nothing in between. Now it slides, so hiding it\n // is the flicker, and it holds the thumb back for a fixed 180ms that\n // has nothing to do with how far the thread actually travelled.\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction MessageScrollerContent({\n className,\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Content>) {\n return (\n <MessageScrollerPrimitive.Content\n data-slot=\"message-scroller-content\"\n className={cn(\"flex h-max min-h-full flex-col gap-6\", className)}\n {...props}\n />\n );\n}\n\nfunction MessageScrollerItem({\n className,\n scrollAnchor = false,\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Item>) {\n return (\n <MessageScrollerPrimitive.Item\n data-slot=\"message-scroller-item\"\n scrollAnchor={scrollAnchor}\n className={cn(\n \"min-w-0 shrink-0 [contain-intrinsic-size:auto_10rem] [content-visibility:auto]\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction MessageScrollerButton({\n direction = \"end\",\n className,\n children,\n render,\n variant = \"secondary\",\n size = \"icon-sm\",\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Button> &\n Pick<React.ComponentProps<typeof Button>, \"variant\" | \"size\">) {\n return (\n <MessageScrollerPrimitive.Button\n data-slot=\"message-scroller-button\"\n data-direction={direction}\n data-variant={variant}\n data-size={size}\n direction={direction}\n className={cn(\n \"absolute start-1/2 -translate-x-1/2 border-border bg-background text-foreground transition-[translate,scale,opacity] duration-200 hover:bg-muted hover:text-foreground data-[active=false]:pointer-events-none data-[active=false]:scale-95 data-[active=false]:opacity-0 data-[active=false]:duration-400 data-[active=false]:ease-[cubic-bezier(0.7,0,0.84,0)] data-[active=true]:translate-y-0 data-[active=true]:scale-100 data-[active=true]:opacity-100 data-[active=true]:ease-[cubic-bezier(0.23,1,0.32,1)] data-[direction=end]:bottom-4 data-[direction=end]:data-[active=false]:translate-y-full data-[direction=start]:top-4 data-[direction=start]:data-[active=false]:-translate-y-full rtl:translate-x-1/2 data-[direction=start]:[&_svg]:rotate-180\",\n className,\n )}\n render={render ?? <Button variant={variant} size={size} />}\n {...props}\n >\n {children ?? (\n <>\n <ArrowDown />\n <span className=\"sr-only\">\n {direction === \"end\" ? \"Scroll to end\" : \"Scroll to start\"}\n </span>\n </>\n )}\n </MessageScrollerPrimitive.Button>\n );\n}\n\nexport {\n MessageScrollerProvider,\n MessageScroller,\n MessageScrollerViewport,\n MessageScrollerContent,\n MessageScrollerItem,\n MessageScrollerButton,\n useMessageScroller,\n useMessageScrollerScrollable,\n useMessageScrollerVisibility,\n};\n","import*as i from'react';function S({defaultTagName:a,props:r,render:t,state:n={},stateAttributesMapping:e}){let o=d(R(n,e),r);if(!t)return i.createElement(a,o);if(typeof t==\"function\")return t(o,n);if(!i.isValidElement(t))return null;let s=t.props,u={...d(o,s),ref:f(o.ref,s.ref)};return i.cloneElement(t,u)}function d(...a){let r={};for(let t of a){if(!t)continue;let n=t;for(let e of Object.keys(n)){let o=n[e];if(o===void 0)continue;let s=r[e];e===\"className\"?r[e]=[s,o].filter(Boolean).join(\" \"):e===\"style\"?r[e]={...s,...o}:e===\"ref\"?r[e]=f(s,o):l(e)&&typeof s==\"function\"&&typeof o==\"function\"?r[e]=p(o,s):r[e]=o;}}return r}function R(a,r){let t={};for(let n of Object.keys(a)){let e=a[n],o=r?.[n]?.(e);if(o){Object.assign(t,o);continue}if(n===\"slot\"){t[\"data-slot\"]=e;continue}let s=`data-${String(n).replace(/[A-Z]/g,c=>`-${c.toLowerCase()}`)}`;typeof e==\"boolean\"?t[s]=e?\"\":void 0:e!=null&&(t[s]=String(e));}return t}function p(a,r){return function(n){a(n),n.defaultPrevented||r(n);}}function l(a){return /^on[A-Z]/.test(a)}function f(...a){let r=a.filter(Boolean);if(r.length!==0)return t=>{for(let n of r)typeof n==\"function\"?n(t):n&&(n.current=t);}}export{S as a,d as b,f as c};","\"use client\";\nimport {c,a,b}from'../chunk-HBS6WEDP.js';import*as g from'react';import {jsx,jsxs}from'react/jsx-runtime';var xe=8,Ve=64,_e=0,J=.5,ke=180,Be=new Set([\"ArrowDown\",\"ArrowUp\",\"End\",\"Home\",\"PageDown\",\"PageUp\",\" \"]),ge={start:false,end:false},dt=[],Q={currentAnchorId:null,visibleMessageIds:dt};function De({content:e,scrollEdgeThreshold:t,spacer:r,viewport:n}){if(!n||!e)return ge;let o=Se({content:e,spacer:r,viewport:n});return {start:n.scrollTop>t,end:o-n.scrollTop-n.clientHeight>t}}function Fe({content:e,scrollMargin:t,scrollPreviousItemPeek:r,spacer:n,viewport:o,visibleMessageIds:i}){if(!e||!o)return Q;let S=o.getBoundingClientRect(),l=S.top+t+r,d=typeof IntersectionObserver>\"u\",E=[],b=null;for(let C of ee(e,n)){let p=C.dataset.messageId;if(!p)continue;let m=C.dataset.scrollAnchor===\"true\",L=m||d?C.getBoundingClientRect():null;(d&&L?L.bottom>l&&L.top<S.bottom:i.has(p))&&E.push(p),m&&L&&L.top<=l+J&&(b=p);}return E.length===0&&b===null?Q:{currentAnchorId:b,visibleMessageIds:E}}function ee(e,t){return Array.from(e.children).filter(r=>r instanceof HTMLElement&&r!==t)}function je(e,t){for(let r=t;r<e.length;r++){let n=e[r];if(n?.dataset.scrollAnchor===\"true\")return n}return null}function Ge(e,t){for(let r of e)if(r.dataset.scrollAnchor===\"true\"&&!t.has(r))return r;return null}function Ne(e,t){let r=0;for(let n=t;n<e.length;n++)if(e[n]?.dataset.scrollAnchor===\"true\"&&(r+=1,r>1))return true;return false}function Ue(e){for(let t=e.length-1;t>=0;t--){let r=e[t];if(r?.dataset.scrollAnchor===\"true\")return r}return null}function Ye({content:e,spacer:t,viewport:r}){let n=r.getBoundingClientRect();for(let o of ee(e,t)){if(!o.dataset.messageId)continue;let i=o.getBoundingClientRect();if(i.bottom>n.top&&i.top<n.bottom)return o}return null}function ze({align:e,element:t,scrollMargin:r,spacer:n,viewport:o}){let i=pe(t,o),S=t.getBoundingClientRect().height,l=bt(n);if(e===\"center\"){let d=Math.max(0,o.clientHeight-l.start-l.end);return i-l.start-(d-S)/2-r}if(e===\"end\")return i-o.clientHeight+S+l.end+r;if(e===\"nearest\"){let d=i+S,E=o.scrollTop+l.start,b=o.scrollTop+o.clientHeight-l.end;return i>=E&&d<=b?o.scrollTop:i<E?i-l.start-r:d-o.clientHeight+l.end+r}return i-l.start-r}function pe(e,t){let r=e.getBoundingClientRect(),n=t.getBoundingClientRect();return r.top-n.top+t.scrollTop}function te(e,t){return e.getBoundingClientRect().top-t.getBoundingClientRect().top}function qe({content:e,scrollTop:t,spacer:r,viewport:n}){let o=Se({content:e,spacer:r,viewport:n});return t+n.clientHeight-o}function Se({content:e,spacer:t,viewport:r}){let n=ee(e,t),o=We(e),i=r.getBoundingClientRect(),S=r.scrollTop,l=o.start+o.end;for(let d of n){let E=d.getBoundingClientRect();l=Math.max(l,E.bottom-i.top+S+o.end);}return l}function Ke(e){return Math.max(0,e.scrollHeight-e.clientHeight)}function We(e){let t=window.getComputedStyle(e);return {end:Re(t.paddingBlockEnd||t.paddingBottom),start:Re(t.paddingBlockStart||t.paddingTop)}}function bt(e){let t=e?.parentElement;return t?We(t):{end:0,start:0}}function $e(e){if(!e)return 0;let t=window.getComputedStyle(e),r=t.rowGap===\"normal\"?t.gap:t.rowGap;return Re(r)}function Re(e){if(!e)return 0;let t=Number.parseFloat(e);return Number.isFinite(t)?t:0}function Je({refs:e,commitScrollState:t,scheduleStateCommit:r,scheduleVisibilitySync:n}){let{streamingTurnRef:o,autoScrollRef:i,autoscrollingRef:S,autoscrollingTimeoutRef:l,contentRef:d,defaultScrollPositionAppliedRef:E,itemCountRef:b,messageElementsRef:C,modeRef:p,pendingScrollToMessageRef:m,prependRestoreRef:L,scrollMarginRef:y,scrollPreviousItemPeekRef:u,spacerGapRef:x,spacerHeightRef:Y,spacerRef:k,viewportRef:_}=e,G=g.useCallback(c=>{l.current!==null&&(window.clearTimeout(l.current),l.current=null),S.current!==c&&(S.current=c,t()),c&&(l.current=window.setTimeout(()=>{l.current=null,S.current=false,t();},ke));},[t]),v=g.useCallback(c=>{let f=k.current;if(!f)return;let h=Math.max(0,Math.ceil(c));Y.current!==h&&(Y.current=h,f.hidden=h===0,f.style.height=`${h}px`,f.style.marginTop=h>0?`${-x.current}px`:\"\");},[]),H=g.useCallback((c,{behavior:f=\"auto\",autoscrolling:h=false}={})=>{let P=_.current;if(!P)return;let V=Math.max(0,c);if(Math.abs(P.scrollTop-V)<=J){P.scrollTop=V,t();return}h&&G(true),P.scrollTo({top:V,behavior:f}),r();},[t,r,G]),N=g.useCallback(({behavior:c=\"auto\"}={})=>_.current?(v(0),o.current=null,p.current=\"free-scrolling\",H(0,{behavior:c}),n(),true):false,[n,H,v]),ne=g.useCallback(({behavior:c=\"auto\"}={})=>{let f=_.current;return f?(v(0),o.current=null,p.current=i.current?\"following-bottom\":\"free-scrolling\",H(Ke(f),{autoscrolling:true,behavior:c}),n(),true):false},[n,H,v]),A=g.useCallback((c,{align:f=\"start\",behavior:h=\"auto\",scrollMargin:P=y.current}={},{keepPreviousPeek:V=false}={})=>{let z=d.current,q=_.current;if(!z||!q||!z.contains(c))return false;let K=ze({align:f,element:c,scrollMargin:V?P+u.current:P,spacer:k.current,viewport:q}),se=qe({content:z,scrollTop:K,spacer:k.current,viewport:q});return v(se),L.current={element:c,viewportTop:te(c,q)},p.current=V?\"anchored-to-message\":\"settling-jump\",o.current=V?c:null,H(K,{behavior:h}),n(),true},[n,H,v]),B=g.useCallback(()=>{let c=o.current;return !c||!c.isConnected||p.current!==\"anchored-to-message\"?false:A(c,{align:\"start\"},{keepPreviousPeek:true})},[A]),D=g.useCallback((c,f)=>{let h=C.current.get(c);return h?(E.current=true,A(h,f)?(m.current=null,true):(m.current={messageId:c,options:f},true)):b.current===0?(m.current={messageId:c,options:f},E.current=true,true):false},[A]);return {flushPendingScrollToMessage:g.useCallback(()=>{let c=m.current;if(!c)return false;let f=C.current.get(c.messageId);return !f||!A(f,c.options)?false:(m.current=null,E.current=true,true)},[A]),reanchorToAnchoredMessage:B,scrollToElement:A,scrollToEnd:ne,scrollToMessage:D,scrollToStart:N}}function Qe(e,t){let r=e,n=new Set;return {getSnapshot:()=>r,hasListeners:()=>n.size>0,setSnapshot:o=>{t(r,o)||(r=o,n.forEach(i=>i()));},subscribe:(o,i,S)=>{let l=n.size===0;return n.add(o),l&&i?.(),()=>{n.delete(o),n.size===0&&S?.();}}}}function Xe(e,t){return Qe(e,t)}function Ze(){return Qe(Q,Tt)}function et(e,t){return e.start===t.start&&e.end===t.end}function Tt(e,t){return e.currentAnchorId!==t.currentAnchorId||e.visibleMessageIds.length!==t.visibleMessageIds.length?false:e.visibleMessageIds.every((r,n)=>r===t.visibleMessageIds[n])}function tt({autoScroll:e,scrollEdgeThreshold:t,scrollMargin:r,scrollPreviousItemPeek:n}){let o=g.useRef(e),i=g.useRef(false),S=g.useRef(null),l=g.useRef(false),d=g.useRef(t),E=g.useRef(0),b=g.useRef(null),C=g.useRef(0),p=g.useRef(e?\"following-bottom\":\"free-scrolling\"),m=g.useRef(new Map),L=g.useRef(null),y=g.useRef(null),u=g.useRef(null),x=g.useRef(n),Y=g.useRef(true),k=g.useRef(null),_=g.useRef(r),G=g.useRef(null),v=g.useRef(0),H=g.useRef(0),N=g.useRef(null),ne=g.useRef(null),A=g.useRef(null),B=g.useRef(null),D=g.useRef(null),U=g.useRef(null),c=g.useRef(null),f=g.useRef(null),h=g.useRef(new Set),P=g.useRef(new WeakSet);return A.current===null&&(A.current=Xe(ge,et)),f.current===null&&(f.current=Ze()),o.current=e,d.current=t,_.current=r,x.current=n,{autoScrollRef:o,autoscrollingRef:i,autoscrollingTimeoutRef:B,streamingTurnRef:u,contentRef:S,defaultScrollPositionAppliedRef:l,firstItemRef:b,itemCountRef:E,lastScrollTopRef:C,messageElementsRef:m,modeRef:p,pendingScrollFrameRef:G,pendingScrollToMessageRef:L,prependRestoreRef:y,preserveScrollOnPrependRef:Y,rootRef:k,scrollEdgeThresholdRef:d,scrollMarginRef:_,scrollPreviousItemPeekRef:x,spacerGapRef:v,spacerHeightRef:H,spacerRef:N,stateFrameRef:ne,stateStore:A.current,viewportRef:D,visibilityFrameRef:U,visibilityObserverRef:c,visibilityStore:f.current,visibleMessageIdsRef:h,handledScrollAnchorsRef:P}}function rt(e,t){return g.useCallback(r=>{e.current=r,r&&t();},[e,t])}function nt({autoScroll:e=false,defaultScrollPosition:t=\"end\",scrollEdgeThreshold:r=xe,scrollPreviousItemPeek:n=Ve,scrollMargin:o=_e}){let i=tt({autoScroll:e,scrollEdgeThreshold:r,scrollMargin:o,scrollPreviousItemPeek:n}),{streamingTurnRef:S,autoScrollRef:l,autoscrollingRef:d,autoscrollingTimeoutRef:E,contentRef:b,defaultScrollPositionAppliedRef:C,firstItemRef:p,itemCountRef:m,lastScrollTopRef:L,messageElementsRef:y,modeRef:u,pendingScrollFrameRef:x,pendingScrollToMessageRef:Y,prependRestoreRef:k,preserveScrollOnPrependRef:_,rootRef:G,scrollEdgeThresholdRef:v,scrollMarginRef:H,scrollPreviousItemPeekRef:N,spacerGapRef:ne,spacerHeightRef:A,spacerRef:B,stateFrameRef:D,stateStore:U,viewportRef:c,visibilityFrameRef:f,visibilityObserverRef:h,visibilityStore:P,visibleMessageIdsRef:V,handledScrollAnchorsRef:z}=i,q=g.useRef(t);q.current!==t&&(q.current=t,C.current=false);let K=g.useCallback(s=>{let a=G.current,T=c.current,O=[s.start&&\"start\",s.end&&\"end\"].filter(Boolean).join(\" \"),ue=d.current;for(let W of [a,T])W&&(O?W.setAttribute(\"data-scrollable\",O):W.removeAttribute(\"data-scrollable\"),W.toggleAttribute(\"data-autoscrolling\",ue));},[]),se=g.useCallback(s=>{let a=c.current?.scrollTop??0,T=a<L.current-J;L.current=a,l.current&&!s.end&&u.current!==\"settling-jump\"&&u.current!==\"anchored-to-message\"?u.current=\"following-bottom\":u.current===\"following-bottom\"&&s.end&&T&&!d.current&&(u.current=\"free-scrolling\");},[]),F=g.useCallback(()=>{let s=De({content:b.current,scrollEdgeThreshold:v.current,spacer:B.current,viewport:c.current});se(s);let a=u.current===\"following-bottom\"?{...s,end:false}:s;K(a),U.setSnapshot(a);},[se,U,K]),oe=g.useCallback(()=>{D.current===null&&(D.current=window.requestAnimationFrame(()=>{D.current=null,F();}));},[F]),w=g.useCallback(()=>{P.hasListeners()&&f.current===null&&(f.current=window.requestAnimationFrame(()=>{f.current=null,P.hasListeners()&&P.setSnapshot(Fe({content:b.current,scrollMargin:H.current,scrollPreviousItemPeek:N.current,spacer:B.current,viewport:c.current,visibleMessageIds:V.current}));}));},[P]),{flushPendingScrollToMessage:ce,reanchorToAnchoredMessage:me,scrollToElement:le,scrollToEnd:I,scrollToMessage:Me,scrollToStart:ae}=Je({refs:i,commitScrollState:F,scheduleStateCommit:oe,scheduleVisibilitySync:w}),de=g.useCallback(()=>{let s=k.current,a=c.current;if(!s||!a||!s.element.isConnected)return false;let O=te(s.element,a)-s.viewportTop;return Math.abs(O)<=J?false:(a.scrollTop+=O,s.viewportTop=te(s.element,a),oe(),w(),true)},[oe,w]),Z=g.useCallback(()=>{let s=b.current,a=c.current;if(!s||!a){k.current=null;return}let T=Ye({content:s,spacer:B.current,viewport:a});k.current=T?{element:T,viewportTop:te(T,a)}:null;},[]),be=g.useCallback(()=>{x.current===null&&(x.current=window.requestAnimationFrame(()=>{x.current=null,ce()&&Z();}));},[Z,ce]),ie=g.useCallback(()=>{if(!t||C.current||m.current===0)return false;let s=false;if(t===\"last-anchor\"){let a=b.current,T=c.current,O=a&&T?Ue(ee(a,B.current)):null;if(!a||!T||!O)s=I({behavior:\"auto\"});else {let ue=pe(O,T);s=Se({content:a,spacer:B.current,viewport:T})-ue<=T.clientHeight?I({behavior:\"auto\"}):le(O,{align:\"start\"},{keepPreviousPeek:true});}}else s=t===\"end\"?I({behavior:\"auto\"}):ae({behavior:\"auto\"});return s?(C.current=true,true):false},[t,le,I,ae]),Te=g.useCallback(()=>{let s=b.current;if(!s)return;let a=ee(s,B.current),T=m.current,O=p.current;m.current=a.length,p.current=a[0]??null,(()=>{if(ce())return;if(T===0){if(ie()||a.length>0&&l.current&&I({behavior:\"auto\"}))return;F(),w();return}let W=O?a.indexOf(O):-1;if(_.current&&W>0){de();return}if(a.length>T){let $=je(a,T);if($){if(l.current&&u.current===\"following-bottom\"&&Ne(a,T)){I({behavior:\"auto\"});return}le($,{align:\"start\"},{keepPreviousPeek:true}),z.current.add($);return}}if(a.length===T){let $=Ge(a,z.current);if($){le($,{align:\"start\"},{keepPreviousPeek:true}),z.current.add($);return}}u.current===\"following-bottom\"&&l.current?I({behavior:\"auto\"}):(F(),w());})(),Z();},[ie,Z,F,ce,de,w,le,I]),Ee=g.useCallback(()=>{if(u.current===\"following-bottom\"&&l.current){I({behavior:\"auto\"});return}let s=A.current;if(me()){l.current&&s>0&&A.current===0&&I({behavior:\"auto\"});return}oe(),w();},[me,oe,w,I]),ve=g.useCallback(()=>{let s=c.current;if(!(!s||!P.hasListeners())){if(typeof IntersectionObserver>\"u\"){w();return}h.current||(h.current=new IntersectionObserver(a=>{for(let T of a){let O=T.target.dataset.messageId;O&&(T.isIntersecting?V.current.add(O):V.current.delete(O));}w();},{root:s,rootMargin:`${-(H.current+N.current)}px 0px 0px 0px`,threshold:[0,.01,.5,1]})),y.current.forEach(a=>{h.current?.observe(a);}),w();}},[w,P]),he=g.useCallback(()=>{f.current!==null&&(window.cancelAnimationFrame(f.current),f.current=null),h.current?.disconnect(),h.current=null,V.current.clear(),P.setSnapshot(Q);},[P]),pt=g.useCallback((s,a,T)=>{if(a){y.current.set(s,a),h.current?.observe(a),w(),Y.current?.messageId===s&&be();return}T&&y.current.get(s)===T&&(y.current.delete(s),V.current.delete(s),h.current?.unobserve(T),w());},[be,w]),Le=g.useCallback(()=>{(u.current===\"following-bottom\"||u.current===\"anchored-to-message\"||u.current===\"settling-jump\")&&(S.current=null,u.current=\"free-scrolling\");},[]),Ce=g.useCallback(()=>K(U.getSnapshot()),[U,K]),Pe=rt(G,Ce),we=rt(c,Ce),Oe=g.useCallback(s=>{b.current=s;},[]),ye=g.useCallback(s=>{B.current=s,ne.current=$e(s?.parentElement??null);},[]),He=g.useCallback(()=>{F(),w(),Z();},[Z,F,w]),mt=g.useMemo(()=>({handleContentChange:Te,handleResize:Ee,observeVisibility:ve,preserveScrollOnPrependRef:_,scrollToEnd:I,scrollToMessage:Me,scrollToStart:ae,setContentElement:Oe,setRootElement:Pe,setSpacerElement:ye,setViewportElement:we,stateStore:U,syncAfterScroll:He,unobserveVisibility:he,userScrollIntent:Le,viewportRef:c,visibilityStore:P}),[Te,Ee,ve,I,Me,ae,Oe,Pe,ye,we,U,He,he,Le,P]);return g.useLayoutEffect(()=>{ie();},[ie]),g.useEffect(()=>()=>{D.current!==null&&(window.cancelAnimationFrame(D.current),D.current=null),f.current!==null&&(window.cancelAnimationFrame(f.current),f.current=null),E.current!==null&&(window.clearTimeout(E.current),E.current=null),x.current!==null&&(window.cancelAnimationFrame(x.current),x.current=null),h.current?.disconnect(),h.current=null;},[]),g.useLayoutEffect(()=>{if(e&&u.current===\"following-bottom\"&&m.current>0){I({behavior:\"auto\"});return}F();},[e,F,I]),{context:mt,registerMessage:pt}}function lt(e){let t=g.useRef(e);return t.current=e,t}var st=g.createContext(null),ct=g.createContext(null);function X(){let e=g.useContext(st);if(!e)throw new Error(\"useMessageScroller must be used within a MessageScroller.\");return e}function Et(){let e=g.useContext(ct);if(!e)throw new Error(\"MessageScrollerItem must be used within a MessageScroller.\");return e}function vt(){let{scrollToEnd:e,scrollToMessage:t,scrollToStart:r}=X();return g.useMemo(()=>({scrollToEnd:e,scrollToMessage:t,scrollToStart:r}),[e,t,r])}function ht(){let{stateStore:e}=X();return g.useSyncExternalStore(e.subscribe,e.getSnapshot,e.getSnapshot)}function Lt(){let{observeVisibility:e,unobserveVisibility:t,visibilityStore:r}=X(),n=g.useCallback(o=>r.subscribe(o,e,t),[e,t,r]);return g.useSyncExternalStore(n,r.getSnapshot,r.getSnapshot)}function at({autoScroll:e=false,children:t,defaultScrollPosition:r=\"end\",scrollEdgeThreshold:n,scrollPreviousItemPeek:o,scrollMargin:i}){let{context:S,registerMessage:l}=nt({autoScroll:e,defaultScrollPosition:r,scrollEdgeThreshold:n,scrollPreviousItemPeek:o,scrollMargin:i});return jsx(st.Provider,{value:S,children:jsx(ct.Provider,{value:l,children:t})})}function it({children:e,...t}){let{setRootElement:r}=X();return jsx(\"div\",{ref:r,...t,children:e})}function ut({\"aria-label\":e,children:t,onKeyDown:r,onScroll:n,onTouchMove:o,onWheel:i,preserveScrollOnPrepend:S=true,ref:l,role:d,tabIndex:E,...b}){let{handleResize:C,preserveScrollOnPrependRef:p,setViewportElement:m,syncAfterScroll:L,userScrollIntent:y,viewportRef:u}=X();p.current=S;let x=g.useCallback(v=>{m(v),c(l)?.(v);},[l,m]);function Y(v){L(),n?.(v);}function k(v){y(),i?.(v);}function _(v){y(),o?.(v);}function G(v){Be.has(v.key)&&y(),r?.(v);}return g.useEffect(()=>{let v=u.current;if(!v||typeof ResizeObserver>\"u\")return;let H=0,N=new ResizeObserver(()=>{window.cancelAnimationFrame(H),H=window.requestAnimationFrame(C);});return N.observe(v),()=>{window.cancelAnimationFrame(H),N.disconnect();}},[C,u]),jsx(\"div\",{ref:x,role:d??\"region\",\"aria-label\":e??\"Messages\",tabIndex:E??0,onKeyDown:G,onScroll:Y,onTouchMove:_,onWheel:k,...b,children:t})}function ft({\"aria-relevant\":e,children:t,ref:r,role:n,spacerClassName:o,...i}){let{handleContentChange:S,handleResize:l,setContentElement:d,setSpacerElement:E}=X(),b=g.useRef(null),C=g.useCallback(p=>{b.current=p,d(p),c(r)?.(p);},[r,d]);return g.useLayoutEffect(()=>{let p=b.current;if(!p||(S(),typeof MutationObserver>\"u\"))return;let m=new MutationObserver(()=>{S();});return m.observe(p,{childList:true}),()=>m.disconnect()},[S]),g.useEffect(()=>{let p=b.current;if(!p||typeof ResizeObserver>\"u\")return;let m=0,L=new ResizeObserver(()=>{window.cancelAnimationFrame(m),m=window.requestAnimationFrame(l);});return L.observe(p),()=>{window.cancelAnimationFrame(m),L.disconnect();}},[l]),jsxs(\"div\",{ref:C,role:n??\"log\",\"aria-relevant\":e??\"additions\",...i,children:[t,jsx(\"div\",{ref:E,\"aria-hidden\":\"true\",\"data-message-scroller-spacer\":\"\",hidden:true,className:o})]})}function gt({messageId:e,ref:t,scrollAnchor:r=false,...n}){let o=Et(),i=g.useRef(null),S=g.useCallback(l=>{let d=i.current;i.current=l,e&&o(e,l,d),c(t)?.(l);},[e,t,o]);return jsx(\"div\",{ref:S,\"data-message-id\":e,\"data-scroll-anchor\":r?\"true\":\"false\",...n})}function St({behavior:e=\"smooth\",children:t,direction:r=\"end\",onClick:n,render:o,tabIndex:i,type:S=\"button\",...l}){let{scrollToEnd:d,scrollToStart:E,stateStore:b$1}=X(),C=lt(n),p=g.useCallback(u=>b$1.subscribe(u),[b$1]),m=g.useCallback(()=>{let u=b$1.getSnapshot();return r===\"start\"?u.start:u.end},[r,b$1]),L=g.useSyncExternalStore(p,m,m),y=g.useCallback(u=>{L&&(C.current?.(u),u.defaultPrevented||(u.currentTarget.blur(),r===\"start\"?E({behavior:e}):d({behavior:e})));},[e,r,L,C,d,E]);return a({defaultTagName:\"button\",props:b({type:S,inert:!L,tabIndex:L?i:-1,children:t??jsxs(\"span\",{children:[\"Scroll to \",r]}),onClick:y},l),render:o,state:{active:L,direction:r},stateAttributesMapping:{active:u=>({\"data-active\":u?\"true\":\"false\"})}})}var $t={Provider:at,Root:it,Viewport:ut,Content:ft,Item:gt,Button:St};export{$t as MessageScroller,vt as useMessageScroller,ht as useMessageScrollerScrollable,Lt as useMessageScrollerVisibility};","/**\n * Copyright 2022 Joe Bell. All rights reserved.\n *\n * This file is licensed to you under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with the\n * License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */ import { clsx } from \"clsx\";\nconst falsyToString = (value)=>typeof value === \"boolean\" ? `${value}` : value === 0 ? \"0\" : value;\nexport const cx = clsx;\nexport const cva = (base, config)=>(props)=>{\n var _config_compoundVariants;\n if ((config === null || config === void 0 ? void 0 : config.variants) == null) return cx(base, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);\n const { variants, defaultVariants } = config;\n const getVariantClassNames = Object.keys(variants).map((variant)=>{\n const variantProp = props === null || props === void 0 ? void 0 : props[variant];\n const defaultVariantProp = defaultVariants === null || defaultVariants === void 0 ? void 0 : defaultVariants[variant];\n if (variantProp === null) return null;\n const variantKey = falsyToString(variantProp) || falsyToString(defaultVariantProp);\n return variants[variant][variantKey];\n });\n const propsWithoutUndefined = props && Object.entries(props).reduce((acc, param)=>{\n let [key, value] = param;\n if (value === undefined) {\n return acc;\n }\n acc[key] = value;\n return acc;\n }, {});\n const getCompoundVariantClassNames = config === null || config === void 0 ? void 0 : (_config_compoundVariants = config.compoundVariants) === null || _config_compoundVariants === void 0 ? void 0 : _config_compoundVariants.reduce((acc, param)=>{\n let { class: cvClass, className: cvClassName, ...compoundVariantOptions } = param;\n return Object.entries(compoundVariantOptions).every((param)=>{\n let [key, value] = param;\n return Array.isArray(value) ? value.includes({\n ...defaultVariants,\n ...propsWithoutUndefined\n }[key]) : ({\n ...defaultVariants,\n ...propsWithoutUndefined\n })[key] === value;\n }) ? [\n ...acc,\n cvClass,\n cvClassName\n ] : acc;\n }, []);\n return cx(base, getVariantClassNames, getCompoundVariantClassNames, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);\n };\n\n","import * as React from 'react';\nimport { useComposedRefs } from '@radix-ui/react-compose-refs';\n\ndeclare module 'react' {\n interface ReactElement {\n $$typeof?: symbol | string;\n }\n}\n\n/* -------------------------------------------------------------------------------------------------\n * Slot\n * -----------------------------------------------------------------------------------------------*/\n\nexport type Usable<T> = PromiseLike<T> | React.Context<T>;\n\ntype SlotProps<Elem extends Element = HTMLElement, Props = React.HTMLAttributes<Elem>> = Props & {\n children?: React.ReactNode;\n};\n\n/* @__NO_SIDE_EFFECTS__ */ export function createSlot<\n Elem extends Element = HTMLElement,\n Props = React.HTMLAttributes<Elem>,\n>(ownerName: string) {\n const Slot = React.forwardRef<Elem, SlotProps<Elem, Props>>((props, forwardedRef) => {\n let { children, ...slotProps } = props;\n let slottableElement: React.ReactElement | null = null;\n let hasSlottable = false;\n const newChildren: React.ReactNode[] = [];\n\n if (isLazyComponent(children) && typeof use === 'function') {\n children = use(children._payload);\n }\n\n React.Children.forEach(children, (maybeSlottable) => {\n if (isSlottable(maybeSlottable)) {\n hasSlottable = true;\n const slottable = maybeSlottable;\n let child = 'child' in slottable.props ? slottable.props.child : slottable.props.children;\n\n if (isLazyComponent(child) && typeof use === 'function') {\n child = use(child._payload);\n }\n\n slottableElement = getSlottableElementFromSlottable(slottable, child);\n newChildren.push((slottableElement?.props as any)?.children);\n } else {\n newChildren.push(maybeSlottable);\n }\n });\n\n if (slottableElement) {\n slottableElement = React.cloneElement(slottableElement, undefined, newChildren);\n } else if (\n // A `Slottable` was found but it didn't resolve to a single element (e.g.\n // it wrapped multiple elements, text, or a render-prop `child` that\n // wasn't an element). Don't fall back to treating the `Slottable` wrapper\n // itself as the slot target — throw a descriptive error below instead.\n !hasSlottable &&\n React.Children.count(children) === 1 &&\n React.isValidElement(children)\n ) {\n slottableElement = children;\n }\n\n const slottableElementRef = slottableElement ? getElementRef(slottableElement) : undefined;\n const composedRef = useComposedRefs(forwardedRef, slottableElementRef);\n\n if (!slottableElement) {\n // Empty/falsy children (`null`, `undefined`, `false`, no children, etc.) are valid and\n // render nothing. Anything else is content we couldn't slot onto a single element, which\n // is a usage error, so we fail loudly with a clear message.\n if (children || children === 0) {\n throw new Error(\n hasSlottable ? createSlottableError(ownerName) : createSlotError(ownerName),\n );\n }\n return children;\n }\n\n const mergedProps = mergeProps(slotProps, slottableElement.props ?? {});\n\n // do not pass ref to React.Fragment for React 19 compatibility\n if (slottableElement.type !== React.Fragment) {\n mergedProps.ref = forwardedRef ? composedRef : slottableElementRef;\n }\n\n return React.cloneElement(slottableElement, mergedProps);\n });\n\n Slot.displayName = `${ownerName}.Slot`;\n return Slot;\n}\n\nconst Slot = createSlot('Slot');\n\n/* -------------------------------------------------------------------------------------------------\n * Slottable\n * -----------------------------------------------------------------------------------------------*/\n\nconst SLOTTABLE_IDENTIFIER = Symbol.for('radix.slottable');\n\ntype SlottableChildrenProps = { children: React.ReactNode };\ntype SlottableRenderFnProps = {\n child: React.ReactNode;\n children: (slottable: React.ReactNode) => React.ReactNode;\n};\n\ntype SlottableProps = SlottableRenderFnProps | SlottableChildrenProps;\ninterface SlottableComponent extends React.FC<SlottableProps> {\n __radixId: symbol;\n}\n\n/* @__NO_SIDE_EFFECTS__ */ export function createSlottable(ownerName: string) {\n const Slottable: SlottableComponent = (props) =>\n 'child' in props ? props.children(props.child) : props.children;\n\n Slottable.displayName = `${ownerName}.Slottable`;\n Slottable.__radixId = SLOTTABLE_IDENTIFIER;\n return Slottable;\n}\n\nconst Slottable = createSlottable('Slottable');\n\n/* -------------------------------------------------------------------------------------------------\n * getSlottableElementFromSlottable\n * -----------------------------------------------------------------------------------------------*/\n\nconst getSlottableElementFromSlottable = (slottable: SlottableElement, child: React.ReactNode) => {\n if ('child' in slottable.props) {\n const child = slottable.props.child;\n if (!React.isValidElement<React.PropsWithChildren>(child)) return null;\n return React.cloneElement(child, undefined, slottable.props.children(child.props.children));\n }\n\n return React.isValidElement(child) ? child : null;\n};\n\n/* -------------------------------------------------------------------------------------------------\n * mergeProps\n * -----------------------------------------------------------------------------------------------*/\n\ntype AnyProps = Record<string, any>;\n\nfunction mergeProps(slotProps: AnyProps, childProps: AnyProps) {\n // all child props should override\n const overrideProps = { ...childProps };\n\n for (const propName in childProps) {\n const slotPropValue = slotProps[propName];\n const childPropValue = childProps[propName];\n\n const isHandler = /^on[A-Z]/.test(propName);\n if (isHandler) {\n // if the handler exists on both, we compose them\n if (slotPropValue && childPropValue) {\n overrideProps[propName] = (...args: unknown[]) => {\n const result = childPropValue(...args);\n slotPropValue(...args);\n return result;\n };\n }\n // but if it exists only on the slot, we use only this one\n else if (slotPropValue) {\n overrideProps[propName] = slotPropValue;\n }\n }\n // if it's `style`, we merge them\n else if (propName === 'style') {\n overrideProps[propName] = { ...slotPropValue, ...childPropValue };\n } else if (propName === 'className') {\n overrideProps[propName] = [slotPropValue, childPropValue].filter(Boolean).join(' ');\n }\n }\n\n return { ...slotProps, ...overrideProps };\n}\n\n/* -------------------------------------------------------------------------------------------------\n * getElementRef\n * -----------------------------------------------------------------------------------------------*/\n\n// Before React 19 accessing `element.props.ref` will throw a warning and suggest using `element.ref`\n// After React 19 accessing `element.ref` does the opposite.\n// https://github.com/facebook/react/pull/28348\n//\n// Access the ref using the method that doesn't yield a warning.\nfunction getElementRef(element: React.ReactElement) {\n // React <=18 in DEV\n let getter = Object.getOwnPropertyDescriptor(element.props, 'ref')?.get;\n let mayWarn = getter && 'isReactWarning' in getter && getter.isReactWarning;\n if (mayWarn) {\n return (element as any).ref;\n }\n\n // React 19 in DEV\n getter = Object.getOwnPropertyDescriptor(element, 'ref')?.get;\n mayWarn = getter && 'isReactWarning' in getter && getter.isReactWarning;\n if (mayWarn) {\n return (element.props as { ref?: React.Ref<unknown> }).ref;\n }\n\n // Not DEV\n return (element.props as { ref?: React.Ref<unknown> }).ref || (element as any).ref;\n}\n\n/* ---------------------------------------------------------------------------------------------- */\n\ntype SlottableElement = React.ReactElement<SlottableProps, SlottableComponent>;\n\nfunction isSlottable(\n child: React.ReactNode,\n): child is React.ReactElement<SlottableProps, typeof Slottable> {\n return (\n React.isValidElement(child) &&\n typeof child.type === 'function' &&\n '__radixId' in child.type &&\n child.type.__radixId === SLOTTABLE_IDENTIFIER\n );\n}\n\nconst REACT_LAZY_TYPE = Symbol.for('react.lazy');\n\ninterface LazyReactElement extends React.ReactElement {\n $$typeof: typeof REACT_LAZY_TYPE;\n _payload: PromiseLike<Exclude<React.ReactNode, PromiseLike<any>>>;\n}\n\nfunction isLazyComponent(element: React.ReactNode): element is LazyReactElement {\n return (\n element != null &&\n typeof element === 'object' &&\n '$$typeof' in element &&\n element.$$typeof === REACT_LAZY_TYPE &&\n '_payload' in element &&\n isPromiseLike(element._payload)\n );\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return typeof value === 'object' && value !== null && 'then' in value;\n}\n\nconst createSlotError = (ownerName: string) => {\n return `${ownerName} failed to slot onto its children. Expected a single React element child or \\`Slottable\\`.`;\n};\n\nconst createSlottableError = (ownerName: string) => {\n return `${ownerName} failed to slot onto its \\`Slottable\\`. Expected \\`Slottable\\` to receive a single React element child.`;\n};\n\nconst use: typeof React.use | undefined = (React as any)[' use '.trim().toString()];\n\nexport {\n Slot,\n Slottable,\n //\n Slot as Root,\n};\nexport type { SlotProps };\n","import * as React from 'react';\n\ntype PossibleRef<T> = React.Ref<T> | undefined;\n\n/**\n * Set a given ref to a given value\n * This utility takes care of different types of refs: callback refs and RefObject(s)\n */\nfunction setRef<T>(ref: PossibleRef<T>, value: T) {\n if (typeof ref === 'function') {\n return ref(value);\n } else if (ref !== null && ref !== undefined) {\n ref.current = value;\n }\n}\n\n/**\n * A utility to compose multiple refs together\n * Accepts callback refs and RefObject(s)\n */\nfunction composeRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {\n return (node) => {\n let hasCleanup = false;\n const cleanups = refs.map((ref) => {\n const cleanup = setRef(ref, node);\n if (!hasCleanup && typeof cleanup == 'function') {\n hasCleanup = true;\n }\n return cleanup;\n });\n\n // React <19 will log an error to the console if a callback ref returns a\n // value. We don't use ref cleanups internally so this will only happen if a\n // user's ref callback returns a value, which we only expect if they are\n // using the cleanup functionality added in React 19.\n if (hasCleanup) {\n return () => {\n for (let i = 0; i < cleanups.length; i++) {\n const cleanup = cleanups[i];\n if (typeof cleanup == 'function') {\n cleanup();\n } else {\n setRef(refs[i], null);\n }\n }\n };\n }\n };\n}\n\n/**\n * A custom hook that composes multiple refs\n * Accepts callback refs and RefObject(s)\n */\nfunction useComposedRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n return React.useCallback(composeRefs(...refs), refs);\n}\n\nexport { composeRefs, useComposedRefs };\n","import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { Slot } from \"radix-ui\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n \"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-[background-color,border-color,color,box-shadow,translate] duration-(--duration-quick) ease-(--ease-smooth-out) motion-reduce:transition-none outline-none select-none touch-manipulation focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/25 motion-safe:active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n {\n variants: {\n variant: {\n default: \"surface-primary bg-primary bg-clip-border text-primary-foreground hover:bg-primary/90 active:shadow-field\",\n outline:\n \"surface-control border-input/80 bg-card text-foreground hover:border-input hover:bg-muted/60 aria-expanded:bg-muted aria-expanded:text-foreground active:shadow-field\",\n secondary:\n \"surface-control border-border bg-secondary text-secondary-foreground hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground active:shadow-field\",\n ghost:\n \"text-muted-foreground hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground\",\n destructive:\n \"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default:\n \"h-9 gap-1.5 px-3.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3\",\n xs: \"h-7 gap-1 px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3\",\n sm: \"h-8 gap-1 px-3 text-[0.8rem] has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5 [&_svg:not([class*='size-'])]:size-3.5\",\n lg: \"h-10 gap-1.5 px-4 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3\",\n icon: \"size-9\",\n \"icon-xs\": \"size-7 [&_svg:not([class*='size-'])]:size-3\",\n \"icon-sm\": \"size-8\",\n \"icon-lg\": \"size-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean\n }) {\n const Comp = asChild ? Slot.Root : \"button\"\n\n return (\n <Comp\n data-slot=\"button\"\n data-variant={variant}\n data-size={size}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n )\n}\n\nexport { Button, buttonVariants }\n","/**\n * Runs of consecutive messages from the same voice, so a burst of three reads\n * as one turn rather than as three separate arrivals. A run says who is\n * speaking once, at the top, and carries the time and the face at the foot;\n * everything in between is just a bubble.\n *\n * What joins what is the caller's rule, since the inbox and the widget hold\n * different shapes. All this does is walk the list and mark the edges.\n */\n\n/** How long a run stays open. A message later than this starts a new one. */\nconst RUN_MS = 5 * 60_000;\n\n/** Where a message sits in its run. Both true when it is a run of one. */\nexport type MessageGroup = {\n /** Carries the name. */\n isFirst: boolean;\n /** Carries the face and the time. */\n isLast: boolean;\n};\n\n/** A message standing on its own, for anywhere nothing is grouped. */\nexport const LONE_MESSAGE: MessageGroup = { isFirst: true, isLast: true };\n\n/**\n * Marks each message with where it sits in its run.\n *\n * `joinsPrevious` is only ever asked about neighbours, so it can compare\n * senders, timestamps, day labels, or whatever else the shape at hand knows.\n */\nexport function groupMessages<T>(\n messages: T[],\n joinsPrevious: (message: T, previous: T) => boolean,\n): MessageGroup[] {\n /** Whether the message at `index` carries on the one above it. */\n const continues = (index: number) => {\n const message = messages[index];\n const previous = messages[index - 1];\n if (message === undefined || previous === undefined) return false;\n return joinsPrevious(message, previous);\n };\n\n return messages.map((_, index) => ({\n isFirst: !continues(index),\n isLast: !continues(index + 1),\n }));\n}\n\n/**\n * Whether two messages are close enough together to belong to one run. A\n * missing timestamp means canned content with no clock behind it, which groups\n * on who is speaking alone.\n */\nexport function withinRun(previous?: number, next?: number): boolean {\n if (previous === undefined || next === undefined) return true;\n return next - previous <= RUN_MS;\n}\n","\"use client\";\n\nimport { useCallback, useLayoutEffect, useRef } from \"react\";\nimport { prefersReducedMotion } from \"./motion\";\n\nconst SHIFT_MS = 420;\n/** Matches --ease-spring. Kept in step by hand, since JS cannot read a theme token. */\nconst SHIFT_EASING = \"cubic-bezier(0.34, 1.45, 0.64, 1)\";\n\n/** How far the first message sits from the top of the stack, or null if there is none. */\nfunction topOfStack(element: HTMLElement | null): number | null {\n const first = element?.firstElementChild as HTMLElement | null;\n\n return first ? first.offsetTop : null;\n}\n\n/**\n * Slides a bottom anchored stack up as it grows, rather than letting it jump.\n *\n * A short thread sits on the composer, so a new message does not push the ones\n * above it down: it lifts them. Left alone that happens between two frames and\n * the whole conversation teleports.\n *\n * So it is measured instead. The first message's offset from the top of the\n * stack says how far everything moved, the stack is put straight back where it\n * was, and it travels the distance properly. Nothing moves once the thread is\n * long enough to scroll, because then the first message stops moving, the\n * distance is zero, and the scroller has it.\n *\n * The starting measurement is taken as the element attaches rather than in the\n * effect below, because the effect only runs when the message count changes. A\n * thread that arrives after a loading state would otherwise have nothing to\n * compare against until its second message, and the first one would jump.\n */\nexport function useStackShift(dependency: number) {\n const stack = useRef<HTMLDivElement | null>(null);\n const previousTop = useRef<number | null>(null);\n const watcher = useRef<ResizeObserver | null>(null);\n\n // Attaching also starts watching the stack for a change of size, and letting\n // go stops. Both here rather than in an effect, because the stack arrives\n // whenever the first message does: a thread that starts empty has nothing to\n // attach to on mount, and an effect that ran then would have found nothing\n // and never looked again.\n const ref = useCallback((node: HTMLDivElement | null) => {\n stack.current = node;\n previousTop.current = topOfStack(node);\n\n watcher.current?.disconnect();\n watcher.current = null;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n\n // The stack moves when the panel changes size too, and that is not a shift\n // worth animating: nothing arrived, the box around it grew. It does have to\n // be written down though. The effect below only runs on a new message, so\n // without this the measurement from before a resize is the one the next\n // message is compared against, and it travels the difference between two\n // panel heights on its way in.\n watcher.current = new ResizeObserver(() => {\n previousTop.current = topOfStack(node);\n });\n watcher.current.observe(node);\n }, []);\n\n useLayoutEffect(() => {\n const element = stack.current;\n if (!element) return;\n\n const top = topOfStack(element);\n const from = previousTop.current;\n previousTop.current = top;\n\n if (from === null || top === null) return;\n\n const distance = from - top;\n if (distance <= 0) return;\n if (prefersReducedMotion()) return;\n\n element.animate(\n [\n { transform: `translateY(${distance}px)` },\n { transform: \"translateY(0)\" },\n ],\n { duration: SHIFT_MS, easing: SHIFT_EASING },\n );\n }, [dependency]);\n\n return ref;\n}\n","/**\n * CSS carries `motion-reduce:` for anything animated in a stylesheet. The two\n * animations driven from JavaScript cannot read that, so they ask here.\n *\n * Guarded for the server, where there is no window to ask.\n */\nexport function prefersReducedMotion(): boolean {\n if (typeof window === \"undefined\") return false;\n\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n}\n","import { cn } from \"@/lib/utils\";\n\n/**\n * How the inside of the panel arrives once the panel has.\n *\n * The corner opens as a clip growing out of the button, which reveals a\n * picture that was already painted: the header, the thread and the composer\n * are simply there behind the edge as it passes. This is what makes them\n * arrive instead. Each part sits a few pixels low and faded while the panel\n * is closed, and rises into place once it opens, the header a beat ahead of\n * the rest, on the same curve everything else in the widget settles on.\n *\n * Read off the panel rather than passed down: the panel wrapper says\n * `data-state=\"open\"` or `\"closed\"`, and the parts answer. A panel that is\n * framed rather than floated, as in the gallery, has no such wrapper and\n * shows the parts at rest, which is exactly what a panel that never opens\n * or closes should do.\n *\n * Closing does not move the parts. The clip closes over them, and their\n * low pose is taken up silently once it has: no motion, after a delay the\n * length of the corner's own morph. Reopened before then, they are already\n * home and simply stay.\n */\nconst SETTLE = cn(\n \"transition-[translate,opacity] ease-out-quint motion-reduce:transition-none\",\n \"[[data-state=open]_&]:duration-500\",\n \"[[data-state=closed]_&]:translate-y-2 [[data-state=closed]_&]:opacity-0\",\n \"[[data-state=closed]_&]:duration-0 [[data-state=closed]_&]:[transition-delay:450ms]\",\n);\n\n/** The header, first. */\nexport const SETTLE_HEADER = cn(\n SETTLE,\n \"[[data-state=open]_&]:[transition-delay:60ms]\",\n);\n\n/** Everything under it, a beat behind. */\nexport const SETTLE_BODY = cn(\n SETTLE,\n \"[[data-state=open]_&]:[transition-delay:120ms]\",\n);\n","\"use client\";\n\nimport {\n useEffect,\n useRef,\n useState,\n type ChangeEvent,\n type KeyboardEvent,\n} from \"react\";\nimport {\n AttachmentDraftChip,\n useAttachmentDrafts,\n useAttachmentTarget,\n} from \"@/components/attachments\";\nimport { ArrowUp, Check, EnvelopeSimple, Plus } from \"@/components/icons\";\nimport { WidgetButton } from \"./widget-button\";\nimport { WIDGET_TEXT_FIELD_SHELL as TEXT_FIELD_SHELL } from \"./widget-field\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { useTypingSignal } from \"@/hooks/use-typing-signal\";\nimport { cn } from \"@/lib/utils\";\nimport { useEasedHeight } from \"./use-eased-height\";\nimport { PoweredByMedian } from \"./widget-badge\";\n\n/**\n * Loose on purpose: it decides whether the arrow lights up, not whether the\n * address is real. Only a mail server can answer that, and a visitor locked\n * out by a suspicious regex has nobody to appeal to.\n */\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\n/**\n * How the pill changes its mind.\n *\n * `translate` rather than `transform`, because that is the property Tailwind's\n * `translate-y-*` actually sets. Named as `transform` it transitions nothing:\n * the opacity eases and the travel lands in one frame, which is the jump this\n * was written to avoid.\n */\nconst SWAP =\n \"transition-[opacity,translate] duration-300 ease-out motion-reduce:transition-none\";\n\n/**\n * The notch coming up out of the composer, with the spring's small overshoot\n * at the top of the travel: it is a thing being pushed up from underneath, and\n * a thing pushed up passes where it lands.\n */\nconst RISE =\n \"transition-transform duration-450 ease-spring motion-reduce:transition-none\";\n\n/**\n * And going back into it. Quicker, and accelerating rather than easing off,\n * which is the difference between something leaving and something being taken\n * back: it is not travelling to a new place, it is going home.\n */\nconst SINK =\n \"transition-transform duration-250 ease-in motion-reduce:transition-none\";\n\n/**\n * The two phrases passing each other, both moving the same way, like a word on\n * a wheel. No fade in it: two things at half opacity on top of each other is\n * what makes a swap look like a smudge.\n *\n * A pure deceleration rather than the spring above, because this one runs\n * inside a box exactly one line tall. Anything that overshoots its landing has\n * its ascenders shaved off on the way past.\n */\nconst ROLL =\n \"transition-transform duration-350 ease-out-quint motion-reduce:transition-none\";\n\n/**\n * The box growing a line, or giving one back, while there is a draft to\n * size it. Quick and decelerating: the caret is already on the new line,\n * and the box has to show it before the next word lands there.\n *\n * An emptied box is a sent message, and that one snaps. The message is\n * landing in the thread at that moment, and the composer stepping aside\n * behind it is a second motion for the eye to follow, against the first.\n */\nconst GROW =\n \"transition-[height] duration-200 ease-out-quint motion-reduce:transition-none\";\n\n/**\n * How long the tab holds its answer before tucking away. Long enough to read\n * two words, and short enough that nobody is waiting on it: the box to write\n * in is already underneath by then.\n */\nconst SAVED_MS = 1200;\n\n/**\n * The composer's two shapes.\n *\n * `pill` is one round row, add on the left, write in the middle, send on the\n * right, which is the corner panel's and always has been. `stacked` is a\n * box: the words on top and a row of controls underneath, add on the left\n * and send on the right. For the modal, where there is room for a box and\n * a pill that wide reads as a search bar.\n */\nexport type ComposerVariant = \"pill\" | \"stacked\";\n\n/**\n * One pill at the foot of the panel: add on the left, write in the middle,\n * send on the right. The shape is round rather than square so the composer\n * reads as a control rather than as another message, and it holds that shape\n * as the box grows because the corners are already as round as they get.\n *\n * Enter sends, shift and enter starts a new line. Files arrive from the plus\n * button, from a drop anywhere along the foot of the panel, or from a paste,\n * which is how anybody who has just taken a screenshot expects to send it.\n * They stack above the pill as chips, each with a thumbnail and a way out, and\n * one that is too big says so and holds the send button until it goes. Nothing\n * is uploaded yet.\n *\n * The draft clears when the send lands rather than when the button goes down,\n * so a send that fails costs a retry, not a retype.\n *\n * The box grows a line at a time as the words wrap, and it travels the line\n * rather than jumping it. See `useEasedHeight`.\n *\n * Before a first message, the same pill can ask who they are instead: a tab\n * rises from behind its top edge and says what is being asked, while the\n * writing box makes way for an email field underneath. Nothing remounts, so\n * both movements are transitions rather than things appearing.\n *\n * An answer is taken here rather than waited on. The tab says it has it, the\n * box to write in comes back under it, and the caret is already there before\n * the tab has finished tucking away, so a phone keeps the keyboard it has\n * instead of losing it and asking for it again.\n */\nexport function WidgetComposer({\n onSend,\n onTypingChange,\n askForEmail = false,\n onSubmitEmail,\n placeholder = \"Type your message...\",\n showBadge = true,\n isSending = false,\n variant = \"pill\",\n compact = false,\n}: {\n /** Awaited if it returns a promise. A rejection keeps the draft in place. */\n onSend?: (body: string, files: File[]) => unknown;\n /** Best effort, throttled here. */\n onTypingChange?: (isTyping: boolean) => unknown;\n /**\n * Ask for an email before a first message. The pill asks instead of the\n * composer: no attachments, no typing dots, nothing to send but the\n * address.\n */\n askForEmail?: boolean;\n /** Called with the address once it looks like one. */\n onSubmitEmail?: (email: string) => void;\n placeholder?: string;\n /** The \"Powered by Median\" line. Hosts on a paid plan can turn it off. */\n showBadge?: boolean;\n isSending?: boolean;\n /** A round row, or a box with its controls underneath. */\n variant?: ComposerVariant;\n /** The denser scale's padding. See `SupportWidget`. */\n compact?: boolean;\n}) {\n const isStacked = variant === \"stacked\";\n const [draft, setDraft] = useState(\"\");\n const [email, setEmail] = useState(\"\");\n // Where the notch has got to. `none` is the question, `saved` is the word\n // that answers it, and `settled` is that same word on its way down: the\n // notch goes back to asking only if the question itself comes back.\n const [answer, setAnswer] = useState<\"none\" | \"saved\" | \"settled\">(\"none\");\n const fileInputRef = useRef<HTMLInputElement>(null);\n const draftRef = useRef<HTMLTextAreaElement>(null);\n const draftBoxRef = useEasedHeight(draftRef);\n const attachments = useAttachmentDrafts();\n const target = useAttachmentTarget({ onFiles: attachments.add });\n const typing = useTypingSignal(onTypingChange);\n\n const hasContent = draft.trim().length > 0 || attachments.drafts.length > 0;\n const canSend = hasContent && !attachments.hasBlocker && !isSending;\n\n // True once what they typed could be an address. It decides the arrow, not\n // whether the address is real.\n const isValid = EMAIL_PATTERN.test(email.trim());\n\n // A question that has come back outranks an answer already given, which is\n // `medianSupport.reset()` and nothing else: the stored address went, so the\n // notch has something to ask again rather than something to hold. Worked out\n // here rather than written back into the state, since the two facts are the\n // prop and the beat, and this is only what they add up to.\n const stage = askForEmail && answer === \"settled\" ? \"none\" : answer;\n\n // What the notch says, and whether it is up at all. It holds the word it\n // answered with the whole way down: a notch that changed its mind back to\n // the question as it left would be asking on its way out the door.\n const isAsking = askForEmail && stage === \"none\";\n const isSaved = stage !== \"none\";\n const showTab = isAsking || stage === \"saved\";\n\n function submitEmail() {\n if (!isValid || !onSubmitEmail) return;\n setAnswer(\"saved\");\n onSubmitEmail(email.trim());\n }\n\n // The beat itself. Nothing waits on it: the composer is already back\n // underneath, so this only decides how long the word stays up before the\n // notch takes it down.\n useEffect(() => {\n if (answer !== \"saved\") return;\n const timer = setTimeout(() => setAnswer(\"settled\"), SAVED_MS);\n return () => clearTimeout(timer);\n }, [answer]);\n\n // Straight on to the message, since that is what the address was in the way\n // of. On a phone it also keeps the keyboard where it is: sending it away and\n // asking for it back is the whole screen bouncing twice for nothing.\n // `preventScroll`, because the browser's idea of bringing a field into view\n // is to scroll a page that is holding a sheet still.\n useEffect(() => {\n if (answer !== \"saved\" || askForEmail) return;\n draftRef.current?.focus({ preventScroll: true });\n }, [answer, askForEmail]);\n\n async function send() {\n if (!canSend) return;\n // Quiet locally only: the message itself is the stop, server side.\n typing.settle();\n\n const body = draft.trim();\n try {\n await onSend?.(body, attachments.files);\n } catch {\n // The notice above the composer says why it failed. The draft stays\n // where it was written.\n return;\n }\n\n // Only what was sent is cleared: anything typed while the message was in\n // flight is the next draft, not part of the one that just went.\n setDraft((now) => (now.trim() === body ? \"\" : now));\n attachments.clear();\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLTextAreaElement>) {\n if (event.key !== \"Enter\") return;\n // Shift for a new line, and never while an IME is open, where Enter is\n // accepting a candidate rather than finishing the message.\n if (event.shiftKey || event.nativeEvent.isComposing) return;\n\n event.preventDefault();\n void send();\n }\n\n function handlePick(event: ChangeEvent<HTMLInputElement>) {\n if (event.target.files?.length) attachments.add(event.target.files);\n // Cleared so picking the same file twice in a row still fires a change.\n event.target.value = \"\";\n }\n\n // The controls, built once and laid out by whichever shape this is. The\n // box's buttons are a size down from the pill's, since they sit on a row\n // of their own rather than bracketing a line of text.\n const controlSize = isStacked ? \"icon-lg\" : \"icon-xl\";\n\n const fileInput = (\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n tabIndex={-1}\n onChange={handlePick}\n className=\"sr-only\"\n />\n );\n\n const addButton = (\n <WidgetButton\n type=\"button\"\n variant=\"ghost\"\n size={controlSize}\n onClick={() => fileInputRef.current?.click()}\n aria-label=\"Add an attachment\"\n className=\"rounded-full text-muted-foreground hover:rotate-90 motion-reduce:hover:rotate-0\"\n >\n <Plus className={isStacked ? \"size-5\" : undefined} />\n </WidgetButton>\n );\n\n const draftBox = (\n // The field sizes itself to its words and sits at the foot of the box,\n // so a line that wraps stays where it was typed and the box's top edge\n // rises to take the first line in: the composer grows upward, and the\n // words hold still under the caret. The box follows the field on a\n // curve and clips whatever it is behind by.\n <div\n ref={draftBoxRef}\n className={cn(\n \"flex flex-col justify-end overflow-clip\",\n isStacked ? \"w-full\" : \"min-w-0 flex-1\",\n draft.length > 0 && GROW,\n )}\n >\n <Textarea\n ref={draftRef}\n rows={1}\n value={draft}\n onChange={(event) => {\n setDraft(event.target.value);\n typing.ping();\n }}\n onKeyDown={handleKeyDown}\n // The same swap the inbox does, so both ends of a conversation\n // confirm a drop the same way.\n placeholder={target.isDropTarget ? \"Drop to attach\" : placeholder}\n aria-label=\"Message\"\n // 16px on phones, and in pixels: any focused field smaller than\n // that makes iOS zoom the host's whole page in, and a host who has\n // resized their root rem would smuggle the zoom back past `text-base`.\n //\n // `shrink-0`, or the box's pinned height would size the field\n // instead of the other way round, and it would never grow at all.\n className={cn(\n \"max-h-40 shrink-0 max-sm:text-[16px]\",\n isStacked\n ? \"min-h-9 px-2 pt-1.5 pb-1 text-sm\"\n : \"min-h-10 px-2 py-2 text-base\",\n )}\n />\n </div>\n );\n\n // Pops in as the first character lands, rather than sitting there greyed\n // out waiting to be earned.\n const sendButton = (\n <WidgetButton\n type=\"button\"\n size={controlSize}\n disabled={!canSend}\n onClick={() => void send()}\n aria-label=\"Send message\"\n className={cn(\n \"rounded-full\",\n canSend\n ? \"scale-100 opacity-100\"\n : \"scale-90 opacity-40 motion-reduce:scale-100\",\n )}\n >\n <ArrowUp className={isStacked ? \"size-5\" : undefined} />\n </WidgetButton>\n );\n\n const emailBox = (\n <input\n type=\"email\"\n value={email}\n onChange={(event) => setEmail(event.target.value)}\n onKeyDown={(event) => {\n if (event.key !== \"Enter\" || event.nativeEvent.isComposing) return;\n event.preventDefault();\n submitEmail();\n }}\n placeholder=\"you@example.com\"\n aria-label=\"Email address\"\n autoComplete=\"email\"\n spellCheck={false}\n // The same rule as the message field, for the same reason.\n className={cn(\n \"min-w-0 flex-1 px-2 outline-none placeholder:text-muted-foreground max-sm:text-[16px]\",\n isStacked ? \"h-9 text-sm\" : \"h-10 text-base\",\n )}\n />\n );\n\n const badge = showBadge ? <PoweredByMedian /> : null;\n\n const emailButton = (\n <WidgetButton\n type=\"button\"\n size={controlSize}\n disabled={!isValid}\n onClick={() => submitEmail()}\n aria-label=\"Continue with this email\"\n className={cn(\n \"rounded-full\",\n isValid\n ? \"scale-100 opacity-100\"\n : \"scale-90 opacity-40 motion-reduce:scale-100\",\n )}\n >\n <ArrowUp className={isStacked ? \"size-5\" : undefined} />\n </WidgetButton>\n );\n\n return (\n // The whole foot of the panel takes a drop, not just the pill: the pill is\n // a thin thing to aim a file at, and the chips that appear sit up here\n // anyway. Paste rides on the same element, so it is caught wherever the\n // caret happens to be inside the composer. None of that while the email\n // is asked for: there is no message yet to attach anything to.\n <div\n className={cn(\n \"flex shrink-0 flex-col gap-2 pt-1\",\n compact ? \"px-5 pb-4\" : \"px-4 pb-4\",\n )}\n {...(isAsking ? {} : target.targetProps)}\n >\n {!isAsking && attachments.drafts.length > 0 && (\n <ul className=\"flex flex-wrap gap-1.5 px-1.5\">\n {attachments.drafts.map((item) => (\n <li key={item.id} className=\"min-w-0\">\n <AttachmentDraftChip\n draft={item}\n onRemove={() => attachments.remove(item.id)}\n />\n </li>\n ))}\n </ul>\n )}\n\n <div className=\"relative\">\n {/* The slot the notch lives in: the full width of the pill, ending two\n pixels inside its top edge, and clipped.\n\n That clip is the whole trick. The notch does not fade and does not\n need to: it slides down the height of itself and the slot cuts it\n off exactly where the pill begins, so it goes into the composer\n rather than off it. Coming back it comes up out of the same seam.\n The two pixels are what guarantee no hairline of daylight between\n the two shapes, and they are behind the pill, which paints over\n them. */}\n <div\n aria-hidden={!showTab}\n inert={!showTab}\n // The headroom is for the spring: the notch passes where it lands on\n // the way up, and a clip tight to its resting height would flatten\n // the top edge for those few frames.\n className=\"pointer-events-none absolute inset-x-0 bottom-full z-0 -mb-0.5 flex items-end justify-center overflow-hidden pt-2\"\n >\n {/* Square at the bottom and no bottom hairline: that edge is the\n seam with the pill, not an edge of its own. */}\n <div\n className={cn(\n \"grid max-w-[calc(100%-2.5rem)] select-none\",\n \"rounded-t-2xl border border-b-0 bg-muted px-4 pt-2 pb-2.5 text-muted-foreground\",\n isStacked ? \"text-sm\" : \"text-base\",\n showTab ? RISE : SINK,\n !showTab && \"translate-y-full\",\n )}\n >\n {/* One line tall and clipped, so the phrases roll through it.\n\n Both share the cell, so the notch is as wide as the longer of\n the two and holds that width while they change: the words move\n inside a shape that does not. Each is centred, so the short one\n sits in the middle of the notch rather than off to its left. */}\n <span className=\"grid overflow-hidden\">\n <span\n aria-hidden={isSaved}\n className={cn(\n \"col-start-1 row-start-1 flex items-center gap-2 justify-self-center\",\n ROLL,\n isSaved && \"-translate-y-full\",\n )}\n >\n <EnvelopeSimple aria-hidden className=\"size-5 shrink-0\" />\n <span className=\"truncate\">Enter your email</span>\n </span>\n\n <span\n aria-hidden={!isSaved}\n className={cn(\n \"col-start-1 row-start-1 flex items-center gap-2 justify-self-center\",\n ROLL,\n !isSaved && \"translate-y-full\",\n )}\n >\n <Check aria-hidden className=\"size-5 shrink-0\" />\n <span className=\"truncate\">Saved</span>\n </span>\n </span>\n </div>\n </div>\n\n {/* The same surface as the panel behind it, so the composer is a shape\n rather than a slab. Painted rather than left transparent, since it\n is what hides the foot of the tab. A grid with both composers in\n one cell: they sit exactly on top of each other, same height, so\n the swap moves nothing. Whichever one is waiting wears `inert`,\n which takes it out of the tab order and the accessibility tree\n along with the pointer events. */}\n <div\n className={cn(\n TEXT_FIELD_SHELL,\n \"relative z-10 grid bg-card p-2 transition-colors dark:bg-card\",\n // Half the pill's one line height, so it is a full pill at one\n // line and keeps those corners as it grows. The box keeps a\n // card's corner instead: its height is never one line.\n isStacked ? \"rounded-2xl\" : \"items-center rounded-[1.75rem]\",\n // The pill lights up rather than the whole foot of the panel, so a\n // file being dragged in points at the thing it is about to join.\n !isAsking &&\n target.isDropTarget &&\n \"border-ring bg-muted/60 ring-1 ring-ring dark:bg-muted/60\",\n )}\n >\n {/* The message half. Both halves travel down together, the same way\n the notch above them goes, so an answer settles the whole thing\n rather than sending three pieces of it in three directions. */}\n <div\n aria-hidden={isAsking}\n inert={isAsking}\n className={cn(\n \"col-start-1 row-start-1\",\n isStacked ? \"flex flex-col gap-1\" : \"flex items-end gap-1.5\",\n SWAP,\n isAsking && \"-translate-y-1 opacity-0 pointer-events-none\",\n )}\n >\n {/* The same four pieces in both shapes. The pill lays them in a\n row; the box puts the words first and the buttons on a row\n of their own underneath. */}\n {isStacked ? (\n <>\n {draftBox}\n <div className=\"flex items-center gap-1.5\">\n {fileInput}\n {addButton}\n {/* Our name rides the controls row, beside the send, so\n the box is the whole foot of the panel and nothing\n hangs under it. */}\n <div className=\"ml-auto flex items-center gap-2\">\n {badge}\n {sendButton}\n </div>\n </div>\n </>\n ) : (\n <>\n {fileInput}\n {addButton}\n {draftBox}\n {sendButton}\n </>\n )}\n </div>\n\n {/* The answer half. */}\n <div\n aria-hidden={!isAsking}\n inert={!isAsking}\n className={cn(\n \"col-start-1 row-start-1\",\n isStacked ? \"flex flex-col gap-1\" : \"flex items-center gap-1.5\",\n SWAP,\n !isAsking && \"pointer-events-none translate-y-1 opacity-0\",\n )}\n >\n {isStacked ? (\n <>\n {emailBox}\n <div className=\"flex items-center justify-end gap-2\">\n {badge}\n {emailButton}\n </div>\n </>\n ) : (\n <>\n {emailBox}\n {emailButton}\n </>\n )}\n </div>\n </div>\n </div>\n\n {/* Under the pill. The box carries it inside instead. */}\n {!isStacked && badge}\n </div>\n );\n}\n","import { forwardRef, type ImgHTMLAttributes } from \"react\";\n\n/**\n * Stands in for `next/image` when the widget is bundled for npm, via the\n * `paths` entry in this package's tsconfig. The app renders avatars and\n * marks through the framework's image pipeline; the package has no framework,\n * and everything in the widget's closure is an avatar, a mark, or an object\n * URL that never left the browser, all of which a plain img carries the same.\n *\n * Next-only props are swallowed here so they do not land on a DOM element\n * that has never heard of them.\n */\ntype NextOnlyProps = {\n unoptimized?: boolean;\n priority?: boolean;\n quality?: number | string;\n placeholder?: string;\n blurDataURL?: string;\n fill?: boolean;\n loader?: unknown;\n};\n\nconst Image = forwardRef<\n HTMLImageElement,\n ImgHTMLAttributes<HTMLImageElement> & NextOnlyProps\n>(function Image(\n {\n unoptimized: _unoptimized,\n priority: _priority,\n quality: _quality,\n placeholder: _placeholder,\n blurDataURL: _blurDataURL,\n fill: _fill,\n loader: _loader,\n ...props\n },\n ref,\n) {\n return <img ref={ref} {...props} />;\n});\n\nexport default Image;\n","\"use client\";\n\nimport Image from \"next/image\";\n\n/**\n * One picture, drawn by whichever loader is actually allowed to fetch it.\n *\n * A file we hold goes through next/image, which is what keeps a four megabyte\n * screenshot from arriving whole inside a tile the size of a stamp. A linked\n * one lives on somebody else's machine, and the optimizer is deliberately not\n * allowed to fetch arbitrary hosts: `/_next/image` is public and billed per\n * distinct source, so an open host list is somebody else's loop running up\n * our bill. Those are drawn plainly instead.\n *\n * No referrer goes with an outside fetch. Opening a bug report should not\n * tell whoever is hosting the picture which page it was opened from.\n */\nexport function AttachmentImage({\n src,\n alt,\n external = false,\n width,\n height,\n className,\n onError,\n}: {\n src: string;\n alt: string;\n /** True for an address we do not serve, which changes how it is loaded. */\n external?: boolean;\n width: number;\n height: number;\n className?: string;\n onError?: () => void;\n}) {\n if (external) {\n return (\n // eslint-disable-next-line @next/next/no-img-element -- an outside host the optimizer is not allowed to fetch.\n <img\n src={src}\n alt={alt}\n width={width}\n height={height}\n loading=\"lazy\"\n referrerPolicy=\"no-referrer\"\n onError={onError}\n draggable={false}\n className={className}\n />\n );\n }\n\n return (\n <Image\n src={src}\n alt={alt}\n width={width}\n height={height}\n onError={onError}\n draggable={false}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport Image from \"next/image\";\nimport { File, ImageSquare, Play, WarningCircle, X } from \"@/components/icons\";\nimport { Button } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\nimport type { AttachmentDraft } from \"./use-attachment-drafts\";\n\nconst KIND_ICONS = {\n image: ImageSquare,\n video: Play,\n file: File,\n};\n\n/**\n * One staged file, with a thumbnail where there is one and a way out.\n *\n * `max-w-full` so the chip gives up its preferred width inside something\n * narrow, like the support widget, rather than pushing out of the box.\n */\nexport function AttachmentDraftChip({\n draft,\n onRemove,\n className,\n}: {\n draft: AttachmentDraft;\n onRemove: () => void;\n className?: string;\n}) {\n const hasError = draft.error !== null;\n const Glyph = hasError ? WarningCircle : KIND_ICONS[draft.kind];\n\n return (\n <div\n className={cn(\n \"flex w-48 max-w-full items-center gap-2 rounded-lg border bg-card/60 p-1.5\",\n \"animate-in fade-in-0 zoom-in-95 duration-200 ease-out motion-reduce:animate-none\",\n hasError && \"border-destructive/40\",\n className,\n )}\n >\n {draft.previewUrl ? (\n // Object URLs cannot be optimised, and there is nothing to optimise:\n // the file has not left the browser.\n <Image\n src={draft.previewUrl}\n alt=\"\"\n width={64}\n height={64}\n unoptimized\n className=\"size-8 shrink-0 rounded-md object-cover\"\n />\n ) : (\n <span className=\"grid size-8 shrink-0 place-items-center rounded-md bg-muted\">\n <Glyph\n className={cn(\n \"size-4\",\n hasError ? \"text-destructive\" : \"text-muted-foreground\",\n )}\n />\n </span>\n )}\n\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <p className=\"truncate text-xs font-medium\">{draft.name}</p>\n <p\n className={cn(\n \"truncate text-xs\",\n hasError ? \"text-destructive\" : \"text-muted-foreground\",\n )}\n >\n {draft.error ?? draft.size}\n </p>\n </div>\n\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n onClick={onRemove}\n aria-label={`Remove ${draft.name}`}\n className=\"shrink-0 rounded-full text-muted-foreground\"\n >\n <X />\n </Button>\n </div>\n );\n}\n","\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport type { AttachmentKind } from \"./types\";\n\nconst MAX_BYTES = 25 * 1024 * 1024;\n\n/**\n * Types the backend turns away, checked here too so the chip says so before\n * anything is uploaded rather than after the send fails. The list that counts\n * is `UNSAFE_ATTACHMENT_TYPES` in the backend's `shared/support.ts`; this one\n * is the same list wearing a friendlier moment.\n */\nconst UNSAFE_TYPES = [\n \"text/html\",\n \"application/xhtml+xml\",\n \"image/svg+xml\",\n \"text/xsl\",\n \"application/xslt+xml\",\n];\n\nfunction isUnsafeType(type: string): boolean {\n return UNSAFE_TYPES.includes(type.split(\";\")[0].trim().toLowerCase());\n}\n\nexport type AttachmentDraft = {\n id: string;\n /** The file itself, kept so a caller can hand it on once it is sent. */\n file: File;\n name: string;\n size: string;\n kind: AttachmentKind;\n /** An object URL for pictures, so the chip can show what was picked. */\n previewUrl: string | null;\n /** Set when the file cannot be sent. Shown on the chip in place of the size. */\n error: string | null;\n};\n\nlet nextDraftId = 0;\n\n/**\n * Bytes, written the way a person would say them. Exported because whoever\n * receives a sent file has to label it the same way the chip that staged it\n * did, or the same file changes size on its way into the thread.\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n\n const units = [\"KB\", \"MB\", \"GB\"];\n let value = bytes / 1024;\n let unit = 0;\n\n while (value >= 1024 && unit < units.length - 1) {\n value /= 1024;\n unit += 1;\n }\n\n return `${value < 10 ? value.toFixed(1) : Math.round(value)} ${units[unit]}`;\n}\n\n/** The kinds in the plural, for the line on a chip that has to say no. */\nconst KIND_NAMES: Record<AttachmentKind, string> = {\n image: \"pictures\",\n video: \"videos\",\n file: \"files\",\n};\n\n/** What a file is, from its MIME type. Anything unrecognised is just a file. */\nexport function attachmentKindOf(type: string): AttachmentKind {\n if (type.startsWith(\"image/\")) return \"image\";\n if (type.startsWith(\"video/\")) return \"video\";\n return \"file\";\n}\n\nfunction release(draft: AttachmentDraft) {\n if (draft.previewUrl) URL.revokeObjectURL(draft.previewUrl);\n}\n\n/**\n * Files staged on a composer, from either the picker or a drop. Shared by the\n * agent's composer in the inbox, the visitor's in the support widget, and the\n * feedback panel, so all three agree on what is too big and what a size looks\n * like written down.\n *\n * Nothing is uploaded: a draft is checked, previewed and then thrown away when\n * the message is sent. A file that fails the check stays in the list wearing\n * its reason, so it gets read rather than silently dropped.\n *\n * `files` hands back the originals in the same order, which is what a caller\n * passes on once there is somewhere to send them.\n */\nexport function useAttachmentDrafts({\n accept,\n max,\n}: {\n /**\n * The one kind this composer takes. Anything else still appears, wearing\n * the reason it cannot go, because a picture that vanished on the way into\n * the box reads as the box being broken.\n */\n accept?: AttachmentKind;\n /** The most that may be staged at once. Unbounded when left off. */\n max?: number;\n} = {}) {\n const [drafts, setDrafts] = useState<AttachmentDraft[]>([]);\n\n // Object URLs outlive the elements that used them, so any still open when\n // the composer goes away get revoked on the way out.\n const draftsRef = useRef(drafts);\n useEffect(() => {\n draftsRef.current = drafts;\n }, [drafts]);\n useEffect(() => () => draftsRef.current.forEach(release), []);\n\n function add(files: FileList | File[]) {\n const picked = Array.from(files).map((file, index): AttachmentDraft => {\n const isTooBig = file.size > MAX_BYTES;\n const isUnsafe = isUnsafeType(file.type);\n const kind = attachmentKindOf(file.type);\n const isWrongKind = accept !== undefined && kind !== accept;\n const isOverCap = max !== undefined && drafts.length + index >= max;\n const error = isTooBig\n ? `Larger than the ${formatFileSize(MAX_BYTES)} limit`\n : isUnsafe\n ? \"This kind of file cannot be sent\"\n : isWrongKind\n ? `Only ${KIND_NAMES[accept]} can go here`\n : isOverCap\n ? `Only ${max} at a time`\n : null;\n\n return {\n id: `draft-${(nextDraftId += 1)}`,\n file,\n name: file.name,\n size: formatFileSize(file.size),\n kind,\n previewUrl:\n kind === \"image\" && error === null ? URL.createObjectURL(file) : null,\n error,\n };\n });\n\n setDrafts([...drafts, ...picked]);\n }\n\n function remove(id: string) {\n const going = drafts.find((draft) => draft.id === id);\n if (going) release(going);\n setDrafts(drafts.filter((draft) => draft.id !== id));\n }\n\n function clear() {\n drafts.forEach(release);\n setDrafts([]);\n }\n\n return {\n drafts,\n add,\n remove,\n clear,\n /** The originals, in the order they were picked. */\n files: drafts.map((draft) => draft.file),\n /** True while something in the list cannot be sent. */\n hasBlocker: drafts.some((draft) => draft.error !== null),\n /** True once the cap is reached, for a picker that should go quiet. */\n isFull: max !== undefined && drafts.length >= max,\n };\n}\n","\"use client\";\n\nimport { useRef, useState, type ClipboardEvent, type DragEvent } from \"react\";\n\n/**\n * The two ways a file arrives that are not the picker: dragged onto the\n * composer, or pasted into it. Both are how somebody who has just taken a\n * screenshot expects to send it, and neither should need the plus button.\n *\n * Shared by the inbox and the widget, so a screenshot lands the same way on\n * both sides of a conversation. This only notices and collects; what becomes\n * of the files is the caller's business, which is what lets an internal note\n * turn them away.\n */\nexport function useAttachmentTarget({\n onFiles,\n disabled = false,\n}: {\n onFiles: (files: File[]) => void;\n /** Somewhere a file is not welcome, like a note the backend keeps to words. */\n disabled?: boolean;\n}) {\n const [isDropTarget, setIsDropTarget] = useState(false);\n\n // Dragging over a child element fires leave on the parent, so the highlight\n // counts its way in and out rather than trusting a single leave to mean the\n // pointer has actually gone.\n const depth = useRef(0);\n\n function handleDragEnter(event: DragEvent<HTMLElement>) {\n // Dragging a selection of text across the page is not an attachment.\n if (disabled || !event.dataTransfer.types.includes(\"Files\")) return;\n depth.current += 1;\n setIsDropTarget(true);\n }\n\n function handleDragLeave() {\n depth.current = Math.max(0, depth.current - 1);\n if (depth.current === 0) setIsDropTarget(false);\n }\n\n function handleDrop(event: DragEvent<HTMLElement>) {\n event.preventDefault();\n depth.current = 0;\n setIsDropTarget(false);\n if (disabled) return;\n\n const files = Array.from(event.dataTransfer.files);\n if (files.length > 0) onFiles(files);\n }\n\n function handlePaste(event: ClipboardEvent<HTMLElement>) {\n if (disabled) return;\n\n // Ordinary text carries no files, and is left alone entirely: caret,\n // selection, undo history and all. A screenshot does carry one, and\n // taking it means the markup the clipboard came with does not also end\n // up in the message.\n const files = Array.from(event.clipboardData.files);\n if (files.length === 0) return;\n\n event.preventDefault();\n onFiles(files);\n }\n\n return {\n /** True while something is being dragged over it, for the highlight. */\n isDropTarget,\n /** Spread onto whatever counts as the target. */\n targetProps: {\n onDragEnter: handleDragEnter,\n onDragLeave: handleDragLeave,\n // Without this the browser refuses the drop and opens the file instead,\n // taking the page, and whatever was being written, with it.\n onDragOver: (event: DragEvent<HTMLElement>) => event.preventDefault(),\n onDrop: handleDrop,\n onPaste: handlePaste,\n },\n };\n}\n","import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Slot } from \"radix-ui\";\n\nimport { cn } from \"@/lib/utils\";\n\n/**\n * The widget's own button, deliberately forked from `ui/button`.\n *\n * The widget ships on other people's sites, so its look is a contract with\n * them, not with our chrome. When the app restyles its buttons, the widget\n * must not come along for the ride; this file is where it stays put.\n */\nconst widgetButtonVariants = cva(\n \"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/80\",\n outline:\n \"border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary,oklch(0.97_0_0)),var(--foreground,oklch(0.145_0_0))_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground\",\n ghost:\n \"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50\",\n destructive:\n \"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default:\n \"h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2\",\n xs: \"h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3\",\n sm: \"h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5\",\n lg: \"h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2\",\n // The support panel's scale: 16px words, 20px glyphs.\n xl: \"h-10 gap-2 px-4 text-base has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3 [&_svg:not([class*='size-'])]:size-5\",\n icon: \"size-8\",\n \"icon-xs\":\n \"size-6 rounded-[min(var(--radius-md),10px)] [&_svg:not([class*='size-'])]:size-3\",\n \"icon-sm\": \"size-7 rounded-[min(var(--radius-md),12px)]\",\n \"icon-lg\": \"size-9\",\n \"icon-xl\": \"size-10 [&_svg:not([class*='size-'])]:size-5\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nfunction WidgetButton({\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof widgetButtonVariants> & {\n asChild?: boolean;\n }) {\n const Comp = asChild ? Slot.Root : \"button\";\n\n return (\n <Comp\n data-slot=\"widget-button\"\n data-variant={variant}\n data-size={size}\n className={cn(widgetButtonVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\n\nexport { WidgetButton, widgetButtonVariants };\n","/** The widget keeps its original fields when the app's controls change. */\nexport const WIDGET_TEXT_FIELD_SHELL = [\n \"rounded-lg border border-input bg-transparent text-sm max-sm:text-base transition-[color,box-shadow] outline-none dark:bg-input/30\",\n \"placeholder:text-muted-foreground\",\n \"focus-within:border-ring\",\n \"aria-invalid:border-destructive\",\n \"disabled:pointer-events-none disabled:opacity-50\",\n].join(\" \");\n","import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/**\n * Grows with its content up to `max-h`, courtesy of `field-sizing-content`.\n * Browsers without it fall back to the `rows` the caller sets.\n */\nfunction Textarea({ className, ...props }: React.ComponentProps<\"textarea\">) {\n return (\n <textarea\n data-slot=\"textarea\"\n className={cn(\n \"field-sizing-content w-full resize-none bg-transparent text-sm outline-none max-sm:text-base\",\n \"placeholder:text-muted-foreground\",\n \"disabled:pointer-events-none disabled:opacity-50\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { Textarea };\n","\"use client\";\n\nimport { useCallback, useEffect, useRef } from \"react\";\n\n/** Refreshed well inside the server's TTL, without spamming a mutation. */\nconst REFRESH_MS = 2500;\n/**\n * This long without a keystroke means the sentence stalled. Generous on\n * purpose: dots that vanish at every thinking pause and pop back at the next\n * key read as flicker, not presence.\n */\nconst IDLE_MS = 8000;\n\n/**\n * Turns keystrokes into a calm typing signal: `ping` on every change sends\n * \"typing\" at most every couple of seconds, going quiet after a pause, on\n * `stop`, and on unmount.\n *\n * `settle` goes quiet locally without announcing it. That is for sending:\n * the message itself is the stop, and racing a \"stopped typing\" mutation in\n * ahead of it would delete the pending bubble the reply is about to fill,\n * which is the difference between a bubble that morphs and one that flashes.\n *\n * Failures are swallowed on purpose. A typing indicator is decoration, and\n * decoration does not get to put errors in front of anyone.\n */\nexport function useTypingSignal(onChange?: (isTyping: boolean) => unknown) {\n // Held in a ref so ping and stop keep stable identities while the caller\n // re-renders with fresh closures.\n const onChangeRef = useRef(onChange);\n useEffect(() => {\n onChangeRef.current = onChange;\n }, [onChange]);\n\n const isTypingRef = useRef(false);\n const lastSentRef = useRef(0);\n const idleTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const send = useCallback((isTyping: boolean) => {\n try {\n void Promise.resolve(onChangeRef.current?.(isTyping)).catch(() => {});\n } catch {\n // Decorative; see above.\n }\n }, []);\n\n /** Goes quiet without telling anyone. The send that follows says it. */\n const settle = useCallback(() => {\n if (idleTimer.current) clearTimeout(idleTimer.current);\n idleTimer.current = null;\n isTypingRef.current = false;\n lastSentRef.current = 0;\n }, []);\n\n const stop = useCallback(() => {\n const wasTyping = isTypingRef.current;\n settle();\n if (wasTyping) send(false);\n }, [send, settle]);\n\n const ping = useCallback(() => {\n if (idleTimer.current) clearTimeout(idleTimer.current);\n idleTimer.current = setTimeout(stop, IDLE_MS);\n\n const now = Date.now();\n if (!isTypingRef.current || now - lastSentRef.current > REFRESH_MS) {\n isTypingRef.current = true;\n lastSentRef.current = now;\n send(true);\n }\n }, [send, stop]);\n\n useEffect(() => () => stop(), [stop]);\n\n return { ping, stop, settle };\n}\n","\"use client\";\n\nimport { useLayoutEffect, useRef, type RefObject } from \"react\";\n\n/**\n * A box whose height follows the field inside it, on a curve.\n *\n * The field sizes itself to its own words, which is what `field-sizing:\n * content` does, and it does it between one frame and the next: a line\n * wraps, and the box around it is a line taller with nothing in between.\n * So the field goes in a box with a height of its own, written from the\n * field's every time the field changes, and a transition on the box does\n * the travel. The field keeps its natural size underneath, which means the\n * caret and the words are already where they will end up; the box is what\n * catches them up.\n *\n * The box clips rather than scrolls while it is behind. A box that can\n * scroll is a box the browser will scroll to show a caret in, and that would\n * drag the words up inside it on every new line.\n *\n * The first height is written in the same layout pass as the first paint,\n * so the first keystroke that wraps has a number to travel from. A\n * transition from `auto` is not a transition at all.\n *\n * `offsetHeight` rather than the client rect, since the modal's panel is\n * scaled down while it is closed and a rect read then is scaled with it.\n * The field's height is whole lines of whole pixels anyway.\n */\nexport function useEasedHeight(field: RefObject<HTMLElement | null>) {\n const box = useRef<HTMLDivElement>(null);\n\n useLayoutEffect(() => {\n const outer = box.current;\n const inner = field.current;\n if (!outer || !inner) return;\n\n const follow = () => {\n outer.style.height = `${inner.offsetHeight}px`;\n };\n follow();\n if (typeof ResizeObserver === \"undefined\") return;\n\n const observer = new ResizeObserver(follow);\n observer.observe(inner);\n return () => {\n observer.disconnect();\n outer.style.height = \"\";\n };\n }, [field]);\n\n return box;\n}\n","/**\n * The Median marks, as geometry rather than as files.\n *\n * The same brand components render inside the widget package, which is\n * installed into someone else's app and has no way to reach our public\n * directory. Keeping the paths here is what lets a mark draw itself\n * anywhere: in the app, in the npm bundle, and through a CSS mask, all from\n * one copy.\n *\n * `public/` still carries the SVGs, for the things outside the app that want\n * a file to point at: favicons, OG images, the docs site.\n */\n\n/** What the artwork is drawn in. Dark mode inverts it rather than restating it. */\nexport const BRAND_INK = \"#191919\";\n\n/** The mark, in its own square box. */\nexport const LOGO = {\n viewBox: \"0 0 58 58\",\n paths: [\n \"M48.9788 0H8.97304C4.01742 0 0 4.11877 0 9.22508V20.7384C0 21.5573 0.577354 22.0631 1.37122 21.8223L46.068 10.1885C47.2227 9.87541 47.8963 10.7666 47.5595 11.9228L35.8922 56.5789C35.6757 57.446 36.2049 58 37.0709 58H48.7383C53.7661 58 58 53.6885 58 48.799V9.22508C58 4.11877 53.9585 0 48.9788 0Z\",\n \"M0 26.976V49.0151C0 53.9287 3.99336 57.9993 8.97304 57.9993H30.8884C31.7545 57.9993 32.1634 57.2767 31.9229 56.4337L28.579 44.3182C28.4347 43.8606 28.4587 43.4511 28.6993 42.9935L39.3803 21.5084C39.9818 20.3041 38.5384 19.4129 37.6723 20.3282L10.4164 47.3531C9.64662 48.1721 8.41974 47.2327 8.85276 46.1488L13.9527 30.9744C14.2173 30.1073 13.8565 29.2642 12.8221 29.0716L1.4915 25.9644C0.60141 25.6513 0 26.133 0 26.976Z\",\n ],\n} as const;\n\n/** The word, sharing the mark's 58 unit height so the two set together. */\nexport const WORDMARK = {\n viewBox: \"0 0 253 58\",\n width: 253,\n height: 58,\n paths: [\n \"M10.1999 2.52174L29.0138 35.0522L47.8637 2.52174H57.8834V56.9913H47.7916V21.1106L33.1586 46.2199H24.6527L9.98362 20.9665V56.9913H0V2.52174H10.1999Z\",\n \"M102.616 40.6H71.5115C72.1363 43.4099 73.5058 45.5834 75.6203 47.1205C77.7588 48.6335 80.3778 49.3901 83.4775 49.3901C85.664 49.3901 87.6823 48.9458 89.5325 48.0571C91.4067 47.1445 92.8363 45.9317 93.8215 44.4186L101.57 49.318C99.8165 51.9598 97.3176 54.0613 94.0738 55.6224C90.83 57.2075 87.2979 58 83.4775 58C79.2966 58 75.5362 57.0874 72.1963 55.2621C68.8805 53.4609 66.2975 50.9752 64.4473 47.805C62.6212 44.6108 61.7081 41.0563 61.7081 37.1416C61.7081 34.2116 62.2247 31.4617 63.2579 28.8919C64.3152 26.2981 65.7448 24.0766 67.5469 22.2273C69.349 20.3781 71.5235 18.9251 74.0705 17.8683C76.6175 16.7876 79.3326 16.2472 82.216 16.2472C88.1509 16.2472 93.0526 18.1085 96.9211 21.8311C100.814 25.5536 102.76 30.3329 102.76 36.1689C102.76 36.8654 102.712 38.3424 102.616 40.6ZM71.5115 33.5031H93.0646C92.8003 30.8373 91.695 28.6998 89.7487 27.0907C87.8265 25.4816 85.3396 24.677 82.2881 24.677C79.5969 24.677 77.2662 25.4576 75.2959 27.0186C73.3497 28.5797 72.0882 30.7412 71.5115 33.5031Z\",\n \"M136.608 1.54907H146.735V56.9913H136.608V51.3714C135.286 53.5329 133.508 55.1781 131.273 56.3068C129.039 57.4356 126.516 58 123.705 58C118.202 58 113.601 55.9946 109.901 51.9839C106.2 47.9731 104.35 43.0257 104.35 37.1416C104.326 34.2836 104.807 31.5578 105.792 28.964C106.777 26.3702 108.122 24.1366 109.828 22.2634C111.558 20.366 113.625 18.877 116.028 17.7963C118.454 16.7155 121.013 16.1992 123.705 16.2472C126.516 16.2472 129.039 16.8116 131.273 17.9404C133.508 19.0451 135.286 20.6663 136.608 22.8037V1.54907ZM133.436 45.8957C135.55 43.6861 136.608 40.7681 136.608 37.1416C136.608 33.5151 135.55 30.5971 133.436 28.3876C131.345 26.154 128.642 25.0373 125.326 25.0373C122.011 25.0373 119.271 26.1901 117.109 28.4957C115.042 30.7772 114.009 33.6592 114.009 37.1416C114.009 40.6 115.067 43.482 117.181 45.7876C119.295 48.0692 122.011 49.2099 125.326 49.2099C128.642 49.2099 131.345 48.1052 133.436 45.8957Z\",\n \"M150.957 5.98012C150.957 4.29896 151.557 2.88199 152.759 1.72919C153.984 0.576398 155.486 0 157.264 0C159.018 0 160.496 0.588406 161.697 1.76522C162.923 2.91801 163.535 4.32298 163.535 5.98012C163.535 7.70932 162.935 9.1383 161.733 10.2671C160.556 11.3959 159.066 11.9602 157.264 11.9602C155.462 11.9602 153.96 11.3959 152.759 10.2671C151.557 9.1383 150.957 7.70932 150.957 5.98012ZM162.274 17.2919V56.9913H152.218V17.2919H162.274Z\",\n \"M198.176 17.2919H208.304V56.9913H198.176V51.3714C196.854 53.5329 195.076 55.1781 192.842 56.3068C190.607 57.4356 188.084 58 185.273 58C179.77 58 175.169 55.9946 171.469 51.9839C167.768 47.9731 165.918 43.0257 165.918 37.1416C165.894 34.2836 166.375 31.5578 167.36 28.964C168.345 26.3702 169.691 24.1366 171.397 22.2634C173.127 20.366 175.193 18.877 177.596 17.7963C180.023 16.7155 182.582 16.1992 185.273 16.2472C188.084 16.2472 190.607 16.8116 192.842 17.9404C195.076 19.0451 196.854 20.6663 198.176 22.8037V17.2919ZM195.004 45.8957C197.119 43.6861 198.176 40.7681 198.176 37.1416C198.176 33.5151 197.119 30.5971 195.004 28.3876C192.914 26.154 190.211 25.0373 186.895 25.0373C183.579 25.0373 180.84 26.1901 178.677 28.4957C176.611 30.7772 175.578 33.6592 175.578 37.1416C175.578 40.6 176.635 43.482 178.749 45.7876C180.864 48.0692 183.579 49.2099 186.895 49.2099C190.211 49.2099 192.914 48.1052 195.004 45.8957Z\",\n \"M253 34.5478V56.9913H242.872V35.2683C242.872 31.882 242.115 29.3362 240.602 27.6311C239.088 25.9019 236.865 25.0373 233.934 25.0373C231.002 25.0373 228.588 26.082 226.689 28.1714C224.791 30.2369 223.842 32.9267 223.842 36.241V56.9913H213.786V17.2919H223.842V22.9478C224.995 20.8824 226.665 19.2493 228.852 18.0484C231.062 16.8476 233.549 16.2472 236.313 16.2472C241.503 16.2472 245.575 17.8563 248.531 21.0745C251.51 24.2928 253 28.7839 253 34.5478Z\",\n ],\n} as const;\n\n/**\n * The mark as a `url()`, for CSS that paints through its shape rather than\n * painting the artwork itself. Inline, because a mask is fetched the way an\n * image is and a host has nothing at `/median-logo.svg` to answer with.\n *\n * A mask reads only coverage, so the paths go in without a fill and come out\n * opaque.\n */\nexport const LOGO_MASK_URL = `url(\"data:image/svg+xml,${encodeURIComponent(\n `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"${LOGO.viewBox}\">` +\n LOGO.paths.map((d) => `<path d=\"${d}\"/>`).join(\"\") +\n `</svg>`,\n)}\")`;\n","import { BRAND_INK, LOGO, LOGO_MASK_URL, WORDMARK } from \"@/components/brand-art\";\nimport { cn } from \"@/lib/utils\";\n\n/** The artwork is solid black, so it gets inverted for dark mode. */\nconst INVERT_IN_DARK = \"dark:invert\";\n\n/**\n * The marks, drawn inline.\n *\n * Inline rather than fetched, because these render inside the widget package\n * too, where there is no public directory of ours to ask. See\n * `brand-art.ts` for the geometry.\n */\n\nexport function Wordmark({\n width = 132,\n className = \"\",\n}: {\n width?: number;\n className?: string;\n}) {\n return (\n <svg\n viewBox={WORDMARK.viewBox}\n width={width}\n height={Math.round((width * WORDMARK.height) / WORDMARK.width)}\n role=\"img\"\n aria-label=\"Median\"\n className={`${INVERT_IN_DARK} ${className}`}\n >\n {WORDMARK.paths.map((d) => (\n <path key={d} d={d} fill={BRAND_INK} />\n ))}\n </svg>\n );\n}\n\nexport function Logo({\n size = 104,\n className = \"\",\n}: {\n size?: number;\n className?: string;\n}) {\n return (\n <svg\n viewBox={LOGO.viewBox}\n width={size}\n height={size}\n aria-hidden\n className={`${INVERT_IN_DARK} ${className}`}\n >\n {LOGO.paths.map((d) => (\n <path key={d} d={d} fill={BRAND_INK} />\n ))}\n </svg>\n );\n}\n\n/**\n * The mark and the word set together on one line, which is the logo as it is\n * signed: for the foot of a page outside the app, where the name has to be\n * said rather than assumed.\n */\nexport function FullLogo({\n /** How tall the mark stands. The word is scaled to match it. */\n height = 20,\n className = \"\",\n}: {\n height?: number;\n className?: string;\n}) {\n return (\n <span\n role=\"img\"\n aria-label=\"Median\"\n className={`inline-flex items-center gap-2 ${className}`}\n >\n {/* `role=\"img\"` above takes the whole lockup out of the tree, so the\n word's own label inside it is never read twice. */}\n <Logo size={height} />\n <Wordmark width={Math.round((height * WORDMARK.width) / WORDMARK.height)} />\n </span>\n );\n}\n\n/**\n * The mark with a band of light travelling through it, for a wait that has\n * no shape yet: a conversation loading, an answer on its way.\n *\n * Painted by a gradient showing through a mask cut in the logo's shape,\n * rather than by the artwork itself, so the band travels across the mark\n * instead of over the top of it.\n */\nexport function ShimmerLogo({\n label,\n className,\n}: {\n label: string;\n className?: string;\n}) {\n return (\n <span\n role=\"status\"\n aria-label={label}\n style={{\n maskImage: LOGO_MASK_URL,\n maskSize: \"contain\",\n maskRepeat: \"no-repeat\",\n maskPosition: \"center\",\n WebkitMaskImage: LOGO_MASK_URL,\n WebkitMaskSize: \"contain\",\n WebkitMaskRepeat: \"no-repeat\",\n WebkitMaskPosition: \"center\",\n backgroundImage:\n // With the widget's own light values behind the host's, since this\n // mark also draws on pages that define neither.\n \"linear-gradient(115deg, var(--muted, oklch(0.955 0 0)) 34%, var(--muted-foreground, oklch(0.556 0 0)) 50%, var(--muted, oklch(0.955 0 0)) 66%)\",\n backgroundSize: \"300% 100%\",\n }}\n className={cn(\n \"size-12 animate-shimmer motion-reduce:animate-none motion-reduce:bg-muted\",\n className,\n )}\n />\n );\n}\n","import { Logo, Wordmark } from \"@/components/brand\";\nimport { cn } from \"@/lib/utils\";\n\nconst MEDIAN_URL = \"https://median.sh\";\n\n/**\n * Our name on somebody else's site, so it sits under the composer rather than\n * inside it: a quiet line at the foot of the panel, not a control.\n *\n * It opens in a new tab, since a visitor who taps it is halfway through asking\n * for help and should not lose the page they were on.\n */\nexport function PoweredByMedian({ className }: { className?: string }) {\n return (\n <a\n href={MEDIAN_URL}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className={cn(\n // It shares the foot of the feedback panel with two buttons, so it is\n // held at its own width and kept on one line: a flex item that can be\n // squeezed folds \"Powered by\" onto a second row.\n \"flex shrink-0 items-center justify-center gap-1.5 rounded-md py-1 text-xs whitespace-nowrap text-muted-foreground\",\n \"transition-opacity hover:opacity-70\",\n \"focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-none\",\n className,\n )}\n >\n Powered by\n <Logo size={12} className=\"opacity-80\" />\n <Wordmark width={40} className=\"opacity-80\" />\n </a>\n );\n}\n","/**\n * The corner every widget surface is cut to: the panel, the teaser, the\n * feedback panel, the contact form, and the clip and shadow the corner\n * launcher draws the panel through.\n *\n * Written out rather than `rounded-xl`, because the app's radius scale is\n * inlined when the stylesheet is built, and pinning `--radius` on the widget\n * cannot pin the multiplier. When the app's scale moved, the panel grew\n * rounder than the clip behind it, and the clip's fill showed at each\n * corner. This is 0.625rem at the 1.4 the widget's published stylesheet\n * uses, which is the corner the panel had before that.\n *\n * Two spellings of one number: the class where the Tailwind compiler can\n * read it, and the length for a clip path. Keep them together.\n */\nexport const WIDGET_RADIUS = \"0.875rem\";\nexport const WIDGET_CORNER = \"rounded-[0.875rem]\";\n","import { ArrowRight } from \"@/components/icons\";\nimport { WidgetButton } from \"./widget-button\";\nimport type { SupportDestination, SupportFollow } from \"./types\";\n\n/**\n * The button a tool's destination becomes.\n *\n * One of the team's tools has worked out which page answers the question,\n * and this is the offer of it. The page changes when this is pressed and at\n * no other time, so nobody is carried off a form they were halfway through\n * by a reply they had not finished reading.\n *\n * A filled pill, because it is the one thing in a thread to press rather\n * than read, and an outline of it would sit under a bubble looking like more\n * of the bubble. The arrow leans into the press on the way. The label says\n * what happens rather than where, since the message above it has just said\n * where in the team's own words; the address goes to a screen reader, which\n * has no message above it to read from.\n */\nexport function WidgetDestination({\n destination,\n onFollow,\n}: {\n destination: SupportDestination;\n onFollow: SupportFollow;\n}) {\n return (\n <WidgetButton\n type=\"button\"\n onClick={() => onFollow(destination)}\n aria-label={`Take me to ${destination.path}`}\n // The same shape wherever it turns up. It is one button doing one\n // thing, so it hugs its label under a message and over the composer\n // alike, rather than stretching to whatever it happens to sit in.\n //\n // The side padding is the bubble's own, to the rem, so the label starts\n // exactly under the words that explain it. A pill and a bubble stacked\n // with their text on two different left edges is the thing that reads\n // as crooked.\n className=\"group/go h-auto max-w-full justify-start gap-2.5 rounded-full px-4 py-2.5 text-left text-base\"\n >\n <span className=\"min-w-0 flex-1 truncate\">Take me there</span>\n <ArrowRight\n aria-hidden\n className=\"size-4 transition-transform group-hover/go:translate-x-0.5\"\n />\n </WidgetButton>\n );\n}\n","\"use client\";\n\nimport Image from \"next/image\";\nimport { useState } from \"react\";\nimport { InitialBadge } from \"@/components/initial-badge\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * A profile photo, falling back to a letter if there is no image or it fails\n * to load. `className` lands last on both branches, so a caller can override\n * the size and the letter's type scale together.\n */\nexport function Avatar({\n src,\n name,\n sourceSize = 48,\n unoptimized = false,\n className,\n}: {\n src: string | null;\n name: string;\n /** Intrinsic pixels to request. Aim for twice the rendered size. */\n sourceSize?: number;\n /**\n * Serve the picture as it came rather than through the image optimizer.\n * For faces from somebody else's site: the optimizer only fetches hosts\n * next.config names, and a customer's picture could be on any of them.\n */\n unoptimized?: boolean;\n className?: string;\n}) {\n // Which picture failed, rather than whether one did. A plain boolean never\n // cleared itself, so one bad response left the letter in place for the life\n // of the component: switch organizations and the next face inherited the\n // last one's failure, and a picture that was fine again stayed a letter\n // until the whole page reloaded.\n const [failedSrc, setFailedSrc] = useState<string | null>(null);\n const hasFailed = failedSrc !== null && failedSrc === src;\n const shape = \"size-4.5 shrink-0 rounded-full\";\n\n if (!src || hasFailed) {\n return (\n <InitialBadge\n value={name}\n className={cn(shape, \"border-none text-[10px]\", className)}\n />\n );\n }\n\n return (\n <Image\n src={src}\n alt=\"\"\n width={sourceSize}\n height={sourceSize}\n unoptimized={unoptimized}\n onError={() => setFailedSrc(src)}\n draggable={false}\n className={cn(shape, \"bg-muted object-cover ring-1 ring-foreground/5\", className)}\n />\n );\n}\n","import { cn } from \"@/lib/utils\";\n\nconst TONES = [\n \"bg-[var(--avatar-blue,#e0edfa)] text-[var(--avatar-blue-foreground,#37628c)]\",\n \"bg-[var(--avatar-violet,#eae4f7)] text-[var(--avatar-violet-foreground,#705894)]\",\n \"bg-[var(--avatar-amber,#f8ead6)] text-[var(--avatar-amber-foreground,#8b6434)]\",\n \"bg-[var(--avatar-teal,#dbefea)] text-[var(--avatar-teal-foreground,#3c766d)]\",\n \"bg-[var(--avatar-rose,#f5e2e8)] text-[var(--avatar-rose-foreground,#92586c)]\",\n];\n\n/** A name keeps its color across lists, menus, and both themes. */\nfunction toneFor(value: string): string {\n let hash = 0;\n for (const character of value.trim().toLowerCase()) {\n hash = (hash * 31 + character.charCodeAt(0)) >>> 0;\n }\n return TONES[hash % TONES.length];\n}\n\n/**\n * A letter stand in for an avatar. Decorative: the readable name always sits\n * next to it, so this stays out of the accessibility tree.\n */\nexport function InitialBadge({\n value,\n className,\n}: {\n value: string;\n className?: string;\n}) {\n return (\n <span\n aria-hidden\n className={cn(\n // Sized to match UserAvatar, since the two sit side by side in the nav.\n \"surface-control grid size-4.5 shrink-0 place-items-center rounded border border-foreground/5 text-[10px] font-medium\",\n toneFor(value),\n className,\n )}\n >\n {value.trim().charAt(0).toUpperCase() || \"?\"}\n </span>\n );\n}\n","import { Avatar } from \"@/components/avatar\";\nimport { Sparkle } from \"@/components/icons\";\nimport { cn } from \"@/lib/utils\";\nimport type { SupportAgent } from \"./types\";\n\n/**\n * An agent's face, or a mark where the AI is concerned. Squares rather than\n * the circles the inbox uses: the widget sits on somebody else's site next to\n * their own chrome, and a rounded square reads as a product mark there instead\n * of as another person's photo.\n *\n * Sizing comes from `className` so a caller can pick one scale for the header\n * and a smaller one for a read receipt. `label` overrides the name a screen\n * reader hears, which is how a receipt says \"Seen by Median AI\" from the same\n * one element rather than announcing the name twice.\n */\nexport function WidgetAvatar({\n agent,\n label,\n className,\n}: {\n agent: SupportAgent;\n label?: string;\n className?: string;\n}) {\n const shape = \"size-7 shrink-0 rounded-lg\";\n\n // The AI wears the mark until somebody gives it a face in settings; a\n // configured picture takes over and renders like anyone else's.\n if (agent.kind === \"ai\" && !agent.avatarUrl) {\n return (\n <span\n role=\"img\"\n aria-label={label ?? agent.name}\n className={cn(\n \"grid place-items-center bg-foreground text-background\",\n shape,\n className,\n )}\n >\n <Sparkle className=\"size-[55%]\" />\n </span>\n );\n }\n\n // Wrapped rather than labelled directly, because Avatar renders a decorative\n // image and has no name of its own to override.\n return (\n <span\n role=\"img\"\n aria-label={label ?? agent.name}\n className={cn(\"inline-flex\", shape, className)}\n >\n <Avatar\n src={agent.avatarUrl}\n name={agent.name}\n className=\"size-full rounded-[inherit] bg-muted bg-none font-semibold text-muted-foreground shadow-none ring-0\"\n />\n </span>\n );\n}\n\n/**\n * Who is around, in the corner of the header. A row rather than an overlap,\n * since two or three faces read more clearly side by side at this size.\n */\nexport function WidgetAvatarStack({\n team,\n className,\n avatarClassName,\n}: {\n team: SupportAgent[];\n className?: string;\n /** Each face's size. The header's default, unless it is compact. */\n avatarClassName?: string;\n}) {\n if (team.length === 0) return null;\n\n return (\n <div className={cn(\"flex items-center gap-1.5\", className)}>\n {team.map((agent) => (\n <WidgetAvatar\n key={agent.id}\n agent={agent}\n className={avatarClassName ?? \"size-9\"}\n />\n ))}\n </div>\n );\n}\n","import type { ComponentType } from \"react\";\nimport { Bug, CaretRight, ChatCircleDots, Lightbulb } from \"@/components/icons\";\nimport { cn } from \"@/lib/utils\";\nimport { WidgetAvatar } from \"./widget-avatar\";\nimport { WidgetButton } from \"./widget-button\";\nimport type { SupportAgent } from \"./types\";\n\n/**\n * The three things people open a support widget to do, and the message each\n * row sends.\n *\n * The messages are written as something a person would actually type, since\n * that is exactly what they become: the row sends it and the agent takes it\n * from there.\n */\nconst STARTERS: {\n label: string;\n glyph: ComponentType<{ className?: string }>;\n message: string;\n}[] = [\n {\n label: \"Ask a question\",\n glyph: ChatCircleDots,\n message: \"I have a question\",\n },\n {\n label: \"Report a bug\",\n glyph: Bug,\n message: \"I want to report a bug\",\n },\n {\n label: \"Make a suggestion\",\n glyph: Lightbulb,\n message: \"I have a suggestion\",\n },\n];\n\n/**\n * A conversation with nothing in it yet.\n *\n * Who is about to answer, and three ways to start. The AI goes first because\n * it is the one that always replies; a team of only people falls back to\n * whoever is at the front.\n *\n * The invitation names the organization, because that is the whole of what\n * the agent will talk about. Saying so here is cheaper than declining later.\n *\n * The rows send rather than fill the box in. Somebody who knows what they\n * want types it; somebody who is looking at three rows wants the shortest\n * way to the first reply.\n */\nexport function WidgetEmpty({\n team,\n orgName,\n onPick,\n isSending = false,\n isWaiting = false,\n compact = false,\n}: {\n team: SupportAgent[];\n /** Whose site this is, so the invitation can say what it covers. */\n orgName?: string;\n /** Omit to leave the rows off, for a widget with nowhere to send them. */\n onPick?: (message: string) => void;\n /** Holds the rows while a message is on its way. */\n isSending?: boolean;\n /**\n * Holds them for something else the visitor has to do first, which is the\n * composer's question. The rows stay where they are while it is up rather\n * than arriving once it is answered: three rows appearing in a column that\n * centres itself would shove everything above them up the panel, and an\n * answer should not move the furniture.\n */\n isWaiting?: boolean;\n /** The denser scale. See `SupportWidget`. */\n compact?: boolean;\n}) {\n const agent = team.find((member) => member.kind === \"ai\") ?? team[0];\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col items-center justify-center gap-5 px-6 pb-2 text-center\">\n {agent && (\n <WidgetAvatar\n agent={agent}\n className={cn(\n \"animate-in rounded-2xl duration-500 ease-out fade-in-0 zoom-in-95 motion-reduce:animate-none\",\n compact ? \"size-14\" : \"size-16\",\n )}\n />\n )}\n\n {/* A beat behind the mark, so the two read as one thing settling rather\n than as a block appearing. Backwards fill holds it out of sight\n through the delay instead of showing it and taking it away. */}\n <div className=\"flex max-w-72 animate-in flex-col gap-1 delay-150 duration-500 ease-out fade-in-0 slide-in-from-bottom-2 fill-mode-backwards motion-reduce:animate-none\">\n {agent && (\n <p\n className={cn(\n \"font-semibold tracking-tight\",\n compact ? \"text-lg\" : \"text-xl\",\n )}\n >\n {agent.name}\n </p>\n )}\n <p\n className={cn(\n \"text-balance text-muted-foreground\",\n compact ? \"text-sm\" : \"text-base\",\n )}\n >\n {orgName ? `How can we help you with ${orgName}?` : \"How can we help?\"}\n </p>\n </div>\n\n {/* Held to a column the corner panel's width, so the same three rows\n sit in the middle of the modal rather than stretching across it. */}\n {onPick && (\n <div className=\"flex w-full max-w-md flex-col gap-2 pt-1\">\n {STARTERS.map((starter, index) => (\n <WidgetButton\n key={starter.label}\n type=\"button\"\n variant=\"outline\"\n size=\"xl\"\n disabled={isSending || isWaiting}\n onClick={() => onPick(starter.message)}\n // Each one a beat behind the last, so the three deal out rather\n // than land as a block. Backwards fill keeps them out of sight\n // until their turn.\n style={{ animationDelay: `${300 + index * 60}ms` }}\n className={cn(\n \"group/row h-auto w-full animate-in justify-start gap-3 text-left\",\n compact ? \"px-3.5 py-2.5 text-sm\" : \"px-4 py-3\",\n \"duration-500 ease-out fade-in-0 slide-in-from-bottom-2 fill-mode-backwards motion-reduce:animate-none\",\n \"hover:border-foreground/15 hover:shadow-sm\",\n )}\n >\n <starter.glyph\n className={cn(\n \"text-muted-foreground\",\n compact ? \"size-4\" : \"size-5\",\n )}\n />\n <span className=\"min-w-0 flex-1 truncate\">{starter.label}</span>\n <CaretRight\n aria-hidden\n className=\"size-3.5 text-muted-foreground/50 transition-transform group-hover/row:translate-x-0.5\"\n />\n </WidgetButton>\n ))}\n </div>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { ArrowsIn, ArrowsOut, X } from \"@/components/icons\";\nimport { WidgetButton } from \"./widget-button\";\nimport { cn } from \"@/lib/utils\";\nimport type { DragHandlers } from \"./use-launcher-drag\";\nimport { WidgetAvatarStack } from \"./widget-avatar\";\nimport type { SupportPresence } from \"./types\";\n\n/**\n * Who you are talking to, and the one way out. There is no way back, because\n * there is nothing behind it: a visitor gets one thread, not a queue of them.\n *\n * It is also the handle. A panel floating on somebody's page is in the way of\n * something eventually, and the strip along the top is where anyone would\n * reach for to move it. The close button and the faces stay their own: a press\n * that lands on a control belongs to that control, which the drag checks\n * before it takes hold of anything.\n */\nexport function WidgetHeader({\n presence,\n onClose,\n isExpanded = false,\n onToggleExpanded,\n grabHandlers,\n isMoving = false,\n compact = false,\n className,\n}: {\n presence: SupportPresence;\n onClose?: () => void;\n /** Standing taller than it normally does. */\n isExpanded?: boolean;\n /** Omit to hide the control, for a widget whose size is not its own. */\n onToggleExpanded?: () => void;\n /** Omit for a widget that is framed rather than floating. */\n grabHandlers?: DragHandlers;\n isMoving?: boolean;\n /** The denser scale. See `SupportWidget`. */\n compact?: boolean;\n className?: string;\n}) {\n return (\n <header\n {...grabHandlers}\n className={cn(\n \"flex shrink-0 items-start gap-3\",\n compact ? \"px-5 pt-4 pb-2\" : \"px-4 pt-4 pb-3\",\n className,\n grabHandlers && [\n // Nothing here is selectable while it is being used to drag, and the\n // browser must not claim the gesture for scrolling either. Both end\n // the same way: the drag becomes the page's instead of ours.\n \"touch-none select-none\",\n isMoving ? \"cursor-grabbing\" : \"cursor-grab\",\n ],\n )}\n >\n <div className=\"flex min-w-0 flex-1 flex-col pt-0.5\">\n <h2\n className={cn(\n \"truncate font-semibold tracking-tight\",\n compact ? \"text-base\" : \"text-lg\",\n )}\n >\n {presence.name}\n </h2>\n <p\n className={cn(\n \"truncate text-muted-foreground\",\n compact ? \"text-xs\" : \"text-sm\",\n )}\n >\n {presence.status}\n </p>\n </div>\n\n <div className=\"flex shrink-0 items-center gap-3\">\n <WidgetAvatarStack\n team={presence.team}\n avatarClassName={compact ? \"size-8\" : undefined}\n />\n\n {/* The controls sit closer to each other than to the faces, so they\n read as a pair rather than as the end of the row. */}\n <div className=\"flex items-center gap-0.5\">\n {onToggleExpanded && (\n <WidgetButton\n variant=\"ghost\"\n size=\"icon-lg\"\n onClick={onToggleExpanded}\n aria-pressed={isExpanded}\n aria-label={isExpanded ? \"Shrink the panel\" : \"Expand the panel\"}\n className=\"text-muted-foreground\"\n >\n {isExpanded ? (\n <ArrowsIn className=\"size-5\" />\n ) : (\n <ArrowsOut className=\"size-5\" />\n )}\n </WidgetButton>\n )}\n\n {onClose && (\n <WidgetButton\n variant=\"ghost\"\n size=\"icon-lg\"\n onClick={onClose}\n aria-label=\"Close support\"\n // Pulled out by the glyph's own inset, so the cross ends on the\n // same line the name starts on.\n className=\"-mr-2 text-muted-foreground\"\n >\n <X className=\"size-5\" />\n </WidgetButton>\n )}\n </div>\n </div>\n </header>\n );\n}\n","import { ShimmerLogo } from \"@/components/brand\";\n\n/**\n * Waiting on the conversation.\n *\n * One mark and nothing else. Skeleton bubbles would be guessing at a shape\n * nobody knows yet, and they always guess wrong.\n */\nexport function WidgetLoading() {\n return (\n <div className=\"grid min-h-0 flex-1 place-items-center p-6\">\n <ShimmerLogo label=\"Loading the conversation\" className=\"size-14\" />\n </div>\n );\n}\n","import { LockSimple } from \"@/components/icons\";\nimport { WidgetButton } from \"./widget-button\";\nimport { PoweredByMedian } from \"./widget-badge\";\n\n/**\n * What a visitor meets when the host only takes questions from people who are\n * signed in.\n *\n * The same shape as the empty conversation it stands in for, and it arrives\n * the same way: the mark, then the words a beat behind it, then the one thing\n * to do about it. The button is only there when the host said where their\n * sign in lives, because a button that goes nowhere is worse than none.\n */\nexport function WidgetLocked({\n orgName,\n onSignIn,\n showBadge = true,\n}: {\n /** Whose site this is, so the line can name who is waiting. */\n orgName?: string;\n onSignIn?: () => void;\n /** The line the composer usually carries, which is not here to carry it. */\n showBadge?: boolean;\n}) {\n return (\n <div className=\"flex min-h-0 flex-1 flex-col\">\n <div className=\"flex min-h-0 flex-1 flex-col items-center justify-center gap-5 px-8 text-center\">\n <span className=\"grid size-16 animate-in place-items-center rounded-2xl bg-muted duration-500 ease-out fade-in-0 zoom-in-95 motion-reduce:animate-none\">\n <LockSimple className=\"size-7 text-muted-foreground\" />\n </span>\n\n <div className=\"flex max-w-72 animate-in flex-col gap-1 delay-150 duration-500 ease-out fade-in-0 slide-in-from-bottom-2 fill-mode-backwards motion-reduce:animate-none\">\n <p className=\"text-xl font-semibold tracking-tight\">\n Sign in to chat\n </p>\n <p className=\"text-base text-balance text-muted-foreground\">\n {orgName\n ? `Ask ${orgName} anything once you are signed in.`\n : \"Sign in to contact our support team.\"}\n </p>\n </div>\n\n {onSignIn && (\n <WidgetButton\n size=\"xl\"\n onClick={onSignIn}\n className=\"animate-in delay-300 duration-500 ease-out fade-in-0 slide-in-from-bottom-2 fill-mode-backwards motion-reduce:animate-none\"\n >\n Sign in\n </WidgetButton>\n )}\n </div>\n\n {showBadge && <PoweredByMedian className=\"shrink-0 px-4 pb-4\" />}\n </div>\n );\n}\n","\"use client\";\n\nimport { useState } from \"react\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { MORPH_MS } from \"@/components/ui/morph-bubble\";\nimport { LONE_MESSAGE, type MessageGroup } from \"@/lib/message-groups\";\nimport { cn } from \"@/lib/utils\";\nimport { WidgetAttachments } from \"./widget-attachments\";\nimport { WidgetAvatar } from \"./widget-avatar\";\nimport { WidgetBubble } from \"./widget-bubble\";\nimport { WidgetDestination } from \"./widget-destination\";\nimport { WidgetMessageBody, type BodyArrival } from \"./widget-message-body\";\nimport { WidgetTyping } from \"./widget-typing\";\nimport type { SupportFollow, SupportMessage } from \"./types\";\n\n/**\n * Both bubbles keep the page's text colour and separate by surface instead.\n * The visitor's own is a step further from the panel than the agent's, which\n * tells the two apart without a slab of pure black in the light, or pure white\n * in the dark.\n *\n * Four fifths of the thread, and never wider than a line worth reading. The\n * corner panel is narrow enough that the fraction is the whole rule; the\n * modal is twice as wide, and a bubble stretched across it would be a\n * paragraph of ninety character lines. Thirty six rem is about seventy.\n */\nconst BUBBLE = \"max-w-[min(80%,36rem)] rounded-2xl px-4 py-2.5 text-base\";\n\n/** The same bubble a size down, for a panel on the denser scale. */\nconst COMPACT_BUBBLE = \"max-w-[min(80%,36rem)] rounded-2xl px-3.5 py-2 text-sm\";\n/**\n * The fallbacks are the widget's own light values, the ones its stylesheet\n * puts behind every other colour: a host page without `--muted` or\n * `--foreground` would otherwise get a bubble with no background at all.\n */\nconst VISITOR_BUBBLE =\n \"bg-[color-mix(in_oklch,var(--muted,oklch(0.955_0_0)),var(--foreground,oklch(0.145_0_0))_12%)]\";\n\n/**\n * New messages arrive rather than appear. Small on purpose: the scroller is\n * gliding down to the newest message at the same time, and a bubble\n * travelling any further than this reads as a wobble against it rather than\n * as one thing arriving. The curve is the app's, a pure deceleration, so\n * the bubble and the glide it rides in on settle the same way. Off for\n * anyone who asked.\n */\nconst ARRIVES =\n \"animate-in fade-in-0 slide-in-from-bottom-1 duration-300 ease-out-quint motion-reduce:animate-none\";\n\n/**\n * A message the visitor just sent lands rather than appears: it comes up\n * out of the composer's corner, a little further and a touch smaller to\n * start than a reply's arrival, on the same curve. The origin is the\n * bottom right, which is where the send button is, so it grows out of the\n * press that sent it. The thread is gliding up to make room at the same\n * time, and both movements decelerate the same way, so the two read as one\n * thing settling rather than two things happening. Still small: a bubble\n * that travels much further than this reads as thrown.\n */\nconst SENT =\n \"animate-in fade-in-0 slide-in-from-bottom-3 zoom-in-95 origin-bottom-right duration-400 ease-out-quint motion-reduce:animate-none\";\n\n/**\n * How far into the bubble's growth the first line starts rising. Not all of\n * it: the box eases into its last stretch, and a line that starts then is\n * still faint when the box stops, so the two read as one thing landing\n * rather than a box, a pause, and some words.\n */\nconst LADDER_DELAY_MS = Math.round(MORPH_MS * 0.7);\n\n/**\n * The break above a message, when there is one: a new day, or a conversation\n * that starts here because the one before it was resolved.\n *\n * One line either way, and never two stacked. A conversation starting is the\n * bigger of the two breaks, so it stands in for the date rather than sitting\n * under it.\n */\nexport function WidgetDivider({ message }: { message: SupportMessage }) {\n const label = message.startsConversation\n ? \"New conversation\"\n : message.dayLabel;\n if (!label) return null;\n\n return (\n <div className=\"flex items-center gap-3 py-1\">\n <span className=\"h-px flex-1 bg-border\" />\n <span className=\"text-sm text-muted-foreground\">{label}</span>\n <span className=\"h-px flex-1 bg-border\" />\n </div>\n );\n}\n\n/**\n * One message. The two sides are built differently on purpose.\n *\n * A visitor's own message is only the bubble. They know what they said and\n * when, so a name, a time and a read receipt underneath are three things that\n * earn nothing. An agent's turn gets one line above it saying who is speaking\n * and when, because you want both before you read what they said. One line per\n * turn, not per message: a reply that comes in three parts says it once.\n */\nexport function WidgetMessage({\n message,\n group = LONE_MESSAGE,\n onFollow,\n compact = false,\n isLive = false,\n}: {\n message: SupportMessage;\n /** Where it sits in a run from the same voice. Alone, by default. */\n group?: MessageGroup;\n /**\n * What the button under a message does, when one of the team's tools has\n * offered a page. Omit it and the offer is not shown at all, which is the\n * right answer for a panel with no way to move anybody.\n */\n onFollow?: SupportFollow;\n /** The denser scale. See `SupportWidget`. */\n compact?: boolean;\n /**\n * Landed while the thread was already on screen, rather than being part\n * of the history it opened with. Only a reply somebody watched arrive is\n * worth arriving a line at a time, and only a message they just sent is\n * worth seeing land.\n */\n isLive?: boolean;\n}) {\n return message.sender === \"visitor\" ? (\n <VisitorMessage message={message} compact={compact} isLive={isLive} />\n ) : (\n <AgentMessage\n message={message}\n group={group}\n onFollow={onFollow}\n compact={compact}\n isLive={isLive}\n />\n );\n}\n\n/**\n * What goes in a bubble: the files first, then what was said about them. The\n * text is skipped entirely when there is none, so a picture sent on its own\n * gets a bubble that hugs it rather than one with an empty line underneath.\n *\n * A reply that has not landed yet holds three bouncing dots in the same place\n * the words are about to be, which is what the bubble grows out of.\n */\nfunction BubbleContent({\n message,\n arrival = null,\n}: {\n message: SupportMessage;\n /** How the words come in, if they are coming in now. */\n arrival?: BodyArrival | null;\n}) {\n const hasAttachments = (message.attachments?.length ?? 0) > 0;\n\n if (message.pending) {\n return (\n <WidgetTyping\n label={`${message.agent?.name ?? \"Support\"} is writing a reply`}\n />\n );\n }\n\n return (\n <>\n {hasAttachments && (\n <WidgetAttachments attachments={message.attachments ?? []} />\n )}\n\n {message.body && (\n <WidgetMessageBody\n body={message.body}\n sender={message.sender}\n arrival={arrival}\n />\n )}\n </>\n );\n}\n\nfunction VisitorMessage({\n message,\n compact,\n isLive,\n}: {\n message: SupportMessage;\n compact: boolean;\n /** Just sent from the composer underneath, rather than read back. */\n isLive: boolean;\n}) {\n return (\n <div className={cn(\"flex justify-end\", isLive ? SENT : ARRIVES)}>\n <WidgetBubble\n morphKey=\"settled\"\n className={cn(compact ? COMPACT_BUBBLE : BUBBLE, VISITOR_BUBBLE)}\n >\n <BubbleContent message={message} />\n </WidgetBubble>\n </div>\n );\n}\n\nfunction AgentMessage({\n message,\n group,\n onFollow,\n compact,\n isLive,\n}: {\n message: SupportMessage;\n group: MessageGroup;\n onFollow?: SupportFollow;\n compact: boolean;\n isLive: boolean;\n}) {\n const { agent } = message;\n\n // Whether the words took the place of three dots, which is a bubble still\n // growing as the reply renders. Remembered rather than read, since by the\n // render that has the words the dots are already gone. Only ever set: the\n // arrival plays once, on the first render with words in it, and never\n // asks again.\n const [sawDots, setSawDots] = useState(message.pending === true);\n if (message.pending && !sawDots) setSawDots(true);\n\n // A reply that landed in front of somebody arrives a line at a time. One\n // that grew out of the dots gives the bubble most of its growth first, so\n // the lines climb into a box that is all but still.\n const arrival: BodyArrival | null =\n isLive && !message.pending && message.body.length > 0\n ? { key: message.id, delay: sawDots ? LADDER_DELAY_MS : 0 }\n : null;\n const destination =\n message.navigateTo === undefined\n ? null\n : {\n key: message.id,\n path: message.navigateTo,\n highlight: message.highlight ?? null,\n };\n\n return (\n // One line saying who and when, then the thing they said. The face used to\n // sit in a gutter beside the bubble with the name above it and the time\n // below, which spread four small things across three rows and pushed every\n // bubble in off the edge. All of it is one line now, and the bubble gets\n // the full width back.\n <div className={cn(\"flex flex-col items-start gap-1\", ARRIVES)}>\n {/* Once per turn rather than once per message: a reply that arrives in\n three parts is still one agent saying one thing. */}\n {group.isFirst && (\n <div\n className={cn(\n \"flex max-w-full items-center gap-2 px-0.5\",\n compact ? \"text-xs\" : \"text-sm\",\n )}\n >\n {agent && (\n <>\n <WidgetAvatar\n agent={agent}\n className={cn(\"rounded-md\", compact ? \"size-5\" : \"size-6\")}\n />\n <span className=\"truncate font-medium\">{agent.name}</span>\n\n {/* Said plainly, because a name and a face are exactly what a\n machine can be given, and a visitor should never have to\n work out which one they got. Once per turn, since that is\n where the name is: a burst of three says it once. */}\n {agent.kind === \"ai\" && (\n <Badge variant=\"muted\" className=\"shrink-0 rounded-md bg-muted px-1 inset-ring-0\">\n AI\n </Badge>\n )}\n </>\n )}\n\n {/* No time on a reply that has not been written yet. */}\n {!message.pending && (\n <span className=\"shrink-0 text-muted-foreground tabular-nums\">\n {message.time}\n </span>\n )}\n </div>\n )}\n\n {/* The same bubble either way. It is what the dots are in, so when the\n answer replaces them it grows out of where they were rather than\n appearing somewhere near them. */}\n <WidgetBubble\n // Derived from the contents, so an answer that arrives in pieces\n // keeps growing rather than morphing once and then snapping.\n morphKey={\n message.pending\n ? \"pending\"\n : `${message.body.length}:${message.attachments?.length ?? 0}`\n }\n className={cn(compact ? COMPACT_BUBBLE : BUBBLE, \"bg-muted\")}\n // The ladder is the arrival, so the box has nothing to hold back.\n holdContent={arrival === null}\n >\n <BubbleContent message={message} arrival={arrival} />\n </WidgetBubble>\n\n {/* Under the words that explain it, and inside the same arrival, so the\n offer lands as part of the reply rather than after it.\n\n Twice the column's gap, which is the one place that gap is wrong: it\n is set for a name sitting on its bubble, and a button that close to\n one reads as part of it rather than as something to press. Still\n well inside the space between turns, so it stays this turn's. */}\n {destination !== null && onFollow !== undefined && (\n <div className=\"pt-1.5\">\n <WidgetDestination destination={destination} onFollow={onFollow} />\n </div>\n )}\n </div>\n );\n}\n","import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Slot } from \"radix-ui\";\n\nimport { cn } from \"@/lib/utils\";\n\n// Quiet, softly edged labels. Status colors can still be supplied by callers.\nconst badgeVariants = cva(\n \"inline-flex w-fit shrink-0 items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium whitespace-nowrap [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3\",\n {\n variants: {\n variant: {\n default: \"bg-secondary text-secondary-foreground inset-ring-1 inset-ring-foreground/5\",\n muted: \"bg-muted/70 text-muted-foreground inset-ring-1 inset-ring-foreground/5\",\n outline: \"border border-border text-muted-foreground\",\n solid: \"surface-primary bg-primary text-primary-foreground\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n },\n);\n\nfunction Badge({\n className,\n variant = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"span\"> &\n VariantProps<typeof badgeVariants> & {\n asChild?: boolean;\n }) {\n const Comp = asChild ? Slot.Root : \"span\";\n\n return (\n <Comp\n data-slot=\"badge\"\n data-variant={variant}\n className={cn(badgeVariants({ variant, className }))}\n {...props}\n />\n );\n}\n\nexport { Badge, badgeVariants };\n","\"use client\";\n\nimport { useLayoutEffect, useRef, type ReactNode } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * How long the box takes to travel. `--duration-medium`, which is what the\n * palette's list resizes on too. Exported for whatever waits inside it to\n * start once there is room: see the widget's line by line arrival.\n */\nexport const MORPH_MS = 350;\n/**\n * Matches --ease-in-out. A resize wants easing at both ends: leaving at\n * speed reads as a jump with a tail, and arriving at speed reads as a snap.\n * No spring either, since an overshoot carries the box past its final size\n * and back, and a box that clips its contents makes that read as the text\n * being cut off and restored.\n */\nconst MORPH_EASING = \"ease-in-out\";\n\n/** How long the new contents take to appear once the box has room for them. */\nconst CONTENT_FADE_MS = 140;\n\n/** Sub pixel differences are not worth an animation. */\nconst EPSILON = 1;\n\nfunction prefersReducedMotion(): boolean {\n if (typeof window === \"undefined\") return false;\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n}\n\n/**\n * Hand the contents back to the layout: no held width, no held opacity.\n *\n * Also what has to happen before measuring, since a box still pinned by the\n * last morph measures at the size that morph was holding it at.\n */\nfunction release(inner: HTMLElement) {\n inner.style.width = \"\";\n inner.style.boxSizing = \"\";\n inner.style.opacity = \"\";\n}\n\n/**\n * Hold the contents at the width they just laid out at, in full precision.\n *\n * The precision is the whole point. `offsetWidth` is rounded to a whole\n * pixel, and a bubble that sizes itself to its own text sits on a fractional\n * width far more often than not, so half the time that rounding goes down and\n * pins the text a hair narrower than the width it measured at. That is enough\n * to push the last word of a line onto the next one. It happens under the\n * pin, where it is hidden, so the box travels to the height it measured\n * before the wrap and the words spring back a line the moment the pin comes\n * off. The computed width is the used width, fractions and all, and it is\n * layout space rather than screen space, so an ancestor mid animation cannot\n * skew it either.\n *\n * Held as a content box so the number means the same thing going back in as\n * it did coming out, whatever padding the contents carry.\n */\nfunction pin(inner: HTMLElement, hold: boolean) {\n const width = getComputedStyle(inner).width;\n inner.style.boxSizing = \"content-box\";\n inner.style.width = width;\n if (hold) inner.style.opacity = \"0\";\n}\n\n/**\n * A bubble that grows into its new contents rather than snapping to them.\n *\n * `morphKey` is what it watches. Change it and the bubble measures where it\n * was, where it now is, and travels between the two. That is what turns three\n * bouncing dots into an answer: same element, same node, a different size.\n *\n * The contents wait, and they wait for the whole morph. React puts the new\n * text in at its full size the moment it arrives, so without this it is\n * already sitting there while the box is still growing, and the box, which\n * clips, wipes it into view a sliver at a time. Fading it in partway through\n * only moves the problem: a multi line reply is taller than the box until the\n * very end, so it fades in already cut off. The box travels first and the\n * words arrive in it once there is room for all of them.\n *\n * They are also pinned to the width they just laid out at for the duration.\n * Without that the text re-wraps on every frame while the box around it is\n * still moving, which looks far worse than no animation at all.\n */\nexport function MorphBubble({\n morphKey,\n holdContent = true,\n className,\n contentClassName,\n children,\n}: {\n /** Derive it from the content, so any change to that content morphs. */\n morphKey: string;\n /**\n * Keep the new contents out of sight until the box has finished growing,\n * then fade them in. Off for contents that bring an arrival of their own,\n * which is the widget's reply climbing in a line at a time: the box still\n * pins their width while it travels, and shows them as they come.\n */\n holdContent?: boolean;\n className?: string;\n contentClassName?: string;\n children: ReactNode;\n}) {\n const box = useRef<HTMLDivElement>(null);\n const content = useRef<HTMLDivElement>(null);\n const previous = useRef<{ width: number; height: number } | null>(null);\n const running = useRef<Animation[]>([]);\n\n useLayoutEffect(() => {\n const outer = box.current;\n const inner = content.current;\n if (!outer || !inner) return;\n\n // Where the box is right now, mid travel included, so a change that lands\n // during a morph carries on from what is on screen rather than from where\n // that morph happened to start.\n const from = running.current.length\n ? { width: outer.offsetWidth, height: outer.offsetHeight }\n : previous.current;\n\n // Everything the last morph was holding comes off before this one\n // measures, or \"the natural size of the new contents\" is really just the\n // size the last one was pinning them at.\n const interrupted = running.current;\n running.current = [];\n release(inner);\n for (const animation of interrupted) animation.cancel();\n\n const next = { width: outer.offsetWidth, height: outer.offsetHeight };\n previous.current = next;\n\n // Nothing to travel from on the first paint, and nothing to travel to if\n // the new contents happen to be the same size.\n if (!from) return;\n if (\n Math.abs(from.width - next.width) < EPSILON &&\n Math.abs(from.height - next.height) < EPSILON\n ) {\n return;\n }\n if (prefersReducedMotion()) return;\n\n // Set before the browser paints, so the new contents are never on screen\n // at full size for even one frame while the box is still small.\n pin(inner, holdContent);\n\n const size = outer.animate(\n [\n { width: `${from.width}px`, height: `${from.height}px` },\n { width: `${next.width}px`, height: `${next.height}px` },\n ],\n { duration: MORPH_MS, easing: MORPH_EASING },\n );\n\n // Contents arriving on their own only need the width held while the box\n // moves, and get it back the moment it stops.\n if (!holdContent) {\n running.current = [size];\n size.finished\n .catch(() => {})\n .finally(() => {\n if (!running.current.includes(size)) return;\n running.current = [];\n release(inner);\n });\n return;\n }\n // Filled both ways on purpose. Backwards holds it invisible through the\n // delay, while the box is still moving. Forwards is what stops it\n // blinking at the other end: the inline opacity above is what hides it\n // before the animation starts, and without a forwards fill the finished\n // animation stops applying and that inline zero shows through again for\n // a frame before the cleanup below can clear it.\n const reveal = inner.animate([{ opacity: 0 }, { opacity: 1 }], {\n duration: CONTENT_FADE_MS,\n delay: MORPH_MS,\n easing: \"ease-out\",\n fill: \"both\",\n });\n\n running.current = [size, reveal];\n\n reveal.finished\n .catch(() => {})\n .finally(() => {\n if (!running.current.includes(reveal)) return;\n running.current = [];\n // Drop the inline hold first and only then the animation holding it\n // open, so there is never a frame where neither is keeping it up.\n release(inner);\n reveal.cancel();\n });\n // `holdContent` is read here and settled per arrival on purpose: a key\n // that stays put has nothing to morph, whatever the hold says now.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [morphKey]);\n\n return (\n <div ref={box} className={cn(\"overflow-hidden\", className)}>\n <div ref={content} className={contentClassName}>\n {children}\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport { useState, type ReactNode } from \"react\";\nimport { File, ImageSquare, Play } from \"@/components/icons\";\nimport { cn } from \"@/lib/utils\";\nimport type { SupportAttachment } from \"./types\";\n\n/** Tiles sit on the panel colour so they read as objects inside the bubble. */\nconst SURFACE = \"rounded-xl border border-foreground/10 bg-card/70\";\n\n/**\n * Opens the file, for anything that has somewhere to be opened.\n *\n * A new tab, because this is running on somebody else's page and a visitor\n * halfway through asking for help should not lose it. No `download`: the file\n * is served from another origin, where browsers ignore that attribute, so it\n * would promise a save and quietly do something else.\n *\n * Falls through to the plain contents when there is no URL, which is a file\n * still on its way or one whose link could not be signed. Better a tile that\n * does nothing than a link that goes nowhere.\n */\nfunction OpenAttachment({\n attachment,\n className,\n children,\n}: {\n attachment: SupportAttachment;\n className?: string;\n children: ReactNode;\n}) {\n if (!attachment.url) return <>{children}</>;\n\n return (\n <a\n href={attachment.url}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n aria-label={`Open ${attachment.name}`}\n className={cn(\n \"block rounded-xl transition-opacity outline-none hover:opacity-80\",\n \"focus-visible:ring-3 focus-visible:ring-ring/50\",\n className,\n )}\n >\n {children}\n </a>\n );\n}\n\nfunction MediaTile({\n attachment,\n className,\n}: {\n attachment: SupportAttachment;\n className?: string;\n}) {\n const [hasFailed, setHasFailed] = useState(false);\n\n // A preview that will not load says so in place, rather than leaving a grey\n // rectangle to puzzle over.\n if (!attachment.previewUrl || hasFailed) {\n return (\n <div\n className={cn(\n SURFACE,\n \"flex flex-col items-center justify-center gap-1 px-2 text-center\",\n className,\n )}\n >\n <ImageSquare className=\"size-6 text-muted-foreground\" />\n <p className=\"text-sm text-muted-foreground\">Preview unavailable</p>\n </div>\n );\n }\n\n return (\n <figure className={cn(SURFACE, \"relative overflow-hidden\", className)}>\n {/* A plain img, not the framework's: a freshly picked file is an object\n URL that has not left the browser, so there is nothing for an image\n pipeline to do, and this file also ships in the npm package where\n there is no framework to ask. */}\n {/* eslint-disable-next-line @next/next/no-img-element -- an object URL or signed link, nothing for the optimizer to do, and the npm bundle has no next/image. */}\n <img\n src={attachment.previewUrl}\n alt={attachment.name}\n width={640}\n height={480}\n onError={() => setHasFailed(true)}\n draggable={false}\n className=\"size-full object-cover\"\n />\n\n {attachment.kind === \"video\" && (\n <span\n aria-hidden\n className=\"absolute inset-0 grid place-items-center bg-foreground/15\"\n >\n <span className=\"grid size-10 place-items-center rounded-full bg-background/85 shadow-sm\">\n <Play className=\"size-4 translate-x-px\" />\n </span>\n </span>\n )}\n </figure>\n );\n}\n\nfunction FileRow({ attachment }: { attachment: SupportAttachment }) {\n return (\n <OpenAttachment attachment={attachment}>\n <div className={cn(SURFACE, \"flex items-center gap-2.5 px-2.5 py-2\")}>\n <File className=\"size-5 shrink-0 text-muted-foreground\" />\n\n <div className=\"flex min-w-0 flex-1 flex-col text-left\">\n <p className=\"truncate text-sm font-medium\">{attachment.name}</p>\n <p className=\"truncate text-sm text-muted-foreground\">\n {attachment.size}\n </p>\n </div>\n </div>\n </OpenAttachment>\n );\n}\n\n/**\n * Everything hanging off a message. Pictures and clips get a tile, anything\n * else gets a named row.\n *\n * Tiles go shorter once they share a row, so a pair reads as a pair rather than\n * two letterboxes stacked in a box.\n */\nexport function WidgetAttachments({\n attachments,\n}: {\n attachments: SupportAttachment[];\n}) {\n const media = attachments.filter((one) => one.kind !== \"file\");\n const files = attachments.filter((one) => one.kind === \"file\");\n\n return (\n <div className=\"flex w-64 max-w-full flex-col gap-2\">\n {media.length > 0 && (\n <div className={cn(\"grid gap-2\", media.length > 1 && \"grid-cols-2\")}>\n {media.map((attachment) => (\n <OpenAttachment\n key={attachment.id}\n attachment={attachment}\n className={media.length > 1 ? \"h-24\" : \"h-36\"}\n >\n {/* Opens in a tab rather than a lightbox. The inbox has one\n because a teammate is working through a queue; a visitor is\n looking at a picture they sent a minute ago. */}\n <MediaTile attachment={attachment} className=\"size-full\" />\n </OpenAttachment>\n ))}\n </div>\n )}\n\n {files.map((attachment) => (\n <FileRow key={attachment.id} attachment={attachment} />\n ))}\n </div>\n );\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { MorphBubble } from \"@/components/ui/morph-bubble\";\n\n/**\n * The widget's bubble. The morph itself is shared with the inbox, so both\n * sides of a conversation grow into a reply the same way; this only fixes\n * how the contents stack inside it.\n */\nexport function WidgetBubble({\n morphKey,\n holdContent,\n className,\n children,\n}: {\n morphKey: string;\n /** See `MorphBubble`. Off while the reply is arriving a line at a time. */\n holdContent?: boolean;\n className?: string;\n children: ReactNode;\n}) {\n return (\n <MorphBubble\n morphKey={morphKey}\n holdContent={holdContent}\n className={className}\n contentClassName=\"flex flex-col gap-2\"\n >\n {children}\n </MorphBubble>\n );\n}\n","import { Suspense } from \"react\";\nimport { LineReveal } from \"@/components/ui/line-reveal\";\nimport { MessageMarkdown, useMarkdownReady } from \"./markdown-chunk\";\nimport type { SupportSender } from \"./types\";\n\n/**\n * How a reply's words arrive, for one that lands while somebody is watching.\n *\n * `key` is what the arrival hangs on: the same key holds the words still\n * through every render after. `delay` is how long the first line waits,\n * which is the time the bubble around it spends growing into the room the\n * lines need.\n */\nexport type BodyArrival = { key: string; delay: number };\n\n/**\n * What was said.\n *\n * The agent's side renders markdown, because the house voice writes it: a\n * bold word, a short list, a line of code. The visitor's side is quoted\n * verbatim, asterisks and all, since nothing they type is markup.\n *\n * A reply that lands while the panel is open arrives a line at a time, each\n * rising on its own beat. The bubble around it is already growing, so the\n * ladder waits for it: the box travels first, and the words climb into it\n * once there is room for all of them. History, and anything the visitor\n * wrote themselves, sits still.\n */\nexport function WidgetMessageBody({\n body,\n sender,\n arrival = null,\n}: {\n body: string;\n sender: SupportSender;\n /** Null for words that were already here, which is most of them. */\n arrival?: BodyArrival | null;\n}) {\n // False until the renderer has been fetched. See `markdown-chunk.tsx` for\n // when that happens and why it is not simply imported.\n const isReady = useMarkdownReady();\n\n const plain = <p className=\"break-words whitespace-pre-line\">{body}</p>;\n if (sender === \"visitor\") return plain;\n\n // The same paragraph again as the fallback, so a render that beats the\n // module here by a frame shows the words rather than a gap.\n const words = isReady ? (\n <Suspense fallback={plain}>\n <MessageMarkdown body={body} />\n </Suspense>\n ) : (\n plain\n );\n if (arrival === null) return words;\n\n return (\n <LineReveal contentKey={arrival.key} delay={arrival.delay}>\n {words}\n </LineReveal>\n );\n}\n","\"use client\";\n\nimport { useLayoutEffect, useRef, useState, type ReactNode } from \"react\";\nimport { useMediaQuery } from \"@/hooks/use-media-query\";\nimport { durationMs } from \"@/lib/motion\";\nimport { cn } from \"@/lib/utils\";\n\n/** Past this, extra lines share the last beat so the wait cannot outrun reading. */\nconst LAST_BEAT = 11;\n\ntype Slice = {\n top: number;\n height: number;\n};\n\ntype Delay = number | `--${string}`;\n\n/** One arrival: what it is of, how long it waited, and whether it is still on. */\ntype Play = {\n key: string;\n delay: Delay;\n isPlaying: boolean;\n};\n\n/**\n * Unique line tops inside a laid-out reply, then gapless windows from one\n * top to the next. A wrapped paragraph is several lines; a code block is\n * one object. That is the difference between a line arriving and a chunk.\n */\nfunction lineSlices(root: HTMLElement): Slice[] {\n const host = root.getBoundingClientRect();\n if (host.height < 1) return [];\n\n const tops: number[] = [];\n const skip = \"pre, table, img, hr, [data-streamdown='code-block']\";\n\n for (const block of root.querySelectorAll(skip)) {\n const box = block.getBoundingClientRect();\n if (box.height < 1) continue;\n tops.push(box.top - host.top);\n }\n\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {\n acceptNode(node) {\n if (node.textContent?.trim() === \"\") return NodeFilter.FILTER_REJECT;\n const parent = node.parentElement;\n if (parent !== null && parent.closest(skip) !== null) {\n return NodeFilter.FILTER_REJECT;\n }\n return NodeFilter.FILTER_ACCEPT;\n },\n });\n\n const range = document.createRange();\n let node = walker.nextNode();\n while (node !== null) {\n range.selectNodeContents(node);\n for (const box of range.getClientRects()) {\n if (box.height < 1 || box.width < 1) continue;\n tops.push(box.top - host.top);\n }\n node = walker.nextNode();\n }\n\n if (tops.length === 0) {\n return [{ top: 0, height: host.height }];\n }\n\n const unique = [...new Set(tops.map((top) => Math.round(top)))].sort(\n (a, b) => a - b,\n );\n\n return unique.map((top, index) => {\n const next = unique[index + 1] ?? Math.ceil(host.height);\n return { top, height: Math.max(1, next - top) };\n });\n}\n\n/**\n * Everything past the last beat rises together anyway, so it rides in one\n * window instead of one each. An essay costs the same twelve copies of the\n * reply that a paragraph does, which is what keeps a long answer from\n * arriving as a stall.\n */\nfunction beats(slices: Slice[]): Slice[] {\n if (slices.length <= LAST_BEAT + 1) return slices;\n\n const rest = slices.slice(LAST_BEAT);\n const last = rest[rest.length - 1];\n return [\n ...slices.slice(0, LAST_BEAT),\n { top: rest[0].top, height: last.top + last.height - rest[0].top },\n ];\n}\n\n/**\n * How long the arrival runs, read off the stage rather than the document:\n * inside the support widget on a host's page the tokens live on the\n * widget's own root, and the stage inherits them from there.\n */\nfunction playMs(stage: HTMLElement, delay: number): number {\n const rise = durationMs(\n \"--duration-line-rise\",\n durationMs(\"--duration-very-slow\", 500, stage),\n stage,\n );\n const beat = durationMs(\"--duration-line-beat\", 70, stage);\n // A frame's grace, so the real reply takes over after the last line has\n // landed rather than a tick inside its own arrival.\n return delay + rise + LAST_BEAT * beat + 32;\n}\n\nfunction paintSlices(\n source: HTMLElement,\n stage: HTMLElement,\n delay: number,\n): boolean {\n const slices = beats(lineSlices(source));\n if (slices.length === 0) return false;\n\n // Built off the page and hung in one go. The palette's list is watched by\n // cmdk and by the selection pill, and appending a window at a time gives\n // them a mutation each to answer with a fresh read of the layout.\n const batch = document.createDocumentFragment();\n for (const [index, slice] of slices.entries()) {\n const frame = document.createElement(\"div\");\n frame.className = \"t-line-slice\";\n frame.style.top = `${slice.top}px`;\n frame.style.height = `${slice.height}px`;\n frame.style.setProperty(\"--t-line\", String(index));\n\n const clone = source.cloneNode(true) as HTMLElement;\n clone.style.marginTop = `-${slice.top}px`;\n frame.appendChild(clone);\n batch.appendChild(frame);\n }\n stage.style.setProperty(\"--t-line-delay\", `${delay}ms`);\n stage.replaceChildren(batch);\n return true;\n}\n\n/**\n * A finished answer, read top to bottom: each visual line rises on its own\n * beat, then the real reply takes over so links and the reader land on one\n * tree rather than a stack of copies.\n */\nexport function LineReveal({\n contentKey,\n delay = 0,\n children,\n className,\n}: {\n /** Change this and the arrival plays again. Same key holds still. */\n contentKey: string;\n /**\n * How long the first line waits, in milliseconds or a duration token. For a\n * reply whose bubble is still growing into the room the lines need: the\n * ladder starts once the box has stopped. Read as the arrival starts, so\n * a change afterwards does not replay it.\n */\n delay?: Delay;\n children: ReactNode;\n className?: string;\n}) {\n const sourceRef = useRef<HTMLDivElement>(null);\n const stageRef = useRef<HTMLDivElement>(null);\n const reduce = useMediaQuery(\"(prefers-reduced-motion: reduce)\");\n // The delay rides with the key it was given for, so the one that starts\n // an arrival is the one it keeps.\n const [play, setPlay] = useState<Play>({\n key: contentKey,\n delay,\n isPlaying: !reduce,\n });\n if (play.key !== contentKey) {\n setPlay({ key: contentKey, delay, isPlaying: !reduce });\n }\n const { key: playKey, delay: hold, isPlaying } = play;\n\n useLayoutEffect(() => {\n if (reduce || !isPlaying) return;\n\n const source = sourceRef.current;\n const stage = stageRef.current;\n if (source === null || stage === null) return;\n\n const wait = typeof hold === \"number\" ? hold : durationMs(hold, 0, stage);\n\n let painted = false;\n let timer = 0;\n\n const end = () => setPlay((now) => ({ ...now, isPlaying: false }));\n\n const paint = () => {\n if (painted) return;\n if (!paintSlices(source, stage, wait)) return;\n painted = true;\n timer = window.setTimeout(end, playMs(stage, wait));\n // Nothing else to wait for. Left watching, it would answer the\n // arrival's own reflows with work nobody is going to look at.\n observer.disconnect();\n };\n\n const observer = new ResizeObserver(paint);\n paint();\n observer.observe(source);\n const fallback = window.setTimeout(() => {\n if (!painted) end();\n }, 120);\n\n return () => {\n observer.disconnect();\n window.clearTimeout(timer);\n window.clearTimeout(fallback);\n stage.replaceChildren();\n };\n }, [playKey, hold, isPlaying, reduce]);\n\n const showSlices = isPlaying && !reduce;\n\n return (\n <div className={cn(\"relative\", className)}>\n <div className={showSlices ? \"pointer-events-none opacity-0\" : undefined}>\n <div ref={sourceRef}>{children}</div>\n </div>\n {showSlices && (\n <div\n ref={stageRef}\n aria-hidden\n className=\"absolute inset-0 overflow-hidden\"\n />\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { useSyncExternalStore } from \"react\";\n\n/**\n * Whether a media query currently matches.\n *\n * `ssrValue` is what the server (and the first paint) answer, so a query that\n * is only true on a phone must default to false: the desktop layout is the\n * one the HTML is built for, and a phone hydrates into its own.\n */\nexport function useMediaQuery(query: string, ssrValue = false): boolean {\n return useSyncExternalStore(\n (onChange) => {\n const media = window.matchMedia(query);\n media.addEventListener(\"change\", onChange);\n return () => media.removeEventListener(\"change\", onChange);\n },\n () => window.matchMedia(query).matches,\n () => ssrValue,\n );\n}\n\n/**\n * Tailwind's `sm` breakpoint, written out: a phone is narrower than 40rem.\n *\n * The server answers false, matching every `max-sm:` class that hides until\n * the client knows the width.\n */\nexport function usePhone(): boolean {\n return useMediaQuery(\"(max-width: 39.999rem)\");\n}\n\n/**\n * Tailwind's `lg` breakpoint. The server answers true, so a three-pane\n * desktop layout does not collapse in the HTML and then reopen.\n */\nexport function useLgUp(): boolean {\n return useMediaQuery(\"(min-width: 64rem)\", true);\n}\n\n/**\n * Tailwind's `md` breakpoint, where the sidebar opens out beside the page.\n * The server answers true, matching every `md:` class that shows until the\n * client knows the width.\n */\nexport function useMdUp(): boolean {\n return useMediaQuery(\"(min-width: 48rem)\", true);\n}\n","/**\n * The motion scale, read from the page rather than written down twice.\n *\n * `motion.css` declares every duration in milliseconds, but a custom\n * property computes to whatever the build chose to print, and the minifier\n * prints the shorter of the two: `80ms` stays, `350ms` becomes `.35s`. So\n * the unit has to be read. Parsing the number alone turns a third of a\n * second into a third of a millisecond, and whatever was waiting on it\n * stops waiting at all.\n */\nexport function durationMs(\n token: string,\n fallback: number,\n /**\n * Where to read it from. The page's root by default; the widget passes one\n * of its own nodes, because on a host's page its tokens are scoped to the\n * widget's roots and the document's root has never heard of them.\n */\n from: Element = document.documentElement,\n): number {\n const raw = getComputedStyle(from).getPropertyValue(token).trim();\n const value = Number.parseFloat(raw);\n if (Number.isNaN(value)) return fallback;\n if (raw.endsWith(\"ms\")) return value;\n if (raw.endsWith(\"s\")) return value * 1000;\n return fallback;\n}\n","/** Offsets, so the bounce travels along the row instead of all three hopping together. */\nconst DOTS = [0, 160, 320];\n\n/**\n * An agent mid sentence. It lives inside the reply's own bubble rather than in\n * a bubble of its own, which is what lets the bubble stay put and grow into the\n * answer when it arrives.\n */\nexport function WidgetTyping({ label }: { label: string }) {\n return (\n <span className=\"flex items-center gap-1.5 py-2\">\n <span className=\"sr-only\">{label}</span>\n\n {DOTS.map((delay) => (\n <span\n key={delay}\n aria-hidden\n style={{ animationDelay: `${delay}ms` }}\n className=\"size-2 animate-typing-dot rounded-full bg-foreground/45 motion-reduce:animate-none\"\n />\n ))}\n </span>\n );\n}\n","/**\n * The rules the support side of Median follows: how long a message can be,\n * what an agent can be called, what an API key looks like.\n *\n * Deliberately pure, like shared/organizations.ts: no Convex imports, so the\n * web app can import this (`@median/backend/support`) and complain in the same\n * words the mutations would.\n */\n\n/** What an organization's AI agent is called until someone renames it. */\nexport const DEFAULT_AGENT_NAME = \"Median AI\";\n\n/**\n * Whether the agent may end a conversation on its own: resolving one it\n * finished, or closing one that was only ever spam.\n *\n * On unless a team says otherwise. A queue is only useful if what is left in\n * it is what somebody still has to look at, and a thread the agent answered\n * end to end is not one of those. Turning it off means every conversation\n * waits for a person to end it, which is the right call for a team that\n * reads all of them anyway.\n */\nexport const DEFAULT_AUTO_RESOLVE = true;\n\n/**\n * Whether resolved conversations get read for knowledge base suggestions.\n * On by default: the suggestions only ever land in a review queue, so the\n * cost of being wrong is a dismissal, not a bad answer.\n */\nexport const DEFAULT_LEARN_FROM_THREADS = true;\n\n/**\n * Whether the live agent may file knowledge and tool suggestions from the\n * middle of a conversation, the moment something teaches it or stumps it.\n * On by default for the same reason the reader is: everything lands in a\n * review queue, so the cost of a bad one is a dismissal.\n */\nexport const DEFAULT_SUGGEST_KNOWLEDGE = true;\n\n/**\n * Whether the nightly sweep may resolve this organization's finished\n * threads. On by default for the same reason auto-resolve is: an inbox\n * that closes what is clearly done is the behavior a team expects.\n */\nexport const DEFAULT_NIGHTLY_SWEEP = true;\n\n/**\n * Whether the agent may file a bug or a suggestion when a customer hands one\n * over. On by default: a signal is a note in a list a person reads, not a\n * change to anything, and a complaint nobody wrote down is a complaint the\n * team never hears.\n */\nexport const DEFAULT_FILE_SIGNALS = true;\n\n/**\n * Whether the widget asks for an email before a visitor's first message. On\n * by default: an anonymous thread cannot be followed up, and half of support\n * is getting back to somebody. A host can waive it in code, and this is the\n * team's side of the same switch.\n */\nexport const DEFAULT_REQUIRE_EMAIL = true;\n\n/**\n * Whether the closed widget shows the first lines of a reply nobody has read\n * yet. On by default: a count on the button says something arrived, and the\n * card is what says whether it is worth opening. A host can waive it in\n * code, and this is the team's side of the same switch.\n */\nexport const DEFAULT_MESSAGE_PREVIEW = true;\n\nexport const AGENT_NAME_MIN_LENGTH = 2;\nexport const AGENT_NAME_MAX_LENGTH = 40;\nexport const PERSONALITY_MAX_LENGTH = 2000;\n\nexport const MESSAGE_MAX_LENGTH = 4000;\n\n/**\n * How many live Discord ticket channels the bot's subscription carries. A\n * full list means there may be more, and the bot asks about the rest one\n * channel at a time.\n */\nexport const DISCORD_LIVE_CHANNEL_LIMIT = 500;\n\nexport const VISITOR_NAME_MAX_LENGTH = 80;\nexport const VISITOR_EMAIL_MAX_LENGTH = 320;\nexport const VISITOR_AVATAR_URL_MAX_LENGTH = 512;\n/** Extra facts a host attaches to a visitor. Small on purpose. */\nexport const VISITOR_METADATA_MAX_ENTRIES = 16;\nexport const VISITOR_METADATA_MAX_LENGTH = 200;\n\n/** A message can carry a few files, not a filesystem. */\nexport const MESSAGE_MAX_ATTACHMENTS = 6;\nexport const ATTACHMENT_MAX_BYTES = 25 * 1024 * 1024;\n\n/**\n * What a visitor makes of a finished conversation: stars, and whatever they\n * wanted to say about it. Both sides check the score, since the widget draws\n * exactly this many stars and the backend takes nothing else.\n */\nexport const RATING_MIN_STARS = 1;\nexport const RATING_MAX_STARS = 5;\nexport const RATING_FEEDBACK_MAX_LENGTH = 1000;\n\n/** Whether a score is one of the stars actually offered. */\nexport function isRatingStars(stars: number): boolean {\n return (\n Number.isInteger(stars) &&\n stars >= RATING_MIN_STARS &&\n stars <= RATING_MAX_STARS\n );\n}\n\n/** Their words, trimmed and capped. Empty means they had none. */\nexport function cleanFeedback(feedback: string): string {\n return feedback.trim().slice(0, RATING_FEEDBACK_MAX_LENGTH);\n}\n\n/**\n * Publishable, like a Stripe pk_ or an Intercom app id: it ships in the\n * browser on the host's site, so it grants nothing beyond opening a support\n * conversation with that organization.\n */\nexport const API_KEY_PREFIX = \"median_pk_\";\n\n/**\n * The only credential a new Median integration needs. The public id is part\n * of the value so server-side SDKs can safely hand the widget its\n * `median_pk_...` identifier without another environment variable. The\n * secret tail never leaves the server.\n */\nexport const MEDIAN_KEY_PREFIX = \"median_key_\";\n\n/** Purpose-separated signing material is derived from the Median key. */\nexport const MEDIAN_SIGNATURE_PREFIX = \"median_sig_\";\n\nexport const MEDIAN_KEY_PUBLIC_ID_LENGTH = 32;\nexport const MEDIAN_KEY_SECRET_LENGTH = 64;\n\n/** Enough separate credentials for production, staging, and local work. */\nexport const MAX_MEDIAN_KEYS_PER_ORG = 10;\nexport const MEDIAN_KEY_NAME_MAX_LENGTH = 40;\n\n/**\n * The other kind, and the opposite of the one above: this one never goes near\n * a browser. It lives on the host's server, where it signs the id of whoever\n * is signed in, and anybody holding it can claim to be any of their users.\n */\nexport const SIGNING_KEY_PREFIX = \"median_sk_\";\n\n/**\n * What the REST API takes. Secret like the signing key: it lives on the\n * host's server and can read and write their whole inbox's visitor side, so\n * shipping one in a browser is handing the room key to the lobby.\n */\nexport const SECRET_API_KEY_PREFIX = \"median_api_\";\n\n/**\n * What webhook deliveries are signed with, one per endpoint. Held by us and\n * by the receiver, so the receiver can tell our POST from anybody else's.\n */\nexport const WEBHOOK_SECRET_PREFIX = \"median_whsec_\";\n\n/**\n * What tool endpoint calls are signed with, one per organization. Held by us\n * and by the host's server, so their handler can tell our POST from anybody\n * else's. The `@mediansh/agent-tools` package mirrors this literal in\n * `src/handler.ts`; change them together.\n */\nexport const TOOL_SECRET_PREFIX = \"median_toolsec_\";\n\n/**\n * What every prefix above starts with, and what it started with when the\n * product was called Portal.\n *\n * A key issued then is still a key. The rename went all the way to the wire\n * for anything minted from here on, but nothing already in somebody's\n * environment was invalidated by it, so every prefix has a twin and one rule\n * makes it. Read through `legacyPrefix` rather than writing `portal_` out\n * again: there is one family to retire, and this is the only line that names\n * it.\n */\nconst FAMILY = \"median_\";\nconst LEGACY_FAMILY = \"portal_\";\n\n/** The same prefix under the old name. */\nexport function legacyPrefix(prefix: string): string {\n return prefix.startsWith(FAMILY)\n ? `${LEGACY_FAMILY}${prefix.slice(FAMILY.length)}`\n : prefix;\n}\n\n/** Whether a credential carries this prefix under either name. */\nexport function hasPrefix(value: string, prefix: string): boolean {\n return value.startsWith(prefix) || value.startsWith(legacyPrefix(prefix));\n}\n\n/**\n * The prefix a credential actually carries, so anything derived from it\n * comes out in the family it belongs to. A legacy key's signing material\n * was derived as `portal_sig_` and is held that way, and re-deriving it as\n * `median_sig_` would stop it matching.\n */\nexport function prefixCarriedBy(value: string, prefix: string): string | null {\n if (value.startsWith(prefix)) return prefix;\n const legacy = legacyPrefix(prefix);\n return value.startsWith(legacy) ? legacy : null;\n}\n\n/**\n * The bounds on a session token, here rather than in the widget so the REST\n * API and the SDK refuse the same strings in the same words.\n */\nexport const SESSION_ID_MIN_LENGTH = 8;\nexport const SESSION_ID_MAX_LENGTH = 128;\n\n/**\n * A host's own id for a person. Long enough for a UUID or a Stripe style id\n * and short enough that nobody is putting a document in it.\n */\nexport const EXTERNAL_ID_MAX_LENGTH = 128;\n\n/**\n * How long a typing signal counts as live. Composers refresh it well inside\n * this while keys are hit, so anything older is a browser that went away.\n */\nexport const TYPING_TTL_MS = 12_000;\n\n/**\n * The bounds on a snooze. When it comes back is worked out on the client,\n * where the clock and the timezone are, so these only catch a number that\n * cannot have been meant: one already gone, or one so far out that nobody was\n * ever going to see the conversation again.\n */\nexport const SNOOZE_MIN_MS = 60_000;\nexport const SNOOZE_MAX_MS = 365 * 24 * 60 * 60_000;\n\n/** What is wrong with this snooze, in a sentence, or null if nothing is. */\nexport function snoozeIssue(until: number, now: number): string | null {\n if (!Number.isFinite(until)) return \"Choose a valid date and time.\";\n if (until - now < SNOOZE_MIN_MS) {\n return \"Choose a time at least one minute from now.\";\n }\n if (until - now > SNOOZE_MAX_MS) {\n return \"Conversations can be snoozed for up to a year.\";\n }\n return null;\n}\n\n/**\n * A visitor's picture, or null if what we were handed cannot be one.\n *\n * https only. It ends up in an `<img>` on the inbox, and anything else is\n * either blocked as mixed content or was never a picture in the first place.\n * Whatever a host sends is theirs, so it is checked rather than trusted.\n */\nexport function cleanAvatarUrl(input: string | undefined): string | null {\n const url = input?.trim();\n if (!url || url.length > VISITOR_AVATAR_URL_MAX_LENGTH) return null;\n\n try {\n return new URL(url).protocol === \"https:\" ? url : null;\n } catch {\n return null;\n }\n}\n\n/**\n * An address, or null if what we were handed cannot be one.\n *\n * Deliberately barely a check: the shape of an address is the only thing\n * anyone can tell without sending mail to it. It is here because the agent\n * writes these down from whatever a customer typed, and \"looks like an\n * address\" should be decided in one place rather than at each caller.\n */\nexport function cleanEmail(input: string | undefined): string | null {\n const email = input?.trim();\n if (!email || email.length > VISITOR_EMAIL_MAX_LENGTH) return null;\n\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email) ? email : null;\n}\n\n/**\n * What to call a customer wherever a name is printed. Their name when we have\n * one, then their address, since a directory of \"Customer #12\"s all reads\n * the same and an address at least says who. Then the number they were\n * handed on arrival, so an anonymous customer is one person everywhere\n * rather than one of a crowd of anonymous visitors. The last fallback is for\n * rows written before numbers existed.\n */\nexport function customerDisplayName(\n name: string | null | undefined,\n customerNumber: number | null | undefined,\n email?: string | null,\n): string {\n if (name) return name;\n if (email) return email;\n if (customerNumber) return `Customer #${customerNumber}`;\n return \"Anonymous visitor\";\n}\n\n/** What a thread id starts with when the workspace's name gives no letter. */\nconst THREAD_PREFIX_FALLBACK = \"T\";\n\n/**\n * The letter a workspace's thread ids start with: the first letter or digit\n * of its name, in capitals. \"Whirl\" numbers its threads W-1, W-2. A name\n * that opens with neither, all punctuation or an emoji, falls back to T.\n */\nexport function threadPrefixOf(orgName: string): string {\n const first = /[a-z0-9]/i.exec(orgName);\n return first === null\n ? THREAD_PREFIX_FALLBACK\n : first[0].toLocaleUpperCase(\"en-US\");\n}\n\n/**\n * What a thread is called out loud: W-12, the workspace's initial and the\n * number it was handed on arrival. Null for a row that has no number, which\n * is only one written before numbers existed and not yet backfilled.\n */\nexport function threadIdOf(\n orgName: string,\n threadNumber: number | null | undefined,\n): string | null {\n if (threadNumber === null || threadNumber === undefined) return null;\n return `${threadPrefixOf(orgName)}-${threadNumber}`;\n}\n\n/** Collapses the whitespace someone pasted in without changing their words. */\nexport function normalizeAgentName(input: string): string {\n return input.replace(/\\s+/g, \" \").trim();\n}\n\n/** What is wrong with this agent name, in a sentence, or null if nothing is. */\nexport function agentNameIssue(name: string): string | null {\n if (name.length === 0) return \"Enter an agent name.\";\n if (name.length < AGENT_NAME_MIN_LENGTH) {\n return `Use at least ${AGENT_NAME_MIN_LENGTH} characters for the agent name.`;\n }\n if (name.length > AGENT_NAME_MAX_LENGTH) {\n return `Use ${AGENT_NAME_MAX_LENGTH} characters or fewer for the agent name.`;\n }\n return null;\n}\n\n/** What is wrong with this personality, or null. Empty is fine: quiet is a personality. */\nexport function personalityIssue(personality: string): string | null {\n if (personality.length > PERSONALITY_MAX_LENGTH) {\n return `Keep personality instructions to ${PERSONALITY_MAX_LENGTH} characters or fewer.`;\n }\n return null;\n}\n\n/**\n * File types an attachment may not be, whatever it says it is.\n *\n * Storage serves a file back under the type it was stored with, on its own\n * host under the same registrable domain as the app. A browser opening one of\n * these runs it there: a teammate clicking what a stranger called `invoice.pdf`\n * would land on a real Median subdomain running somebody else's page.\n *\n * A refusal list rather than an allow list, because support is where people\n * send whatever they have. Logs, archives, dumps and video are all somebody\n * trying to explain a problem; only the handful of types a browser treats as\n * a document to run have to be turned away.\n *\n * Short on purpose, and it costs something. An exported HTML error report and\n * a diagram saved as SVG are both ordinary evidence, and both are turned away\n * here, because a browser opening either one runs it. Plain XML stays: a\n * config file or a SOAP body is read, not run.\n */\nexport const UNSAFE_ATTACHMENT_TYPES = [\n \"text/html\",\n \"application/xhtml+xml\",\n \"image/svg+xml\",\n \"text/xsl\",\n \"application/xslt+xml\",\n];\n\n/** Whether a content type is one a browser would run rather than show. */\nexport function isUnsafeAttachmentType(contentType: string): boolean {\n const bare = contentType.split(\";\")[0].trim().toLowerCase();\n return UNSAFE_ATTACHMENT_TYPES.includes(bare);\n}\n\n/**\n * The session id namespaces the channels own.\n *\n * A browser mints a random token for itself, but an email or a Discord\n * account is not a browser: those doors derive a session from the address or\n * the account so that everything a person writes from it lands on one\n * visitor. Derived means workable out, and an address is not a secret.\n *\n * What makes them safe is that nothing on the outside may present one.\n * `cleanSessionId` refuses the whole namespace, so an id in it can only ever\n * have come from the ingest that made it.\n */\nexport const CHANNEL_SESSION_PREFIXES = [\"email:\", \"discord:\"] as const;\n\n/** Whether a session token belongs to a channel rather than to a browser. */\nexport function isChannelSessionId(sessionId: string): boolean {\n const lowered = sessionId.toLowerCase();\n return CHANNEL_SESSION_PREFIXES.some((prefix) => lowered.startsWith(prefix));\n}\n","\"use client\";\n\nimport {\n RATING_FEEDBACK_MAX_LENGTH,\n RATING_MAX_STARS,\n} from \"@median/backend/support\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { Star } from \"@/components/icons\";\nimport { WidgetButton } from \"./widget-button\";\nimport { Collapse, COLLAPSE_MS } from \"@/components/ui/collapse\";\nimport { WIDGET_TEXT_FIELD_SHELL as TEXT_FIELD_SHELL } from \"./widget-field\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { cn } from \"@/lib/utils\";\nimport type { SupportRating } from \"./types\";\n\nconst STARS = Array.from({ length: RATING_MAX_STARS }, (_, index) => index + 1);\n\n/** How long the thank you stays before the card folds itself away. */\nconst THANKS_MS = 1400;\n\n/** Between one star lighting up and the next, so a pick reads as a sweep. */\nconst STAR_STAGGER_MS = 40;\n\n/**\n * The question a resolved conversation asks, sitting on the composer.\n *\n * A star saves the moment it is pressed rather than waiting for a Send,\n * because most people stop there and four stars with nothing after them is\n * still the answer. The box for words opens underneath afterwards, so\n * somebody who has more to say has somewhere to say it and nobody is held up\n * by a form they did not want.\n *\n * Nothing here appears or vanishes. The card opens out of the composer, the\n * box for words opens under the stars, the thank you takes the question's\n * place, and the whole thing folds away when it is finished, each of them the\n * same height travelling. Which is why leaving tells the panel it is done\n * only once it has finished folding: unmounting on the click would take the\n * animation with it.\n *\n * Above the composer rather than in place of it: the thread is finished, not\n * closed, and writing again is how a visitor starts the next one.\n */\nexport function WidgetRating({ rating }: { rating: SupportRating }) {\n const [isOpen, setIsOpen] = useState(false);\n const [picked, setPicked] = useState<number | null>(rating.stars);\n const [hovered, setHovered] = useState<number | null>(null);\n const [feedback, setFeedback] = useState(\"\");\n const [isSending, setIsSending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [isDone, setIsDone] = useState(false);\n\n const timers = useRef<ReturnType<typeof setTimeout>[]>([]);\n useEffect(() => {\n // Shut for one frame, open on the next, which is what turns a card that\n // was suddenly there into one that grew out of the composer.\n const frame = requestAnimationFrame(() => setIsOpen(true));\n const pending = timers.current;\n return () => {\n cancelAnimationFrame(frame);\n for (const timer of pending) clearTimeout(timer);\n };\n }, []);\n\n function later(ms: number, run: () => void) {\n timers.current.push(setTimeout(run, ms));\n }\n\n /** Fold away first, and only then tell the panel there is nothing to ask. */\n function leave() {\n setIsOpen(false);\n later(COLLAPSE_MS, rating.skip);\n }\n\n const shown = hovered ?? picked ?? 0;\n\n async function send(stars: number, words?: string) {\n setError(null);\n setIsSending(true);\n try {\n await rating.submit(stars, words);\n return true;\n } catch (caught) {\n console.error(\"Rating the conversation failed\", caught);\n setError(\"Could not submit your rating. Try again.\");\n return false;\n } finally {\n setIsSending(false);\n }\n }\n\n async function choose(stars: number) {\n // On screen first: the star they pressed is the answer whether or not\n // the network agrees yet.\n setPicked(stars);\n await send(stars);\n }\n\n /**\n * Finishing, with or without words. The stars are already saved, so an\n * empty box is somebody who has said all they wanted to and does not owe\n * anybody a sentence about it: no round trip, straight to the thanks.\n */\n async function finish() {\n if (picked === null) return;\n const words = feedback.trim();\n if (words.length > 0 && !(await send(picked, words))) return;\n\n setIsDone(true);\n later(THANKS_MS, leave);\n }\n\n return (\n <Collapse isOpen={isOpen} className=\"shrink-0\">\n {/* The spacing lives inside each section rather than as a gap between\n them, so a section that is closed takes up nothing at all. */}\n <div className=\"mx-4 mb-3 rounded-xl border bg-muted/40 px-4 dark:bg-muted/20\">\n <Collapse isOpen={!isDone}>\n <div className=\"py-3\">\n <div className=\"flex items-start justify-between gap-2\">\n <p className=\"text-base font-medium\">\n {picked === null ? \"How did we do?\" : \"Anything we could improve?\"}\n </p>\n {/* A word rather than a cross: this is a question, and the way\n out of a question is an answer, not a close button. */}\n <WidgetButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"-mt-0.5 -mr-1.5 text-muted-foreground\"\n disabled={isSending}\n onClick={leave}\n >\n Skip\n </WidgetButton>\n </div>\n\n <div\n role=\"radiogroup\"\n aria-label=\"How did we do?\"\n className=\"flex items-center justify-center gap-1.5 pt-2\"\n onMouseLeave={() => setHovered(null)}\n >\n {STARS.map((value) => (\n <button\n key={value}\n type=\"button\"\n role=\"radio\"\n aria-checked={picked === value}\n aria-label={`${value} of ${RATING_MAX_STARS}`}\n disabled={isSending}\n onMouseEnter={() => setHovered(value)}\n onFocus={() => setHovered(value)}\n onBlur={() => setHovered(null)}\n onClick={() => void choose(value)}\n className={cn(\n \"rounded-md p-1 outline-none transition-transform duration-150 ease-out\",\n \"hover:scale-115 focus-visible:ring-3 focus-visible:ring-ring/50\",\n \"active:scale-95 disabled:pointer-events-none\",\n \"motion-reduce:transition-none\",\n )}\n >\n <Star\n // A pick lights the row up one star at a time, left to\n // right. Only a pick: a stagger under the pointer would\n // read as the widget lagging behind the mouse.\n style={{\n transitionDelay:\n hovered === null && picked !== null\n ? `${(value - 1) * STAR_STAGGER_MS}ms`\n : undefined,\n }}\n className={cn(\n \"size-7 transition-colors duration-200 ease-out motion-reduce:transition-none\",\n value <= shown\n ? \"text-foreground\"\n : \"text-muted-foreground/30\",\n )}\n />\n </button>\n ))}\n </div>\n\n <Collapse isOpen={picked !== null}>\n <div className=\"flex flex-col gap-2 pt-2\">\n <Textarea\n value={feedback}\n onChange={(event) => setFeedback(event.target.value)}\n placeholder=\"Add a comment (optional)\"\n aria-label=\"Comment (optional)\"\n rows={2}\n maxLength={RATING_FEEDBACK_MAX_LENGTH}\n disabled={isSending}\n // The field's own shell, over the card's tint rather than in\n // it, so the box to write in reads as a box to write in.\n className={cn(\n TEXT_FIELD_SHELL,\n \"max-h-28 min-h-20 bg-card px-3 py-2.5 text-base\",\n )}\n />\n {/* Never held shut by an empty box. The words are the optional\n half of this, and a button that refuses to finish without\n them is a required field in a hat. */}\n <WidgetButton\n type=\"button\"\n className=\"self-end\"\n disabled={isSending}\n onClick={() => void finish()}\n >\n {isSending\n ? \"Sending\"\n : feedback.trim().length === 0\n ? \"Done\"\n : \"Send\"}\n </WidgetButton>\n </div>\n </Collapse>\n\n <Collapse isOpen={error !== null}>\n <p className=\"pt-2 text-center text-sm text-destructive\">\n {error}\n </p>\n </Collapse>\n </div>\n </Collapse>\n\n <Collapse isOpen={isDone}>\n <p className=\"py-3.5 text-center text-base font-medium\">\n Thanks for the feedback.\n </p>\n </Collapse>\n </div>\n </Collapse>\n );\n}\n","import { UserCircle } from \"@/components/icons\";\n\n/**\n * A quiet line about where the conversation stands, rather than about the\n * last thing said in it.\n *\n * Above the composer rather than under the header, for the same reason the\n * inbox puts its own status line there: the question it settles is who writes\n * the next reply, and that belongs next to the box you would write it in.\n *\n * Not a live region. The handoff arrives as a message too, in a thread that\n * already announces itself, and a screen reader should hear that once.\n *\n * No arrival of its own: the panel folds it open and shut.\n */\nexport function WidgetAwaitingHuman() {\n return (\n <p className=\"flex items-center justify-center gap-1.5 px-4 pb-1.5 text-sm text-muted-foreground\">\n <UserCircle aria-hidden className=\"size-4\" />\n Waiting for the support team\n </p>\n );\n}\n","\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n useSyncExternalStore,\n type PointerEvent as ReactPointerEvent,\n type RefObject,\n} from \"react\";\nimport { flushSync } from \"react-dom\";\nimport {\n getDefaultSpot,\n getSpot,\n metricsFor,\n setSpot,\n spotFrom,\n spotFromPanel,\n subscribeSpot,\n type Bounds,\n type Size,\n} from \"./launcher-position\";\nimport { prefersReducedMotion } from \"./motion\";\n\n/** Further than this and it is a drag. Under it, it is a click that wobbled. */\nconst DRAG_THRESHOLD = 5;\n\n/**\n * What a press takes hold of: the button while it is closed, and the panel\n * itself, by its header, once it is open.\n *\n * Both move the same box. The difference is what is being kept on the page\n * while it travels, and what the spot is read back off when it lands.\n */\nexport type DragSubject = \"button\" | \"panel\";\n\n/** What a surface wears to become something the widget can be dragged by. */\nexport type DragHandlers = {\n onPointerDown: (event: ReactPointerEvent<HTMLElement>) => void;\n onPointerMove: (event: ReactPointerEvent<HTMLElement>) => void;\n onPointerUp: (event: ReactPointerEvent<HTMLElement>) => void;\n onPointerCancel: (event: ReactPointerEvent<HTMLElement>) => void;\n};\n\n/**\n * A press that landed on a control belongs to that control.\n *\n * Only the panel needs asking. The button is the handle and the whole of it,\n * so a press anywhere on it is a press on the thing being dragged; the header\n * is a strip with a close button and a row of faces on it, and grabbing the\n * panel by the close button would be a drag that shuts it.\n */\nfunction isControl(target: EventTarget | null): boolean {\n return (\n target instanceof Element &&\n target.closest(\"a, button, input, select, textarea\") !== null\n );\n}\n\n/** The flight to the edge once it is let go. Matches --ease-spring. */\nconst SNAP_MS = 420;\nconst SNAP_EASING = \"cubic-bezier(0.34, 1.45, 0.64, 1)\";\n\n/**\n * The button's height inside the box, which the clip and the button both read.\n * A custom property because it inherits: one write on the box reaches the\n * panel, the shadow and the button, and none of them has to be told twice.\n */\nexport const BUTTON_Y = \"--median-button-y\";\n\n/** Everything the drag needs, measured once, while it is still standing still. */\ntype Grip = {\n subject: DragSubject;\n grabX: number;\n grabY: number;\n startX: number;\n startY: number;\n bounds: Bounds;\n margin: number;\n /** The button, which the spot is always defined in terms of. */\n size: Size;\n box: Size;\n /** The one actually travelling: the button, or the whole box. */\n dragged: Size;\n /** Where that one was before any of this, and where it is now. */\n resting: { x: number; y: number };\n at: { x: number; y: number };\n moved: boolean;\n};\n\n/**\n * Dragging the launcher, and the edge it settles against.\n *\n * The box that holds the panel is positioned to contain the button, and the\n * button is pinned inside it, so moving one moves both. The closed panel is a\n * circle sitting exactly under the button; if only the button travelled it\n * would slide off its own shadow.\n *\n * Nothing goes through React state while a finger is down. The offset is\n * written straight onto the box, because a `setState` per pointermove renders\n * the whole panel underneath, and the panel is an entire conversation. React\n * hears the spot once, at the end.\n *\n * The resting geometry cannot be written in CSS, because the button's height\n * inside the box is the difference between two things measured against\n * different boxes. So it is measured and written, before paint, and the style\n * carries a plain calc as a fallback for the frame before that happens, which\n * is the one the server rendered.\n */\nexport function useLauncherDrag({\n boxRef,\n buttonRef,\n margin,\n enabled,\n subject,\n}: {\n boxRef: RefObject<HTMLDivElement | null>;\n buttonRef: RefObject<HTMLButtonElement | null>;\n /** Any CSS length. How far off the edges it sits. */\n margin: string;\n /** Off when there is nothing on the page to take hold of. */\n enabled: boolean;\n /** Which of the two a press is grabbing, which is to say whether it is open. */\n subject: DragSubject;\n}) {\n const spot = useSyncExternalStore(subscribeSpot, getSpot, getDefaultSpot);\n\n // Two renders a drag, at the ends of it. Never one per move. It stays true\n // through the flight as well, because the clip is keyed to the button's\n // height and that changes as it lands; transitioning between the two would\n // wipe the panel across the box on the way.\n const [isMoving, setIsMoving] = useState(false);\n\n const grip = useRef<Grip | null>(null);\n /** Set by the drag that just ended, read by the click it turns into. */\n const justDragged = useRef(false);\n\n /** The space it lives in: the page, or whatever is positioning it. */\n const measure = useCallback((): {\n bounds: Bounds;\n margin: number;\n size: Size;\n box: Size;\n } | null => {\n const box = boxRef.current;\n const button = buttonRef.current;\n if (!box) return null;\n\n const parent = box.offsetParent;\n const rect =\n parent instanceof HTMLElement\n ? parent.getBoundingClientRect()\n : new DOMRect(0, 0, window.innerWidth, window.innerHeight);\n const boxRect = box.getBoundingClientRect();\n\n return {\n bounds: {\n left: rect.left,\n top: rect.top,\n width: rect.width,\n height: rect.height,\n },\n // Asked of the resolved style rather than parsed out of the string we\n // were handed, so a margin in any unit at all comes back in pixels.\n margin: Math.round(\n Number.parseFloat(getComputedStyle(box).getPropertyValue(spot.side)) ||\n 0,\n ),\n // Offset sizes, so a button mid press is not measured through whatever\n // scale it happens to be wearing.\n //\n // No button at all is a widget the host opens themselves. Nothing\n // hangs below the panel then, so it is a button of no size: the same\n // arithmetic, with the box parked against the bottom margin.\n size: button\n ? { width: button.offsetWidth, height: button.offsetHeight }\n : { width: 0, height: 0 },\n // The box gets the fractional one instead, and the difference is the\n // whole of whether a resize is smooth. Its top is worked out as its\n // bottom edge minus its height, so a height rounded to a whole pixel\n // against a box that is mid animation puts the bottom edge up to half a\n // pixel out, in a different direction on every frame. The panel shimmers\n // in place rather than growing. Nothing scales the box, so a rect is as\n // safe here as an offset and says the same thing more precisely.\n box: { width: boxRect.width, height: boxRect.height },\n };\n }, [boxRef, buttonRef, spot.side]);\n\n /**\n * Writes the resting geometry onto the nodes. Before paint on the way in,\n * and again whenever the space it is measured against changes.\n */\n const place = useCallback(() => {\n const box = boxRef.current;\n const taken = measure();\n if (!box || taken === null) return;\n\n const metrics = metricsFor(\n spot,\n taken.bounds,\n taken.size,\n taken.box.height,\n taken.margin,\n );\n\n box.style.top = `${metrics.boxTop - taken.bounds.top}px`;\n box.style.setProperty(BUTTON_Y, `${metrics.buttonInBox}px`);\n }, [boxRef, measure, spot]);\n\n useLayoutEffect(place, [place]);\n\n useEffect(() => {\n window.addEventListener(\"resize\", place);\n return () => window.removeEventListener(\"resize\", place);\n }, [place]);\n\n // Its own size counts as the space changing too, and this is the one that\n // moves on its own. The box hangs upward from the button, so where its top\n // belongs is worked out from how tall it is, and a panel growing into its\n // expanded height has a different answer on every frame of the way there.\n // Left to the effect above it would get the final answer on the first frame,\n // and the panel would jump off the button and then grow back down onto it.\n useEffect(() => {\n const box = boxRef.current;\n if (!box || typeof ResizeObserver === \"undefined\") return;\n\n // Nothing here changes the box's size, so this cannot feed itself.\n const observer = new ResizeObserver(place);\n observer.observe(box);\n return () => observer.disconnect();\n }, [boxRef, place]);\n\n /** Puts the box back where `place` left it, with no animation. */\n const settle = useCallback(() => {\n const box = boxRef.current;\n if (box) box.style.transform = \"\";\n }, [boxRef]);\n\n /**\n * The page while something is being dragged across it: nothing selectable,\n * and the closed hand everywhere.\n *\n * Both belong on the body rather than on what is being held, because the\n * pointer spends the whole drag over everything else. Left to the handle's\n * own classes the cursor turns back into a caret the moment it crosses the\n * thread, and the browser reads the gesture as a sweep through the host's\n * text, which leaves the next press picking up that selection instead of\n * the widget.\n *\n * Written and cleared in one place because these outlive the widget: a drag\n * that ended without this running leaves somebody else's page unselectable\n * and wearing a cursor that is not theirs.\n */\n const holdPointer = useCallback((held: boolean) => {\n document.body.style.userSelect = held ? \"none\" : \"\";\n document.body.style.cursor = held ? \"grabbing\" : \"\";\n if (held) window.getSelection()?.removeAllRanges();\n }, []);\n\n const handlePointerDown = useCallback(\n (event: ReactPointerEvent<HTMLElement>) => {\n justDragged.current = false;\n\n // A press starts from nothing held. If the last one never heard its own\n // release, this is where the page gets its cursor and its selection\n // back, rather than the next drag inheriting them.\n if (grip.current !== null) {\n grip.current = null;\n holdPointer(false);\n setIsMoving(false);\n }\n\n if (!enabled || !event.isPrimary) return;\n // A right click is a menu, not a move.\n if (event.pointerType === \"mouse\" && event.button !== 0) return;\n if (subject === \"panel\" && isControl(event.target)) return;\n\n // The button when it is closed, the whole box when it is open. Either\n // way the box is what travels; this is only what is being aimed.\n const moving = subject === \"panel\" ? boxRef.current : buttonRef.current;\n const taken = measure();\n if (moving === null || taken === null) return;\n\n // Refused so the press cannot begin a selection, or pick up one already\n // lying around and drag that instead. Focus is handed back by hand,\n // since that was the other thing the default was going to do.\n event.preventDefault();\n // Only the button takes it. An open panel is being moved, not entered,\n // so whatever was half typed in the composer keeps the caret.\n if (subject === \"button\") buttonRef.current?.focus({ preventScroll: true });\n\n const rect = moving.getBoundingClientRect();\n grip.current = {\n subject,\n grabX: event.clientX - rect.left,\n grabY: event.clientY - rect.top,\n startX: event.clientX,\n startY: event.clientY,\n ...taken,\n dragged: subject === \"panel\" ? taken.box : taken.size,\n resting: { x: rect.left, y: rect.top },\n at: { x: rect.left, y: rect.top },\n moved: false,\n };\n\n event.currentTarget.setPointerCapture(event.pointerId);\n },\n [boxRef, buttonRef, enabled, holdPointer, measure, subject],\n );\n\n const handlePointerMove = useCallback(\n (event: ReactPointerEvent<HTMLElement>) => {\n const held = grip.current;\n const box = boxRef.current;\n if (held === null || box === null) return;\n\n if (!held.moved) {\n const travelled = Math.hypot(\n event.clientX - held.startX,\n event.clientY - held.startY,\n );\n if (travelled < DRAG_THRESHOLD) return;\n held.moved = true;\n holdPointer(true);\n setIsMoving(true);\n }\n\n // Kept inside the edges it can settle against. Arithmetic only:\n // everything it needs was measured on the way down.\n held.at = {\n x: clamp(\n event.clientX - held.grabX,\n held.bounds.left + held.margin,\n held.bounds.left +\n held.bounds.width -\n held.margin -\n held.dragged.width,\n ),\n y: clamp(\n event.clientY - held.grabY,\n held.bounds.top + held.margin,\n held.bounds.top +\n held.bounds.height -\n held.margin -\n held.dragged.height,\n ),\n };\n\n box.style.transform = `translate3d(${held.at.x - held.resting.x}px, ${held.at.y - held.resting.y}px, 0)`;\n },\n [boxRef, holdPointer],\n );\n\n /**\n * The end of a drag, wherever the news of it came from.\n *\n * Separate from the handler because a release is not reliably an event on\n * the thing that was grabbed. Pointer capture usually delivers it there, but\n * a capture lost to the window manager, a switch to another window mid\n * press, or an element that went away underneath one all end the gesture\n * with nothing firing on the handle at all. Every one of those used to leave\n * the page holding a grabbing cursor and nothing selectable, with no way\n * back except starting another drag.\n *\n * Nothing to end is not a problem to report: the flight home runs after the\n * grip is let go, and the same release arrives twice on the way out.\n */\n const finishDrag = useCallback(() => {\n const held = grip.current;\n if (held === null) return;\n grip.current = null;\n\n holdPointer(false);\n\n const box = boxRef.current;\n if (!held.moved || box === null) {\n setIsMoving(false);\n return;\n }\n\n justDragged.current = true;\n\n // Where it landed, read back as a spot. Which end of the widget that is\n // asked of is the whole of the difference between the two drags.\n const target =\n held.subject === \"panel\"\n ? spotFromPanel(held.at, held.bounds, held.size, held.box, held.margin)\n : spotFrom(held.at, held.bounds, held.size, held.margin);\n\n const home = metricsFor(\n target,\n held.bounds,\n held.size,\n held.box.height,\n held.margin,\n );\n // Where the thing that travelled belongs once it is there, so the flight\n // below is exactly the gap between that and the finger.\n const from = {\n x:\n held.at.x -\n (target.side === \"left\"\n ? held.bounds.left + held.margin\n : held.bounds.left +\n held.bounds.width -\n held.margin -\n held.dragged.width),\n y: held.at.y - (held.subject === \"panel\" ? home.boxTop : home.buttonY),\n };\n\n // All of this is one frame, and the order is the whole trick.\n //\n // Committing the spot synchronously runs the layout effect above with it,\n // so the box is already positioned where it is going before anything is\n // painted. Clearing the transform then puts it there, which is not where\n // the finger left it, and the animation starts from exactly the gap\n // between the two. The first frame drawn still has whatever was being\n // held under the finger, and the rest of them carry it home.\n flushSync(() => setSpot(target));\n settle();\n\n if (prefersReducedMotion()) {\n setIsMoving(false);\n return;\n }\n\n const flight = box.animate(\n [\n { transform: `translate3d(${from.x}px, ${from.y}px, 0)` },\n { transform: \"none\" },\n ],\n { duration: SNAP_MS, easing: SNAP_EASING },\n );\n // Only once it has landed: the clip must not start transitioning between\n // heights while one of them is still travelling.\n flight.finished.catch(() => {}).finally(() => setIsMoving(false));\n }, [boxRef, holdPointer, settle]);\n\n const handlePointerUp = useCallback(\n (event: ReactPointerEvent<HTMLElement>) => {\n if (event.currentTarget.hasPointerCapture(event.pointerId)) {\n event.currentTarget.releasePointerCapture(event.pointerId);\n }\n finishDrag();\n },\n [finishDrag],\n );\n\n // The safety net, live only while something is actually being dragged. A\n // release anywhere at all ends it, and so does the window losing focus,\n // which is the one that catches a press abandoned to another app.\n useEffect(() => {\n if (!isMoving) return;\n\n const end = () => finishDrag();\n window.addEventListener(\"pointerup\", end);\n window.addEventListener(\"pointercancel\", end);\n window.addEventListener(\"blur\", end);\n return () => {\n window.removeEventListener(\"pointerup\", end);\n window.removeEventListener(\"pointercancel\", end);\n window.removeEventListener(\"blur\", end);\n };\n }, [isMoving, finishDrag]);\n\n const dragHandlers: DragHandlers = {\n onPointerDown: handlePointerDown,\n onPointerMove: handlePointerMove,\n onPointerUp: handlePointerUp,\n onPointerCancel: handlePointerUp,\n };\n\n // Everything above was measured against a page that is no longer that size.\n // Nothing to rescue: it goes back to wherever `place` puts it.\n useEffect(() => {\n function abandon() {\n if (grip.current === null) return;\n grip.current = null;\n holdPointer(false);\n settle();\n setIsMoving(false);\n }\n\n window.addEventListener(\"resize\", abandon);\n return () => {\n window.removeEventListener(\"resize\", abandon);\n // A widget that goes away mid drag must not leave the host's whole page\n // unselectable and wearing a cursor it never asked for. This is their\n // body, not ours.\n holdPointer(false);\n };\n }, [holdPointer, settle]);\n\n return {\n side: spot.side,\n /** Being dragged, or on its way to an edge. Nothing may transition. */\n isMoving,\n /** The side only. Height is measured and written straight to the node. */\n style: { [spot.side]: margin } as { left?: string; right?: string },\n /** True for the click that ended a drag, so it does not also open. */\n swallowClick: () => {\n if (!justDragged.current) return false;\n justDragged.current = false;\n return true;\n },\n /**\n * The same four handlers whichever end is being held. Both ends can wear\n * them at once: only one of the two is ever reachable, since the button is\n * inert while the panel is up and the panel is inert while it is down.\n */\n dragHandlers,\n };\n}\n\nfunction clamp(value: number, low: number, high: number): number {\n return Math.min(Math.max(value, low), Math.max(low, high));\n}\n","\"use client\";\n\nimport { useLayoutEffect, useState, type RefObject } from \"react\";\n\n/**\n * How much room the card needs above the button before it goes there.\n * Roughly its own height plus the gap, with a little to spare.\n */\nconst ROOM_ABOVE_PX = 128;\n\nexport type TeaserPlacement = \"above\" | \"below\";\n\n/**\n * Which side of the button the card hangs off.\n *\n * Above, nearly always: the box the widget lives in hangs upward from the\n * button, so above is where the room is. The exception is a button somebody\n * dragged to the top of the window, where the box is pinned under the top\n * margin and the button sits at its top edge with nothing over it. There\n * the card goes below instead.\n *\n * Measured off the button's own offset inside the box rather than worked\n * out from the spot, since the box is clamped in ways the spot alone does\n * not say. Measured again whenever a drag settles, which is the only time\n * the answer can change short of a resize.\n */\nexport function useTeaserPlacement({\n buttonRef,\n enabled,\n isMoving,\n}: {\n buttonRef: RefObject<HTMLElement | null>;\n /** Whether there is a card to place. Nothing is measured otherwise. */\n enabled: boolean;\n /** A drag is on. The answer waits for it to land. */\n isMoving: boolean;\n}): TeaserPlacement {\n const [placement, setPlacement] = useState<TeaserPlacement>(\"above\");\n\n useLayoutEffect(() => {\n if (!enabled || isMoving) return;\n\n const measure = () => {\n const button = buttonRef.current;\n if (button === null) return;\n setPlacement(button.offsetTop < ROOM_ABOVE_PX ? \"below\" : \"above\");\n };\n\n measure();\n window.addEventListener(\"resize\", measure);\n return () => window.removeEventListener(\"resize\", measure);\n }, [buttonRef, enabled, isMoving]);\n\n return placement;\n}\n","\"use client\";\n\nimport {\n useLayoutEffect,\n useState,\n useSyncExternalStore,\n type RefObject,\n} from \"react\";\nimport { usePageLock } from \"./use-page-lock\";\n\n/**\n * When the panel stops floating and takes the screen.\n *\n * Tailwind's `sm` breakpoint, written out, so this hook and any `max-sm:`\n * styles riding along with it change their minds at the same width.\n */\nconst PHONE = \"(max-width: 40rem)\";\n\n/**\n * How long the panel keeps following the visual viewport after it last heard\n * from it.\n *\n * The keyboard takes about a quarter of a second to come up and reports its\n * progress two or three times on the way, so a panel that moved only when it\n * was told would arrive in two or three jumps. This keeps it measuring every\n * frame for a beat afterwards instead, which is what turns those jumps into\n * the panel riding the keyboard up.\n */\nconst SETTLE_MS = 400;\n\n/**\n * How much of the visible height has to go missing before it is the keyboard\n * and not the browser's own chrome sliding away.\n */\nconst KEYBOARD_PX = 96;\n\nfunction subscribePhone(onChange: () => void) {\n const media = window.matchMedia(PHONE);\n media.addEventListener(\"change\", onChange);\n return () => media.removeEventListener(\"change\", onChange);\n}\n\nconst isPhoneNow = () => window.matchMedia(PHONE).matches;\n\n/** The server's answer. Nothing is open on the server, so nothing is lost. */\nconst isPhoneOnServer = () => false;\n\n/**\n * Whether the open panel should be the whole screen rather than a floating\n * card, and everything that has to be true while it is.\n *\n * A phone has no room for a card floating over a page: the card is nearly the\n * page already, and the margins around it are the only thing the extra rem\n * buys. So under `sm` the panel opens edge to edge instead, the way every\n * sheet on a phone does.\n *\n * Only when the widget is the viewport's, though. The gallery pins its\n * previews inside a frame with `absolute`, and a preview that burst out of\n * its frame to swallow the page would be demonstrating exactly the wrong\n * thing. `offsetParent` is the tell: a `fixed` box answers null, a framed one\n * answers with its frame. An ancestor wearing a transform also answers, and\n * should: a box positioned against somebody's transformed container is not\n * positioned against the screen, so it has no business covering it.\n *\n * While the sheet is up this also does two jobs the floating panel never\n * needed:\n *\n * - The panel is sized to `visualViewport` rather than to `inset-0`, and keeps\n * measuring for a beat after each report so it rides the keyboard up instead\n * of arriving in two jumps. The keyboard shrinks the visual viewport and not\n * the layout one, so a panel pinned to the layout viewport keeps its composer\n * underneath the keyboard, which is the one place the thing being typed into\n * cannot be. Written straight onto the node, the way the drag writes its\n * travel: a render per frame of that would run the whole conversation through\n * React to move an edge.\n *\n * - The host's page is pinned where it stands, by `usePageLock`. A full screen\n * sheet with a live page rumbling underneath it reads as broken, and a page\n * the keyboard is free to scroll takes the sheet's own footing with it.\n */\nexport function useWidgetSheet({\n boxRef,\n panelRef,\n isOpen,\n}: {\n boxRef: RefObject<HTMLDivElement | null>;\n panelRef: RefObject<HTMLDivElement | null>;\n isOpen: boolean;\n}): boolean {\n const isPhone = useSyncExternalStore(\n subscribePhone,\n isPhoneNow,\n isPhoneOnServer,\n );\n\n // Asked once the box is on the page, and asked again if the viewport\n // changed, since that is the only thing here that changes the answer's\n // consequences. The layout effect answers before anything is painted.\n const [isFramed, setIsFramed] = useState(false);\n useLayoutEffect(() => {\n setIsFramed(boxRef.current?.offsetParent != null);\n }, [boxRef, isPhone]);\n\n const isSheet = isPhone && !isFramed;\n const isUp = isSheet && isOpen;\n\n useLayoutEffect(() => {\n if (!isUp) return;\n const panel = panelRef.current;\n const viewport = window.visualViewport;\n if (!panel || !viewport) return;\n\n let frame = 0;\n let until = 0;\n let top = -1;\n let height = -1;\n let keyboard = \"\";\n\n // Exactly what can be seen, top edge included: the keyboard coming up can\n // shove the layout viewport as well as shrink the visible one, and\n // `offsetTop` is the sum of that shove.\n //\n // Whole pixels, and only ever written when they change. Measuring runs\n // every frame while the keyboard moves, and the browser cannot tell a\n // style it has already been given from a new one: it lays the panel out\n // again either way, and a fractional height rewritten sixty times a second\n // is the thread re-wrapping sixty times a second.\n const fit = () => {\n const nextTop = Math.round(viewport.offsetTop);\n const nextHeight = Math.round(viewport.height);\n if (nextTop === top && nextHeight === height) return;\n top = nextTop;\n height = nextHeight;\n\n panel.style.top = `${top}px`;\n panel.style.height = `${height}px`;\n panel.style.bottom = \"auto\";\n\n // The home bar's inset is only worth room while the home bar is what is\n // down there. With the keyboard up the foot of the panel is the top of\n // the keyboard, and the inset is a strip of nothing between the two.\n const next = height < window.innerHeight - KEYBOARD_PX ? \"up\" : \"down\";\n if (next === keyboard) return;\n keyboard = next;\n panel.dataset.keyboard = next;\n };\n\n const follow = () => {\n fit();\n frame = performance.now() < until ? requestAnimationFrame(follow) : 0;\n };\n\n // The viewport reports where it has got to, not where it is going. Each\n // report is taken as a sign it is still moving, and the panel measures for\n // itself until the reports stop coming.\n const track = () => {\n until = performance.now() + SETTLE_MS;\n if (frame === 0) frame = requestAnimationFrame(follow);\n };\n\n fit();\n viewport.addEventListener(\"resize\", track);\n viewport.addEventListener(\"scroll\", track);\n // A tap on the composer is the first anybody knows about a keyboard: the\n // viewport says nothing until it has already started moving.\n panel.addEventListener(\"focusin\", track);\n\n return () => {\n if (frame !== 0) cancelAnimationFrame(frame);\n viewport.removeEventListener(\"resize\", track);\n viewport.removeEventListener(\"scroll\", track);\n panel.removeEventListener(\"focusin\", track);\n // Back to the classes' `inset-0`, for the floating panel these would\n // otherwise misplace.\n panel.style.top = \"\";\n panel.style.height = \"\";\n panel.style.bottom = \"\";\n delete panel.dataset.keyboard;\n };\n }, [panelRef, isUp]);\n\n usePageLock(isUp);\n\n return isSheet;\n}\n","\"use client\";\n\nimport { useEffect } from \"react\";\n\n/**\n * Holds the host's page still underneath a full screen sheet.\n *\n * `overflow: hidden` is not a lock on a phone. Tapping a field inside a fixed\n * panel makes the browser scroll the page to bring the field above the\n * keyboard, and it scrolls a page that only said it had no overflow: the sheet\n * stays where it is, the page slides about behind it, and everything pinned to\n * the visual viewport chases that a frame late. That is the shudder.\n *\n * So the page is pinned instead, fixed at the offset it is already scrolled to.\n * It looks exactly the same and there is nothing left to scroll, which is the\n * only answer the keyboard takes. Letting go puts the offset back first, so the\n * page never flashes at the top on the way out.\n *\n * Width is deliberately not set. A fixed box with both edges pinned already\n * works out to the width the page had, margins and all, and saying `100%` on\n * top of that hangs the host's own margin off the right hand side.\n *\n * Everything it touches is written down first and put back after. This is\n * somebody else's body, and they may have had an opinion about all of it.\n */\nexport function usePageLock(isLocked: boolean) {\n useEffect(() => {\n if (!isLocked) return;\n\n const { style } = document.body;\n const held = {\n position: style.position,\n top: style.top,\n left: style.left,\n right: style.right,\n overflow: style.overflow,\n };\n const scrollY = window.scrollY;\n\n style.position = \"fixed\";\n style.top = `${-scrollY}px`;\n style.left = \"0\";\n style.right = \"0\";\n style.overflow = \"hidden\";\n\n return () => {\n style.position = held.position;\n style.top = held.top;\n style.left = held.left;\n style.right = held.right;\n style.overflow = held.overflow;\n // Instant, or a host who asked for smooth scrolling watches their page\n // sail back down from the top while the sheet is still coming off it.\n window.scrollTo({ top: scrollY, behavior: \"instant\" });\n };\n }, [isLocked]);\n}\n","\"use client\";\n\nimport { useRef, type ReactNode, type RefObject } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/lib/utils\";\nimport { useHydrated } from \"./use-hydrated\";\nimport { useScrollLock } from \"./use-scroll-lock\";\nimport { useWidgetSheet } from \"./use-widget-sheet\";\nimport { WIDGET_CORNER } from \"./widget-corner\";\n\n/** Above the host's own chrome. Their z-indexes are not ours to guess at. */\nconst LAYER = 2_147_483_000;\n\n/**\n * Wide enough to read a conversation the way you read a page, and tall\n * enough for a thread rather than a glance. Not quite twice the corner\n * panel's width, and its height: on the denser scale the panel wears in\n * here, that is more thread than the corner shows expanded, without the\n * modal turning into the whole screen.\n *\n * Neither is a prop. The size of a modal is ours to get right once.\n */\nconst WIDTH = \"50rem\";\nconst HEIGHT = \"40rem\";\n\n/**\n * How the panel arrives and leaves.\n *\n * A modal comes up out of the middle: a little smaller and a little lower,\n * growing into place, on the same spring the corner panel opens with. It\n * overshoots by a hair, which is the difference between a panel that\n * appears and one that pops. Leaving is quicker and has no spring.\n */\n// `scale` and `translate` by name: those are the properties Tailwind's\n// `scale-*` and `translate-y-*` set. Listed as `transform` the panel has\n// neither, and pops open on opacity alone.\nconst MOTION =\n \"transition-[opacity,scale,translate] motion-reduce:transition-none\";\nconst OPEN = \"duration-250 ease-spring\";\nconst SHUT = \"duration-150 ease-out-quint\";\n\n/**\n * The phone's version: a rise into place as a sheet, which is the shape\n * every thumb has already been taught. A pure deceleration, since a\n * spring's overshoot would carry the top edge off the screen and back.\n */\nconst SHEET =\n \"transition-[opacity,translate] duration-300 ease-out-quint motion-reduce:transition-none\";\n\n/**\n * The scrim. Black rather than the host's foreground, because a light\n * theme's foreground is black and a dark theme's is white, and a white\n * scrim over a dark page reads as the page fogging up rather than stepping\n * back. A little slower in than out, like the panel it is behind.\n */\nconst SCRIM =\n \"absolute inset-0 bg-black/40 backdrop-blur-sm transition-opacity ease-out motion-reduce:transition-none\";\n\n/**\n * The support panel in the middle of the screen, over a scrim, rather than\n * in the corner.\n *\n * The same panel: whoever renders this hands it the very element the corner\n * would have held, and only where it is drawn changes. Portalled to the body\n * rather than kept in the launcher's box, because a modal answers to the\n * window and the box answers to wherever the host put the button.\n *\n * On a phone it is the screen, exactly as the corner panel is: same sheet,\n * same ride up over the keyboard, same lock on the page underneath. Bigger\n * screens get a scrim, a centred card, and a page that holds still.\n *\n * Closed, it stays mounted and goes `inert`: the conversation is still live,\n * and nothing behind the fade can be tabbed into.\n */\nexport function SupportModal({\n isOpen,\n onClose,\n panelRef,\n label,\n children,\n}: {\n isOpen: boolean;\n /** A press on the scrim. Escape and the header's cross are the launcher's. */\n onClose: () => void;\n /** The panel, for whoever moves focus into it and hands it back after. */\n panelRef: RefObject<HTMLDivElement | null>;\n /** What a screen reader calls the dialog. */\n label: string;\n /** The panel's contents, told whether they are a sheet right now. */\n children: (isSheet: boolean) => ReactNode;\n}) {\n const layerRef = useRef<HTMLDivElement>(null);\n // The layer is portalled into `document.body`, which does not exist until\n // there is one. Nothing at all until then, on the server and through\n // hydration alike, so both passes agree.\n const isHydrated = useHydrated();\n\n // Never framed, since the layer sits on the body: the only question the\n // hook has left to answer is how wide the screen is.\n const isSheet = useWidgetSheet({ boxRef: layerRef, panelRef, isOpen });\n // The sheet pins the page itself. Up here the page only has to stop.\n useScrollLock(isOpen && !isSheet);\n\n if (!isHydrated) return null;\n\n return createPortal(\n <div\n ref={layerRef}\n inert={!isOpen}\n // The hook the published stylesheet scopes its resets to. On a page\n // without Tailwind there is no preflight, so the package ships one\n // that reaches no further than this layer.\n data-median-widget=\"\"\n style={{ zIndex: LAYER }}\n className={cn(\n // Room to breathe on a screen that has it. A phone gives the sheet\n // every pixel anyway.\n \"fixed inset-0 grid place-items-center p-4 sm:p-8\",\n !isOpen && \"pointer-events-none\",\n )}\n >\n <div\n aria-hidden\n onClick={onClose}\n className={cn(\n SCRIM,\n isOpen ? \"opacity-100 duration-250\" : \"opacity-0 duration-150\",\n )}\n />\n\n <div\n ref={panelRef}\n tabIndex={-1}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={label}\n // What the parts inside read to know when to settle into place.\n // See `widget-settle.ts`.\n data-state={isOpen ? \"open\" : \"closed\"}\n style={isSheet ? undefined : { width: WIDTH, height: HEIGHT }}\n className={cn(\n \"outline-none\",\n isSheet\n ? [\n // The keyboard fit writes its edges straight onto the node,\n // over these.\n \"fixed inset-0\",\n SHEET,\n isOpen ? \"translate-y-0 opacity-100\" : \"translate-y-8 opacity-0\",\n ]\n : [\n // Never past the layer's padding, whatever the size says.\n `relative max-h-full max-w-full ${WIDGET_CORNER} shadow-2xl`,\n MOTION,\n isOpen\n ? [OPEN, \"scale-100 opacity-100 translate-y-0\"]\n : [SHUT, \"scale-[0.96] opacity-0 translate-y-2\"],\n ],\n )}\n >\n {children(isSheet)}\n </div>\n </div>,\n document.body,\n );\n}\n","\"use client\";\n\nimport { useSyncExternalStore } from \"react\";\n\n/**\n * Whether this is running in a browser that has finished hydrating.\n *\n * For the things that cannot exist until there is a document: a median has to\n * be handed a node, and there is no `document.body` on a server. Returning\n * null for those and the real thing once mounted is a server/client branch,\n * which is the first thing React names when it reports a hydration mismatch:\n * the server sends nothing, the client's first pass renders a panel, and the\n * whole tree is thrown away and built again.\n *\n * So it is read as an external store rather than as a mounted flag. React\n * takes the server snapshot both on the server and through hydration, so both\n * passes agree on nothing, and only the commit after it moves. Same trick the\n * session token is read with, for the same reason.\n *\n * The store never changes, so nothing ever subscribes to anything.\n */\nconst subscribe = () => () => {};\nconst onClient = () => true;\nconst onServer = () => false;\n\nexport function useHydrated(): boolean {\n return useSyncExternalStore(subscribe, onClient, onServer);\n}\n","\"use client\";\n\nimport { useEffect } from \"react\";\n\n/**\n * Holds the host's page still under the modal, on a screen big enough to\n * have a scrollbar.\n *\n * The phone's lock is `usePageLock`, which pins the body in place because\n * that is the only answer the keyboard takes. Up here there is no keyboard\n * to argue with, and pinning the body would throw away the page's scroll\n * position for nothing, so this only stops it scrolling.\n *\n * The scrollbar's width goes onto the body as padding as the bar goes, so\n * the page does not lurch sideways by the width of the bar it just lost.\n * Whatever the host had there already is added to rather than replaced,\n * and put back after. This is somebody else's body.\n */\nexport function useScrollLock(isLocked: boolean) {\n useEffect(() => {\n if (!isLocked) return;\n\n const { body } = document;\n const held = {\n overflow: body.style.overflow,\n paddingRight: body.style.paddingRight,\n };\n const gutter = window.innerWidth - document.documentElement.clientWidth;\n\n body.style.overflow = \"hidden\";\n if (gutter > 0) {\n const existing = parseFloat(getComputedStyle(body).paddingRight) || 0;\n body.style.paddingRight = `${existing + gutter}px`;\n }\n\n return () => {\n body.style.overflow = held.overflow;\n body.style.paddingRight = held.paddingRight;\n };\n }, [isLocked]);\n}\n","import { cn } from \"@/lib/utils\";\n\n/**\n * How the panel dresses when it is the whole screen, shared by the corner\n * widget and the modal: both open as a sheet on a phone, and a sheet is a\n * sheet whichever of them it grew out of.\n *\n * A sheet is the screen, so the card's own dress goes: no corners against\n * edges that already have their own, no border where the screen ends, and\n * room for the notch and the home bar on hosts that let content under them.\n *\n * The home bar's room goes back while the keyboard has the bottom of the\n * screen, which is the sheet's own attribute for it: the panel's foot is then\n * the keyboard's top edge, and the inset would hold the composer a thumb's\n * width above the keys for no reason. It transitions rather than snapping,\n * since the keyboard is on its way in while this changes its mind.\n */\nexport const SHEET_PANEL = cn(\n \"rounded-none border-0 pt-[env(safe-area-inset-top)] pb-[env(safe-area-inset-bottom)]\",\n \"transition-[padding] duration-200 ease-out motion-reduce:transition-none\",\n \"[[data-keyboard=up]_&]:pb-0\",\n);\n","\"use client\";\n\nimport { useState } from \"react\";\nimport { X } from \"@/components/icons\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { cn } from \"@/lib/utils\";\nimport type { LauncherSide } from \"./launcher-position\";\nimport { plainTextOf } from \"./plain-text\";\nimport type { TeaserPlacement } from \"./use-teaser-placement\";\nimport { WidgetAvatar } from \"./widget-avatar\";\nimport { WIDGET_CORNER } from \"./widget-corner\";\nimport type { SupportMessage } from \"./types\";\n\n/**\n * The card's own width. Narrower than the panel it stands in for, so it\n * reads as a note left on the button rather than the panel half open. Never\n * wider than the box, which on a phone is the box saying so.\n */\nconst WIDTH = \"min(20rem, 100%)\";\n\n/** The gap between the card and the button it belongs to. */\nconst GAP = \"0.5rem\";\n\n/**\n * A reply, shown on the closed button so somebody sees what came in without\n * opening anything.\n *\n * The count on the button says something arrived; this says what. One card,\n * for the newest unread reply, with the sender's name and face over the\n * first couple of lines. Pressing it opens the panel, and the cross in the\n * corner waves it away without opening anything.\n *\n * Two buttons rather than one with a cross inside it, because a button\n * inside a button is not a thing a browser will let you have. The cross is\n * a sibling, sat over the corner of the card it dismisses.\n *\n * Kept mounted while it leaves rather than unmounted on the spot, so the\n * dismissal is the card fading rather than vanishing. The last message is\n * held for that, since by then the caller has stopped passing one.\n */\nexport function WidgetTeaser({\n message,\n isShown,\n side,\n placement,\n buttonY,\n buttonSize,\n onOpen,\n onDismiss,\n}: {\n /** The reply to show, or null once there is none. */\n message: SupportMessage | null;\n /** Whether it is on screen. False leaves it fading out in place. */\n isShown: boolean;\n /** Which edge the button is parked against. The card lines up with it. */\n side: LauncherSide;\n placement: TeaserPlacement;\n /** The button's top inside the box, as a CSS length. */\n buttonY: string;\n buttonSize: string;\n onOpen: () => void;\n onDismiss: () => void;\n}) {\n // What was last shown, for the fade out after the caller lets go of it.\n // Settled during render rather than in an effect, so the frame the caller\n // drops the message is the frame the card starts leaving.\n const [held, setHeld] = useState(message);\n if (message !== null && message !== held) setHeld(message);\n const shown = message ?? held;\n if (shown === null) return null;\n\n const agent = shown.agent;\n const text = plainTextOf(shown.body);\n\n return (\n <div\n key={shown.id}\n inert={!isShown}\n // Announced as it lands, so a screen reader hears that a reply came\n // in without having to find the corner of the page it landed in.\n role=\"status\"\n aria-live=\"polite\"\n style={{\n width: WIDTH,\n [side]: 0,\n ...(placement === \"above\"\n ? { bottom: `calc(100% - ${buttonY} + ${GAP})` }\n : { top: `calc(${buttonY} + ${buttonSize} + ${GAP})` }),\n }}\n className={cn(\n \"absolute\",\n // Grows out of the corner nearest the button, so it reads as coming\n // from it rather than landing beside it.\n placement === \"above\"\n ? side === \"right\"\n ? \"origin-bottom-right\"\n : \"origin-bottom-left\"\n : side === \"right\"\n ? \"origin-top-right\"\n : \"origin-top-left\",\n \"transition-[opacity,translate,scale] motion-reduce:transition-none\",\n isShown\n ? [\n \"pointer-events-auto opacity-100 duration-300 ease-spring\",\n // The arrival, once. The transition above takes over after.\n \"animate-in fade-in-0 zoom-in-95 duration-300 ease-spring motion-reduce:animate-none\",\n placement === \"above\"\n ? \"slide-in-from-bottom-2\"\n : \"slide-in-from-top-2\",\n ]\n : \"pointer-events-none scale-95 opacity-0 duration-150 ease-out-quint\",\n )}\n >\n <button\n type=\"button\"\n onClick={onOpen}\n aria-label={\n agent ? `Read the reply from ${agent.name}` : \"Read the reply\"\n }\n className={cn(\n // Its own radius scale, like the panel, so the app's token cannot\n // reshape a card that sits on somebody else's site.\n `block w-full ${WIDGET_CORNER} border bg-card p-3.5 pr-10 text-left text-card-foreground shadow-lg [--radius:0.625rem]`,\n \"transition-[background-color,box-shadow] outline-none\",\n \"hover:bg-muted/50 focus-visible:ring-3 focus-visible:ring-ring/50\",\n \"cursor-pointer select-none\",\n )}\n >\n <span className=\"flex items-center gap-2\">\n {agent && (\n <>\n <WidgetAvatar agent={agent} className=\"size-6 rounded-md\" />\n <span className=\"truncate text-sm font-medium\">\n {agent.name}\n </span>\n {agent.kind === \"ai\" && (\n <Badge variant=\"muted\" className=\"shrink-0 rounded-md bg-muted px-1 inset-ring-0\">\n AI\n </Badge>\n )}\n </>\n )}\n <span className=\"ml-auto shrink-0 text-sm text-muted-foreground tabular-nums\">\n {shown.time}\n </span>\n </span>\n\n <span className=\"mt-1.5 line-clamp-2 block text-base leading-snug\">\n {text.length > 0 ? text : \"Sent a file\"}\n </span>\n </button>\n\n <button\n type=\"button\"\n onClick={onDismiss}\n aria-label=\"Dismiss\"\n className={cn(\n \"absolute top-2.5 right-2.5 grid size-7 place-items-center rounded-md\",\n \"text-muted-foreground transition-colors outline-none\",\n \"hover:bg-muted hover:text-foreground focus-visible:ring-3 focus-visible:ring-ring/50\",\n \"cursor-pointer\",\n )}\n >\n <X className=\"size-4\" />\n </button>\n </div>\n );\n}\n","/**\n * A message read aloud rather than rendered: what a reply says, with the\n * markdown taken off it, for the one line a teaser has room for.\n *\n * Light-handed on purpose, and the same reading the inbox's own preview\n * uses. A fence line goes entirely and the code between stays, a link reads\n * as its text, and emphasis marks come off. Anything this misses is shown\n * as typed, which is a fair result for a preview.\n */\nexport function plainTextOf(markdown: string): string {\n return (\n markdown\n .replace(/^\\s*(?:```|~~~).*$/gm, \"\")\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\")\n .replace(/[*`_~]/g, \"\")\n .replace(/^\\s*#{1,6}\\s+/gm, \"\")\n .replace(/^\\s*[-+]\\s+/gm, \"\")\n .replace(/^\\s*>\\s?/gm, \"\")\n .replace(/\\s+/g, \" \")\n .trim()\n );\n}\n","\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\n\n/**\n * How long to keep looking for something that may not be on the page yet.\n * A navigation is usually still resolving when the instruction lands, so the\n * element the tool means is often a route change away.\n */\nconst FIND_TIMEOUT_MS = 4_000;\n\n/** How long the ring stays once it has found something. */\nconst HOLD_MS = 2_600;\n\n/** The fade at the end, which is also how long the node outlives the hold. */\nconst FADE_MS = 300;\n\n/** Above the host's own chrome. Their z-indexes are not ours to guess at. */\nconst LAYER = 2_147_483_000;\n\nfunction prefersReducedMotion(): boolean {\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n}\n\n/**\n * A ring drawn over something on the host's page, because one of their tools\n * asked for it to be pointed at.\n *\n * Nothing on their page is touched. The ring is a box of our own, positioned\n * over the element's rectangle and following it every frame, so a page that\n * scrolls, resizes or animates underneath keeps the ring where the thing is,\n * and there is no style of theirs to put back afterwards.\n *\n * It also waits. The instruction rides the same reply that moves the page, so\n * the element it names is usually a route change away and not in the document\n * yet; this keeps looking for a few seconds and gives up quietly rather than\n * ringing the wrong thing or nothing at all.\n *\n * In `document.body` rather than where the widget sits, since a `fixed` box\n * inside a host container that happens to carry a transform is fixed to that\n * container instead of to the window, and this one has to line up with an\n * element anywhere on their page.\n */\nexport function WidgetSpotlight({ selector }: { selector: string }) {\n const [isShown, setIsShown] = useState(false);\n const [isLeaving, setIsLeaving] = useState(false);\n const ring = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n let target: Element | null = null;\n let frame = 0;\n let startedAt = 0;\n let foundAt = 0;\n let stopped = false;\n let leaveTimer: ReturnType<typeof setTimeout> | undefined;\n\n function find(): Element | null {\n try {\n return document.querySelector(selector);\n } catch {\n // Not a selector at all. Median checks the shape of one before it is\n // stored, but only a browser can say whether it parses.\n console.error(\n `Median: a tool asked to highlight ${selector}, which is not a valid CSS selector.`,\n );\n stopped = true;\n return null;\n }\n }\n\n function tick(now: number) {\n if (stopped) return;\n if (startedAt === 0) startedAt = now;\n\n if (target === null) {\n target = find();\n if (stopped) return;\n\n if (target === null) {\n // Still not there. It may be one route change behind us.\n if (now - startedAt > FIND_TIMEOUT_MS) return;\n frame = requestAnimationFrame(tick);\n return;\n }\n\n foundAt = now;\n target.scrollIntoView({\n behavior: prefersReducedMotion() ? \"auto\" : \"smooth\",\n block: \"center\",\n });\n setIsShown(true);\n }\n\n // Written straight onto the node rather than held in state: this runs\n // every frame for a couple of seconds, and a re-render per frame to\n // move one box is a cost the host's page would pay for nothing.\n const node = ring.current;\n if (node !== null) {\n const rect = target.getBoundingClientRect();\n node.style.transform = `translate(${rect.left}px, ${rect.top}px)`;\n node.style.width = `${rect.width}px`;\n node.style.height = `${rect.height}px`;\n }\n\n if (now - foundAt > HOLD_MS) {\n setIsLeaving(true);\n leaveTimer = setTimeout(() => setIsShown(false), FADE_MS);\n return;\n }\n frame = requestAnimationFrame(tick);\n }\n\n frame = requestAnimationFrame(tick);\n return () => {\n stopped = true;\n cancelAnimationFrame(frame);\n clearTimeout(leaveTimer);\n };\n }, [selector]);\n\n if (!isShown || typeof document === \"undefined\") return null;\n\n return createPortal(\n <div\n ref={ring}\n aria-hidden\n // Portalled to the body, outside the launcher's box, so it carries the\n // published stylesheet's scope hook itself.\n data-median-widget=\"\"\n style={{ zIndex: LAYER, top: 0, left: 0 }}\n className=\"pointer-events-none fixed\"\n >\n {/* Two layers of the same box: one that holds the outline, and one\n that swells out of it and fades, which is the part the eye catches\n on a page somebody is already looking at. */}\n <span\n className={[\n \"absolute -inset-1 rounded-lg outline-2 outline-offset-0 outline-primary\",\n // With the widget's own primary behind the host's, like every\n // other colour it draws.\n \"shadow-[0_0_0_6px_color-mix(in_oklch,var(--primary,oklch(0.205_0_0))_20%,transparent)]\",\n \"transition-opacity duration-300 ease-out motion-reduce:transition-none\",\n isLeaving ? \"opacity-0\" : \"opacity-100\",\n ].join(\" \")}\n />\n <span\n className={[\n \"absolute -inset-1 rounded-lg outline-2 outline-primary\",\n \"animate-ping opacity-60 motion-reduce:animate-none\",\n isLeaving ? \"hidden\" : \"\",\n ].join(\" \")}\n />\n </div>,\n document.body,\n );\n}\n","\"use client\";\n\nimport { useSyncExternalStore } from \"react\";\nimport { hasCustomDataSender, queueCustomData } from \"./custom-data\";\nimport {\n createOpenStore,\n isDevelopment,\n useOpenState,\n useRegister,\n} from \"./open-store\";\nimport {\n reportError,\n type MedianErrorReportOptions,\n type MedianErrorReportOutcome,\n} from \"./report-error\";\nimport { resetSessions } from \"./session\";\nimport { signOutHeldUsers } from \"./signed-out\";\nimport { unreadStore, useUnreadCount } from \"./unread-store\";\nimport { resetVisitorEmails } from \"./visitor-email\";\n\n/**\n * Driving the support panel from anywhere on the page. The open state lives\n * in `open-store.ts`, which the feedback panel keeps its own copy of, and\n * the unread count in `unread-store.ts`; this file is the three things that\n * are the support widget's alone, `attach`, `reportError` and `reset`, and\n * the shape a host reaches for all of it through.\n */\nconst store = createOpenStore(\"MedianSupport\");\n\n/**\n * The widget from anywhere, React or not: the same shape the hook hands\n * back, for a plain `onclick` or a shortcut listener that never sees a\n * component. `isOpen` and `unreadCount` read live but do not re-render\n * anything; the hook is for that.\n */\nexport const medianSupport = {\n /** What the panel is doing right now. */\n get isOpen() {\n return store.readEffective();\n },\n /** Replies the visitor has not read. Zero while nothing is mounted. */\n get unreadCount() {\n return unreadStore.read();\n },\n open() {\n store.request(true);\n store.warnIfNothingIsMounted(\"the widget was asked to open\");\n },\n close() {\n store.request(false);\n },\n toggle() {\n const next = !store.readEffective();\n store.request(next);\n if (next) store.warnIfNothingIsMounted(\"the widget was asked to open\");\n },\n /**\n * Put your own data on the next bug report: a crash trace from your error\n * handler, a request id, the state of a store. Objects are written out as\n * JSON. Entries ride the visitor's next message and land on the report,\n * whether or not diagnostics are on.\n */\n attach(label: string, value: unknown) {\n if (!queueCustomData(label, value)) {\n if (isDevelopment) {\n console.warn(\n \"Median: attach() needs a label and a value, and nothing was queued. Pass a short label naming the data, and the data itself.\",\n );\n }\n return;\n }\n // The support panel is one of three things that send it, so the warning\n // waits on all three rather than on the panel alone.\n if (!isDevelopment || hasCustomDataSender()) return;\n setTimeout(() => {\n if (hasCustomDataSender()) return;\n console.warn(\n \"Median: data was attached, but no <MedianSupport>, <MedianFeedback> or contact form is on the page to send it. It goes with the next message, note, sendMedianContact() or reportError() call.\",\n );\n }, 1000);\n },\n /**\n * File the crash your error screen is showing, with everything the browser\n * was doing when it happened. One press from a \"Report error\" button:\n *\n * const outcome = await medianSupport.reportError(error);\n *\n * It answers rather than throws, so the screen can say whether it landed.\n * It works from a boundary with no widget left under it, which is most of\n * them.\n */\n reportError(error: unknown, options?: MedianErrorReportOptions) {\n return reportError(error, options);\n },\n /**\n * Forget this browser's conversation, so the next one starts fresh and\n * anonymous. Call it when your user signs out: whoever sits down next\n * should not find somebody else's thread waiting.\n *\n * The user the widget holds right now is signed out with it, even if your\n * own state still passes them for a moment, and an identity route is read\n * again.\n */\n reset() {\n signOutHeldUsers();\n resetSessions();\n resetVisitorEmails();\n },\n} satisfies MedianSupportControl;\n\n/** The shape of {@link medianSupport}, and of what the hook returns. */\nexport type MedianSupportControl = {\n /** Whether the panel is open. */\n isOpen: boolean;\n /**\n * Replies the visitor has not read: the same count the closed button\n * wears and the tab's title carries. For a badge on a button of your own,\n * on a page where the launcher is hidden. Zero while the panel is open,\n * once it has been looked at, and whenever no panel is mounted.\n */\n unreadCount: number;\n open: () => void;\n close: () => void;\n toggle: () => void;\n /** Put your own data on the next bug report: a crash trace, an id. */\n attach: (label: string, value: unknown) => void;\n /** File the crash your error screen is showing. For a report button. */\n reportError: (\n error: unknown,\n options?: MedianErrorReportOptions,\n ) => Promise<MedianErrorReportOutcome>;\n /** Forget this browser's conversation. For sign out. */\n reset: () => void;\n};\n\n/**\n * The widget, from anywhere else on the page:\n *\n * const support = useMedianSupport();\n *\n * <button onClick={support.open}>\n * Contact us\n * {support.unreadCount > 0 && <span>{support.unreadCount}</span>}\n * </button>\n *\n * `isOpen` follows the panel however it was opened, so a trigger can render\n * itself as pressed, or step out of the way while the panel is up.\n * `unreadCount` follows the thread, so the trigger can say a reply is\n * waiting where the corner button would have.\n */\nexport function useMedianSupport(): MedianSupportControl {\n const isOpen = useSyncExternalStore(\n store.subscribe,\n store.readEffective,\n store.readServerEffective,\n );\n const unreadCount = useUnreadCount();\n\n return {\n isOpen,\n unreadCount,\n open: medianSupport.open,\n close: medianSupport.close,\n toggle: medianSupport.toggle,\n attach: medianSupport.attach,\n reportError: medianSupport.reportError,\n reset: medianSupport.reset,\n };\n}\n\n/** The panel's own settled open state. See {@link useOpenState}. */\nexport function useMedianOpenState(options: {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n}): [boolean, (next: boolean) => void] {\n return useOpenState(store, options);\n}\n\n/** One widget per page, and a word in the console when there are two. */\nexport function useRegisterMedianSupport(name = \"MedianSupport\") {\n useRegister(store, name);\n}\n","/**\n * Data somebody chose to put on a bug report: a crash trace, a request id,\n * the state of a store when it fell over.\n *\n * The deliberate third of the telemetry. Context is what the browser knows,\n * diagnostics are what the widget caught on its own; this is what the host's\n * code or the agent decided the team should see, word for word. Because it is\n * handed over on purpose, it does not wait behind the diagnostics switch.\n *\n * Pure on purpose, like `diagnostics.ts` next door: the SDK cleans what it\n * queues with this file before it sends, and the backend cleans it again with\n * the same file before it stores, so neither side is trusting the other's\n * arithmetic.\n */\n\nexport type CustomDataEntry = {\n /** What the value is, in a few words. The entry's heading on the report. */\n label: string;\n /** The thing itself, kept exactly as written, up to the cap. */\n value: string;\n};\n\n/** The most entries one report carries. Newest kept, oldest dropped. */\nexport const MAX_CUSTOM_ENTRIES = 8;\n\nexport const CUSTOM_LABEL_MAX_LENGTH = 60;\n\n/** Room for a real stack trace, not just its first line. */\nexport const CUSTOM_VALUE_MAX_LENGTH = 4_000;\n\nfunction cleanLabel(input: unknown): string | undefined {\n if (typeof input !== \"string\") return undefined;\n const label = input.trim().slice(0, CUSTOM_LABEL_MAX_LENGTH);\n return label.length > 0 ? label : undefined;\n}\n\n/**\n * Whatever was handed over, written out as text. Objects become readable\n * JSON, because \"the state of the store\" arrives as an object, and making the\n * caller serialize it first is exactly the chore this exists to remove.\n */\nfunction cleanValue(input: unknown): string | undefined {\n let written: string | undefined;\n if (typeof input === \"string\") {\n written = input.trim();\n } else if (typeof input === \"number\" || typeof input === \"boolean\") {\n written = String(input);\n } else {\n try {\n written = JSON.stringify(input, null, 2);\n } catch {\n written = undefined;\n }\n }\n\n if (written === undefined || written.length === 0) return undefined;\n return written.length > CUSTOM_VALUE_MAX_LENGTH\n ? `${written.slice(0, CUSTOM_VALUE_MAX_LENGTH)}…`\n : written;\n}\n\n/**\n * Whatever arrived, cut down to what may be stored. Undefined when nothing\n * survived, so an empty list never lands on a row and reads later as \"they\n * attached something and it was blank\".\n *\n * One entry per label, the newest one winning: attaching a second crash\n * trace means the first one is no longer the crash.\n */\nexport function cleanCustomData(input: unknown): CustomDataEntry[] | undefined {\n if (!Array.isArray(input)) return undefined;\n\n const byLabel = new Map<string, string>();\n for (const raw of input) {\n if (typeof raw !== \"object\" || raw === null) continue;\n const entry = raw as Record<string, unknown>;\n const label = cleanLabel(entry.label);\n const value = cleanValue(entry.value);\n if (label === undefined || value === undefined) continue;\n // Re-set so a replaced label moves to the end, where the newest live.\n byLabel.delete(label);\n byLabel.set(label, value);\n }\n\n const entries = [...byLabel].map(([label, value]) => ({ label, value }));\n return entries.length > 0 ? entries.slice(-MAX_CUSTOM_ENTRIES) : undefined;\n}\n\n/**\n * Several sources of entries, oldest source first, folded into one list. The\n * same rules as cleaning: one entry per label, later wins, newest kept when\n * the cap says something has to go.\n */\nexport function mergeCustomData(\n ...sources: (CustomDataEntry[] | undefined)[]\n): CustomDataEntry[] | undefined {\n return cleanCustomData(sources.flatMap((entries) => entries ?? []));\n}\n","\"use client\";\n\nimport {\n cleanCustomData,\n mergeCustomData,\n type CustomDataEntry,\n} from \"@median/backend/customData\";\nimport { useEffect } from \"react\";\n\n/**\n * Data the host hands over on purpose, waiting to ride the next message: a\n * crash trace from their error handler, a request id, the state of a store.\n *\n * The deliberate cousin of the diagnostics ring buffer next door, and module\n * level for the same reason: a crash handler runs wherever the crash was,\n * usually nowhere near React, and sometimes before the widget has mounted.\n * Unlike diagnostics it does not wait behind a switch, because calling\n * `attach` is the host offering it.\n *\n * Nothing is sent on its own. Entries ride the next message the visitor\n * sends, land on the conversation, and are copied onto a bug report the\n * moment one is filed.\n */\n\nlet queued: CustomDataEntry[] = [];\n\n/** How many mounted components take the queue with their next send. */\nlet senders = 0;\n\n/**\n * Marks a component that sends the queue: the support panel, the feedback\n * panel, and the contact form. `attach()` warns only when none is mounted.\n */\nexport function useCustomDataSender(): void {\n useEffect(() => {\n senders += 1;\n return () => {\n senders -= 1;\n };\n }, []);\n}\n\n/** Whether anything on the page will send what was attached. */\nexport function hasCustomDataSender(): boolean {\n return senders > 0;\n}\n\n/** True when the entry survived cleaning and is waiting for a send. */\nexport function queueCustomData(label: unknown, value: unknown): boolean {\n const cleaned = cleanCustomData([{ label, value }]);\n if (cleaned === undefined) return false;\n\n queued = mergeCustomData(queued, cleaned) ?? [];\n return true;\n}\n\n/**\n * What a send takes with it: everything attached since the last one, or\n * nothing, so a quiet page costs nothing. Drained rather than copied; a send\n * that fails hands its entries back through {@link restoreCustomData}.\n */\nexport function takeCustomData(): CustomDataEntry[] | undefined {\n if (queued.length === 0) return undefined;\n const taken = queued;\n queued = [];\n return taken;\n}\n\n/** A failed send's entries, back in the queue so the retry still has them. */\nexport function restoreCustomData(entries: CustomDataEntry[]): void {\n // The failed send's entries are the older side: anything attached since\n // the failure wins a collision.\n queued = mergeCustomData(entries, queued) ?? [];\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useRef, useSyncExternalStore } from \"react\";\n\n/**\n * Whether a panel is open, kept outside React.\n *\n * The widget is mounted once, usually in a root layout. The thing that opens\n * it is usually somewhere else entirely: a \"Contact us\" item in a nav, a\n * \"Share feedback\" row in a menu, a keyboard shortcut. Holding this in a\n * module rather than in the panel's own state is what lets those reach it\n * without a host threading state through their whole tree, or wrapping their\n * app in a provider of ours.\n *\n * Two values rather than one, because the host has the last word:\n *\n * - `requested` is what the page last asked for, including the widget's own\n * controls. Null until something asks, so `defaultOpen` still means\n * something on first paint.\n * - `effective` is what the panel is actually doing. A host driving it with\n * the `open` prop can ignore a request, so anything reading the state back\n * reads this one.\n *\n * One store per panel: the support widget and the feedback panel are two\n * things a page can open, and opening one must never open the other.\n */\n\n// The `typeof` guard is for the published bundle, where no framework has\n// promised that `process` exists. A bundler that defines it leaves this line\n// statically true or false; a page without one gets production behavior.\nexport const isDevelopment =\n typeof process !== \"undefined\" && process.env.NODE_ENV === \"development\";\n\nexport type OpenStore = ReturnType<typeof createOpenStore>;\n\nexport function createOpenStore(component: string) {\n let requested: boolean | null = null;\n let effective = false;\n let mounted = 0;\n /** Which components are on the page, by the name the host wrote. */\n const mountedNames: string[] = [];\n const listeners = new Set<() => void>();\n /** Told about every request, wherever it came from. */\n const requestListeners = new Set<(next: boolean) => void>();\n /** Whether a request is being reported right now. */\n let isReporting = false;\n\n function emit() {\n for (const listener of listeners) listener();\n }\n\n return {\n /** The component a warning should name, so it reads like the host's code. */\n component,\n\n subscribe(listener: () => void) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n\n readRequested: () => requested,\n readEffective: () => effective,\n\n /**\n * The server has no page to open a panel on, and neither does the first\n * client paint, which is what keeps hydration quiet.\n */\n readServerRequested: () => null as boolean | null,\n readServerEffective: () => false,\n\n request(next: boolean) {\n requested = next;\n emit();\n // A host whose `onOpenChange` calls `open()` or `close()` back would\n // otherwise go round forever. The request it makes from in there is\n // its own, so it is carried out without being reported back to it.\n if (isReporting) return;\n isReporting = true;\n try {\n for (const listener of requestListeners) listener(next);\n } finally {\n isReporting = false;\n }\n },\n\n /** Hears each request: a trigger, the hook, or the panel's own button. */\n onRequest(listener: (next: boolean) => void) {\n requestListeners.add(listener);\n return () => {\n requestListeners.delete(listener);\n };\n },\n\n /** Told, not asked: the panel saying what it settled on. */\n settle(next: boolean) {\n if (effective === next) return;\n effective = next;\n emit();\n },\n\n countMounted(name: string = component) {\n mounted += 1;\n mountedNames.push(name);\n return mounted;\n },\n\n /** The components on the page, for a warning that names them. */\n mountedNames: () => [...mountedNames],\n\n countUnmounted(name: string = component) {\n mounted -= 1;\n const at = mountedNames.indexOf(name);\n if (at !== -1) mountedNames.splice(at, 1);\n // With nothing on the page, nothing is open, whatever the last one to\n // leave was doing. What was asked for is kept, so a widget that comes\n // back on the next route comes back the way it went.\n if (mounted === 0 && effective) {\n effective = false;\n emit();\n }\n },\n\n /**\n * The likeliest way to spend an afternoon on this: call `open()`, watch\n * nothing happen, and never find out that the component is not on the\n * page. So it says so, in the console, where that afternoon is being\n * spent.\n *\n * Asked a second later rather than now, because a static button wired up\n * before React has hydrated is early, not wrong. The request is kept\n * either way, so a panel that mounts afterwards picks it up.\n */\n warnIfNothingIsMounted(did: string) {\n if (!isDevelopment || mounted > 0) return;\n\n setTimeout(() => {\n if (mounted > 0) return;\n console.warn(\n `Median: ${did}, but no <${component}> is on the page. Mount it once, near the root of your app.`,\n );\n }, 1000);\n },\n };\n}\n\n/**\n * The state a panel actually renders with, and the one place the three ways\n * of driving it are settled:\n *\n * 1. `open`, if the host passed it. Controlled wins, always.\n * 2. Whatever the page last asked for, through a trigger or the hook.\n * 3. `defaultOpen`, until anything at all has happened.\n *\n * A request is reported to `onOpenChange` either way, so a controlled host\n * hears about their own nav item pressing open just as they hear about ours.\n */\nexport function useOpenState(\n store: OpenStore,\n {\n open,\n defaultOpen = false,\n onOpenChange,\n }: {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n },\n): [boolean, (next: boolean) => void] {\n const asked = useSyncExternalStore(\n store.subscribe,\n store.readRequested,\n store.readServerRequested,\n );\n const isOpen = open ?? asked ?? defaultOpen;\n\n // So a trigger reading `isOpen` back is never a frame behind the panel it\n // opened.\n useEffect(() => {\n store.settle(isOpen);\n }, [store, isOpen]);\n\n // Every request reaches `onOpenChange`: the panel's own button, and\n // `open()`, `close()` and `toggle()` from anywhere on the page. Read\n // through a ref so a host passing an inline function does not resubscribe\n // on every render.\n const reportRef = useRef(onOpenChange);\n useEffect(() => {\n reportRef.current = onOpenChange;\n });\n useEffect(\n () => store.onRequest((next) => reportRef.current?.(next)),\n [store],\n );\n\n const setOpen = useCallback(\n (next: boolean) => {\n store.request(next);\n },\n [store],\n );\n\n return [isOpen, setOpen];\n}\n\n/**\n * One of each per page. Two would share the state above and open together,\n * which is never what anyone meant, so it says so where it will be read.\n */\nexport function useRegister(store: OpenStore, name: string = store.component) {\n useEffect(() => {\n if (store.countMounted(name) > 1 && isDevelopment) {\n const names = [...new Set(store.mountedNames())];\n const which =\n names.length === 1\n ? `more than one <${names[0]}> is mounted`\n : `${names.map((one) => `<${one}>`).join(\" and \")} are both mounted`;\n console.warn(\n `Median: ${which}. They share one open state and open together. Mount one, once, near the root of your app.`,\n );\n }\n return () => store.countUnmounted(name);\n }, [store, name]);\n}\n","import { ConvexError } from \"convex/values\";\n\n/**\n * A refusal handed back as a value rather than thrown, by the widget and\n * bridge endpoints whose limits must stick even when the work fails. See\n * `model/trustedMutation.ts`.\n *\n * The key is `auditResult` because widgets already installed on customer\n * sites read it under that name.\n */\nexport type MutationError = {\n code: string;\n message: string;\n retryAfterMs?: number;\n reason?: string;\n};\nexport type MutationResult<T> =\n | { auditResult: \"ok\"; value: T }\n | { auditResult: \"error\"; data: MutationError };\n\n/** Throws a refusal the way a plain mutation would have. */\nexport function unwrapResult<T>(result: MutationResult<T>): T {\n if (result.auditResult === \"error\") throw new ConvexError(result.data);\n return result.value;\n}\n\n/** What a caller may be told about a failure: a refusal's own words, or a generic line. */\nexport function safeError(error: unknown): MutationError {\n if (error instanceof ConvexError) {\n const raw: unknown = error.data;\n if (raw && typeof raw === \"object\") {\n const data = raw as Record<string, unknown>;\n return {\n code:\n typeof data.code === \"string\"\n ? data.code.slice(0, 80)\n : \"invalid_request\",\n message:\n typeof data.message === \"string\"\n ? data.message.slice(0, 1000)\n : \"This action could not be completed.\",\n ...(typeof data.retryAfterMs === \"number\" &&\n Number.isFinite(data.retryAfterMs)\n ? { retryAfterMs: Math.max(0, data.retryAfterMs) }\n : {}),\n ...(data.reason === \"fair_use\" ? { reason: \"fair_use\" } : {}),\n };\n }\n if (typeof raw === \"string\")\n return { code: \"invalid_request\", message: raw.slice(0, 1000) };\n }\n return {\n code: \"internal_error\",\n message: \"This action could not be completed. Please try again.\",\n };\n}\n","import {\n unwrapResult,\n type MutationResult,\n} from \"../convex/shared/mutationResult\";\n\n/** A mutation's value, whether or not it came wrapped in a refusal envelope. */\nexport type MutationValue<T> = T extends { auditResult: \"ok\"; value: infer R }\n ? R\n : T extends { auditResult: \"error\" }\n ? never\n : T;\n\n/**\n * Unwraps what the widget and bridge endpoints answer, which hand a refusal\n * back as a value (see `convex/model/trustedMutation.ts`), and throws it the\n * way a plain mutation would. Any other result passes through untouched.\n */\nexport function unwrapMutationResult<T>(result: T): MutationValue<T> {\n if (\n result &&\n typeof result === \"object\" &&\n \"auditResult\" in result &&\n ((result.auditResult === \"ok\" && \"value\" in result) ||\n (result.auditResult === \"error\" && \"data\" in result))\n ) {\n return unwrapResult(result as MutationResult<unknown>) as MutationValue<T>;\n }\n return result as MutationValue<T>;\n}\n","/* eslint-disable */\n/**\n * Generated `api` utility.\n *\n * THIS CODE IS AUTOMATICALLY GENERATED.\n *\n * To regenerate, run `npx convex dev`.\n * @module\n */\n\nimport { anyApi, componentsGeneric } from \"convex/server\";\n\n/**\n * A utility for referencing Convex functions in your app's API.\n *\n * Usage:\n * ```js\n * const myFunctionReference = api.myModule.myFunction;\n * ```\n */\nexport const api = anyApi;\nexport const internal = anyApi;\nexport const components = componentsGeneric();\n","import { cleanTitle } from \"./signals\";\n\n/**\n * A crash, in the vocabulary both sides share.\n *\n * A page that has fallen over is the one report nobody types: whoever was\n * using it can see that it broke and has nothing to add about why. So the\n * error describes itself, and this file is what it turns into on the Signal\n * tab.\n *\n * Deliberately pure, like `signals.ts` and `feedback.ts` next door. The SDK\n * trims a crash with this file before it sends and the mutation trims it\n * again on arrival, so neither side is trusting the other's arithmetic.\n *\n * Nothing here asks a model. A report somebody pressed a button for has to\n * land, and it has to land under the same title as the last twenty copies of\n * the same crash, which is what makes the list one row rather than twenty.\n */\n\n/** What the boundary saw, as it hands it over. */\nexport type Crash = {\n /** The error's own line, without its stack. */\n message: string;\n /** \"TypeError\", when the thrown thing had a name worth keeping. */\n name?: string;\n stack?: string;\n /** The framework's id for an error that happened on the server. */\n digest?: string;\n /** The component stack, when the boundary was handed one. */\n componentStack?: string;\n /** The route it happened on, as the app's own router writes it. */\n route?: string;\n};\n\n/** One line of error, the same cap a diagnostic message gets. */\nexport const CRASH_MESSAGE_MAX_LENGTH = 300;\n\n/** Room for a real stack, matching what one attachment may hold. */\nexport const CRASH_STACK_MAX_LENGTH = 4_000;\n\nexport const CRASH_NAME_MAX_LENGTH = 60;\nexport const CRASH_ROUTE_MAX_LENGTH = 200;\n\n/** When the thrown thing said nothing for itself. */\nconst UNNAMED = \"Unnamed error\";\n\n/**\n * Names that add nothing to a title. Every thrown thing in a browser is an\n * error, so saying so twice only costs room the message wanted.\n */\nconst GENERIC_NAMES = [\"error\", \"exception\"];\n\nfunction trim(value: unknown, limit: number): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const clean = value.trim().slice(0, limit);\n return clean.length > 0 ? clean : undefined;\n}\n\n/** Whatever arrived, cut down to what may be stored. */\nexport function cleanCrash(input: unknown): Crash {\n const raw =\n typeof input === \"object\" && input !== null\n ? (input as Record<string, unknown>)\n : {};\n\n const name = trim(raw.name, CRASH_NAME_MAX_LENGTH);\n const stack = trim(raw.stack, CRASH_STACK_MAX_LENGTH);\n const digest = trim(raw.digest, CRASH_NAME_MAX_LENGTH);\n const componentStack = trim(raw.componentStack, CRASH_STACK_MAX_LENGTH);\n const route = trim(raw.route, CRASH_ROUTE_MAX_LENGTH);\n\n return {\n message: trim(raw.message, CRASH_MESSAGE_MAX_LENGTH) ?? UNNAMED,\n ...(name !== undefined ? { name } : {}),\n ...(stack !== undefined ? { stack } : {}),\n ...(digest !== undefined ? { digest } : {}),\n ...(componentStack !== undefined ? { componentStack } : {}),\n ...(route !== undefined ? { route } : {}),\n };\n}\n\n/**\n * The error on one line: \"TypeError: user.plan is not a function\".\n *\n * The name is dropped when the message already opens with it, which is what\n * a stack's first line looks like when a host passes one straight through.\n */\nexport function crashLabel(crash: Crash): string {\n const name = crash.name;\n if (name === undefined) return crash.message;\n if (GENERIC_NAMES.includes(name.toLowerCase())) return crash.message;\n if (crash.message.toLowerCase().startsWith(name.toLowerCase())) {\n return crash.message;\n }\n return `${name}: ${crash.message}`;\n}\n\n/**\n * How the row reads in the bug list.\n *\n * The route leads, because it is what makes two crashes the same crash: the\n * error's own line can run long and be cut, and what survives the cut should\n * still be the thing that groups them. The exact words matter more here than\n * anywhere else, since the filing mutation dedupes on them.\n */\nexport function crashTitle(crash: Crash): string {\n const label = crashLabel(crash);\n return cleanTitle(\n crash.route === undefined\n ? `Crash: ${label}`\n : `Crash on ${crash.route}: ${label}`,\n );\n}\n\n/** What the team reads first: what broke, and where. */\nexport function crashBody(crash: Crash): string {\n return [\n `The page stopped working${\n crash.route === undefined ? \"\" : ` on ${crash.route}`\n } and somebody reported it from the error screen.`,\n \"\",\n `**Error**: ${crashLabel(crash)}`,\n ...(crash.digest === undefined ? [] : [`**Digest**: ${crash.digest}`]),\n ].join(\"\\n\");\n}\n\n/**\n * The reporter's own line on the report. They pressed a button rather than\n * writing a sentence, so it says what the button did and what it saw.\n */\nexport function crashQuote(crash: Crash): string {\n return `Reported from the error screen: ${crashLabel(crash)}`;\n}\n\n/**\n * The long parts, as attachments rather than as body text: the stack belongs\n * to this one crash, and the body is shared with every other report of it.\n */\nexport function crashAttachments(crash: Crash): { label: string; value: string }[] {\n return [\n ...(crash.stack === undefined\n ? []\n : [{ label: \"Crash trace\", value: crash.stack }]),\n ...(crash.componentStack === undefined\n ? []\n : [{ label: \"Component stack\", value: crash.componentStack }]),\n ];\n}\n","\"use client\";\n\nimport { unwrapMutationResult } from \"@median/backend/mutationResult\";\n\nimport { api } from \"@median/backend\";\nimport { cleanCrash, type Crash } from \"@median/backend/crashes\";\nimport { mergeCustomData } from \"@median/backend/customData\";\nimport { ConvexHttpClient } from \"convex/browser\";\nimport { useSyncExternalStore } from \"react\";\nimport { uploadAttachments, type SentAttachment } from \"@/lib/upload\";\nimport { collectContext, collectPage } from \"./context\";\nimport { restoreCustomData, takeCustomData } from \"./custom-data\";\nimport { MEDIAN_DEPLOYMENT_URL } from \"./deployment\";\nimport { snapshotNow } from \"./diagnostics\";\nimport { hasInstall, readInstall, subscribeInstall } from \"./install\";\nimport { capturePage } from \"./screenshot\";\nimport { getOrCreateSessionId } from \"./session\";\nimport { splitIdentity, type MedianUser } from \"./use-median-thread\";\n\n/**\n * Filing the crash the visitor is looking at.\n *\n * Every other send in the SDK starts with somebody typing. This one starts\n * with a page that has stopped working, which is the report nobody writes:\n * whoever was using it can see that it broke and has nothing to add about\n * why. So the error describes itself, the browser hands over what it was\n * doing, and one press files it.\n *\n * It brings its own connection and reads its key from the last widget that\n * mounted, because by the time this runs the tree that widget lived in has\n * usually been replaced by the error screen calling it. Nothing here needs a\n * component, a provider, or a panel on the page.\n *\n * It never throws. A crash screen that crashes reporting a crash is a poor\n * apology, so everything that can go wrong comes back as a reason instead.\n */\n\nexport type MedianErrorReportOptions = {\n /**\n * The key, when no widget has mounted on this page to remember it. Both\n * panels write theirs down as they mount, so an app with either one\n * installed can leave this alone.\n */\n apiKey?: string;\n /** Who is in the chair. Defaults to whoever the mounted widget was given. */\n user?: MedianUser;\n /**\n * Where it happened, when your router names the page better than its URL\n * does: \"/orders/[id]\" rather than \"/orders/8912\". It leads the title, so\n * the same crash on the same route is one row on the list however many\n * people hit it. Defaults to the path in the address bar.\n */\n route?: string;\n /** React's component stack, when your boundary was handed one. */\n componentStack?: string;\n /** Facts to put on the report, the same shape as `appState`. */\n state?: Record<string, string | number | boolean>;\n /** Blocks to attach, the same shape and limits as `attach`. */\n attach?: Record<string, unknown>;\n /** Whether the report carries what the browser knows. On by default. */\n telemetry?: boolean;\n /**\n * Whether the report carries a picture of the screen it was filed from. On\n * by default: what a crash screen actually looks like is the one thing the\n * stack does not say. Pass `false` and no picture is taken.\n */\n screenshot?: boolean;\n};\n\n/** Where the report went, or why it did not go. */\nexport type MedianErrorReportOutcome =\n | {\n reported: true;\n /** True when somebody had already reported this one. */\n joined: boolean;\n }\n | { reported: false; reason: string };\n\n/** Why a report did not land, in words the host can act on. */\nconst REASONS = {\n noKey:\n \"Median has no API key on this page, so the crash could not be reported. Mount <MedianSupport> or pass apiKey to reportError.\",\n noDeployment:\n \"The Median widget was built without a deployment URL, so the crash could not be reported.\",\n noBrowser: \"reportError only runs in a browser.\",\n off: \"Error reporting is currently unavailable. Try again later.\",\n failed: \"The report could not be sent. Check the connection and try again.\",\n} as const;\n\nlet client: ConvexHttpClient | null = null;\n\n/**\n * Anything thrown, as the fields a report is made of.\n *\n * Hosts throw all sorts. An Error is the common case, a string is the lazy\n * one, and a framework error is an Error with an id stapled to it. All three\n * arrive here and none of them should cost the report.\n */\nfunction crashFrom(error: unknown, options: MedianErrorReportOptions): Crash {\n const raw = (\n typeof error === \"object\" && error !== null ? error : {}\n ) as Record<string, unknown>;\n\n const message =\n error instanceof Error\n ? error.message\n : typeof error === \"string\"\n ? error\n : typeof raw.message === \"string\"\n ? raw.message\n : String(error);\n\n return cleanCrash({\n message,\n name: error instanceof Error ? error.name : raw.name,\n stack: raw.stack,\n // Next puts one on every error that came from the server, and it is the\n // id in that server's own log line.\n digest: raw.digest,\n componentStack: options.componentStack ?? raw.componentStack,\n route: options.route ?? currentRoute(),\n });\n}\n\n/** The page it happened on, minus anything after the path. */\nfunction currentRoute(): string | undefined {\n if (typeof window === \"undefined\") return undefined;\n return window.location.pathname || undefined;\n}\n\n/**\n * The facts that belong beside the browser's, rather than inside the write-up.\n * Both of these read as a row on the report next to the viewport and the page.\n */\nfunction factsOf(crash: Crash): Record<string, string> {\n return {\n ...(crash.route !== undefined ? { route: crash.route } : {}),\n ...(crash.digest !== undefined ? { digest: crash.digest } : {}),\n };\n}\n\nexport async function reportError(\n error: unknown,\n options: MedianErrorReportOptions = {},\n): Promise<MedianErrorReportOutcome> {\n if (typeof window === \"undefined\") {\n return { reported: false, reason: REASONS.noBrowser };\n }\n\n const apiKey = options.apiKey ?? readInstall()?.apiKey;\n if (!apiKey) {\n console.error(REASONS.noKey);\n return { reported: false, reason: REASONS.noKey };\n }\n if (!MEDIAN_DEPLOYMENT_URL) {\n console.error(REASONS.noDeployment);\n return { reported: false, reason: REASONS.noDeployment };\n }\n\n const sessionId = getOrCreateSessionId(apiKey);\n if (sessionId === null) {\n return { reported: false, reason: REASONS.failed };\n }\n\n const crash = crashFrom(error, options);\n const telemetry = options.telemetry ?? true;\n const user = options.user ?? readInstall()?.user;\n const { facts, claim } = splitIdentity(user ?? null);\n\n // Whatever the host attached since the last send, plus whatever they handed\n // this call, drained the way a message drains it so nothing is sent twice.\n const custom = mergeCustomData(\n takeCustomData(),\n Object.entries(options.attach ?? {}).map(([label, value]) => ({\n label,\n value: typeof value === \"string\" ? value : JSON.stringify(value),\n })),\n );\n\n // Read here rather than on mount, like every other send: all of it is\n // wanted as \"what was true when it broke\".\n const context = telemetry ? collectContext() : undefined;\n const page = telemetry ? collectPage() : undefined;\n const diagnostics = snapshotNow({ ...factsOf(crash), ...options.state });\n\n try {\n client ??= new ConvexHttpClient(MEDIAN_DEPLOYMENT_URL);\n const images =\n options.screenshot === false\n ? []\n : await screenshotOf(client, apiKey, sessionId);\n const outcome = await client\n .mutation(api.feedback.reportErrorV2, {\n apiKey,\n sessionId,\n error: crash,\n ...(images.length > 0 ? { images } : {}),\n ...(user ? { user: facts } : {}),\n ...(claim ? { identity: claim } : {}),\n ...(context ? { context } : {}),\n ...(page ? { page } : {}),\n ...(diagnostics ? { diagnostics } : {}),\n ...(custom ? { customData: custom } : {}),\n })\n .then(unwrapMutationResult);\n\n if (outcome === \"off\") {\n if (custom) restoreCustomData(custom);\n return { reported: false, reason: REASONS.off };\n }\n return { reported: true, joined: outcome === \"joined\" };\n } catch (caught) {\n // Back in the queue, so a second press still carries them.\n if (custom) restoreCustomData(custom);\n console.error(\"Median: sending the crash report failed\", caught);\n return { reported: false, reason: REASONS.failed };\n }\n}\n\n/**\n * The screen they are looking at, uploaded, or nothing.\n *\n * Its own try: a picture is the nicest thing on a crash report and the least\n * important. Whatever goes wrong taking or sending it, the report itself\n * still goes.\n */\nasync function screenshotOf(\n client: ConvexHttpClient,\n apiKey: string,\n sessionId: string,\n): Promise<SentAttachment[]> {\n try {\n const shot = await capturePage();\n if (shot === null) return [];\n return await uploadAttachments(\n () =>\n client\n .mutation(api.widget.generateUploadUrlV2, { apiKey, sessionId })\n .then(unwrapMutationResult),\n [shot],\n );\n } catch (caught) {\n console.warn(\n \"Median: the crash report's screenshot could not be sent\",\n caught,\n );\n return [];\n }\n}\n\n/**\n * Whether a report has anywhere to go: a widget has mounted on this page and\n * left its key, and this build knows which Median to send to.\n *\n * For a \"Report error\" button, which should not be on the screen at all if\n * pressing it could only ever fail. It follows the install rather than\n * reading it once, since an error screen can render before the widget does.\n */\nexport function useCanReportError(): boolean {\n const isInstalled = useSyncExternalStore(\n subscribeInstall,\n hasInstall,\n // The server renders nothing installed: it has no page to have mounted\n // on, and a button that flickered away on hydration is worse than one\n // that arrives a moment late.\n () => false,\n );\n\n return isInstalled && Boolean(MEDIAN_DEPLOYMENT_URL);\n}\n","import type { Id } from \"@median/backend/dataModel\";\n\n/**\n * POSTs one file to a one shot Convex upload URL and hands back its storage\n * id. The URL comes from whichever mutation guards the caller, so the same\n * helper serves the inbox, the widget, and the settings page.\n */\nexport async function uploadFile(\n getUploadUrl: () => Promise<string>,\n file: File,\n): Promise<Id<\"_storage\">> {\n const url = await getUploadUrl();\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": file.type || \"application/octet-stream\" },\n body: file,\n });\n if (!response.ok) {\n throw new Error(`Uploading ${file.name} failed with ${response.status}`);\n }\n\n const { storageId } = (await response.json()) as {\n storageId: Id<\"_storage\">;\n };\n return storageId;\n}\n\n/** The shape a message mutation wants a sent file described in. */\nexport type SentAttachment = {\n storageId: Id<\"_storage\">;\n name: string;\n size: number;\n type: string;\n};\n\n/** Uploads a batch in order and describes each one for the send mutation. */\nexport async function uploadAttachments(\n getUploadUrl: () => Promise<string>,\n files: File[],\n): Promise<SentAttachment[]> {\n const attachments: SentAttachment[] = [];\n for (const file of files) {\n attachments.push({\n storageId: await uploadFile(getUploadUrl, file),\n name: file.name,\n size: file.size,\n type: file.type || \"application/octet-stream\",\n });\n }\n return attachments;\n}\n","import type {\n ConversationPage,\n DeviceKind,\n VisitorContext,\n} from \"@median/backend/telemetry\";\n\n/**\n * The handful of things the browser already knows that a support reply needs.\n *\n * Read at send time rather than watched, because all of it is only ever wanted\n * as \"what was true when they wrote\". Nothing is stored, nothing is polled,\n * and nothing leaves the page unless somebody actually gets in touch.\n *\n * The user agent is parsed here rather than sent up raw. A name and a major\n * version is what the inbox shows and all it can act on; the full string is a\n * fingerprint we would then be keeping on somebody else's customers.\n */\n\n/**\n * Browser names in the order they have to be tested.\n *\n * Every one of these lies about being the others. Chrome's string contains\n * Safari, Edge's contains Chrome and Safari, Opera's contains all three, so\n * the only thing that works is checking the most specific claim first and\n * taking the first hit.\n */\nconst BROWSERS: { name: string; token: RegExp }[] = [\n { name: \"Edge\", token: /Edg(?:e|A|iOS)?\\/(\\d+)/ },\n { name: \"Opera\", token: /OPR\\/(\\d+)/ },\n { name: \"Samsung Internet\", token: /SamsungBrowser\\/(\\d+)/ },\n { name: \"Firefox\", token: /(?:Firefox|FxiOS)\\/(\\d+)/ },\n { name: \"Chrome\", token: /(?:Chrome|CriOS)\\/(\\d+)/ },\n { name: \"Safari\", token: /Version\\/(\\d+).*Safari/ },\n];\n\nconst OPERATING_SYSTEMS: { name: string; token: RegExp }[] = [\n // Before Android, whose string also says Linux.\n { name: \"Android\", token: /Android/ },\n { name: \"iOS\", token: /iPhone|iPad|iPod/ },\n { name: \"macOS\", token: /Mac OS X|Macintosh/ },\n { name: \"Windows\", token: /Windows NT/ },\n { name: \"ChromeOS\", token: /CrOS/ },\n { name: \"Linux\", token: /Linux/ },\n];\n\nfunction browserOf(ua: string): string | undefined {\n for (const { name, token } of BROWSERS) {\n const match = ua.match(token);\n if (match) return match[1] ? `${name} ${match[1]}` : name;\n }\n return undefined;\n}\n\nfunction osOf(ua: string): string | undefined {\n return OPERATING_SYSTEMS.find(({ token }) => token.test(ua))?.name;\n}\n\n/**\n * Desktop, tablet, or phone.\n *\n * An iPad reports itself as a Mac and has done for years, so the touch check\n * is what tells the two apart. Everything else is honest enough about it.\n */\nfunction deviceOf(ua: string): DeviceKind {\n const isTouchMac =\n /Macintosh/.test(ua) && navigator.maxTouchPoints > 1;\n\n if (/iPad|Tablet/.test(ua) || isTouchMac) return \"tablet\";\n if (/Mobi|iPhone|iPod|Android.*Mobile/.test(ua)) return \"mobile\";\n if (/Android/.test(ua)) return \"tablet\";\n return \"desktop\";\n}\n\n/** What the browser will admit to, or nothing if it is not a browser at all. */\nexport function collectContext(): VisitorContext | undefined {\n if (typeof navigator === \"undefined\" || typeof window === \"undefined\") {\n return undefined;\n }\n\n const ua = navigator.userAgent ?? \"\";\n const { width, height } = window.screen ?? { width: 0, height: 0 };\n\n const context: VisitorContext = {\n // Wrapped: a browser with a broken or absent Intl is rare but real, and\n // failing to work out a time zone should not stop a message sending.\n timeZone: safely(() => Intl.DateTimeFormat().resolvedOptions().timeZone),\n locale: navigator.language || undefined,\n browser: browserOf(ua),\n os: osOf(ua),\n device: deviceOf(ua),\n // The multiplication sign rather than an x, since this is being read as a\n // measurement rather than run as one.\n screen: width && height ? `${width} × ${height}` : undefined,\n };\n\n return Object.values(context).some((one) => one !== undefined)\n ? context\n : undefined;\n}\n\n/**\n * The page they are writing from.\n *\n * The query string is dropped on the way out, before it is ever sent, and\n * again on the way in. A support agent needs to know somebody was on the\n * checkout page; the password reset token that got them there is not ours to\n * put in somebody's inbox.\n */\nexport function collectPage(): ConversationPage | undefined {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return undefined;\n }\n\n const url = withoutQuery(window.location.href);\n if (url === undefined) return undefined;\n\n return {\n url,\n title: document.title || undefined,\n referrer: withoutQuery(document.referrer),\n };\n}\n\nfunction withoutQuery(href: string): string | undefined {\n if (!href) return undefined;\n try {\n const url = new URL(href);\n if (url.protocol !== \"https:\" && url.protocol !== \"http:\") return undefined;\n return `${url.origin}${url.pathname}`;\n } catch {\n return undefined;\n }\n}\n\nfunction safely<T>(read: () => T): T | undefined {\n try {\n return read();\n } catch {\n return undefined;\n }\n}\n","/**\n * Which Median the widget talks to.\n *\n * Deliberately not a prop. A host plugs in their API key and that is the whole\n * install; our address is our business, not something they should have to know\n * or keep in sync. It reads from the env while the widget ships inside this\n * app, and the published package bakes it in at build time.\n */\nexport const MEDIAN_DEPLOYMENT_URL = process.env.NEXT_PUBLIC_CONVEX_URL;\n","/**\n * What was going wrong around a bug report, from both ends.\n *\n * The client half is collected by the widget SDK: uncaught errors, rejected\n * promises, failed requests, and whatever the host chose to tell us about\n * their own app. The server half is asked for at report time, from the same\n * endpoint the agent's tools live at, so a team that has already wired up\n * tools gets it for nothing.\n *\n * Pure on purpose. The widget cleans what it collects with this file before\n * it sends, and the backend cleans it again with the same file before it\n * stores, so neither side is trusting the other's arithmetic.\n *\n * The rule everywhere here is the one the rest of the telemetry follows: a\n * fact about a program, never a fact about a person. Nothing reads cookies,\n * nothing reads storage, and every URL loses its query string on the way.\n */\n\n/**\n * Where one entry came from. `error` is an uncaught exception, `rejection`\n * is a promise nobody caught, `console` is a console.error, and `network` is\n * a request that failed or came back unhappy.\n */\nexport const DIAGNOSTIC_KINDS = [\n \"error\",\n \"rejection\",\n \"console\",\n \"network\",\n] as const;\n\nexport type DiagnosticKind = (typeof DIAGNOSTIC_KINDS)[number];\n\nexport type DiagnosticEvent = {\n at: number;\n kind: DiagnosticKind;\n message: string;\n /** The file, or the request's method and path. Never a query string. */\n source?: string;\n /** The first frames, for an exception. */\n stack?: string;\n /** The HTTP status, on a network entry that got one. */\n status?: number;\n};\n\nexport type ClientDiagnostics = {\n /** When the snapshot was taken, by the browser's own clock. */\n at: number;\n /** The page they were on. Query string already dropped. */\n url?: string;\n /** \"1512 × 982\", already formatted, like the rest of the telemetry. */\n viewport?: string;\n /** Whatever the host declared about their own app: version, route, flags. */\n state?: Record<string, string>;\n events: DiagnosticEvent[];\n};\n\n/** One tool endpoint's answer to \"how are you\". */\nexport type ServerDiagnostics = {\n /** Which endpoint answered, so a team with two can tell them apart. */\n url: string;\n ok: boolean;\n /** What it said, as JSON text, or why it could not be asked. */\n detail: string;\n};\n\n/** The most entries one snapshot carries. Newest kept, oldest dropped. */\nexport const MAX_EVENTS = 12;\n\nexport const MESSAGE_MAX_LENGTH = 300;\nexport const SOURCE_MAX_LENGTH = 200;\nexport const STACK_MAX_LENGTH = 800;\n\n/** The host's own state, bounded the same way visitor metadata is. */\nexport const MAX_STATE_ENTRIES = 20;\nexport const STATE_KEY_MAX_LENGTH = 60;\nexport const STATE_VALUE_MAX_LENGTH = 200;\n\n/** What one endpoint may say for itself. Enough to hold a health payload. */\nexport const SERVER_DETAIL_MAX_LENGTH = 2_000;\n\n/** How long the widget waits before deciding an endpoint is not answering. */\nexport const SERVER_TIMEOUT_MS = 8_000;\n\nfunction trim(value: unknown, limit: number): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const clean = value.trim().slice(0, limit);\n return clean.length > 0 ? clean : undefined;\n}\n\n/** A URL with its query string taken off, or nothing if it is not one. */\nexport function withoutQuery(href: unknown): string | undefined {\n if (typeof href !== \"string\" || href.length === 0) return undefined;\n try {\n const url = new URL(href);\n if (url.protocol !== \"https:\" && url.protocol !== \"http:\") return undefined;\n return `${url.origin}${url.pathname}`.slice(0, SOURCE_MAX_LENGTH);\n } catch {\n // Relative paths and the like arrive here. The path is the useful half\n // anyway, so it is kept as written, minus anything after a \"?\".\n return trim(href.split(\"?\")[0], SOURCE_MAX_LENGTH);\n }\n}\n\nfunction cleanEvent(input: unknown): DiagnosticEvent | null {\n if (typeof input !== \"object\" || input === null) return null;\n const raw = input as Record<string, unknown>;\n\n const kind = DIAGNOSTIC_KINDS.find((one) => one === raw.kind);\n const message = trim(raw.message, MESSAGE_MAX_LENGTH);\n if (kind === undefined || message === undefined) return null;\n\n const status =\n typeof raw.status === \"number\" && Number.isFinite(raw.status)\n ? Math.trunc(raw.status)\n : undefined;\n\n return {\n at: typeof raw.at === \"number\" && Number.isFinite(raw.at) ? raw.at : 0,\n kind,\n message,\n ...(trim(raw.source, SOURCE_MAX_LENGTH) !== undefined\n ? { source: trim(raw.source, SOURCE_MAX_LENGTH)! }\n : {}),\n ...(trim(raw.stack, STACK_MAX_LENGTH) !== undefined\n ? { stack: trim(raw.stack, STACK_MAX_LENGTH)! }\n : {}),\n ...(status !== undefined ? { status } : {}),\n };\n}\n\nfunction cleanState(input: unknown): Record<string, string> | undefined {\n if (typeof input !== \"object\" || input === null) return undefined;\n\n const state: Record<string, string> = {};\n for (const [key, value] of Object.entries(input).slice(0, MAX_STATE_ENTRIES)) {\n if (value === null || value === undefined) continue;\n const label = trim(key, STATE_KEY_MAX_LENGTH);\n // Numbers and booleans are the other two things anybody puts in here, so\n // they are written out rather than refused.\n const written =\n typeof value === \"string\"\n ? trim(value, STATE_VALUE_MAX_LENGTH)\n : typeof value === \"number\" || typeof value === \"boolean\"\n ? String(value).slice(0, STATE_VALUE_MAX_LENGTH)\n : undefined;\n if (label === undefined || written === undefined) continue;\n state[label] = written;\n }\n\n return Object.keys(state).length > 0 ? state : undefined;\n}\n\n/**\n * Whatever arrived, cut down to what may be stored. Undefined when there was\n * nothing worth keeping, so an empty snapshot never lands on a row and reads\n * later as \"we looked and the page was fine\".\n */\nexport function cleanClientDiagnostics(\n input: unknown,\n): ClientDiagnostics | undefined {\n if (typeof input !== \"object\" || input === null) return undefined;\n const raw = input as Record<string, unknown>;\n\n const events = Array.isArray(raw.events)\n ? raw.events\n .map(cleanEvent)\n .filter((one): one is DiagnosticEvent => one !== null)\n .slice(-MAX_EVENTS)\n : [];\n\n const url = withoutQuery(raw.url);\n const viewport = trim(raw.viewport, 40);\n const state = cleanState(raw.state);\n\n if (events.length === 0 && state === undefined && url === undefined) {\n return undefined;\n }\n\n return {\n at: typeof raw.at === \"number\" && Number.isFinite(raw.at) ? raw.at : 0,\n ...(url !== undefined ? { url } : {}),\n ...(viewport !== undefined ? { viewport } : {}),\n ...(state !== undefined ? { state } : {}),\n events,\n };\n}\n\n/** Put browser events on the receipt clock while preserving their relative ages. */\nexport function diagnosticEventsAtReceipt(\n snapshot: ClientDiagnostics,\n receivedAt: number,\n): DiagnosticEvent[] {\n if (!Number.isFinite(snapshot.at) || !Number.isFinite(receivedAt)) return [];\n return snapshot.events\n .filter((event) => Number.isFinite(event.at) && event.at <= snapshot.at + 60_000)\n .map((event) => ({\n ...event,\n at: receivedAt - Math.max(0, snapshot.at - event.at),\n }));\n}\n\n/** One endpoint's answer, trimmed to something a card can show. */\nexport function cleanServerDetail(text: string): string {\n return text.length > SERVER_DETAIL_MAX_LENGTH\n ? `${text.slice(0, SERVER_DETAIL_MAX_LENGTH)}…`\n : text;\n}\n","\"use client\";\n\nimport {\n cleanClientDiagnostics,\n MAX_EVENTS,\n MESSAGE_MAX_LENGTH,\n SOURCE_MAX_LENGTH,\n STACK_MAX_LENGTH,\n withoutQuery,\n type ClientDiagnostics,\n type DiagnosticEvent,\n} from \"@median/backend/diagnostics\";\nimport { useCallback, useEffect, useRef } from \"react\";\n\n/**\n * What has been going wrong on the page, kept ready in case somebody asks\n * for help about it.\n *\n * A support agent's first three questions are always the same: what were you\n * doing, what did it say, and what does the console show. The customer can\n * answer the first one. This answers the other two, so a bug report arrives\n * with the error already attached rather than after four messages of asking.\n *\n * Off unless a host switches it on. Everything here reads their program's\n * error stream, which is theirs to offer rather than ours to take.\n *\n * A bounded ring buffer and nothing else. Nothing is sent on its own, nothing\n * is stored, and nothing is polled: the newest snapshot rides the next message\n * the customer sends, and only when there is something new in it.\n */\n\n/** What a host can switch on. `true` is the passive half. */\nexport type MedianDiagnostics =\n | boolean\n | {\n /** Uncaught errors and rejected promises. On whenever diagnostics are. */\n errors?: boolean;\n /** Requests that failed or came back unhappy. Wraps window.fetch. */\n network?: boolean;\n /** console.error calls. Wraps console.error. */\n console?: boolean;\n };\n\ntype Settings = { errors: boolean; network: boolean; console: boolean };\n\nfunction settingsOf(option: MedianDiagnostics | undefined): Settings | null {\n if (option === undefined || option === false) return null;\n if (option === true) return { errors: true, network: false, console: false };\n return {\n errors: option.errors ?? true,\n network: option.network ?? false,\n console: option.console ?? false,\n };\n}\n\n/**\n * The buffer, and how many things have gone into it.\n *\n * Module level rather than per component: a page has one widget, and an\n * error that happened before React mounted it is exactly the kind worth\n * keeping. The count is what lets a sender ask \"anything new since last\n * time\" without comparing arrays.\n */\nconst events: DiagnosticEvent[] = [];\nlet recorded = 0;\n\n/**\n * The host's own state, as the last widget given any was given it. Here\n * rather than only in the hook's ref because a crash report is sent from a\n * tree where that hook no longer exists, and the route a page fell over on\n * is exactly the sort of thing hosts put in `appState`. A panel given none\n * leaves it alone, so the feedback panel cannot blank the widget's.\n */\nlet latestState: Record<string, string | number | boolean> | undefined;\n\nfunction record(event: DiagnosticEvent): void {\n events.push(event);\n if (events.length > MAX_EVENTS) events.shift();\n recorded += 1;\n}\n\nfunction messageOf(value: unknown): string {\n if (value instanceof Error) return value.message.slice(0, MESSAGE_MAX_LENGTH);\n if (typeof value === \"string\") return value.slice(0, MESSAGE_MAX_LENGTH);\n try {\n return JSON.stringify(value).slice(0, MESSAGE_MAX_LENGTH);\n } catch {\n return String(value).slice(0, MESSAGE_MAX_LENGTH);\n }\n}\n\nfunction stackOf(value: unknown): string | undefined {\n return value instanceof Error && typeof value.stack === \"string\"\n ? value.stack.slice(0, STACK_MAX_LENGTH)\n : undefined;\n}\n\n/** Puts one kind of listening in place, and gives back how to take it off. */\ntype Installer = () => () => void;\n\nconst INSTALLERS: Record<keyof Settings, Installer> = {\n errors: () => {\n const onError = (event: ErrorEvent) => {\n // A failed image or script fires here too, with no error on it. Those\n // are the browser's business, not a bug report's.\n if (!event.error && !event.message) return;\n record({\n at: Date.now(),\n kind: \"error\",\n message: messageOf(event.error ?? event.message),\n ...(event.filename\n ? {\n source:\n `${withoutQuery(event.filename) ?? \"\"}:${event.lineno}`.slice(\n 0,\n SOURCE_MAX_LENGTH,\n ),\n }\n : {}),\n ...(stackOf(event.error) !== undefined\n ? { stack: stackOf(event.error)! }\n : {}),\n });\n };\n const onRejection = (event: PromiseRejectionEvent) => {\n record({\n at: Date.now(),\n kind: \"rejection\",\n message: messageOf(event.reason),\n ...(stackOf(event.reason) !== undefined\n ? { stack: stackOf(event.reason)! }\n : {}),\n });\n };\n\n window.addEventListener(\"error\", onError);\n window.addEventListener(\"unhandledrejection\", onRejection);\n return () => {\n window.removeEventListener(\"error\", onError);\n window.removeEventListener(\"unhandledrejection\", onRejection);\n };\n },\n\n network: () => {\n if (typeof window.fetch !== \"function\") return () => {};\n const original = window.fetch;\n window.fetch = async function medianFetch(this: unknown, ...args) {\n const [input, init] = args;\n const method = (init?.method ?? \"GET\").toUpperCase();\n const url =\n typeof input === \"string\"\n ? input\n : input instanceof URL\n ? input.href\n : input.url;\n\n try {\n const response = await original.apply(this as never, args);\n if (!response.ok) {\n record({\n at: Date.now(),\n kind: \"network\",\n message: `${method} ${response.status}`,\n source: `${method} ${withoutQuery(url) ?? \"\"}`.slice(\n 0,\n SOURCE_MAX_LENGTH,\n ),\n status: response.status,\n });\n }\n return response;\n } catch (thrown) {\n record({\n at: Date.now(),\n kind: \"network\",\n message: messageOf(thrown),\n source: `${method} ${withoutQuery(url) ?? \"\"}`.slice(\n 0,\n SOURCE_MAX_LENGTH,\n ),\n });\n // Rethrown untouched. Watching a request is not catching it.\n throw thrown;\n }\n } as typeof window.fetch;\n\n return () => {\n window.fetch = original;\n };\n },\n\n console: () => {\n const original = console.error;\n console.error = function medianConsoleError(...args: unknown[]) {\n record({\n at: Date.now(),\n kind: \"console\",\n message: args.map(messageOf).join(\" \").slice(0, MESSAGE_MAX_LENGTH),\n ...(stackOf(args.find((one) => one instanceof Error)) !== undefined\n ? { stack: stackOf(args.find((one) => one instanceof Error))! }\n : {}),\n });\n original.apply(console, args);\n };\n\n return () => {\n console.error = original;\n };\n },\n};\n\n/**\n * How many mounted components want each kind, and how to take each off.\n * Counted rather than installed per component: a page with the widget and\n * the feedback panel both collecting would otherwise wrap fetch twice and\n * record every event twice.\n */\nconst wanted: Record<keyof Settings, number> = {\n errors: 0,\n network: 0,\n console: 0,\n};\nconst removers: Partial<Record<keyof Settings, () => void>> = {};\n\n/**\n * Installs whatever the host asked for, and gives back the way to take it all\n * off again. Every wrapper calls through to what it replaced first, so a page\n * behaves exactly as it did before the widget arrived.\n */\nfunction collect(settings: Settings): () => void {\n const kinds = (Object.keys(settings) as (keyof Settings)[]).filter(\n (kind) => settings[kind],\n );\n for (const kind of kinds) {\n wanted[kind] += 1;\n if (wanted[kind] === 1) removers[kind] = INSTALLERS[kind]();\n }\n return () => {\n for (const kind of kinds) {\n wanted[kind] -= 1;\n if (wanted[kind] > 0) continue;\n removers[kind]?.();\n delete removers[kind];\n }\n };\n}\n\n/** Everything worth saying about the page right now, or nothing. */\nfunction snapshot(\n state: Record<string, string | number | boolean> | undefined,\n): ClientDiagnostics | undefined {\n if (typeof window === \"undefined\") return undefined;\n\n return cleanClientDiagnostics({\n at: Date.now(),\n url: window.location.href,\n viewport: `${window.innerWidth} × ${window.innerHeight}`,\n state,\n events,\n });\n}\n\n/**\n * Everything the page can say for itself right now, for a send that cannot\n * wait to be asked twice.\n *\n * The hook's `take` answers with nothing when nothing has been recorded since\n * the last send, which is right for a chat message and wrong for a crash\n * report: the viewport and the route are worth having whether or not the ring\n * buffer moved. So this one always answers, and takes anything the caller\n * knows that the host's own state does not.\n */\nexport function snapshotNow(\n extraState?: Record<string, string | number | boolean>,\n): ClientDiagnostics | undefined {\n // The caller's facts first: the report keeps only the first 20 entries,\n // and a host with a full appState would otherwise push the crash's own\n // route and digest off the end. They still win a clash of keys.\n const merged = { ...extraState };\n for (const [key, value] of Object.entries(latestState ?? {})) {\n if (!Object.prototype.hasOwnProperty.call(merged, key)) {\n merged[key] = value;\n }\n }\n return snapshot(merged);\n}\n\n/**\n * The collector, as a component uses it: on while the host wants it, off\n * again when they change their mind or the widget leaves the page.\n *\n * `take` is what a send calls. It answers with a snapshot the first time and\n * whenever something new has been recorded since, and with nothing the rest\n * of the time, so a long conversation on a healthy page sends the state once\n * rather than on every line.\n */\nexport function useDiagnostics(\n option: MedianDiagnostics | undefined,\n appState: Record<string, string | number | boolean> | undefined,\n) {\n const settings = settingsOf(option);\n // Serialised so a host building the object inline does not reinstall the\n // wrappers on every render. The same trick `user` gets in the thread hook.\n const key = settings === null ? null : JSON.stringify(settings);\n\n // Read at send time rather than closed over, since a route or a flag can\n // change between the widget mounting and somebody typing. Written after\n // the render rather than during it: nothing on screen depends on it, and a\n // ref written mid-render is a render that cannot be replayed.\n const stateRef = useRef(appState);\n // What this component last put in `latestState`, so a host that stops\n // passing a state, as on sign out, takes it back rather than leaving the\n // old one for the next crash report. A component that never passed one\n // leaves the others' alone.\n const shared = useRef<typeof appState>(undefined);\n useEffect(() => {\n stateRef.current = appState;\n if (appState !== undefined) {\n latestState = appState;\n } else if (shared.current !== undefined && latestState === shared.current) {\n latestState = undefined;\n }\n shared.current = appState;\n });\n\n const sentAt = useRef<number | null>(null);\n\n useEffect(() => {\n if (key === null) return;\n return collect(JSON.parse(key) as Settings);\n }, [key]);\n\n const take = useCallback((): ClientDiagnostics | undefined => {\n if (key === null) return undefined;\n if (sentAt.current !== null && sentAt.current === recorded)\n return undefined;\n\n sentAt.current = recorded;\n return snapshot(stateRef.current);\n }, [key]);\n\n return take;\n}\n","\"use client\";\n\nimport { useEffect } from \"react\";\nimport type { MedianUser } from \"./use-median-thread\";\n\n/**\n * What the install on this page said, kept where a crash can still reach it.\n *\n * Everything else in the SDK reads its API key and its visitor from props,\n * which is fine while there is a widget on the page to hold them. A crash is\n * the case where there is not: the boundary that catches it has replaced the\n * tree the widget was mounted in, and in the worst of them, the root layout,\n * there is nothing of the app left at all.\n *\n * Module level for the same reason the diagnostics buffer is: it outlives\n * every React tree on the page. A panel writes here as it mounts, and\n * `reportError` reads it long after that panel has been unmounted by the\n * error it is reporting.\n */\n\ntype Install = { apiKey: string; user?: MedianUser };\n\nlet install: Install | null = null;\n\nconst listeners = new Set<() => void>();\n\n/** Called by whichever widget mounted. The newest one wins. */\nexport function rememberInstall(next: Install): void {\n // Only a new key is news. The panels write here after every render, and a\n // report button that re-rendered on each of them would be a waste of a\n // subscription.\n const isNews = install?.apiKey !== next.apiKey;\n install = { apiKey: next.apiKey, ...(next.user ? { user: next.user } : {}) };\n if (!isNews) return;\n for (const listener of listeners) listener();\n}\n\n/** What the last mounted widget was given, or nothing if none ever was. */\nexport function readInstall(): Install | null {\n return install;\n}\n\n/**\n * Whether anything has been installed yet, as an external store.\n *\n * A report button rendered by an error screen can easily be on the page one\n * commit before the widget whose key it needs: a page that failed on the\n * server paints its error boundary and its layout together. Reading this\n * live is what lets that button appear the moment reporting becomes\n * possible, rather than sitting hidden until something else re-renders.\n */\nexport function subscribeInstall(listener: () => void): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\nexport function hasInstall(): boolean {\n return install !== null;\n}\n\n/**\n * Both panels call this as they mount, and again whenever the host changes\n * its mind about who is in the chair.\n *\n * Written after the render rather than during it, and on every one rather\n * than behind a dependency list, because `user` is an object a host builds\n * inline and the write is one assignment.\n */\nexport function useRememberInstall(\n apiKey: string | undefined,\n user?: MedianUser,\n): void {\n useEffect(() => {\n // Nothing to write down yet: a key from an identity route that has not\n // answered is not an install, and a crash before it answers has no key.\n if (apiKey === undefined) return;\n rememberInstall({ apiKey, ...(user ? { user } : {}) });\n });\n}\n","\"use client\";\n\n/**\n * A picture of the page, taken as a report is sent or as the agent asks.\n *\n * Most bug reports are a sentence about something the writer can see and the\n * reader cannot. So the page comes along: what was on the screen, laid out\n * the way it was laid out, at the moment they pressed send. In the chat the\n * agent decides instead, and asks for one when words are not settling it.\n *\n * Nothing is recorded and nothing is watched. This runs once per report and\n * once per ask, and the picture goes with that one report or into that one\n * thread. Hosts who would rather it did not can pass `screenshot={false}` to\n * any of the panels.\n *\n * The page is redrawn from the DOM rather than read off the screen, because\n * the other way round means asking the browser for permission to record, and\n * a \"share your screen\" dialog in front of a bug report is a bug report\n * nobody files. The cost is fidelity: a cross-origin image, a canvas or a\n * video can come out blank, and a font that will not load is drawn in\n * whatever the browser falls back to.\n *\n * It never throws and never blocks for long. A picture that cannot be taken\n * is one the report goes without.\n */\n\n/** The widest picture worth sending. Past this it is a bigger upload, not a clearer page. */\nconst MAX_WIDTH = 1440;\n\n/**\n * How long a capture may take before the report goes without one. Generous,\n * because a page full of images spends most of it fetching them, and mean\n * enough that nobody is left watching a send that will not finish.\n */\nconst TIMEOUT_MS = 6_000;\n\n/** JPEG rather than PNG: a page is a photograph's worth of pixels, not an icon's. */\nconst TYPE = \"image/jpeg\";\nconst QUALITY = 0.85;\n\n/** What the picture is called wherever it lands. */\nexport const SCREENSHOT_NAME = \"screenshot.jpg\";\n\n/**\n * Where a pinned element sits on the screen, written onto it for as long as\n * the capture takes. See `pinFixed`.\n */\nconst PIN_ATTRIBUTE = \"data-median-pin\";\n\n/**\n * The page as it looks right now, or null.\n *\n * Null covers every way this can go wrong: no browser, no page, a capture\n * that took too long, a library that would not load, a canvas the browser\n * refused to read. The caller sends the report either way.\n */\nexport async function capturePage(): Promise<File | null> {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return null;\n }\n const page = document.documentElement;\n if (page === null) return null;\n\n try {\n const blob = await Promise.race([\n draw(page),\n new Promise<null>((resolve) => setTimeout(() => resolve(null), TIMEOUT_MS)),\n ]);\n if (blob === null) return null;\n return new File([blob], SCREENSHOT_NAME, { type: TYPE });\n } catch (caught) {\n // A report that arrives without its picture is worth far more than one\n // that fails to send because of it, so this is the end of it.\n console.warn(\"Median: taking a screenshot for this report failed\", caught);\n return null;\n }\n}\n\n/** The viewport, drawn from the page under it. */\nasync function draw(page: HTMLElement): Promise<Blob> {\n // Loaded here rather than at the top of the file so the renderer is\n // fetched by the people who send reports, not by everybody who loads a\n // page with a widget on it.\n const { domToBlob } = await import(\"modern-screenshot\");\n\n const width = page.clientWidth || window.innerWidth;\n const height = page.clientHeight || window.innerHeight;\n // Noted before the drawing starts, and taken off the page after it,\n // however it goes.\n const unpin = pinFixed();\n\n try {\n return await domToBlob(page, {\n width,\n height,\n // Their view, not the whole document: the page is drawn in full and\n // the part they are looking at is what the frame is put around. A page\n // held still by `usePageLock` has already been scrolled to nothing,\n // and is shifted by nothing here.\n style: {\n transform: `translate(${-window.scrollX}px, ${-window.scrollY}px)`,\n transformOrigin: \"top left\",\n },\n scale: Math.min(1, MAX_WIDTH / Math.max(width, 1)),\n type: TYPE,\n quality: QUALITY,\n // JPEG has no transparency, so anything clear would come out black.\n backgroundColor: pageBackground(),\n filter: notOurs,\n onCloneNode: placePinned,\n timeout: TIMEOUT_MS,\n features: { copyScrollbar: false },\n });\n } finally {\n unpin();\n }\n}\n\n/**\n * The bars that float over the page, noted down before it is drawn.\n *\n * A fixed element is placed against the viewport, and the drawing has no\n * viewport to place it against: it lands wherever it falls in a page that is\n * suddenly its own full height, which for a header at the top of the screen\n * means the top of the document and out of the frame. So each one's place on\n * the screen is written onto it, read back off the copy, and set there.\n *\n * The attribute is on the host's own elements for as long as the capture\n * takes and is taken off again whatever happens.\n */\nfunction pinFixed(): () => void {\n const pinned: Element[] = [];\n for (const element of document.body.querySelectorAll(\"*\")) {\n if (window.getComputedStyle(element).position !== \"fixed\") continue;\n const box = element.getBoundingClientRect();\n if (box.width === 0 || box.height === 0) continue;\n element.setAttribute(\n PIN_ATTRIBUTE,\n [box.left, box.top, box.width, box.height].join(\",\"),\n );\n pinned.push(element);\n }\n return () => {\n for (const element of pinned) element.removeAttribute(PIN_ATTRIBUTE);\n };\n}\n\n/** Each one put back where it was on the screen, in the copy about to be drawn. */\nfunction placePinned(clone: Node): void {\n if (!(clone instanceof Element)) return;\n\n for (const element of clone.querySelectorAll(`[${PIN_ATTRIBUTE}]`)) {\n const [left, top, width, height] = (\n element.getAttribute(PIN_ATTRIBUTE) ?? \"\"\n )\n .split(\",\")\n .map(Number);\n element.removeAttribute(PIN_ATTRIBUTE);\n if (!(element instanceof HTMLElement) || Number.isNaN(top)) continue;\n\n // Still fixed, so it hangs off the page rather than off whichever\n // ancestor happens to be positioned, and moved into the part of the\n // page the frame is around.\n element.style.setProperty(\"left\", `${left + window.scrollX}px`, \"important\");\n element.style.setProperty(\"top\", `${top + window.scrollY}px`, \"important\");\n element.style.setProperty(\"right\", \"auto\", \"important\");\n element.style.setProperty(\"bottom\", \"auto\", \"important\");\n element.style.setProperty(\"width\", `${width}px`, \"important\");\n element.style.setProperty(\"height\", `${height}px`, \"important\");\n element.style.setProperty(\"margin\", \"0\", \"important\");\n }\n}\n\n/**\n * Our own widgets, left out of the picture.\n *\n * The panel they are writing in is usually sitting over the thing they are\n * writing about, and a screenshot of our own panel tells the reader nothing.\n * Every root the SDK renders carries `data-median-widget`, and excluding one\n * excludes everything inside it.\n */\nfunction notOurs(node: Node): boolean {\n return !(\n node instanceof Element && node.hasAttribute(\"data-median-widget\")\n );\n}\n\n/**\n * What the page is painted on. Body first: html is transparent by default,\n * and a host who set a colour usually set it there.\n */\nfunction pageBackground(): string {\n for (const element of [document.body, document.documentElement]) {\n if (element === null) continue;\n const color = window.getComputedStyle(element).backgroundColor;\n if (color && !isClear(color)) return color;\n }\n return \"#ffffff\";\n}\n\n/** Whether a computed colour would paint nothing. */\nfunction isClear(color: string): boolean {\n const clear = color.replace(/\\s/g, \"\");\n return clear === \"transparent\" || clear === \"rgba(0,0,0,0)\";\n}\n","/**\n * Who this browser is, as far as the widget cares: a random token minted on\n * first visit and kept in localStorage, one per API key, so coming back\n * reopens the same conversation without anyone signing in.\n *\n * A small store rather than a bare read, so `resetSessions` can tell a\n * mounted widget its token is gone and the thread re-reads through\n * `useSyncExternalStore`.\n */\n\nconst STORAGE_PREFIX = \"median:session:\";\n\n/**\n * When localStorage is blocked, the token lives here instead: gone on\n * reload, but stable within the page, which is what the reads below need.\n */\nconst inMemorySessions = new Map<string, string>();\n\nconst listeners = new Set<() => void>();\n\n/** How many times `resetSessions` has run on this page. */\nlet resetCount = 0;\n\n/** Read as a store snapshot by anything that re-reads on a reset. */\nexport function getResetCount(): number {\n return resetCount;\n}\n\nexport function subscribeSession(listener: () => void) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\n/**\n * Stable across calls on purpose: this is read as an external-store snapshot,\n * so two calls in a row must agree.\n */\nexport function getOrCreateSessionId(apiKey: string): string | null {\n if (typeof window === \"undefined\") return null;\n\n const storageKey = `${STORAGE_PREFIX}${apiKey}`;\n try {\n const existing = window.localStorage.getItem(storageKey);\n if (existing && existing.length >= 8) return existing;\n\n const created = crypto.randomUUID();\n window.localStorage.setItem(storageKey, created);\n return created;\n } catch {\n // Storage can be blocked entirely. A session that lasts one page load\n // still beats no support at all.\n const remembered = inMemorySessions.get(apiKey) ?? crypto.randomUUID();\n inMemorySessions.set(apiKey, remembered);\n return remembered;\n }\n}\n\n/**\n * Forget every token this browser holds. The next read mints a fresh one, so\n * the next message starts a conversation with nothing behind it.\n *\n * This is for sign out: the conversation belongs to the person who was in the\n * chair, not to the chair. The old thread stays in the team's inbox; it just\n * stops being reachable from this browser.\n */\nexport function resetSessions() {\n resetCount += 1;\n inMemorySessions.clear();\n\n if (typeof window !== \"undefined\") {\n try {\n const stale: string[] = [];\n for (let i = 0; i < window.localStorage.length; i += 1) {\n const key = window.localStorage.key(i);\n if (key !== null && key.startsWith(STORAGE_PREFIX)) stale.push(key);\n }\n for (const key of stale) window.localStorage.removeItem(key);\n } catch {\n // Storage was blocked, so there was nothing durable to forget.\n }\n }\n\n for (const listener of listeners) listener();\n}\n","\"use client\";\n\nimport { api } from \"@median/backend\";\nimport { TYPING_TTL_MS } from \"@median/backend/support\";\nimport { useMutation, useQuery } from \"convex/react\";\nimport { useWidgetMutation } from \"./use-widget-mutation\";\nimport {\n useCallback,\n useEffect,\n useMemo,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport { attachmentKindOf, formatFileSize } from \"@/components/attachments\";\nimport { useNow } from \"@/hooks/use-now\";\nimport { errorMessage } from \"@/lib/errors\";\nimport { formatClockTime, formatDayLabel, isNewDay } from \"@/lib/time\";\nimport { uploadAttachments, type SentAttachment } from \"@/lib/upload\";\nimport type {\n SupportAgent,\n SupportAsk,\n SupportMessage,\n SupportPresence,\n SupportRating,\n} from \"../types\";\nimport { collectContext, collectPage } from \"./context\";\nimport { restoreCustomData, takeCustomData } from \"./custom-data\";\nimport { useDiagnostics, type MedianDiagnostics } from \"./diagnostics\";\nimport { readSkippedRatings, writeSkippedRating } from \"./rating-skips\";\nimport { isUnreadReply, widgetReadReceipts } from \"./read-state\";\nimport { getOrCreateSessionId, subscribeSession } from \"./session\";\nimport { useScreenshotAsk } from \"./use-screenshot-ask\";\nimport { useVisitorEmail } from \"./visitor-email\";\n\n/** What a host can tell us about who is in the widget. All of it optional. */\nexport type MedianUser = {\n /**\n * Your own id for this person, and that id signed from MEDIAN_KEY.\n *\n * The pair is what makes a conversation follow somebody rather than a\n * browser: sign in on a phone and the thread from the laptop is already\n * there. Both or neither, and the signing belongs on your server, because\n * whoever holds the key can claim to be any of your customers.\n *\n * Without them the widget still works. It is just this browser's thread,\n * which is what it has always been.\n */\n id?: string;\n hash?: string;\n name?: string;\n email?: string;\n /**\n * Their profile picture, so the team sees who they are talking to rather\n * than a letter. Has to be an https URL; anything else is ignored.\n */\n avatarUrl?: string;\n /** Anything else worth showing the team: plan, company, whatever. */\n metadata?: Record<string, string>;\n};\n\n/**\n * Whether the host has told us somebody is in the chair.\n *\n * Anything they say about the person counts, because only they can know it: a\n * host who passes a name and an email for whoever is signed in is as sure as\n * one who passes a signed id. An empty object is not an answer, so a session\n * that is still loading does not open the door on its way past.\n */\nexport function isSignedIn(user: MedianUser | undefined): boolean {\n if (user === undefined) return false;\n\n // Every field checked as it arrives rather than as it is typed: a host\n // spreading a session that is not there yet passes `{ name: undefined }`,\n // and a widget that threw on that would be a page broken by signing out.\n const { metadata, ...facts } = user;\n return (\n Object.values(facts).some(\n (value) => typeof value === \"string\" && value.trim().length > 0,\n ) || Object.keys(metadata ?? {}).length > 0\n );\n}\n\n/**\n * The claim, split from the rest.\n *\n * Two things go up rather than one, and the difference between them is the\n * whole of the security. A name is decoration and is taken at its word; an id\n * decides whose history the widget can reach, so it travels with its signature\n * and is believed only if that checks out. Sending them as one object would\n * blur a line the backend needs to keep sharp.\n */\nexport function splitIdentity(user: MedianUser | null): {\n facts: Omit<MedianUser, \"id\" | \"hash\">;\n claim: { userId: string; userHash: string } | undefined;\n} {\n if (user === null) return { facts: {}, claim: undefined };\n\n const { id, hash, ...facts } = user;\n return {\n facts,\n claim: id && hash ? { userId: id, userHash: hash } : undefined,\n };\n}\n\n/** What the backend made of a claim, and what to say about it. */\nconst IDENTITY_COMPLAINTS: Record<string, string> = {\n unverified:\n \"Median: the user hash passed to <MedianSupport> did not match. Sign the same string you pass as `user.id` with the Median key that produced this widget id. Until it matches, this visitor gets a conversation of their own on every device.\",\n unconfigured:\n \"Median: a user hash was passed to <MedianSupport>, but its widget id and hash came from different Median keys. Derive both from the same key.\",\n};\n\n/**\n * The live conversation behind the widget: a session token for this browser,\n * the thread as Convex sees it, and a send that uploads files first. The\n * widget itself stays dumb; this is the seam the SDK promised it.\n */\nexport function useMedianThread({\n apiKey,\n user,\n telemetry = true,\n screenshot = true,\n isLocked = false,\n diagnostics,\n appState,\n requireEmail = true,\n messagePreview = true,\n}: {\n apiKey: string;\n user?: MedianUser;\n /** Whether to read the browser's own answers along with a message. */\n telemetry?: boolean;\n /**\n * Whether the agent may take a picture of this page when it asks to see\n * it. Nothing is taken until it asks, and a page that says no answers the\n * ask with nothing, so the agent knows to ask in words instead.\n */\n screenshot?: boolean;\n /**\n * Nobody may write yet, so nothing is read either: a visitor who has to\n * sign in first is not shown a thread they cannot answer, and the widget\n * holds no conversation open behind the invitation to sign in.\n */\n isLocked?: boolean;\n /** What to watch going wrong on the page, if the host wants any of it. */\n diagnostics?: MedianDiagnostics;\n /** The host's own state, read at send time. */\n appState?: Record<string, string | number | boolean>;\n /**\n * Whether this page asks for an email before a visitor's first message.\n * The team answers through `config.requireEmail`, and both have to agree:\n * either side can waive the question.\n */\n requireEmail?: boolean;\n /**\n * Whether this page lets an unread reply be previewed on the closed\n * button. The team answers through `config.messagePreview`, and both have\n * to agree, exactly as the email question is settled: either side can keep\n * the corner quiet.\n */\n messagePreview?: boolean;\n}) {\n // The session token is external state (localStorage), read as a store: the\n // server snapshot is null, so SSR and the first client paint agree, and the\n // browser's token takes over from there. Subscribed so `reset()` lands\n // here, mints a new token, and the thread starts over.\n const sessionId = useSyncExternalStore(\n subscribeSession,\n useCallback(() => getOrCreateSessionId(apiKey), [apiKey]),\n () => null,\n );\n\n const config = useQuery(api.widget.config, { apiKey });\n\n // The answer to the composer's question, if this browser has given one.\n const [visitorEmail, setVisitorEmail] = useVisitorEmail(apiKey);\n\n /**\n * Whether the composer asks who they are before it takes a message. All\n * three have to say so: the page's own rule, the team's switch, and the\n * fact that nobody here knows an address already. A host who passes\n * `user.email` has answered on their visitor's behalf; the question never\n * comes up. Before the config lands there is no team answer yet, and the\n * panel is still shimmering besides, so the default holds.\n */\n const askForEmail =\n requireEmail &&\n (config?.requireEmail ?? true) &&\n !visitorEmail &&\n !(typeof user?.email === \"string\" && user.email.trim().length > 0);\n\n const thread = useQuery(\n api.widget.thread,\n sessionId === null || isLocked ? \"skip\" : { apiKey, sessionId },\n );\n const send = useWidgetMutation(api.widget.sendV2);\n const sendTyping = useMutation(api.widget.setTyping);\n const sendPresence = useMutation(api.widget.presence);\n const rateConversation = useWidgetMutation(api.widget.rateV2);\n const identify = useWidgetMutation(api.widget.identifyV2);\n const generateUploadUrl = useWidgetMutation(api.widget.generateUploadUrlV2);\n const answerScreenshot = useWidgetMutation(api.widget.answerScreenshotV2);\n\n /**\n * The picture the agent asked for, sent up, or the fact that there is\n * none. An upload that fails turns into the second: the agent is waiting,\n * and \"no picture\" is an answer it knows what to do with.\n */\n const answerAsk = useCallback(\n async (key: string, shot: File | null) => {\n if (sessionId === null) return;\n let attachment: SentAttachment | undefined;\n if (shot !== null) {\n try {\n [attachment] = await uploadAttachments(\n () => generateUploadUrl({ apiKey, sessionId }),\n [shot],\n );\n } catch (caught) {\n console.warn(\n \"Median: the picture the agent asked for could not be uploaded\",\n caught,\n );\n }\n }\n await answerScreenshot({\n apiKey,\n sessionId,\n key,\n ...(attachment === undefined ? {} : { attachment }),\n });\n },\n [apiKey, sessionId, generateUploadUrl, answerScreenshot],\n );\n\n // Who they are can change without them saying a word: they sign in on the\n // host's page while the thread a teammate is reading still says nobody.\n //\n // Serialised and parsed back, which looks like a detour and is the point.\n // A host almost always builds this object inline, so it is a new one on\n // every render and a new identity to any effect watching it; the string is\n // the same until the facts in it are not.\n const identityKey = user ? JSON.stringify(user) : null;\n const identity = useMemo(\n () =>\n identityKey === null ? null : (JSON.parse(identityKey) as MedianUser),\n [identityKey],\n );\n\n useEffect(() => {\n if (sessionId === null || identity === null) return;\n const { facts, claim } = splitIdentity(identity);\n\n // Best effort on the facts: they ride along with the next message anyway,\n // which is exactly where they were before this existed. A signature that\n // was offered and refused is worth saying out loud though, because the\n // host is the only one who can fix it and nothing visibly breaks.\n identify({\n apiKey,\n sessionId,\n user: facts,\n ...(claim ? { identity: claim } : {}),\n })\n .then((status) => {\n const complaint = IDENTITY_COMPLAINTS[status];\n if (complaint) console.error(complaint);\n })\n .catch(() => {});\n }, [apiKey, sessionId, identity, identify]);\n\n // A fast clock only while a teammate's dots are on screen, so they expire\n // on time without the widget re-rendering every second forever.\n const hasLiveDots =\n thread?.messages.some(\n (message) => message.pending && message.typingAt !== null,\n ) ?? false;\n const now = useNow(hasLiveDots ? 1000 : 60_000);\n\n const takeDiagnostics = useDiagnostics(diagnostics, appState);\n\n const [isSending, setIsSending] = useState(false);\n const [sendError, setSendError] = useState<string | null>(null);\n\n const agent: SupportAgent | null = useMemo(\n () =>\n config === undefined\n ? null\n : {\n id: \"median-agent\",\n name: config.agent.name,\n kind: \"ai\",\n avatarUrl: config.agent.avatarUrl,\n },\n [config],\n );\n\n const messages: SupportMessage[] = useMemo(() => {\n if (!thread || agent === null) return [];\n\n const out: SupportMessage[] = [];\n let previousAt: number | undefined;\n let previousConversationId: string | undefined;\n\n for (const message of thread.messages) {\n // A typing bubble whose teammate stopped refreshing it: the browser on\n // the other end went away, so the dots go too.\n if (\n message.pending &&\n message.typingAt !== null &&\n now - message.typingAt > TYPING_TTL_MS\n ) {\n continue;\n }\n\n out.push({\n id: message._id,\n sender: message.sender,\n body: message.body,\n time: formatClockTime(message.createdAt),\n sentAt: message.createdAt,\n dayLabel: isNewDay(previousAt, message.createdAt)\n ? formatDayLabel(message.createdAt, now)\n : undefined,\n // Not on the very first message: the history has to start somewhere,\n // and a line above the only conversation there has ever been would be\n // marking a boundary that does not exist.\n startsConversation:\n previousConversationId !== undefined &&\n previousConversationId !== message.conversationId\n ? true\n : undefined,\n agent:\n message.agent === null\n ? undefined\n : message.agent.kind === \"ai\"\n ? agent\n : {\n id: `human-${message.agent.name}`,\n name: message.agent.name,\n kind: \"human\",\n avatarUrl: message.agent.avatarUrl,\n },\n pending: message.pending || undefined,\n attachments:\n message.attachments.length > 0\n ? message.attachments.map((attachment) => {\n const kind = attachmentKindOf(attachment.type);\n return {\n id: attachment.id,\n kind,\n name: attachment.name,\n size: formatFileSize(attachment.size),\n // Only what can actually be shown small. A text file had\n // one of these too, which read as \"there is a thumbnail\n // here\" everywhere it was checked.\n previewUrl:\n kind === \"file\" ? undefined : (attachment.url ?? undefined),\n url: attachment.url ?? undefined,\n };\n })\n : undefined,\n navigateTo: message.navigateTo ?? undefined,\n highlight: message.highlight ?? undefined,\n });\n previousAt = message.createdAt;\n previousConversationId = message.conversationId;\n }\n\n return out;\n }, [thread, agent, now]);\n\n // The faces in the corner: the AI, then anyone human who has spoken.\n const presence: SupportPresence | null = useMemo(() => {\n if (config === undefined || agent === null) return null;\n\n const humans = new Map<string, SupportAgent>();\n for (const message of messages) {\n if (message.agent && message.agent.kind === \"human\") {\n humans.set(message.agent.id, message.agent);\n }\n }\n\n return {\n name: config.orgName,\n status: \"Support online\",\n team: [...[...humans.values()].slice(-2), agent],\n };\n }, [config, agent, messages]);\n\n /**\n * Replies they have not looked at. Only the other side's, and only ones\n * that have actually been written: a bubble still holding dots is not news.\n */\n const unread = useMemo(() => {\n const unreadIds = new Set<string>(\n thread?.messages.filter(isUnreadReply).map((message) => message._id),\n );\n return messages.filter((message) => unreadIds.has(message.id));\n }, [messages, thread]);\n const unreadCount = unread.length;\n\n /**\n * The newest of those, for the card on the closed button, or null when\n * either side has turned the card off. The count on the button is not\n * affected: this decides whether the words are shown, not whether the\n * reply is announced.\n *\n * Before the config lands there is no team answer, and nothing has been\n * read yet either, so the default holds and nothing is shown regardless.\n */\n const latestUnread =\n messagePreview && (config?.messagePreview ?? true)\n ? (unread[unread.length - 1] ?? null)\n : null;\n\n // Asking how it went, once, on a conversation that is over.\n //\n // Three things decide whether the card is up, and all three are read\n // rather than remembered: the thread is resolved, nobody has answered yet,\n // and they have not waved it away. Pressing a star writes a rating, which\n // would otherwise take the card off screen mid-sentence, so a pick made\n // here keeps it open until they are finished with it.\n const [picked, setPicked] = useState<{\n conversationId: string;\n stars: number;\n } | null>(null);\n // Read from storage rather than started empty: a skip has to outlive the\n // remount that clicking a link on the host's site costs us.\n const [skipped, setSkipped] = useState<ReadonlySet<string>>(() =>\n readSkippedRatings(apiKey),\n );\n\n const conversationId = thread?.conversationId ?? null;\n const savedRating = thread?.rating ?? null;\n const isMine = picked?.conversationId === conversationId;\n\n const rating: SupportRating | null =\n thread?.status === \"resolved\" &&\n conversationId !== null &&\n !skipped.has(conversationId) &&\n (savedRating === null || isMine)\n ? {\n stars: isMine ? picked.stars : (savedRating?.stars ?? null),\n submit: async (stars, feedback) => {\n // Never null by the time a thread has been read, and a quiet\n // return here would read on screen as a rating that landed.\n if (sessionId === null) throw new Error(\"No session yet.\");\n setPicked({ conversationId, stars });\n await rateConversation({\n apiKey,\n sessionId,\n conversationId,\n stars,\n ...(feedback ? { feedback } : {}),\n });\n },\n skip: () => {\n writeSkippedRating(apiKey, conversationId);\n setSkipped((current) => new Set(current).add(conversationId));\n },\n }\n : null;\n\n async function handleSend(body: string, files: File[]) {\n if (sessionId === null || isSending) return;\n\n setSendError(null);\n setIsSending(true);\n // Whatever the host attached since the last send rides this message.\n // Taken outside the try so a failed upload hands it back too.\n const custom = takeCustomData();\n try {\n const attachments = await uploadAttachments(\n () => generateUploadUrl({ apiKey, sessionId }),\n files,\n );\n\n // Read now rather than on mount: all of it is wanted as \"what was true\n // when they wrote\", and a widget can sit closed on a page for an hour\n // before anybody says anything.\n const context = telemetry ? collectContext() : undefined;\n const page = telemetry ? collectPage() : undefined;\n\n // Nothing the first time a healthy page sends twice in a row: the\n // snapshot only travels when something new has gone wrong since.\n const collected = takeDiagnostics();\n\n // The same split as above, and this is the call that needs it most: a\n // visitor's first message is what creates them, so it is the only chance\n // to put them straight onto the account they are already signed in to.\n const { facts, claim } = splitIdentity(user ?? null);\n\n // The address typed at the composer's question rides along the same way\n // a host's facts do, but never over one the host already knows.\n const withEmail =\n facts.email || !visitorEmail\n ? facts\n : { ...facts, email: visitorEmail };\n\n await send({\n apiKey,\n sessionId,\n body,\n ...(Object.keys(withEmail).length > 0 ? { user: withEmail } : {}),\n ...(claim ? { identity: claim } : {}),\n ...(context ? { context } : {}),\n ...(page ? { page } : {}),\n ...(attachments.length > 0 ? { attachments } : {}),\n ...(collected ? { diagnostics: collected } : {}),\n ...(custom ? { customData: custom } : {}),\n });\n } catch (caught) {\n // Back in the queue, so the retry carries them like it keeps the draft.\n if (custom) restoreCustomData(custom);\n console.error(\"Sending from the support widget failed\", caught);\n setSendError(errorMessage(caught));\n // Rethrown so the composer keeps the draft: a failed send should cost\n // a retry, never a retype.\n throw caught;\n } finally {\n setIsSending(false);\n }\n }\n\n function handleTypingChange(isTyping: boolean) {\n if (sessionId === null) return;\n // Best effort: dots that fail to show are not worth an error.\n sendTyping({ apiKey, sessionId, isTyping }).catch(() => {});\n }\n\n /**\n * The panel is visible right now. Whatever renders the thread calls this\n * on open and every so often after, and it is what stands between a\n * customer and an email copy of a reply they are already reading.\n */\n // A receipt must not advance past a reply that is still streaming. The\n // callback changes as soon as a completed reply lands, between heartbeats.\n // Keep the callback stable across receipt updates so acknowledging a reply\n // does not itself trigger another heartbeat.\n const readsKey = JSON.stringify(\n widgetReadReceipts(thread?.messages ?? [], now),\n );\n const reads = useMemo(\n () => JSON.parse(readsKey) as ReturnType<typeof widgetReadReceipts>,\n [readsKey],\n );\n const reportPresence = useCallback(() => {\n if (sessionId === null || isLocked) return;\n // The next heartbeat retries a missed receipt while the panel is visible.\n sendPresence({\n apiKey,\n sessionId,\n reads,\n }).catch((caught) => {\n console.warn(\"Median: support read status could not be synced\", caught);\n });\n }, [apiKey, sessionId, isLocked, sendPresence, reads]);\n\n // Nearly always null. Its own value rather than a message in the list,\n // because it belongs to a tool run and not to anything anybody said.\n const ask: SupportAsk | null = thread?.direction ?? null;\n\n // Nearly always null too: the agent asking to see the page. Answered from\n // here, with a picture or with nothing, and never shown as a message of\n // its own; the picture turns up in the thread as the answer.\n useScreenshotAsk({\n ask: thread?.screenshotAsk ?? null,\n enabled: screenshot,\n answer: answerAsk,\n });\n\n return {\n presence,\n messages,\n /** Something a tool run asked of this page, with no message to ride on. */\n ask,\n /**\n * The live thread, or null before there is one. Read by whatever has to\n * tell \"the next thing in this conversation\" from \"a different\n * conversation altogether\", which is what signing in produces.\n */\n conversationId,\n /**\n * Still finding out whether this browser has a conversation already.\n * Locked, there is only the host's name to wait for, and the invitation\n * to sign in names them.\n */\n isLoading:\n config === undefined ||\n (!isLocked && (sessionId === null || thread === undefined)),\n isSending,\n /**\n * Whether the composer asks for an email before a first message. The\n * page's rule and the team's switch both have to want it, and nobody\n * here may know an address already.\n */\n askForEmail,\n /** What the composer's question does with a good answer. */\n setVisitorEmail,\n /** The agent asked for a person, and nobody has answered yet. */\n awaitingHuman: thread?.awaitingHuman ?? false,\n /** The question a finished conversation asks, while it is worth asking. */\n rating,\n /** Replies that arrived while they were not looking. */\n unreadCount,\n /** The newest of those, for the card on the closed button. */\n latestUnread,\n /** Say the panel is on screen, so replies are not also emailed. */\n reportPresence,\n sendError,\n send: handleSend,\n setTyping: handleTypingChange,\n };\n}\n","\"use client\";\n\nimport { unwrapMutationResult } from \"@median/backend/mutationResult\";\nimport { useMutation } from \"convex/react\";\nimport type { FunctionReference, OptionalRestArgs } from \"convex/server\";\nimport { useCallback } from \"react\";\n\n/**\n * A widget endpoint that hands a refusal back as a value, so its rate\n * limits stick even when the work fails. This throws that refusal the way\n * a plain mutation would, so callers catch it like any other error.\n */\nexport function useWidgetMutation<M extends FunctionReference<\"mutation\">>(\n reference: M,\n) {\n const mutation = useMutation(reference);\n return useCallback(\n async (...args: OptionalRestArgs<M>) =>\n unwrapMutationResult(await mutation(...args)),\n [mutation],\n );\n}\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\n/**\n * The current time, refreshed on an interval, so relative timestamps (\"2m\")\n * drift forward without anyone touching the page. One state per caller keeps\n * this dumb; the inbox only needs it once.\n */\nexport function useNow(intervalMs = 30_000): number {\n const [now, setNow] = useState(() => Date.now());\n\n useEffect(() => {\n const timer = setInterval(() => setNow(Date.now()), intervalMs);\n return () => clearInterval(timer);\n }, [intervalMs]);\n\n return now;\n}\n","import { ConvexError } from \"convex/values\";\n\nconst FALLBACK = \"Something went wrong on our end. Please try again.\";\n\nconst OUT_OF_DATE =\n \"This page is out of date. Refresh to get the latest version.\";\n\n/**\n * A call to a function the backend no longer has: the tab is running a\n * bundle from before a release. Convex words it this way in development and\n * whenever it does not redact the reason.\n */\nfunction isMissingFunction(error: unknown): boolean {\n return (\n error instanceof Error &&\n error.message.includes(\"Could not find public function\")\n );\n}\n\ntype ErrorPayload = { code: string; message: string };\n\nfunction isErrorPayload(data: unknown): data is ErrorPayload {\n return (\n typeof data === \"object\" &&\n data !== null &&\n \"message\" in data &&\n typeof (data as ErrorPayload).message === \"string\"\n );\n}\n\n/** The `code` on an error we raised on purpose, or null for anything else. */\nexport function errorCode(error: unknown): string | null {\n if (\n error instanceof ConvexError &&\n typeof error.data === \"object\" &&\n error.data !== null &&\n typeof (error.data as Partial<ErrorPayload>).code === \"string\"\n )\n return (error.data as ErrorPayload).code;\n return null;\n}\n\n/**\n * Turns anything thrown by a Convex call into a sentence worth showing.\n * Only errors we raised on purpose are surfaced; everything else falls back so\n * we never paste a stack trace into the UI.\n */\nexport function errorMessage(error: unknown): string {\n if (error instanceof ConvexError) {\n if (typeof error.data === \"string\") return error.data;\n if (isErrorPayload(error.data)) return error.data.message;\n }\n if (isMissingFunction(error)) return OUT_OF_DATE;\n return FALLBACK;\n}\n","/**\n * Timestamps, written the way the inbox and the widget say them. All of it\n * runs on the client with a real clock, so the backend only ever stores\n * numbers.\n */\n\nconst MINUTE = 60_000;\nconst HOUR = 60 * MINUTE;\nconst DAY = 24 * HOUR;\n\n/**\n * A time of day, wherever one is printed: beside a message, and under a\n * visitor's name for the clock where they are.\n *\n * Twelve hour on purpose rather than by the reader's locale. Two clocks can\n * sit on one screen here, one for the message and one for the customer, and\n * they came from different formatters; leaving the shape to the locale meant\n * they agreed only by luck, and a reader in a country that counts to\n * twenty-four saw a screen that could not decide.\n *\n * The rest is still the reader's. Which characters mark the afternoon is a\n * question their locale answers better than we can.\n */\nexport const CLOCK_FORMAT = {\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true,\n} as const satisfies Intl.DateTimeFormatOptions;\n\n/** \"3:58 PM\", for the line beside a message. */\nexport function formatClockTime(timestamp: number): string {\n return new Date(timestamp).toLocaleTimeString([], CLOCK_FORMAT);\n}\n\n/** \"now\", \"2m\", \"3h\", \"2d\", then a date, for a queue row. */\nexport function formatTimeAgo(timestamp: number, now: number): string {\n const elapsed = now - timestamp;\n if (elapsed < MINUTE) return \"now\";\n if (elapsed < HOUR) return `${Math.floor(elapsed / MINUTE)}m`;\n if (elapsed < DAY) return `${Math.floor(elapsed / HOUR)}h`;\n if (elapsed < 7 * DAY) return `${Math.floor(elapsed / DAY)}d`;\n return new Date(timestamp).toLocaleDateString([], {\n month: \"short\",\n day: \"numeric\",\n });\n}\n\n/**\n * \"just now\", \"2m ago\", \"Mar 3\", for a line about when something last ran.\n *\n * The queue's own shorthand reads as a column of times, which is right beside\n * a conversation and wrong in a sentence: \"synced 2m\" is not a thing anybody\n * says. Same clock, said out loud.\n */\nexport function formatSyncedAgo(timestamp: number, now: number): string {\n const ago = formatTimeAgo(timestamp, now);\n if (ago === \"now\") return \"just now\";\n return /^\\d/.test(ago) ? `${ago} ago` : ago;\n}\n\n/**\n * \"5 minutes ago\", \"yesterday\": a moment said relative to now, in full\n * words, for a detail pane where there is room to say it properly.\n *\n * Never \"in 12 seconds\". A clock that ticks every half minute, or a server\n * a little ahead of this one, hands over moments that are later than `now`,\n * and anything already recorded has happened, so those read as \"now\".\n */\nexport function formatRelative(timestamp: number, now: number): string {\n const elapsed = Math.min(0, timestamp - now);\n const words = new Intl.RelativeTimeFormat([], { numeric: \"auto\" });\n const steps: [number, Intl.RelativeTimeFormatUnit][] = [\n [DAY, \"day\"],\n [HOUR, \"hour\"],\n [MINUTE, \"minute\"],\n ];\n for (const [size, unit] of steps) {\n if (-elapsed >= size) return words.format(Math.round(elapsed / size), unit);\n }\n return words.format(Math.round(elapsed / 1000), \"second\");\n}\n\n/** Minutes since, for sorting rows the way the demo data did. */\nexport function minutesSince(timestamp: number, now: number): number {\n return Math.max(0, Math.floor((now - timestamp) / MINUTE));\n}\n\n/** \"March 2023\", for the customer since row and past conversations. */\nexport function formatMonthYear(timestamp: number): string {\n return new Date(timestamp).toLocaleDateString([], {\n month: \"long\",\n year: \"numeric\",\n });\n}\n\n/** \"Oct 8\", with the year only when it is not this one. */\nexport function formatShortDate(timestamp: number, now = Date.now()): string {\n const date = new Date(timestamp);\n return date.toLocaleDateString([], {\n month: \"short\",\n day: \"numeric\",\n year:\n date.getFullYear() === new Date(now).getFullYear()\n ? undefined\n : \"numeric\",\n });\n}\n\n/** \"Oct 8, 2026\", for a date that is read on its own, like an invoice's. */\nexport function formatFullDate(timestamp: number): string {\n return new Date(timestamp).toLocaleDateString([], {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n });\n}\n\n/**\n * \"Tue, Sep 23, 2026, 2:05:22 PM GMT+8\": the whole moment with its zone,\n * for a hint over a time that a row says more briefly.\n */\nexport function formatExactMoment(timestamp: number): string {\n return new Date(timestamp).toLocaleString([], {\n ...CLOCK_FORMAT,\n second: \"2-digit\",\n weekday: \"short\",\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n timeZoneName: \"short\",\n });\n}\n\nfunction startOfDay(timestamp: number): number {\n const date = new Date(timestamp);\n date.setHours(0, 0, 0, 0);\n return date.getTime();\n}\n\n/** \"Today\", \"Yesterday\", \"Monday\", then a date, for a thread divider. */\nexport function formatDayLabel(timestamp: number, now: number): string {\n const days = Math.round((startOfDay(now) - startOfDay(timestamp)) / DAY);\n if (days <= 0) return \"Today\";\n if (days === 1) return \"Yesterday\";\n if (days < 7) {\n return new Date(timestamp).toLocaleDateString([], { weekday: \"long\" });\n }\n return new Date(timestamp).toLocaleDateString([], {\n month: \"short\",\n day: \"numeric\",\n });\n}\n\n/** How many whole days apart two timestamps land, by the calendar. */\nfunction daysApart(from: number, to: number): number {\n return Math.round((startOfDay(to) - startOfDay(from)) / DAY);\n}\n\n/**\n * \"9:00 AM\", \"tomorrow, 9:00 AM\", \"Mon, 9:00 AM\", \"Mar 4\": when a snooze is\n * up, as short as it can be said without leaving anyone guessing which day.\n */\nexport function formatSnoozeAt(until: number, now: number): string {\n const days = daysApart(now, until);\n if (days >= 7) {\n return new Date(until).toLocaleDateString([], {\n month: \"short\",\n day: \"numeric\",\n });\n }\n\n const time = formatClockTime(until);\n if (days <= 0) return time;\n if (days === 1) return `tomorrow, ${time}`;\n return `${new Date(until).toLocaleDateString([], { weekday: \"short\" })}, ${time}`;\n}\n\n/** Whether two timestamps land on different days, so a thread can divide. */\nexport function isNewDay(previous: number | undefined, next: number): boolean {\n if (previous === undefined) return true;\n return startOfDay(previous) !== startOfDay(next);\n}\n\n/**\n * Rows under the local day they happened on, in the order they came, for a\n * list cut into days. The rows arrive sorted by time, so a day is a run of\n * neighbours and a row never moves out of its place.\n */\nexport function groupByDay<T>(\n rows: readonly T[],\n at: (row: T) => number,\n): { start: number; rows: T[] }[] {\n const days: { start: number; rows: T[] }[] = [];\n for (const row of rows) {\n const start = startOfDay(at(row));\n const last = days.at(-1);\n if (last?.start === start) last.rows.push(row);\n else days.push({ start, rows: [row] });\n }\n return days;\n}\n","/**\n * Which finished conversations somebody has already waved the rating card\n * away on.\n *\n * Beside the session token and the read marker, and kept for the same\n * reason: a host's site is several pages, so the widget is remounted every\n * time somebody clicks a link. Held in memory, a skip would last until the\n * next click and the card would be back. Skipping is a no thanks, and asking\n * again is how a no thanks turns into a nuisance.\n *\n * One entry per API key, like the others, and capped: a browser that has\n * skipped a hundred conversations has no reason to carry all hundred ids\n * around, and the oldest of them are threads nobody will see again anyway.\n */\n\nconst STORAGE_PREFIX = \"median:rating-skips:\";\n\n/** How many are worth remembering. Past this, the oldest fall off. */\nconst MAX_REMEMBERED = 20;\n\n/** Where they live when localStorage is blocked. Lasts the page load. */\nconst inMemory = new Map<string, string[]>();\n\nfunction read(apiKey: string): string[] {\n if (typeof window === \"undefined\") return [];\n\n try {\n const stored = window.localStorage.getItem(`${STORAGE_PREFIX}${apiKey}`);\n if (stored === null) return [];\n const parsed: unknown = JSON.parse(stored);\n return Array.isArray(parsed)\n ? parsed.filter((one): one is string => typeof one === \"string\")\n : [];\n } catch {\n // Blocked, or somebody put something else under our key.\n return inMemory.get(apiKey) ?? [];\n }\n}\n\n/** The conversations this browser has already said no thanks to. */\nexport function readSkippedRatings(apiKey: string): ReadonlySet<string> {\n return new Set(read(apiKey));\n}\n\n/** One more no thanks, remembered across the pages of a host's site. */\nexport function writeSkippedRating(\n apiKey: string,\n conversationId: string,\n): void {\n if (typeof window === \"undefined\") return;\n\n const next = [\n ...read(apiKey).filter((one) => one !== conversationId),\n conversationId,\n ].slice(-MAX_REMEMBERED);\n\n inMemory.set(apiKey, next);\n try {\n window.localStorage.setItem(\n `${STORAGE_PREFIX}${apiKey}`,\n JSON.stringify(next),\n );\n } catch {\n // Blocked, so the in-memory copy above is all there is. It still holds\n // for as long as they are on the page they skipped it from.\n }\n}\n","import type { Id } from \"@median/backend/dataModel\";\nimport { TYPING_TTL_MS } from \"@median/backend/support\";\n\ntype ReadMessage = {\n _id: Id<\"conversationMessages\">;\n conversationId: Id<\"conversations\">;\n sender: \"agent\" | \"visitor\";\n pending: boolean;\n isRead: boolean;\n typingAt: number | null;\n body: string;\n attachments: readonly unknown[];\n};\n\nexport function isUnreadReply(message: ReadMessage): boolean {\n return message.sender === \"agent\" && !message.pending && !message.isRead;\n}\n\n/** A separate receipt for each conversation; never read past a streaming reply. */\nexport function widgetReadReceipts(messages: readonly ReadMessage[], now: number) {\n const receipts = new Map<\n Id<\"conversations\">,\n {\n conversationId: Id<\"conversations\">;\n messageId?: Id<\"conversationMessages\">;\n }\n >();\n const blocked = new Set<Id<\"conversations\">>();\n\n for (const message of messages) {\n const { conversationId } = message;\n if (!receipts.has(conversationId)) {\n receipts.set(conversationId, { conversationId });\n }\n if (blocked.has(conversationId)) continue;\n if (message.pending) {\n if (message.typingAt === null || now - message.typingAt <= TYPING_TTL_MS) {\n blocked.add(conversationId);\n }\n continue;\n }\n if (\n message.sender === \"agent\" &&\n (message.body.trim() || message.attachments.length > 0)\n ) {\n receipts.set(conversationId, { conversationId, messageId: message._id });\n }\n }\n\n return [...receipts.values()];\n}\n","\"use client\";\n\nimport { useEffect, useRef } from \"react\";\nimport type { SupportScreenshotAsk } from \"../types\";\nimport { capturePage } from \"./screenshot\";\n\n/**\n * Answering the agent when it asks to see the page.\n *\n * The picture is taken when the agent asks for it and not before. Nothing\n * is captured on a first message, and nothing is captured on a schedule: a\n * page is drawn once the agent has said it is going to look, which is also\n * the moment the customer has been told.\n *\n * The ask sits on the live thread until it is answered, so a re-render or a\n * second query update cannot answer it twice: every key is remembered here\n * as it is taken up. It is answered on a first look as well as on a change,\n * because an ask is only on the thread while it is open, and one open when\n * this page mounted is one the last page never got to. A second browser on\n * the same account answers too; the backend takes the first answer and\n * tells the other it was late.\n *\n * A host that passed `screenshot={false}` still answers, with nothing. That\n * is what lets the agent stop waiting and ask in words, rather than sit on\n * a promise the page will never keep.\n */\nexport function useScreenshotAsk({\n ask,\n enabled,\n answer,\n}: {\n /** The open ask on the thread, or null nearly always. */\n ask: SupportScreenshotAsk | null;\n /** Whether this page allows a picture of itself to be taken. */\n enabled: boolean;\n /** Sends the picture, or the fact that there is none, for the given key. */\n answer: (key: string, shot: File | null) => Promise<void>;\n}): void {\n /** Every ask this page has taken up. */\n const answered = useRef<Set<string>>(new Set());\n\n useEffect(() => {\n if (ask === null || answered.current.has(ask.key)) return;\n // Marked before the capture starts, so nothing in the meantime starts\n // a second one for the same ask.\n answered.current.add(ask.key);\n\n void (async () => {\n const shot = enabled ? await capturePage() : null;\n await answer(ask.key, shot);\n })().catch((caught) => {\n // The agent is waiting on this, but a failed answer is bounded on the\n // other side: it stops waiting on its own and asks in words.\n console.warn(\"Median: answering the agent's request for a picture failed\", caught);\n });\n }, [ask, enabled, answer]);\n}\n","\"use client\";\n\n/**\n * The address a visitor typed at the composer's question, kept beside the\n * session token and for the same reason: a host's site is usually several\n * pages, so the widget is remounted every time somebody clicks a link, and\n * asking all over again on each one is not a question, it is a wall.\n *\n * It rides the visitor's first message like any other fact the host passes,\n * which is what puts it on their row in the team's inbox.\n */\n\nimport { useCallback, useSyncExternalStore } from \"react\";\n\nconst STORAGE_PREFIX = \"median:email:\";\n\n/** Where it lives when localStorage is blocked. Lasts the page load. */\nconst inMemory = new Map<string, string>();\n\nconst listeners = new Set<() => void>();\n\nexport function subscribeVisitorEmail(listener: () => void) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\n/** The address this browser gave, or null before it has. */\nexport function readVisitorEmail(apiKey: string): string | null {\n if (typeof window === \"undefined\") return null;\n\n try {\n const stored = window.localStorage.getItem(`${STORAGE_PREFIX}${apiKey}`);\n return stored && stored.length > 0 ? stored : null;\n } catch {\n return inMemory.get(apiKey) ?? null;\n }\n}\n\nexport function writeVisitorEmail(apiKey: string, email: string): void {\n inMemory.set(apiKey, email);\n try {\n window.localStorage.setItem(`${STORAGE_PREFIX}${apiKey}`, email);\n } catch {\n // Blocked, so the in-memory copy above is all there is. It still gets\n // them through this page, where the message that carries it goes out.\n }\n\n for (const listener of listeners) listener();\n}\n\n/**\n * Forget every address this browser holds. Part of `medianSupport.reset()`,\n * which has to detach a browser from its conversation completely: an email\n * that survived a sign out would greet the next person at the keyboard with\n * somebody else's name.\n */\nexport function resetVisitorEmails() {\n inMemory.clear();\n\n if (typeof window !== \"undefined\") {\n try {\n const stale: string[] = [];\n for (let i = 0; i < window.localStorage.length; i += 1) {\n const key = window.localStorage.key(i);\n if (key !== null && key.startsWith(STORAGE_PREFIX)) stale.push(key);\n }\n for (const key of stale) window.localStorage.removeItem(key);\n } catch {\n // Storage was blocked, so there was nothing durable to forget.\n }\n }\n\n for (const listener of listeners) listener();\n}\n\n/**\n * The address, live: subscribed, so a reset while the widget is mounted asks\n * again rather than trusting state that outlived its storage. The setter is\n * what the composer's question calls once the answer checks out.\n */\nexport function useVisitorEmail(\n apiKey: string,\n): [string | null, (email: string) => void] {\n const email = useSyncExternalStore(\n subscribeVisitorEmail,\n useCallback(() => readVisitorEmail(apiKey), [apiKey]),\n () => null,\n );\n\n const setEmail = useCallback(\n (next: string) => writeVisitorEmail(apiKey, next),\n [apiKey],\n );\n\n return [email, setEmail];\n}\n","\"use client\";\n\nimport { useEffect, useRef, useState, useSyncExternalStore } from \"react\";\nimport { getResetCount, subscribeSession } from \"./session\";\nimport type { MedianUser } from \"./use-median-thread\";\n\n/**\n * Who the host had signed in when `reset()` ran.\n *\n * A host usually calls `reset()` a beat before its own state lets go of the\n * user, so the old person is still in the props when the fresh session is\n * minted. Sending them again would bind the new session straight back to\n * them. So each person held at reset time is left out until the host passes\n * somebody else, or nobody, and then they can sign back in as normal.\n *\n * A panel can also leave the page still holding them, as when a host only\n * renders the widget while somebody is signed in. The person is let go a\n * moment later, unless a panel has taken them up again by then, so the next\n * time they sign in on this page they are sent.\n */\n\n/** What each mounted panel was told about the person, keyed by panel. */\nconst held = new Map<symbol, string>();\n\n/** People `reset()` signed out whom the host is still passing in. */\nconst signedOut = new Set<string>();\n\n/**\n * How long a person stays signed out after the last panel holding them\n * leaves, so a panel remounting on the same sign out does not bind them.\n */\nconst RELEASE_AFTER_MS = 2_000;\n\n/** Called by `reset()`, before the sessions are forgotten. */\nexport function signOutHeldUsers() {\n for (const person of held.values()) signedOut.add(person);\n}\n\n/** Lets a signed out person go once no panel is holding them any more. */\nfunction releaseLater(person: string) {\n if (!signedOut.has(person)) return;\n setTimeout(() => {\n for (const other of held.values()) if (other === person) return;\n signedOut.delete(person);\n }, RELEASE_AFTER_MS);\n}\n\n/** The signed id when there is one, since other facts can change under it. */\nfunction personKey(user: MedianUser | undefined): string | null {\n if (user === undefined) return null;\n return user.id && user.hash ? `id:${user.id}` : JSON.stringify(user);\n}\n\n/**\n * The user a panel should send, which is the one it was given unless\n * `reset()` signed them out and the host has not let go of them yet.\n */\nexport function useSignedInUser(\n user: MedianUser | undefined,\n): MedianUser | undefined {\n const person = personKey(user);\n // Re-read on every reset, which is the only time `signedOut` grows.\n useSyncExternalStore(subscribeSession, getResetCount, () => 0);\n\n const [token] = useState(() => Symbol(\"median-panel\"));\n const previous = useRef<string | null>(null);\n\n useEffect(() => {\n // The host moved on from somebody who was signed out, so they are free\n // to sign in again later.\n if (previous.current !== null && previous.current !== person) {\n signedOut.delete(previous.current);\n }\n previous.current = person;\n\n if (person === null) held.delete(token);\n else held.set(token, person);\n }, [person, token]);\n\n useEffect(\n () => () => {\n const person = held.get(token);\n held.delete(token);\n if (person !== undefined) releaseLater(person);\n },\n [token],\n );\n\n return person !== null && signedOut.has(person) ? undefined : user;\n}\n","\"use client\";\n\nimport { useSyncExternalStore } from \"react\";\n\n/**\n * How many replies the visitor has not read, kept outside React.\n *\n * The count is worked out inside the panel, from the thread. The thing that\n * wants to show it is usually somewhere else: a button in the host's own\n * nav, on a page where the corner has been taken back. Same reason the open\n * state lives in a module: the host reaches it without threading state\n * through their tree or wrapping their app in a provider of ours.\n *\n * Zero whenever no panel is on the page, since with nothing mounted there\n * is nothing unread to announce.\n */\nlet count = 0;\nconst listeners = new Set<() => void>();\n\nexport const unreadStore = {\n subscribe(listener: () => void) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n read: () => count,\n /** The server has no thread to count, and neither does the first paint. */\n readServer: () => 0,\n set(next: number) {\n if (count === next) return;\n count = next;\n for (const listener of listeners) listener();\n },\n};\n\nexport function useUnreadCount(): number {\n return useSyncExternalStore(\n unreadStore.subscribe,\n unreadStore.read,\n unreadStore.readServer,\n );\n}\n","\"use client\";\n\nimport { ConvexError } from \"convex/values\";\nimport { Component, type ReactNode } from \"react\";\n\n/**\n * The widget runs on somebody else's page, so whatever goes wrong in here must\n * never take the host's site down with it. The widget quietly steps off the\n * page and says why in the console, where the person installing it is already\n * looking.\n */\n\n/**\n * Name the actual problem when we can. \"Check the apiKey\" is the right advice\n * for a rejected key and a wild goose chase for everything else.\n */\nfunction adviceFor(error: unknown, component: string): string {\n if (\n error instanceof ConvexError &&\n typeof error.data === \"object\" &&\n error.data !== null &&\n (error.data as { code?: string }).code === \"invalid_api_key\"\n ) {\n return `Median did not recognize the apiKey passed to <${component}>, so it has been hidden. Derive it from an active MEDIAN_KEY with publicKeyFromMedianKey.`;\n }\n return `Median: <${component}> hit an error and has been hidden. Your page is unaffected.`;\n}\n\nexport class MedianErrorBoundary extends Component<\n {\n children: ReactNode;\n /** The component inside, as the host wrote it, for the console line. */\n component: string;\n },\n { hasFailed: boolean }\n> {\n state = { hasFailed: false };\n\n static getDerivedStateFromError() {\n return { hasFailed: true };\n }\n\n componentDidCatch(error: unknown) {\n console.error(adviceFor(error, this.props.component), error);\n }\n\n render() {\n return this.state.hasFailed ? null : this.props.children;\n }\n}\n","\"use client\";\n\nimport { useEffect, useState, useSyncExternalStore } from \"react\";\nimport { getResetCount, subscribeSession } from \"./session\";\nimport type { MedianUser } from \"./use-median-thread\";\n\n/**\n * Who is signed in, fetched from the host's own route rather than passed down\n * their tree.\n *\n * The widget is a client component and the signature can only be made on a\n * server, so on an app with a server rendered root layout there is nowhere\n * that has both the session and this component. The route in the middle is\n * always the same one, so `medianIdentity` in @mediansh/agent-tools ships it\n * and this reads it.\n *\n * The publishable key comes down the same way. A host who points at a route\n * has one Median value in their app instead of two, and the browser safe half\n * of the key never has to be plumbed into a client environment by hand.\n */\n\n/** What `medianIdentity` answers with. Both fields absent is a valid answer. */\ntype IdentityResponse = {\n apiKey?: string;\n user?: MedianUser;\n};\n\nexport type ResolvedIdentity = {\n apiKey: string | undefined;\n user: MedianUser | undefined;\n /**\n * The answer is still on its way. True only while a route is actually being\n * read: a widget given no route is never waiting for one.\n *\n * The panel holds its loading state on this rather than showing the sign in\n * invitation, because \"we have not asked yet\" and \"they are not signed in\"\n * look identical from here and only one of them is worth telling somebody.\n */\n isLoading: boolean;\n};\n\nconst NOTHING: ResolvedIdentity = {\n apiKey: undefined,\n user: undefined,\n isLoading: false,\n};\n\n/** A route has been named and has not answered yet. */\nconst WAITING: ResolvedIdentity = { ...NOTHING, isLoading: true };\n\n/**\n * The request, while it is in the air, shared by everything waiting on it.\n *\n * A page with the support panel and the feedback panel on it mounts two\n * components that both want the same answer from the same route. One asks and\n * the other joins, rather than the host's server signing the same person\n * twice.\n *\n * Dropped the moment it settles, so nothing here outlives the answer. Signing\n * out and back in re-reads the route rather than being handed the last\n * person's signature out of a cache. Keyed by reset too, so a read that\n * started before `reset()` is not joined after it.\n */\nconst inFlight = new Map<string, Promise<IdentityResponse>>();\n\nfunction read(endpoint: string, resets: number): Promise<IdentityResponse> {\n const cacheKey = `${resets}:${endpoint}`;\n const existing = inFlight.get(cacheKey);\n if (existing !== undefined) return existing;\n\n const request = (async () => {\n const response = await fetch(endpoint, {\n // The session lives in a cookie, and a cross origin route only sees it\n // if the request asks. Same origin routes are unaffected.\n credentials: \"include\",\n headers: { accept: \"application/json\" },\n });\n if (!response.ok) {\n throw new Error(\n `${endpoint} answered ${response.status}. It should return { apiKey, user } from medianIdentity().`,\n );\n }\n return (await response.json()) as IdentityResponse;\n })().finally(() => inFlight.delete(cacheKey));\n\n inFlight.set(cacheKey, request);\n return request;\n}\n\n/**\n * Neither half was passed, so there is nothing to connect to and nothing on\n * the page. Silence here is the worst outcome: the install looks done, the\n * corner is empty, and there is no error anywhere to explain it.\n *\n * Said from the browser rather than checked by the types alone, because a\n * host on plain JavaScript, or one building options up across an `update()`\n * call, never sees the types.\n */\nexport function useMissingCredentials(\n /** As the host wrote it: `<MedianSupport>`, or `useMedianContact()`. */\n caller: string,\n apiKey: string | undefined,\n endpoint: string | undefined,\n /** What that costs them, for the one that still draws something. */\n consequence = \"nothing is on the page\",\n): void {\n useEffect(() => {\n if (apiKey !== undefined || endpoint !== undefined) return;\n console.error(\n `Median: ${caller} was given neither apiKey nor identity, so ${consequence}. Pass the median_pk_ id from publicKeyFromMedianKey(MEDIAN_KEY), or the path of a route built with medianIdentity().`,\n );\n }, [caller, apiKey, endpoint, consequence]);\n}\n\nexport function useMedianIdentity(\n endpoint: string | undefined,\n): ResolvedIdentity {\n // The answer, with the route it is an answer to.\n //\n // Kept as a pair, and read back below only when the two still agree,\n // because that is what makes \"we are waiting\" a fact about the current\n // route rather than a flag something has to remember to set. Nothing here\n // writes state on the way in, and a route that changes is one comparison\n // rather than a reset.\n const [answered, setAnswered] = useState<{\n endpoint: string;\n identity: ResolvedIdentity;\n } | null>(null);\n\n // `reset()` signs the person out, so the route is asked again. The last\n // answer stands meanwhile; the person in it is already signed out.\n const resets = useSyncExternalStore(subscribeSession, getResetCount, () => 0);\n\n useEffect(() => {\n if (endpoint === undefined) return;\n\n // A flag rather than an AbortController: the request can be shared with\n // the other panel on the page, and one of them unmounting must not cancel\n // the answer the other is still waiting for.\n let listening = true;\n\n void (async () => {\n try {\n const body = await read(endpoint, resets);\n if (!listening) return;\n setAnswered({\n endpoint,\n identity: {\n apiKey: body.apiKey,\n // An id without its signature is ignored by the backend anyway,\n // so it is dropped here rather than sent up to be refused.\n user: body.user?.hash ? body.user : undefined,\n isLoading: false,\n },\n });\n } catch (caught) {\n if (!listening) return;\n // Support stays on the page. Without a key from anywhere the widget\n // has nothing to talk to and says so through its own boundary; with\n // one, this is a visitor whose thread belongs to their browser, which\n // is where every anonymous visitor already is.\n console.error(\n \"Median: could not read the identity route.\",\n caught instanceof Error ? caught.message : caught,\n );\n setAnswered({ endpoint, identity: NOTHING });\n }\n })();\n\n return () => {\n listening = false;\n };\n }, [endpoint, resets]);\n\n if (endpoint === undefined) return NOTHING;\n if (answered?.endpoint === endpoint) return answered.identity;\n return WAITING;\n}\n","\"use client\";\n\nimport { api } from \"@median/backend\";\nimport { ConvexHttpClient } from \"convex/browser\";\nimport { useEffect, useState } from \"react\";\nimport { MEDIAN_DEPLOYMENT_URL } from \"./deployment\";\n\n/** A failed/expired entitlement always restores the default visible badge. */\nexport function useMedianBranding(apiKey: string | undefined) {\n const [state, setState] = useState({ key: apiKey, show: true });\n useEffect(() => {\n if (!apiKey || !MEDIAN_DEPLOYMENT_URL) return;\n const client = new ConvexHttpClient(MEDIAN_DEPLOYMENT_URL);\n let active = true;\n let expiry: ReturnType<typeof setTimeout> | undefined;\n async function refresh() {\n try {\n const result = await client.action(api.billingAccess.branding, {\n apiKey: apiKey!,\n });\n if (!active) return;\n clearTimeout(expiry);\n const expired =\n result.expiresAt !== null && result.expiresAt <= Date.now();\n setState({ key: apiKey, show: result.showBranding || expired });\n if (!result.showBranding && result.expiresAt !== null)\n expiry = setTimeout(\n () => setState({ key: apiKey, show: true }),\n Math.min(2_147_483_647, Math.max(0, result.expiresAt - Date.now())),\n );\n } catch {\n if (active) setState({ key: apiKey, show: true });\n }\n }\n void refresh();\n const timer = setInterval(() => void refresh(), 60_000);\n return () => {\n active = false;\n clearInterval(timer);\n clearTimeout(expiry);\n };\n }, [apiKey]);\n return state.key === apiKey ? state.show : true;\n}\n","\"use client\";\n\nimport { ConvexProvider, ConvexReactClient } from \"convex/react\";\nimport { useState, type ReactNode } from \"react\";\nimport { MEDIAN_DEPLOYMENT_URL } from \"./deployment\";\n\n/**\n * A connection of the widget's own, so it is self contained on a host site\n * that has never heard of Convex. One per mounted component, made lazily so\n * a missing URL fails at render, into the boundary above it, rather than at\n * import on a page that was only loading the module.\n */\nexport function MedianConnection({\n what,\n children,\n}: {\n /** Named in the error, so a broken build says which thing it broke. */\n what: string;\n children: ReactNode;\n}) {\n const [client] = useState(() => {\n if (!MEDIAN_DEPLOYMENT_URL) {\n throw new Error(\n `The Median ${what} was built without a deployment URL. Set NEXT_PUBLIC_CONVEX_URL.`,\n );\n }\n return new ConvexReactClient(MEDIAN_DEPLOYMENT_URL);\n });\n\n return <ConvexProvider client={client}>{children}</ConvexProvider>;\n}\n","\"use client\";\n\nimport { API_KEY_PREFIX, hasPrefix } from \"@median/backend/support\";\nimport { useEffect } from \"react\";\n\n/**\n * A key with the wrong shape would only ever fail later, quietly, as a\n * widget that never appears or a form that never sends. Say so now, in the\n * console, while the person who pasted it is still looking at this page.\n *\n * The key is truncated in the message: if what was pasted is a secret from\n * somewhere else, the console should not repeat all of it.\n */\nexport function useApiKeyShapeWarning(\n /** As the host wrote it: `<MedianSupport>`, or `useMedianContact()`. */\n caller: string,\n /** Undefined while an identity route is still answering, which is fine. */\n apiKey: string | undefined,\n) {\n useEffect(() => {\n if (apiKey === undefined || hasPrefix(apiKey, API_KEY_PREFIX)) return;\n\n const shown =\n apiKey === \"\"\n ? \"an empty string\"\n : `\"${apiKey.length > 16 ? `${apiKey.slice(0, 16)}…` : apiKey}\"`;\n console.error(\n `Median: the apiKey passed to ${caller} is ${shown}, which does not look like the browser-safe id from publicKeyFromMedianKey(MEDIAN_KEY). It should start with ${API_KEY_PREFIX}.`,\n );\n }, [caller, apiKey]);\n}\n","/**\n * The sound palette — layer/recipe types plus the seventeen built-in recipes.\n * Each sound has its own distinct shape — a chime, an arpeggio, a pitch\n * glide, a warm pad, a breath — rather than being a volume/EQ tweak on\n * the same click. Add a new one here without touching any audio graph code.\n */\nexport const RECIPES = {\n /** A soft two-note ascending bell, like an iOS/macOS confirmation tink. */\n chime: {\n masterGain: 0.5,\n layers: [\n { kind: \"tone\", waveform: \"sine\", frequency: 1046.5, attack: 0.006, decay: 0.22, peak: 0.09 },\n { kind: \"tone\", waveform: \"sine\", frequency: 1568, offset: 0.09, attack: 0.006, decay: 0.26, peak: 0.08 },\n ],\n shimmer: { delay: 0.12, feedback: 0.25, wet: 0.18, lowpass: 4000 },\n },\n /** A quick ascending twinkle of four notes — bright and playful. */\n sparkle: {\n masterGain: 0.5,\n layers: [\n { kind: \"tone\", waveform: \"sine\", frequency: 1760, offset: 0, attack: 0.003, decay: 0.09, peak: 0.045 },\n { kind: \"tone\", waveform: \"sine\", frequency: 2217, offset: 0.045, attack: 0.003, decay: 0.09, peak: 0.04 },\n { kind: \"tone\", waveform: \"sine\", frequency: 2637, offset: 0.09, attack: 0.003, decay: 0.1, peak: 0.038 },\n { kind: \"tone\", waveform: \"sine\", frequency: 3520, offset: 0.135, attack: 0.003, decay: 0.12, peak: 0.032 },\n ],\n shimmer: { delay: 0.07, feedback: 0.35, wet: 0.22, lowpass: 6000 },\n },\n /** A single note gliding smoothly downward, like a drop of water. */\n droplet: {\n masterGain: 0.55,\n layers: [\n { kind: \"tone\", waveform: \"sine\", frequency: 1200, glideTo: 550, glideTime: 0.14, attack: 0.004, decay: 0.2, peak: 0.075 },\n ],\n shimmer: { delay: 0.09, feedback: 0.2, wet: 0.15, lowpass: 3000 },\n },\n /** A warm, slow-swelling pad from two gently detuned sines. */\n bloom: {\n masterGain: 0.5,\n layers: [\n { kind: \"tone\", waveform: \"sine\", frequency: 528, attack: 0.06, decay: 0.32, peak: 0.06 },\n { kind: \"tone\", waveform: \"sine\", frequency: 528, detune: 12, attack: 0.06, decay: 0.34, peak: 0.05 },\n ],\n shimmer: { delay: 0.15, feedback: 0.2, wet: 0.12, lowpass: 2500 },\n },\n /** A soft hush with a falling tone — for tooltips and low-priority previews. */\n whisper: {\n masterGain: 0.48,\n layers: [\n { kind: \"noise\", filterType: \"lowpass\", filterFrequency: 1600, filterQ: 0.7, attack: 0.025, decay: 0.13, peak: 0.04 },\n { kind: \"tone\", waveform: \"sine\", frequency: 880, glideTo: 660, glideTime: 0.14, offset: 0.01, attack: 0.012, decay: 0.14, peak: 0.025 },\n ],\n },\n /** A focused, bandpass-filtered tick with a bright sine ping on top — crisp and instant. */\n tick: {\n masterGain: 0.4,\n layers: [\n { kind: \"noise\", filterType: \"bandpass\", filterFrequency: 5400, filterQ: 1.8, attack: 0.001, decay: 0.018, peak: 0.14 },\n { kind: \"tone\", waveform: \"sine\", frequency: 2600, attack: 0.001, decay: 0.012, peak: 0.018 },\n ],\n },\n /** A dull, muted knock — the \"down\" half of a press/release pair, like a key bottoming out. */\n press: {\n masterGain: 0.4,\n layers: [\n { kind: \"noise\", filterType: \"bandpass\", filterFrequency: 1700, filterQ: 1.4, attack: 0.001, decay: 0.02, peak: 0.13 },\n ],\n },\n /** A brighter, springier tick — the \"up\" half of a press/release pair, like a key returning. */\n release: {\n masterGain: 0.4,\n layers: [\n { kind: \"noise\", filterType: \"bandpass\", filterFrequency: 4600, filterQ: 1.8, attack: 0.001, decay: 0.016, peak: 0.12 },\n { kind: \"tone\", waveform: \"sine\", frequency: 3200, offset: 0.006, attack: 0.001, decay: 0.05, peak: 0.02 },\n ],\n },\n /** A two-part click-clack, like a mechanical switch flipping between states. */\n toggle: {\n masterGain: 0.4,\n layers: [\n { kind: \"noise\", filterType: \"bandpass\", filterFrequency: 2200, filterQ: 1.6, attack: 0.001, decay: 0.016, peak: 0.12 },\n { kind: \"noise\", filterType: \"bandpass\", filterFrequency: 3800, filterQ: 1.6, offset: 0.024, attack: 0.001, decay: 0.02, peak: 0.1 },\n ],\n },\n /** A short, warm three-note ascending confirmation — \"done\", not a fanfare. */\n success: {\n masterGain: 0.5,\n layers: [\n { kind: \"tone\", waveform: \"sine\", frequency: 880, attack: 0.004, decay: 0.09, peak: 0.06 },\n { kind: \"tone\", waveform: \"sine\", frequency: 1108.73, offset: 0.06, attack: 0.004, decay: 0.1, peak: 0.06 },\n { kind: \"tone\", waveform: \"sine\", frequency: 1318.51, offset: 0.12, attack: 0.004, decay: 0.18, peak: 0.07 },\n ],\n shimmer: { delay: 0.1, feedback: 0.22, wet: 0.16, lowpass: 4500 },\n },\n /** A muted knock followed by two descending tones — a calm, recoverable refusal. */\n error: {\n masterGain: 0.42,\n layers: [\n { kind: \"noise\", filterType: \"bandpass\", filterFrequency: 850, filterQ: 1.1, attack: 0.001, decay: 0.035, peak: 0.13 },\n { kind: \"tone\", waveform: \"triangle\", frequency: 440, offset: 0.025, attack: 0.004, decay: 0.09, peak: 0.045 },\n { kind: \"tone\", waveform: \"triangle\", frequency: 349.23, offset: 0.1, attack: 0.004, decay: 0.14, peak: 0.04 },\n ],\n },\n /** A papery filtered flick with a tiny glass tick — for pages, galleries, and carousels. */\n page: {\n masterGain: 0.38,\n layers: [\n { kind: \"noise\", filterType: \"lowpass\", filterFrequency: 1800, filterQ: 0.7, attack: 0.006, decay: 0.08, peak: 0.11 },\n { kind: \"noise\", filterType: \"bandpass\", filterFrequency: 4200, filterQ: 1.2, offset: 0.04, attack: 0.004, decay: 0.065, peak: 0.08 },\n { kind: \"tone\", waveform: \"sine\", frequency: 2400, offset: 0.075, attack: 0.002, decay: 0.045, peak: 0.02 },\n ],\n },\n /** A brief unresolved lift — signals that user-initiated work has started. */\n loading: {\n masterGain: 0.42,\n layers: [\n { kind: \"noise\", filterType: \"lowpass\", filterFrequency: 1400, filterQ: 0.6, attack: 0.035, decay: 0.14, peak: 0.035 },\n { kind: \"tone\", waveform: \"sine\", frequency: 420, glideTo: 630, glideTime: 0.18, attack: 0.025, decay: 0.18, peak: 0.05 },\n ],\n shimmer: { delay: 0.11, feedback: 0.18, wet: 0.12, lowpass: 2800 },\n },\n /** A quick lock-on sweep resolving to a clear tone — the system is ready. */\n ready: {\n masterGain: 0.48,\n layers: [\n { kind: \"noise\", filterType: \"bandpass\", filterFrequency: 3600, filterQ: 1.8, attack: 0.001, decay: 0.02, peak: 0.11 },\n { kind: \"tone\", waveform: \"triangle\", frequency: 330, glideTo: 660, glideTime: 0.12, offset: 0.012, attack: 0.004, decay: 0.16, peak: 0.055 },\n { kind: \"tone\", waveform: \"sine\", frequency: 990, offset: 0.13, attack: 0.004, decay: 0.22, peak: 0.06 },\n ],\n shimmer: { delay: 0.1, feedback: 0.16, wet: 0.1, lowpass: 4200 },\n },\n /** A compact synthetic chirp — crisp feedback for primary buttons and controls. */\n pulse: {\n masterGain: 0.42,\n layers: [\n { kind: \"noise\", filterType: \"bandpass\", filterFrequency: 2600, filterQ: 2.4, attack: 0.001, decay: 0.022, peak: 0.08 },\n { kind: \"tone\", waveform: \"triangle\", frequency: 620, glideTo: 1240, glideTime: 0.07, attack: 0.002, decay: 0.085, peak: 0.055 },\n ],\n },\n /** A fast three-step locator signal — playful feedback for menus and secondary buttons. */\n scan: {\n masterGain: 0.4,\n layers: [\n { kind: \"tone\", waveform: \"sine\", frequency: 740, attack: 0.002, decay: 0.055, peak: 0.05 },\n { kind: \"tone\", waveform: \"sine\", frequency: 1110, offset: 0.045, attack: 0.002, decay: 0.055, peak: 0.045 },\n { kind: \"tone\", waveform: \"sine\", frequency: 1665, offset: 0.09, attack: 0.002, decay: 0.07, peak: 0.04 },\n ],\n shimmer: { delay: 0.065, feedback: 0.16, wet: 0.1, lowpass: 4200 },\n },\n /** A rising harmonic portal with a soft tail — for client-side page arrivals. */\n arrival: {\n masterGain: 0.44,\n layers: [\n { kind: \"noise\", filterType: \"lowpass\", filterFrequency: 900, filterQ: 0.8, attack: 0.05, decay: 0.24, peak: 0.035 },\n { kind: \"tone\", waveform: \"sine\", frequency: 220, glideTo: 440, glideTime: 0.32, attack: 0.04, decay: 0.34, peak: 0.055 },\n { kind: \"tone\", waveform: \"sine\", frequency: 659.25, offset: 0.12, attack: 0.045, decay: 0.32, peak: 0.04 },\n { kind: \"tone\", waveform: \"sine\", frequency: 987.77, offset: 0.19, attack: 0.045, decay: 0.34, peak: 0.032 },\n ],\n shimmer: { delay: 0.16, feedback: 0.28, wet: 0.18, lowpass: 3200 },\n },\n};\nexport function isSoundName(value) {\n return typeof value === \"string\" && Object.prototype.hasOwnProperty.call(RECIPES, value);\n}\n/** All available sound names, derived from the recipe palette. */\nexport const sounds = Object.keys(RECIPES);\n","/**\n * The audio engine — synthesizes each sound live via the Web Audio API\n * on one shared, lazily created `AudioContext`. No audio files, no\n * dependencies. Every sound carries a gentle envelope (and often a soft\n * shimmer tail) instead of a hard transient, so nothing feels harsh.\n */\nimport { RECIPES, isSoundName, } from \"../sounds/recipes.js\";\nconst SOURCE_STOP_PADDING = 0.05;\nconst CLEANUP_MARGIN = 0.05;\nconst INAUDIBLE_GAIN = 0.001;\nconst OUTPUT_GAIN = 4;\nfunction renderTone(context, destination, layer, startTime) {\n const oscillator = context.createOscillator();\n oscillator.type = layer.waveform;\n oscillator.frequency.setValueAtTime(layer.frequency, startTime);\n if (layer.detune)\n oscillator.detune.value = layer.detune;\n if (layer.glideTo !== undefined) {\n const glideTime = layer.glideTime ?? layer.attack + layer.decay;\n oscillator.frequency.exponentialRampToValueAtTime(layer.glideTo, startTime + glideTime);\n }\n const gain = context.createGain();\n gain.gain.setValueAtTime(0.0001, startTime);\n gain.gain.exponentialRampToValueAtTime(layer.peak, startTime + layer.attack);\n gain.gain.exponentialRampToValueAtTime(0.0001, startTime + layer.attack + layer.decay);\n oscillator.connect(gain).connect(destination);\n oscillator.start(startTime);\n oscillator.stop(startTime + layer.attack + layer.decay + SOURCE_STOP_PADDING);\n}\nfunction renderNoise(context, destination, layer, startTime) {\n const duration = layer.attack + layer.decay + SOURCE_STOP_PADDING;\n const length = Math.max(1, Math.floor(duration * context.sampleRate));\n const buffer = context.createBuffer(1, length, context.sampleRate);\n const data = buffer.getChannelData(0);\n for (let i = 0; i < length; i++)\n data[i] = 2 * Math.random() - 1;\n const source = context.createBufferSource();\n source.buffer = buffer;\n const filter = context.createBiquadFilter();\n filter.type = layer.filterType;\n filter.frequency.value = layer.filterFrequency;\n if (layer.filterQ !== undefined)\n filter.Q.value = layer.filterQ;\n const gain = context.createGain();\n gain.gain.setValueAtTime(0.0001, startTime);\n gain.gain.exponentialRampToValueAtTime(layer.peak, startTime + layer.attack);\n gain.gain.exponentialRampToValueAtTime(0.0001, startTime + layer.attack + layer.decay);\n source.connect(filter).connect(gain).connect(destination);\n source.start(startTime);\n source.stop(startTime + duration);\n}\n/** Wires a soft echo/shimmer send off `source`, feeding back into `destination`. */\nfunction attachShimmer(context, source, destination, shimmer) {\n const delay = context.createDelay(1);\n delay.delayTime.value = shimmer.delay;\n const feedbackFilter = context.createBiquadFilter();\n feedbackFilter.type = \"lowpass\";\n feedbackFilter.frequency.value = shimmer.lowpass;\n const feedbackGain = context.createGain();\n feedbackGain.gain.value = shimmer.feedback;\n const wetGain = context.createGain();\n wetGain.gain.value = shimmer.wet;\n source.connect(delay);\n delay.connect(feedbackFilter);\n feedbackFilter.connect(feedbackGain);\n feedbackGain.connect(delay);\n feedbackFilter.connect(wetGain);\n wetGain.connect(destination);\n return [delay, feedbackFilter, feedbackGain, wetGain];\n}\nfunction sourceEnd(recipe) {\n return Math.max(...recipe.layers.map((layer) => (layer.offset ?? 0) + layer.attack + layer.decay + SOURCE_STOP_PADDING));\n}\nfunction shimmerTail(shimmer) {\n if (!shimmer || shimmer.feedback <= 0)\n return 0;\n if (shimmer.feedback >= 1)\n return shimmer.delay;\n return shimmer.delay * (1 + Math.ceil(Math.log(INAUDIBLE_GAIN) / Math.log(shimmer.feedback)));\n}\nlet sharedOutput = null;\nfunction getOutput(context) {\n if (sharedOutput)\n return sharedOutput;\n const output = context.createGain();\n output.gain.value = OUTPUT_GAIN;\n const limiter = context.createDynamicsCompressor();\n limiter.threshold.value = -8;\n limiter.knee.value = 6;\n limiter.ratio.value = 12;\n limiter.attack.value = 0.002;\n limiter.release.value = 0.08;\n output.connect(limiter).connect(context.destination);\n sharedOutput = output;\n return output;\n}\nfunction renderRecipe(context, recipe, volume) {\n const now = context.currentTime;\n const output = getOutput(context);\n const master = context.createGain();\n master.gain.value = recipe.masterGain * volume;\n master.connect(output);\n const shimmerNodes = recipe.shimmer\n ? attachShimmer(context, master, output, recipe.shimmer)\n : [];\n for (const layer of recipe.layers) {\n const startTime = now + (layer.offset ?? 0);\n if (layer.kind === \"tone\")\n renderTone(context, master, layer, startTime);\n else\n renderNoise(context, master, layer, startTime);\n }\n const cleanupAfterMs = (sourceEnd(recipe) + shimmerTail(recipe.shimmer) + CLEANUP_MARGIN) * 1000;\n setTimeout(() => {\n master.disconnect();\n for (const node of shimmerNodes)\n node.disconnect();\n }, cleanupAfterMs);\n}\nlet sharedContext = null;\nlet enabled = true;\nlet globalVolume = 1;\nfunction normalizeVolume(value, fallback) {\n return typeof value === \"number\" && Number.isFinite(value)\n ? Math.min(1, Math.max(0, value))\n : fallback;\n}\n/** Enables or disables future playback. Preference storage stays with the app. */\nexport function setEnabled(value) {\n if (typeof value === \"boolean\")\n enabled = value;\n}\n/** Sets the volume multiplier for future playback. Preference storage stays with the app. */\nexport function setVolume(value) {\n globalVolume = normalizeVolume(value, globalVolume);\n}\nfunction getAudioContext() {\n if (sharedContext)\n return sharedContext;\n if (typeof window === \"undefined\")\n return null;\n const Ctor = window.AudioContext ??\n window.webkitAudioContext;\n if (!Ctor)\n return null;\n try {\n sharedContext = new Ctor();\n }\n catch {\n return null;\n }\n return sharedContext;\n}\n/**\n * Plays a sound immediately. Safe to call from anywhere — lazily creates\n * the shared `AudioContext` on first use, resumes it if the browser\n * started it suspended (e.g. before any user gesture), and is a no-op\n * when Web Audio is unavailable (SSR, old browsers).\n */\nexport function play(sound = \"chime\", options) {\n if (!enabled || !isSoundName(sound))\n return;\n if (typeof navigator !== \"undefined\" && navigator.userActivation?.hasBeenActive === false)\n return;\n const playVolume = globalVolume * normalizeVolume(options?.volume, 1);\n if (playVolume === 0)\n return;\n const context = getAudioContext();\n if (!context)\n return;\n const recipe = RECIPES[sound];\n if (context.state === \"running\") {\n renderRecipe(context, recipe, playVolume);\n }\n else {\n try {\n void context.resume().then(() => {\n if (enabled && context.state === \"running\")\n renderRecipe(context, recipe, playVolume);\n }, () => { });\n }\n catch {\n // Some browsers throw synchronously when audio is blocked.\n }\n }\n}\n","\"use client\";\n\nimport { play } from \"cuelume\";\nimport { useEffect, useRef } from \"react\";\nimport type { SupportMessage } from \"../types\";\n\n/**\n * Under the default, because this plays on somebody else's site while they are\n * reading it. Loud enough to catch with the panel closed, quiet enough to sit\n * behind whatever else they have on.\n */\nconst ARRIVAL_VOLUME = 0.45;\n\n/**\n * The twinkle a reply lands on, whether the panel is open or not. Closed is\n * where it earns its keep: the count on the launcher is the only other thing\n * saying anything came in, and nobody is looking at the corner.\n *\n * The same rule the ring and the navigation follow, for the same reason: the\n * first snapshot is history, however recent, and history does not ring. Without\n * it, every page of a host's site would open on a sparkle for a conversation\n * from last week. A thread being fetched again is a thread this page has not\n * read either, so whatever lands after that is history too.\n *\n * And a change of thread is a first snapshot of its own. Somebody signing in\n * hands this page an account's whole history at once, which is a great many\n * replies it has never heard and not one of them worth a sound.\n *\n * Only the other side's messages, and only once they are written. A bubble\n * holding dots keeps its id and fills in later, so waiting for the words means\n * the sound arrives with them rather than with the promise of them.\n *\n * `play` neither throws nor asks: it is a no-op without Web Audio, and stays\n * quiet until the visitor has interacted with the page at all. That last one is\n * the browser's rule about autoplay, and it is a rule worth keeping.\n */\nexport function useMessageSound({\n messages,\n conversationId = null,\n isLoading,\n}: {\n messages: SupportMessage[];\n /** The thread being watched. A different one starts the listening over. */\n conversationId?: string | null;\n /** Nothing is history until the first answer has arrived. */\n isLoading: boolean;\n}) {\n /** Every written reply this page has already heard. */\n const heard = useRef<Set<string> | null>(null);\n /** Which thread that set is about. */\n const watching = useRef<string | null>(null);\n\n useEffect(() => {\n if (isLoading) {\n heard.current = null;\n return;\n }\n\n const written = messages.filter(\n (message) => message.sender === \"agent\" && !message.pending,\n );\n\n if (heard.current === null || watching.current !== conversationId) {\n heard.current = new Set(written.map((message) => message.id));\n watching.current = conversationId;\n return;\n }\n\n // One sparkle however many arrived together: a person taking over and\n // answering in the same breath is still one thing to look up at.\n let hasArrived = false;\n for (const message of written) {\n if (heard.current.has(message.id)) continue;\n heard.current.add(message.id);\n hasArrived = true;\n }\n\n if (hasArrived) play(\"sparkle\", { volume: ARRIVAL_VOLUME });\n }, [messages, conversationId, isLoading]);\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport type {\n SupportAsk,\n SupportDestination,\n SupportMessage,\n} from \"../types\";\n\n/**\n * Doing what one of the team's own tools asked to be done to the page:\n * pointing at something on it, or offering to take the reader somewhere.\n *\n * The two are different kinds of thing, so they happen at different times.\n * A ring is ours: drawn over the host's page for a moment and taken away\n * again, with nothing of theirs touched. Moving somebody is not. A page that\n * changes under a reader who touched nothing is the one thing a widget can\n * do that feels like it has been let loose, so a destination is offered as a\n * button and the page changes when it is pressed, or never.\n *\n * That makes most of the timing questions somebody else's. A button is inert\n * until somebody reaches for it, so a message carrying one can be as old as\n * it likes: it is an offer sitting in a thread, the same as a link would be.\n *\n * The ring still has to be timed, and this is where that is done. The thread\n * is a live query and a message stays in it forever, so \"ring what I can see\"\n * would ring every time the query re-ran, and again every time they came back\n * to the page. What counts as new is what arrived after the widget started\n * watching: the first snapshot is history, however recent, and history is not\n * an instruction.\n *\n * A thread can also change under a page that never stopped watching it.\n * Somebody signs in, the widget says who they are, and a query that answered\n * \"no conversation\" a second ago answers with the account's whole history\n * instead. Every word of it is new to this page and none of it is news, so a\n * change of thread starts the first look over, and a ring has to be recent as\n * well as unseen before it is drawn.\n */\n\n// Guarded as in control.ts: the published bundle cannot assume `process`.\nconst isDevelopment =\n typeof process !== \"undefined\" && process.env.NODE_ENV === \"development\";\n\n/**\n * How recently something has to have been asked for to be worth doing.\n *\n * Long enough that a slow network, a reconnect, or a tab coming back from\n * sleep still lands the one that just happened. Short enough that a\n * conversation from last week cannot ring anything, whatever route it takes\n * into the query: the person reading this page did not ask for it, and\n * whoever did is long gone.\n *\n * Generous because the two ends of the comparison are two different clocks.\n * The instruction is stamped by the server and the age is worked out on a\n * machine that may be a minute out either way, so the window is minutes\n * rather than seconds. Nothing about a live exchange needs it tighter.\n */\nconst STALE_AFTER_MS = 5 * 60 * 1000;\n\n/** The ring, once something has asked for one and it is time to draw it. */\nexport type SupportSpotlight = {\n /** New every time, so the same ask pressed twice rings twice. */\n key: string;\n selector: string;\n};\n\n/** Whether something asked for a beat ago rather than a while ago. */\nfunction isFresh(at: number): boolean {\n const age = Date.now() - at;\n // No clock on it at all reads as old. That is canned content, or a\n // deployment answering from before any of this, and the safe reading of\n // \"I cannot tell how old this is\" is to leave the page alone.\n return Number.isFinite(age) && age <= STALE_AFTER_MS;\n}\n\n/**\n * The destination as somewhere on this site, or null.\n *\n * Median checks the path when the tool answers and again before it is stored,\n * and neither of those is on the machine that navigates. This one is. `//host`\n * and `/\\host` resolve to another origin rather than a path, which is the\n * whole reason this asks the URL parser instead of reading the string.\n */\nfunction pathOnThisSite(destination: string): string | null {\n if (typeof window === \"undefined\") return null;\n if (!destination.startsWith(\"/\")) return null;\n\n let url: URL;\n try {\n url = new URL(destination, window.location.origin);\n } catch {\n return null;\n }\n if (url.origin !== window.location.origin) return null;\n\n return `${url.pathname}${url.search}${url.hash}`;\n}\n\n/**\n * Everything asking for a ring right now, oldest first.\n *\n * Only the ones with nowhere to go. A tool that asked for both is pointing at\n * something on the page it offered, and that page is one nobody has opened\n * yet, so its ring waits for the press that takes them there.\n *\n * Two carriers, one list. A message carries what the agent's own turn\n * produced; a standalone ask carries one from a run that never produced a\n * message, which is a teammate running a tool themselves. The loose one goes\n * last because it is the newer of the two whenever both land together: a\n * teammate reaches for a tool in a thread the agent has already answered.\n */\nfunction ringing(messages: SupportMessage[], ask: SupportAsk | null) {\n const asked: { key: string; at: number; selector: string }[] = [];\n\n for (const message of messages) {\n if (message.highlight === undefined) continue;\n if (message.navigateTo !== undefined) continue;\n asked.push({\n key: message.id,\n // Canned content may leave the clock off, and a message with no time on\n // it reads as old here rather than as now.\n at: message.sentAt ?? 0,\n selector: message.highlight,\n });\n }\n\n if (ask !== null && ask.highlight !== null && ask.navigateTo === null) {\n asked.push({ key: ask.key, at: ask.at, selector: ask.highlight });\n }\n return asked;\n}\n\nexport function useSupportNavigation({\n messages,\n ask = null,\n conversationId = null,\n isLoading,\n onNavigate,\n}: {\n messages: SupportMessage[];\n /** The one not riding a message, when a run produced one. */\n ask?: SupportAsk | null;\n /**\n * The thread being watched. When it changes, what is on screen is somebody\n * else's history rather than this page's next line, so the first look\n * starts again.\n */\n conversationId?: string | null;\n /** Nothing is history until the first answer has arrived. */\n isLoading: boolean;\n onNavigate?: (path: string) => void;\n}): {\n /** What to ring, while there is something. */\n spotlight: SupportSpotlight | null;\n /** A page offered by a run with no message to hang a button on. */\n offer: SupportDestination | null;\n /** What a button does. */\n follow: (destination: SupportDestination) => void;\n} {\n /** Every ask this page has already had a look at. */\n const seen = useRef<Set<string> | null>(null);\n /** Which thread that set is about. */\n const watching = useRef<string | null>(null);\n const [spotlight, setSpotlight] = useState<SupportSpotlight | null>(null);\n /** How many rings have been drawn, so a repeat is never the same one. */\n const drawn = useRef(0);\n /** The loose offer they have already taken, which is not one to make twice. */\n const [taken, setTaken] = useState<string | null>(null);\n\n const follow = useCallback(\n (destination: SupportDestination) => {\n if (onNavigate === undefined) return;\n\n const path = pathOnThisSite(destination.path);\n if (path === null) {\n console.error(\n `Median: refused to navigate to ${destination.path}, which is not a path on this site.`,\n );\n return;\n }\n\n onNavigate(path);\n setTaken(destination.key);\n\n // The page is on its way, so whatever was to be pointed out on it is\n // now this page's business. The ring looks for the element for a few\n // seconds, which is roughly how long a route change takes.\n if (destination.highlight !== null) {\n drawn.current += 1;\n setSpotlight({\n key: `${destination.key}:${drawn.current}`,\n selector: destination.highlight,\n });\n }\n },\n [onNavigate],\n );\n\n // A layout effect, as the ring's own arrival is: it and the message that\n // explains it come in one live-query update, and the ring should be up\n // before the browser paints the words about it.\n useLayoutEffect(() => {\n if (isLoading) return;\n\n const asked = ringing(messages, ask);\n\n // The first look is the thread as it already was. Remembered, never\n // acted on: whatever is in it happened before this page existed.\n //\n // A different thread than the one this set was built for is a first look\n // as well. That is signing in: the account's history arrives whole, into\n // a page that has been watching an empty query, and none of it happened\n // here.\n if (seen.current === null || watching.current !== conversationId) {\n seen.current = new Set(asked.map((one) => one.key));\n watching.current = conversationId;\n return;\n }\n\n // Only the newest is worth drawing. Two in one turn is a turn that has\n // already gone wrong, and the later of them is the one the words\n // underneath are about.\n let latest: SupportSpotlight | null = null;\n\n for (const one of asked) {\n if (seen.current.has(one.key)) continue;\n // Marked before it is drawn, so a re-render cannot ring twice.\n seen.current.add(one.key);\n // Unseen and still not news. Pointing at something is a thing being\n // done to a page in front of a reader, so one that has been sitting in\n // a thread since yesterday is not carried out late.\n if (!isFresh(one.at)) continue;\n\n drawn.current += 1;\n latest = { key: `${one.key}:${drawn.current}`, selector: one.selector };\n }\n\n if (latest !== null) setSpotlight(latest);\n }, [messages, ask, conversationId, isLoading]);\n\n // A host who wired a navigating tool but no handler hears about it here\n // rather than by wondering where the button went. Once per page: the\n // message carrying the offer stays in the thread for good, and a warning\n // that repeated with it would be the console's whole contents.\n const warned = useRef(false);\n useEffect(() => {\n if (!isDevelopment || onNavigate !== undefined || warned.current) return;\n\n const offered =\n messages.some((message) => message.navigateTo !== undefined) ||\n ask?.navigateTo != null;\n if (!offered) return;\n\n warned.current = true;\n console.warn(\n \"Median: a tool offered to take this visitor to a page, but <MedianSupport> has no onNavigate, so no button is shown for it. Pass one, such as onNavigate={router.push}.\",\n );\n }, [messages, ask, onNavigate]);\n\n // A loose offer stands while it is fresh. Nothing else expires here, since\n // nothing else is a button standing over the composer of a conversation the\n // reader may have carried on past.\n const [, expire] = useState(0);\n useEffect(() => {\n if (ask === null || ask.navigateTo === null) return;\n\n const left = ask.at + STALE_AFTER_MS - Date.now();\n if (left <= 0) return;\n const timer = setTimeout(() => expire((count) => count + 1), left);\n return () => clearTimeout(timer);\n }, [ask]);\n\n const offer =\n ask !== null &&\n ask.navigateTo !== null &&\n ask.key !== taken &&\n onNavigate !== undefined &&\n isFresh(ask.at)\n ? { key: ask.key, path: ask.navigateTo, highlight: ask.highlight }\n : null;\n\n return { spotlight, offer, follow };\n}\n","\"use client\";\n\nimport { useCallback, useState } from \"react\";\nimport type { SupportMessage } from \"../types\";\nimport { readTeaserDismissedAt, writeTeaserDismissedAt } from \"./teaser-state\";\n\n/**\n * Which reply, if any, gets the card over the closed button.\n *\n * The newest unread reply, unless this browser already waved it away. A\n * reply that lands after a dismissal is news again and gets a card of its\n * own, which is the whole reason the dismissal is a time rather than a flag.\n *\n * Opening the panel is handled elsewhere: it marks everything seen, the\n * unread list empties, and there is nothing left here to show.\n */\nexport function useTeaser(\n apiKey: string,\n /** The newest reply they have not looked at, or null. */\n latestUnread: SupportMessage | null,\n): { teaser: SupportMessage | null; dismiss: () => void } {\n const [dismissedAt, setDismissedAt] = useState(() =>\n readTeaserDismissedAt(apiKey),\n );\n\n const teaser =\n latestUnread !== null && (latestUnread.sentAt ?? 0) > dismissedAt\n ? latestUnread\n : null;\n\n const dismiss = useCallback(() => {\n if (teaser === null) return;\n // Stamped with when the reply was sent rather than with now, so a reply\n // that has been sitting on the server since before the click still\n // counts as dismissed, and one that arrives a second later does not.\n const at = teaser.sentAt ?? Date.now();\n writeTeaserDismissedAt(apiKey, at);\n setDismissedAt(at);\n }, [apiKey, teaser]);\n\n return { teaser, dismiss };\n}\n","/**\n * A timestamp this browser remembers, per widget.\n *\n * Kept beside the session token and for the same reason: a host's site is\n * usually several pages, so the widget is remounted every time somebody\n * clicks a link. Held in memory, anything decided while the panel was\n * closed would be forgotten the moment they wandered to another page.\n *\n * One entry per API key, like the session, so two widgets on one browser\n * cannot read each other's marks. Used for the dismissed teaser; read\n * receipts are stored on the server so they follow the visitor's devices.\n */\n\n/** Where it lives when localStorage is blocked. Lasts the page load. */\nconst inMemory = new Map<string, number>();\n\nfunction keyOf(name: string, apiKey: string): string {\n return `median:${name}:${apiKey}`;\n}\n\n/** The stamp, or 0 when this browser has never written one. */\nexport function readStamp(name: string, apiKey: string): number {\n if (typeof window === \"undefined\") return 0;\n\n const key = keyOf(name, apiKey);\n try {\n const stored = window.localStorage.getItem(key);\n const at = stored === null ? Number.NaN : Number(stored);\n return Number.isFinite(at) ? at : 0;\n } catch {\n return inMemory.get(key) ?? 0;\n }\n}\n\nexport function writeStamp(name: string, apiKey: string, at: number): void {\n if (typeof window === \"undefined\") return;\n\n const key = keyOf(name, apiKey);\n inMemory.set(key, at);\n try {\n window.localStorage.setItem(key, String(at));\n } catch {\n // Blocked, so the in-memory copy above is all there is. It still holds\n // for as long as this page does, which is the part anybody would notice.\n }\n}\n","import { readStamp, writeStamp } from \"./stamps\";\n\n/**\n * The newest reply whose teaser this browser waved away.\n *\n * Its own stamp rather than the read one, because the two mean different\n * things. Dismissing the card says \"not now\", and the count stays on the\n * button until the panel is actually opened; marking a reply read would\n * take the count with it, and a visitor who closed a card has not read the\n * three replies behind it.\n */\n\nconst TEASER = \"teaser\";\n\n/** When the last dismissed reply was sent, or 0 if none ever was. */\nexport function readTeaserDismissedAt(apiKey: string): number {\n return readStamp(TEASER, apiKey);\n}\n\nexport function writeTeaserDismissedAt(apiKey: string, at: number): void {\n writeStamp(TEASER, apiKey, at);\n}\n","\"use client\";\n\nimport { useEffect } from \"react\";\n\n/** The count ahead of the host's title, the way mail has always shown it. */\nfunction badged(count: number, title: string): string {\n return `(${count}) ${title}`.trim();\n}\n\n/**\n * Puts the unread count in the tab's title while there is one, and takes it\n * back out when there is not.\n *\n * A reply that lands while the visitor is on another tab has no way to say\n * so: the launcher's count and the card over it are in a tab nobody is\n * looking at, and the sound is one moment. The title is the one thing of\n * this page that is visible from every other tab, so it carries the count.\n *\n * The host's own title is held and put back, and a host that changes it\n * while the count is up, which is what a router does on every navigation,\n * has the count moved onto the new one. Nothing is put back over a title\n * that is no longer the badged one: if the host wrote something else in\n * the meantime, that is theirs and it stays.\n */\nexport function useUnreadTitle(unreadCount: number) {\n useEffect(() => {\n if (unreadCount === 0 || typeof document === \"undefined\") return;\n\n // Read now rather than on mount, since the host may have renamed the\n // page any number of times since then.\n let plain = document.title;\n let shown = badged(unreadCount, plain);\n document.title = shown;\n\n // The host writing a new title underneath. Our own write lands here\n // too, and is told apart by being exactly what was written.\n const observer = new MutationObserver(() => {\n const current = document.title;\n if (current === shown) return;\n plain = current;\n shown = badged(unreadCount, plain);\n document.title = shown;\n });\n observer.observe(document.head, {\n childList: true,\n subtree: true,\n characterData: true,\n });\n\n return () => {\n observer.disconnect();\n if (document.title === shown) document.title = plain;\n };\n }, [unreadCount]);\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { isDevelopment } from \"./open-store\";\n\n/**\n * What a mounted component hands back: the two things a host without React\n * has no re-render to do for them. Everything else goes through the\n * component's own control, same as anywhere.\n */\nexport type MedianMountInstance<Options> = {\n /**\n * Change any option after mount. Only the keys passed change, so\n * `update({ user })` when somebody signs in leaves the rest alone.\n */\n update: (options: Partial<Options>) => void;\n /** Take it off the page: the component, its container, its connection. */\n unmount: () => void;\n};\n\n/** An instance that is safe to hold when nothing could be mounted. */\nexport const nothingMounted: MedianMountInstance<object> = {\n update: () => {},\n unmount: () => {},\n};\n\n/**\n * The one way every mount renders, so the four of them cannot drift.\n *\n * A container of ours is appended to `into` and rendered into with the\n * package's copy of React. The host's page needs none and never sees it.\n * `into` is the body unless the caller has somewhere better, which the\n * contact form does: it goes where the host's page has room for it.\n *\n * A server rendering the page has nowhere to put anything, so that case\n * says so where the installer is looking and hands back an instance that is\n * safe to hold, because a broken mount must never take the page down with it.\n */\nexport function mountMedian<Options extends object>({\n caller,\n what,\n marker,\n into,\n options,\n render,\n}: {\n /** The function the host called, for the console. */\n caller: string;\n /** What was mounted, in the console's words: \"the widget\", \"the form\". */\n what: string;\n /** The dataset key the container wears, so it can be found in devtools. */\n marker: string;\n /** Where the container goes. Null for the body. */\n into: Element | null;\n options: Options;\n render: (options: Options) => ReactNode;\n}): MedianMountInstance<Options> {\n if (typeof document === \"undefined\") {\n console.error(\n `Median: ${caller}() was called where there is no document, so nothing was mounted. Call it in the browser, after the page exists.`,\n );\n return nothingMounted;\n }\n\n const container = document.createElement(\"div\");\n container.dataset[marker] = \"\";\n (into ?? document.body).appendChild(container);\n\n const root = createRoot(container);\n let current = options;\n let mounted = true;\n\n const paint = () => {\n root.render(render(current));\n };\n paint();\n\n return {\n update(next) {\n if (!mounted) {\n if (isDevelopment) {\n console.warn(\n `Median: update() was called after unmount(), so nothing changed. Mount again with ${caller}() if ${what} should come back.`,\n );\n }\n return;\n }\n current = { ...current, ...next };\n paint();\n },\n unmount() {\n if (!mounted) return;\n mounted = false;\n root.unmount();\n container.remove();\n },\n };\n}\n","\"use client\";\n\nimport { MedianSupport, type MedianSupportProps } from \"./median-support\";\nimport { mountMedian, type MedianMountInstance } from \"./mount-into\";\n\n/**\n * What a mounted widget hands back: the two things a host without React has\n * no re-render to do for them. Everything else goes through `medianSupport`,\n * same as anywhere.\n */\nexport type MedianSupportInstance = MedianMountInstance<MedianSupportProps>;\n\n/**\n * The same widget, for a page that has never heard of React: Vue, Svelte,\n * Rails, a script on plain HTML. One call puts the launcher in the corner,\n * wired to the same panel, thread and inbox as the component:\n *\n * const support = mountMedianSupport({ apiKey: \"median_pk_...\" });\n *\n * It takes the component's props, as one options object, and the rest of the\n * SDK does not know the difference: `medianSupport.open()` opens this widget\n * exactly as it opens the component.\n *\n * What would have been new props on a re-render is `update`:\n *\n * support.update({ user: { name: \"Ada\", email: \"ada@example.com\" } });\n *\n * It renders into a container of its own, appended to `<body>`, with the\n * package's copy of React. The host's page needs none and never sees it.\n */\nexport function mountMedianSupport(\n options: MedianSupportProps,\n): MedianSupportInstance {\n return mountMedian({\n caller: \"mountMedianSupport\",\n what: \"the widget\",\n marker: \"medianSupport\",\n into: null,\n options,\n render: (current) => <MedianSupport {...current} />,\n });\n}\n","\"use client\";\n\nimport { MedianSupportPreview, type MedianSupportProps } from \"./median-support\";\n\n/**\n * Every prop the widget takes, unchanged. The modal is the widget in a\n * different place, not a different widget.\n */\nexport type MedianSupportModalProps = MedianSupportProps;\n\n/**\n * The support widget as a modal: the same button in the corner, and a panel\n * that opens in the middle of the screen over a scrim rather than out of\n * the corner.\n *\n * <MedianSupportModal apiKey=\"median_pk_...\" />\n *\n * Everything else is the widget's. Same props, same thread, same inbox on\n * the other side, same `useMedianSupport()` to open it from your own nav,\n * same sheet on a phone. Pick this for a site where a conversation deserves\n * the room, or where a panel floating over the page would be in the way of\n * the page.\n *\n * One or the other. Mounting both shares one open state, and the console\n * says so.\n */\nexport function MedianSupportModal(props: MedianSupportModalProps) {\n return <MedianSupportPreview {...props} presentation=\"modal\" />;\n}\n","\"use client\";\n\nimport {\n MedianSupportModal,\n type MedianSupportModalProps,\n} from \"./median-support-modal\";\nimport { mountMedian, type MedianMountInstance } from \"./mount-into\";\n\nexport type MedianSupportModalInstance =\n MedianMountInstance<MedianSupportModalProps>;\n\n/**\n * The modal, for a page that has never heard of React: Vue, Svelte, Rails,\n * a script on plain HTML.\n *\n * const support = mountMedianSupportModal({ apiKey: \"median_pk_...\" });\n *\n * It takes the component's props as one options object, and the rest of the\n * SDK does not know the difference: `medianSupport.open()` opens this modal\n * exactly as it opens the component. `update` and `unmount` are the same two\n * `mountMedianSupport` hands back.\n */\nexport function mountMedianSupportModal(\n options: MedianSupportModalProps,\n): MedianSupportModalInstance {\n return mountMedian({\n caller: \"mountMedianSupportModal\",\n what: \"the modal\",\n marker: \"medianSupportModal\",\n into: null,\n options,\n render: (current) => <MedianSupportModal {...current} />,\n });\n}\n","\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport { FeedbackPanel } from \"../feedback/feedback-panel\";\nimport { FeedbackPopover } from \"../feedback/feedback-popover\";\nimport type { MedianDiagnostics } from \"./diagnostics\";\nimport { MedianErrorBoundary } from \"./error-boundary\";\nimport {\n useMedianFeedbackOpenState,\n useRegisterMedianFeedback,\n} from \"./feedback-control\";\nimport { useRememberInstall } from \"./install\";\nimport { MedianConnection } from \"./median-connection\";\nimport { useApiKeyShapeWarning } from \"./use-api-key-warning\";\nimport { useCustomDataSender } from \"./custom-data\";\nimport { useSignedInUser } from \"./signed-out\";\nimport { useMedianIdentity, useMissingCredentials } from \"./use-identity\";\nimport { useFeedbackNote } from \"./use-feedback-note\";\nimport { useMedianBranding } from \"./use-median-branding\";\nimport type { MedianUser } from \"./use-median-thread\";\n\n/** Wide enough for a paragraph, narrow enough to hang off a menu item. */\nconst DEFAULT_WIDTH = \"20rem\";\n\ntype MedianFeedbackOptions = {\n /**\n * Your own trigger. Whatever you pass is rendered where you put it and the\n * panel hangs off it, the way a dropdown hangs off the thing that opens\n * it:\n *\n * <MedianFeedback apiKey=\"median_pk_...\">\n * <button>Share feedback</button>\n * </MedianFeedback>\n *\n * It is wrapped in an inline span that takes the click, so your button\n * keeps its own styles and needs to know nothing about any of this.\n *\n * Without children the panel sits in the bottom right corner instead, for\n * a host opening it from somewhere else entirely with\n * `useMedianFeedback()`.\n */\n children?: ReactNode;\n /**\n * Something already on your page for the panel to hang off, as a CSS\n * selector. For the case children cannot cover: a row inside a dropdown,\n * which closes itself opening this and takes the trigger with it.\n *\n * <MedianFeedback apiKey=\"median_pk_...\" anchor=\"#account-button\" />\n *\n * Looked up each time the panel is placed, so it works for anything that\n * comes and goes. Unlike a trigger it is only a position: pressing it does\n * not open or close the panel, and a press on it dismisses the panel like\n * a press anywhere else. Ignored when `children` are passed.\n */\n anchor?: string;\n /** Who is signed in on your site, if anyone. Shown to your team. */\n user?: MedianUser;\n /**\n * Whether a note carries what the browser already knows: time zone,\n * language, browser, OS, device, screen, and the page it was written on.\n * On by default, because the page is most of what makes a note actionable,\n * which is what the panel says out loud. Pass `false` and none of it is\n * read at all.\n */\n telemetry?: boolean;\n /**\n * Whether a note carries a picture of the page it was written on, drawn\n * from the page itself rather than recorded off the screen, so nobody is\n * asked for permission to share anything. On by default: a bug report with\n * the screen on it is one nobody has to describe. Pass `false` and no\n * picture is taken.\n *\n * The panel itself is never in the picture, and neither is anything else\n * Median renders.\n */\n screenshot?: boolean;\n /**\n * Watch what goes wrong on the page, so a bug filed from a note arrives\n * with the error already attached. The same switch `<MedianSupport>`\n * takes, and the same buffer behind it: mount either one with it on.\n */\n diagnostics?: MedianDiagnostics;\n /**\n * Anything about your own app worth having next to a bug report: a build,\n * a route, a flag. Read when a note is sent.\n */\n appState?: Record<string, string | number | boolean>;\n /** Pass it, with `onOpenChange`, to hold the open state yourself. */\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n /** Any CSS length. The panel never grows past the viewport whatever this says. */\n width?: string;\n /** The heading, for a host whose product calls this something else. */\n title?: string;\n /** The line under it. */\n description?: string;\n placeholder?: string;\n};\n\n/**\n * Two ways in, the same two `<MedianSupport>` takes.\n *\n * `apiKey` is the publishable id on its own. `identity` is the path to a\n * route built with `medianIdentity`, which answers with the key and with\n * whoever is signed in, already signed. Pass both and the panel is usable on\n * the key from the prop while the route is still answering.\n */\nexport type MedianFeedbackProps = MedianFeedbackOptions &\n (\n | { apiKey: string; identity?: string }\n | { apiKey?: string; identity: string }\n );\n\n/** The same, once the gate below has settled which key and which person. */\ntype ConnectedFeedbackProps = MedianFeedbackOptions & { apiKey: string };\n\n/**\n * The same two ways in, for a host without JSX. Spelled out rather than\n * `Omit`ped off the props above, because omitting a key from a union of two\n * shapes flattens it into one and loses the \"either, but not neither\" the\n * union is there to say.\n */\nexport type MedianFeedbackMountOptions = Omit<\n MedianFeedbackOptions,\n \"children\"\n> &\n (\n | { apiKey: string; identity?: string }\n | { apiKey?: string; identity: string }\n );\n\n/**\n * A feedback panel, hung off a trigger of your own.\n *\n * One box and one button. Whoever is on the page types a sentence, and a\n * fast model reads it: a bug goes on the bug list, an idea goes on the\n * suggestions list, and an advert goes nowhere. Nobody has to pick a\n * category, rank anything, or write a title, which is the point, because a\n * form that asks for those is a form nobody fills in.\n *\n * <MedianFeedback apiKey=\"median_pk_...\">\n * <MenuItem>Share feedback</MenuItem>\n * </MedianFeedback>\n *\n * The page they were on rides along on its own, and so does a picture of it.\n * `screenshot={false}` leaves the picture behind.\n *\n * It brings its own connection, so it works on pages that have never heard\n * of Convex, and it is independent of `<MedianSupport>`: mount either, both,\n * or neither. Mounted together they share a visitor, so the same person\n * writing in and leaving a note is one customer with both to their name.\n */\nexport function MedianFeedback(props: MedianFeedbackProps) {\n return (\n <MedianErrorBoundary component=\"MedianFeedback\">\n <FeedbackIdentity {...props} />\n </MedianErrorBoundary>\n );\n}\n\n/**\n * The same gate `<MedianSupport>` has, for the same reason: the signature can\n * only be made on a server and this is a client component. Both panels on one\n * page read the one route between them. See `use-identity.ts`.\n */\nfunction FeedbackIdentity({\n apiKey,\n identity,\n user,\n ...rest\n}: MedianFeedbackOptions & { apiKey?: string; identity?: string }) {\n const resolved = useMedianIdentity(identity);\n useMissingCredentials(\"<MedianFeedback>\", apiKey, identity);\n const key = apiKey ?? resolved.apiKey;\n const person = useSignedInUser(user ?? resolved.user);\n\n if (key === undefined) return null;\n\n return (\n <MedianConnection what=\"feedback panel\">\n <Feedback {...rest} apiKey={key} {...(person ? { user: person } : {})} />\n </MedianConnection>\n );\n}\n\nfunction Feedback({\n apiKey,\n children,\n anchor,\n user,\n telemetry,\n screenshot,\n diagnostics,\n appState,\n open,\n onOpenChange,\n width = DEFAULT_WIDTH,\n title,\n description,\n placeholder,\n}: ConnectedFeedbackProps) {\n useRegisterMedianFeedback();\n useApiKeyShapeWarning(\"<MedianFeedback>\", apiKey);\n useCustomDataSender();\n // The support panel writes the same thing down. Either one is enough for a\n // crash screen to have a key to report with.\n useRememberInstall(apiKey, user);\n\n const triggerRef = useRef<HTMLSpanElement>(null);\n const [isOpen, setOpen] = useMedianFeedbackOpenState({\n ...(open !== undefined ? { open } : {}),\n ...(onOpenChange ? { onOpenChange } : {}),\n });\n\n const note = useFeedbackNote({\n apiKey,\n ...(user ? { user } : {}),\n ...(telemetry !== undefined ? { telemetry } : {}),\n ...(screenshot !== undefined ? { screenshot } : {}),\n ...(diagnostics !== undefined ? { diagnostics } : {}),\n ...(appState ? { appState } : {}),\n });\n const showBranding = useMedianBranding(apiKey);\n\n const close = useCallback(() => setOpen(false), [setOpen]);\n\n // A fresh box for the next note, but not until this one is out of sight:\n // resetting as the panel closes would swap the thank-you back to an empty\n // form halfway through the fade. So the panel is remounted on the way\n // back in, and only when there is something to clear. A draft somebody\n // closed without sending is theirs, and survives.\n const [attempt, setAttempt] = useState(0);\n const hasSent = useRef(false);\n useEffect(() => {\n if (!isOpen || !hasSent.current) return;\n hasSent.current = false;\n setAttempt((count) => count + 1);\n }, [isOpen]);\n\n return (\n <>\n {children !== undefined && (\n // An inline span rather than a clone of whatever was passed: the\n // host's trigger keeps its own props, its own ref and its own\n // styles, and the click is caught on the way past. `display:\n // contents` would lose the rectangle the panel is measured against.\n <span\n ref={triggerRef}\n onClick={() => setOpen(!isOpen)}\n className=\"inline-flex\"\n >\n {children}\n </span>\n )}\n\n <FeedbackPopover\n isOpen={isOpen}\n onClose={close}\n triggerRef={triggerRef}\n {...(anchor !== undefined ? { anchor } : {})}\n width={width}\n >\n <FeedbackPanel\n showBadge={showBranding}\n key={attempt}\n onSubmit={note.submit}\n onClose={close}\n onSent={() => {\n hasSent.current = true;\n close();\n }}\n notice={note.notice}\n {...(title !== undefined ? { title } : {})}\n {...(description !== undefined ? { description } : {})}\n {...(placeholder !== undefined ? { placeholder } : {})}\n />\n </FeedbackPopover>\n </>\n );\n}\n","/**\n * The feedback panel's vocabulary: what a note may be, what it may point at,\n * and the three answers a reader can give about one.\n *\n * Deliberately pure, like `shared/signals.ts`: no Convex imports, nothing\n * that touches a database. The widget reads these limits before it sends and\n * the mutation enforces them on arrival, so neither side can drift.\n */\n\n/**\n * How much one note may be. Longer than a chat message would be worth\n * reading in a bubble and shorter than the write-up it turns into, because\n * this is a box somebody types a paragraph into, not a document.\n */\nexport const NOTE_MAX_LENGTH = 2_000;\n\n/**\n * Where a note goes.\n *\n * The first two are the Signal tab's own kinds, so a note that survives\n * reading lands on the list the team already triages. The third is the one\n * that goes nowhere: a page open to the internet collects adverts, keyboard\n * mashing, and people testing whether the box works, and none of that is\n * worth a row somebody has to close.\n */\nexport const FEEDBACK_VERDICTS = [\"bug\", \"suggestion\", \"spam\"] as const;\n\nexport type FeedbackVerdict = (typeof FEEDBACK_VERDICTS)[number];\n\n/** Trims a note, keeping its line breaks: somebody wrote it in a textarea. */\nexport function cleanNote(input: string): string {\n return input.trim().slice(0, NOTE_MAX_LENGTH);\n}\n\n/** What is wrong with this note, in a sentence, or null if nothing is. */\nexport function noteIssue(note: string): string | null {\n if (note.trim().length === 0) return \"Enter your feedback before sending.\";\n if (note.length > NOTE_MAX_LENGTH) {\n return `Feedback has to be ${NOTE_MAX_LENGTH} characters or fewer.`;\n }\n return null;\n}\n","import { isUnsafeAttachmentType } from \"./support\";\n\n/**\n * The pictures on a bug report: what a customer sent, what the agent found a\n * link to, and what a host handed over on purpose.\n *\n * A screenshot is the fastest thing on a report to read. Everything else on\n * one is a description of what went wrong; a picture is the thing itself. So\n * they are lifted out of wherever they arrived and kept on the report, where\n * a developer meets them without opening the conversation they came from.\n *\n * Two ways a picture can be here, and the difference is who is holding it.\n * `stored` is a file that came through us and lives in our storage, which is\n * what an upload from the widget or the feedback panel becomes. `linked` is\n * an address on somebody else's machine, which is what an agent or an API\n * caller can offer, since neither has a file to hand over.\n *\n * Deliberately pure, like `signals.ts` and `customData.ts` next door: the SDK\n * trims what it sends with this file and the mutation trims it again on\n * arrival, so neither side is trusting the other's arithmetic.\n */\n\n/**\n * The storage id is a plain string here so this file stays free of Convex.\n * The schema's validator is what pins it to `_storage`, and the model layer\n * hands these functions rows that already carry the real id.\n */\nexport type ReportImage<StorageId extends string = string> =\n | {\n kind: \"stored\";\n storageId: StorageId;\n name: string;\n type: string;\n size: number;\n }\n | { kind: \"linked\"; url: string; name: string };\n\n/** What an image link looks like as it arrives, before it is cleaned. */\nexport type ImageLink = { url: string; name?: string };\n\n/**\n * The most one report holds. Past a handful nobody is looking at the sixth\n * screenshot, and a thread that ran long would otherwise push the write-up\n * off the top of the card.\n */\nexport const MAX_REPORT_IMAGES = 6;\n\n/** Enough to say what a picture is without wrapping the row it sits in. */\nexport const IMAGE_NAME_MAX_LENGTH = 80;\n\n/** Long enough for a signed link with its query string still on it. */\nexport const IMAGE_URL_MAX_LENGTH = 2_000;\n\n/** When the address gave nothing worth calling it. */\nconst UNNAMED = \"Screenshot\";\n\n/**\n * Extensions a lone URL has to end in before we will treat it as a picture.\n *\n * Only used for guessing, never for something a caller explicitly called an\n * image. SVG is missing on purpose: a browser runs one, which is the same\n * reason `UNSAFE_ATTACHMENT_TYPES` turns them away as uploads.\n */\nconst IMAGE_EXTENSIONS = [\"png\", \"jpg\", \"jpeg\", \"gif\", \"webp\", \"avif\", \"bmp\"];\n\n/** Whether a content type is a picture we are willing to show. */\nexport function isImageContentType(type: string): boolean {\n const bare = type.split(\";\")[0].trim().toLowerCase();\n return bare.startsWith(\"image/\") && !isUnsafeAttachmentType(bare);\n}\n\n/**\n * An address a browser may be pointed at, or null.\n *\n * Only http and https: a `javascript:` or `data:` value in an `src` is the\n * whole of how a pasted link becomes somebody else's code running on the\n * dashboard, and neither is ever what a screenshot arrives as.\n */\nfunction cleanUrl(input: unknown): string | null {\n if (typeof input !== \"string\") return null;\n const url = input.trim();\n if (url.length === 0 || url.length > IMAGE_URL_MAX_LENGTH) return null;\n return /^https?:\\/\\/\\S+$/i.test(url) ? url : null;\n}\n\n/** The last part of the path, which is usually what the file is called. */\nfunction nameFromUrl(url: string): string {\n // Past the host as well as the query string: an address with nothing after\n // the domain has no name in it, and \"acme.com\" is not what a picture is\n // called.\n const path = url.split(/[?#]/)[0].replace(/^https?:\\/\\/[^/]*/i, \"\");\n const last = path.split(\"/\").filter(Boolean).pop();\n if (last === undefined) return UNNAMED;\n const name = decodeURIComponent(last).slice(0, IMAGE_NAME_MAX_LENGTH);\n return name.length > 0 ? name : UNNAMED;\n}\n\n/** Whatever it was called, trimmed to something a caption can hold. */\nfunction trimName(input: unknown): string | null {\n if (typeof input !== \"string\") return null;\n const name = input.trim().replace(/\\s+/g, \" \").slice(0, IMAGE_NAME_MAX_LENGTH);\n return name.length > 0 ? name : null;\n}\n\n/**\n * Whether an address looks like a picture from the outside.\n *\n * A guess, and only ever used where guessing is the point: a host attaching\n * `{ label: \"Screenshot\", value: \"https://…/broken.png\" }` through the\n * ordinary custom data door meant a picture, and asking them to learn a\n * second API to say so would be a worse product than reading the extension.\n * Anything that does not end in one stays a link, which is what it was.\n */\nexport function looksLikeImageUrl(value: unknown): string | null {\n const url = cleanUrl(value);\n if (url === null) return null;\n\n const path = url.split(/[?#]/)[0].toLowerCase();\n const dot = path.lastIndexOf(\".\");\n if (dot === -1) return null;\n return IMAGE_EXTENSIONS.includes(path.slice(dot + 1)) ? url : null;\n}\n\n/**\n * Whatever arrived, cut down to the links that may be stored. Undefined when\n * nothing survived, so an empty list never lands on a row and reads later as\n * \"they attached something and it was blank\".\n *\n * No extension check here: a caller passing this has said it is a picture,\n * and plenty of real ones are served from an address that ends in an id.\n */\nexport function cleanImageLinks(\n input: unknown,\n): Extract<ReportImage, { kind: \"linked\" }>[] | undefined {\n if (!Array.isArray(input)) return undefined;\n\n const byUrl = new Map<string, string>();\n for (const raw of input) {\n const source =\n typeof raw === \"string\" ? { url: raw } : (raw as ImageLink | null);\n if (typeof source !== \"object\" || source === null) continue;\n\n const url = cleanUrl(source.url);\n if (url === null) continue;\n // Re-set so a repeated address takes the newer name for it.\n byUrl.delete(url);\n byUrl.set(url, trimName(source.name) ?? nameFromUrl(url));\n }\n\n const links = [...byUrl].map(([url, name]) => ({\n kind: \"linked\" as const,\n url,\n name,\n }));\n return links.length > 0 ? links.slice(-MAX_REPORT_IMAGES) : undefined;\n}\n\n/**\n * Whatever arrived describing files already in our storage, cut down the\n * same way. The ids themselves are checked by the validator on the way in;\n * what this decides is whether the thing is a picture worth showing.\n */\nexport function cleanStoredImages<StorageId extends string>(\n input: readonly {\n storageId: StorageId;\n name: string;\n type: string;\n size: number;\n }[],\n): Extract<ReportImage<StorageId>, { kind: \"stored\" }>[] | undefined {\n const kept = input\n .filter((file) => isImageContentType(file.type))\n .map((file) => ({\n kind: \"stored\" as const,\n storageId: file.storageId,\n name: trimName(file.name) ?? UNNAMED,\n type: file.type,\n size: file.size,\n }));\n return kept.length > 0 ? kept.slice(-MAX_REPORT_IMAGES) : undefined;\n}\n\n/** What makes two pictures the same picture: the thing they point at. */\nfunction keyOf(image: ReportImage): string {\n return image.kind === \"stored\"\n ? `stored:${image.storageId}`\n : `linked:${image.url}`;\n}\n\n/**\n * Several sources of pictures, oldest source first, folded into one list.\n * The same rules as cleaning: one entry per picture, later wins, newest kept\n * when the cap says something has to go.\n */\nexport function mergeReportImages<StorageId extends string>(\n ...sources: (readonly ReportImage<StorageId>[] | undefined)[]\n): ReportImage<StorageId>[] | undefined {\n const byKey = new Map<string, ReportImage<StorageId>>();\n for (const image of sources.flatMap((list) => list ?? [])) {\n byKey.delete(keyOf(image));\n byKey.set(keyOf(image), image);\n }\n\n const images = [...byKey.values()];\n return images.length > 0 ? images.slice(-MAX_REPORT_IMAGES) : undefined;\n}\n","\"use client\";\n\nimport { NOTE_MAX_LENGTH } from \"@median/backend/feedback\";\nimport { MAX_REPORT_IMAGES } from \"@median/backend/reportImages\";\nimport {\n useEffect,\n useRef,\n useState,\n type ChangeEvent,\n type KeyboardEvent,\n} from \"react\";\nimport {\n AttachmentDraftChip,\n useAttachmentDrafts,\n useAttachmentTarget,\n} from \"@/components/attachments\";\nimport { ArrowsClockwise, Check, ImageSquare, X } from \"@/components/icons\";\nimport { Collapse } from \"@/components/ui/collapse\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { cn } from \"@/lib/utils\";\nimport { WIDGET_TEXT_FIELD_SHELL as TEXT_FIELD_SHELL } from \"../widget-field\";\nimport { PoweredByMedian } from \"../widget-badge\";\nimport { WidgetButton } from \"../widget-button\";\nimport { WIDGET_CORNER } from \"../widget-corner\";\nimport { AUTOFOCUS_ATTRIBUTE } from \"./feedback-popover\";\n\n/** How long the thank-you sits there before whoever owns the panel closes it. */\nconst THANKS_MS = 1_600;\n\n/**\n * The feedback panel: one box, one button, and no questions in front of them.\n *\n * A visitor with something to say should not first have to decide whether it\n * is a bug or an idea, rank it, or write a title. So the panel asks for a\n * sentence and nothing else, and the reader on the other side does the rest.\n * The page rides along on its own, and so does a picture of it, so a report\n * arrives with the thing being reported already on it.\n *\n * One surface, not two. The box sits straight on the card rather than inside\n * a tray of its own: a panel this small has no room for a container that\n * contains one thing, and the extra edge only made it look like there was\n * more here than there is.\n *\n * Presentational, like `SupportWidget`: a note goes out through `onSubmit`\n * and nothing is held here but the draft.\n */\nexport function FeedbackPanel({\n onSubmit,\n onClose,\n onSent,\n notice,\n showBadge = true,\n title = \"Share feedback\",\n description = \"Bugs, ideas, anything.\",\n placeholder = \"Tell us what could be better.\",\n className,\n}: {\n /**\n * Awaited. A rejection keeps the draft and the pictures, so a send that\n * fails costs a retry rather than a retype.\n */\n onSubmit?: (note: string, images: File[]) => unknown;\n /** Omit to hide the close button, for a panel that is always on the page. */\n onClose?: () => void;\n /** The thank-you has been read. Whoever opened the panel closes it. */\n onSent?: () => void;\n /** One line under the box, for a send that went wrong. */\n notice?: string | null;\n /** The \"Powered by Median\" line. Hosts on a paid plan can turn it off. */\n showBadge?: boolean;\n title?: string;\n description?: string;\n placeholder?: string;\n className?: string;\n}) {\n const [draft, setDraft] = useState(\"\");\n const [isSending, setSending] = useState(false);\n const [isSent, setSent] = useState(false);\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n // Pictures only. Everything else on a report is words, and a note is not a\n // place to hand over a database dump.\n const images = useAttachmentDrafts({\n accept: \"image\",\n max: MAX_REPORT_IMAGES,\n });\n const target = useAttachmentTarget({ onFiles: images.add });\n\n const canSend =\n draft.trim().length > 0 && !images.hasBlocker && !isSending && !isSent;\n\n // The thank-you is read, then the panel is somebody else's problem. In an\n // effect rather than in the handler so a panel unmounted in between never\n // calls back into a host that has moved on.\n useEffect(() => {\n if (!isSent) return;\n const timer = setTimeout(() => onSent?.(), THANKS_MS);\n return () => clearTimeout(timer);\n }, [isSent, onSent]);\n\n async function send() {\n if (!canSend) return;\n setSending(true);\n try {\n await onSubmit?.(draft.trim(), images.files);\n } catch {\n // The notice under the box says why. The draft and its pictures stay put.\n return;\n } finally {\n setSending(false);\n }\n images.clear();\n setSent(true);\n }\n\n function handlePick(event: ChangeEvent<HTMLInputElement>) {\n if (event.target.files?.length) images.add(event.target.files);\n // Cleared so picking the same file twice in a row still fires a change.\n event.target.value = \"\";\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLTextAreaElement>) {\n // Enter is a new line here, not a send: this is a paragraph box rather\n // than a chat composer, and the button is right there. Meta and enter\n // still sends, which is what anybody who tries it expects.\n if (event.key !== \"Enter\") return;\n if (!event.metaKey && !event.ctrlKey) return;\n event.preventDefault();\n void send();\n }\n\n return (\n <section\n aria-label={title}\n {...target.targetProps}\n className={cn(\n // The widget's own radius scale, so the app's token can climb as the\n // chrome gets rounder without this changing shape under its hosts.\n `flex w-full flex-col gap-2 overflow-hidden ${WIDGET_CORNER} border bg-card p-3 text-card-foreground [--radius:0.625rem]`,\n className,\n )}\n >\n <header className=\"flex items-start gap-2\">\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <h2 className=\"truncate text-sm font-semibold tracking-tight\">\n {title}\n </h2>\n <p className=\"text-xs text-muted-foreground\">{description}</p>\n </div>\n\n {onClose && (\n <WidgetButton\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n onClick={onClose}\n aria-label=\"Close feedback\"\n className=\"-mt-0.5 -mr-1 shrink-0 text-muted-foreground\"\n >\n <X />\n </WidgetButton>\n )}\n </header>\n\n {/* Two rows of one grid, one opening as the other closes, so the swap\n is the panel changing height rather than two panels. */}\n <div className=\"grid\">\n <Collapse isOpen={!isSent}>\n <div className=\"flex flex-col gap-1.5\">\n <Textarea\n rows={3}\n value={draft}\n maxLength={NOTE_MAX_LENGTH}\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={target.isDropTarget ? \"Drop to attach\" : placeholder}\n aria-label={title}\n {...{ [AUTOFOCUS_ATTRIBUTE]: \"\" }}\n className={cn(\n TEXT_FIELD_SHELL,\n \"max-h-40 min-h-18 px-2.5 py-2\",\n // 16px on phones, in pixels: any focused field smaller than\n // that makes iOS zoom the host's whole page in.\n \"max-sm:text-[16px]\",\n )}\n />\n\n {images.drafts.length > 0 && (\n <div className=\"flex flex-wrap gap-1.5\">\n {images.drafts.map((draft) => (\n <AttachmentDraftChip\n key={draft.id}\n draft={draft}\n onRemove={() => images.remove(draft.id)}\n />\n ))}\n </div>\n )}\n\n {notice && (\n <p className=\"animate-in px-0.5 text-xs text-destructive fade-in duration-150\">\n {notice}\n </p>\n )}\n </div>\n </Collapse>\n\n <Collapse isOpen={isSent}>\n <div className=\"flex items-center justify-center gap-2 py-4 text-sm font-medium\">\n <span className=\"grid size-5 shrink-0 place-items-center rounded-full bg-primary text-primary-foreground\">\n <Check className=\"size-3\" />\n </span>\n Thanks for your feedback.\n </div>\n </Collapse>\n </div>\n\n {/* Our name and the one action, on the same line: the foot of a panel\n this small is a row rather than two.\n\n The height is held whether or not the button is in it, so the row\n does not shrink under the thank-you while the panel above it is\n already animating. */}\n <div className=\"flex min-h-8 items-center gap-2\">\n {showBadge && <PoweredByMedian className=\"justify-start\" />}\n\n {!isSent && (\n <>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n multiple\n tabIndex={-1}\n onChange={handlePick}\n className=\"sr-only\"\n />\n <WidgetButton\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n disabled={images.isFull}\n onClick={() => fileInputRef.current?.click()}\n aria-label=\"Add a picture\"\n className=\"ml-auto shrink-0 rounded-full text-muted-foreground\"\n >\n <ImageSquare />\n </WidgetButton>\n\n <WidgetButton\n type=\"button\"\n disabled={!canSend}\n aria-busy={isSending}\n onClick={() => void send()}\n className=\"relative shrink-0 rounded-full px-3\"\n >\n {/* The label stays in the box while the spinner is over it, so\n it goes on setting the width and a send never moves the\n button it is happening in. */}\n <span\n className={cn(\n \"transition-opacity duration-150 motion-reduce:transition-none\",\n isSending && \"opacity-0\",\n )}\n >\n Send\n </span>\n\n {isSending && (\n <ArrowsClockwise\n aria-hidden\n className=\"absolute inset-0 m-auto animate-spin motion-reduce:animate-none\"\n />\n )}\n </WidgetButton>\n </>\n )}\n </div>\n </section>\n );\n}\n","\"use client\";\n\nimport { useEffect, useRef, type ReactNode, type RefObject } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/lib/utils\";\nimport { useHydrated } from \"../use-hydrated\";\nimport { useAnchor } from \"./use-anchor\";\n\n/** Above the host's own chrome. Their z-indexes are not ours to guess at. */\nconst LAYER = 2_147_483_000;\n\n/**\n * What inside the panel takes focus when it opens. The panel says which,\n * because which field is the one somebody came to fill in is the panel's\n * business rather than the box it is drawn in.\n */\nexport const AUTOFOCUS_ATTRIBUTE = \"data-median-autofocus\";\n\n/**\n * How the panel arrives and leaves.\n *\n * A dropdown, so it comes out of the thing that opened it: a little smaller\n * and a little towards its trigger, growing into place. The spring is the\n * same one the support panel opens with, and it overshoots by a hair, which\n * is the difference between a panel that appears and one that pops.\n *\n * Leaving is quicker and has no spring. An overshoot on the way out is a\n * panel arguing about being closed.\n */\n// `scale` and `translate` by name: those are the properties Tailwind's\n// `scale-*` and `translate-y-*` set. Listed as `transform` the panel had\n// neither, and popped open on opacity alone.\nconst MOTION =\n \"transition-[opacity,scale,translate] motion-reduce:transition-none\";\nconst OPEN = \"duration-250 ease-spring\";\nconst SHUT = \"duration-150 ease-out-quint\";\n\n/**\n * The panel, in a layer of its own, under whatever opened it.\n *\n * Portalled to the body rather than rendered where the trigger is. A host's\n * trigger lives inside their layout, and a panel rendered there inherits\n * every `overflow: hidden`, stacking context and transform between it and\n * the page. Out here it answers to the window and to nothing else.\n *\n * Closed, it stays mounted and goes `inert`: the draft somebody was halfway\n * through survives a close, and nothing behind the fade can be tabbed into.\n */\nexport function FeedbackPopover({\n isOpen,\n onClose,\n triggerRef,\n anchor,\n width,\n children,\n}: {\n isOpen: boolean;\n onClose: () => void;\n /** The host's own trigger, when they wrapped one. */\n triggerRef: RefObject<HTMLElement | null>;\n /**\n * Something else on their page to hang off, as a selector. Unlike the\n * trigger it is only a position: a press on it closes the panel like a\n * press anywhere else, because it is not the thing doing the opening.\n */\n anchor?: string;\n /** Any CSS length. Never wider than the window, whatever this says. */\n width: string;\n children: ReactNode;\n}) {\n const panelRef = useRef<HTMLDivElement>(null);\n const side = useAnchor({ triggerRef, anchor, panelRef, isOpen });\n // The panel is medianled into `document.body`, which does not exist until\n // there is one. Nothing at all until then, on the server and through\n // hydration alike, so both passes agree.\n const isHydrated = useHydrated();\n\n // Whatever had focus when the panel came up, so closing can hand it back.\n const opener = useRef<HTMLElement | null>(null);\n // Focus only moves once the panel has actually changed state, or a panel\n // that started open would steal focus on page load.\n const wasOpen = useRef(isOpen);\n\n // Escape closes, wherever focus is inside the panel. The element picker\n // takes its own Escape first, in the capture phase, so backing out of\n // pointing at something never closes the whole panel with it.\n useEffect(() => {\n if (!isOpen) return;\n\n function handleKeyDown(event: KeyboardEvent) {\n if (event.key === \"Escape\") onClose();\n }\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [isOpen, onClose]);\n\n // A press anywhere else closes it, the way every dropdown does. On\n // pointerdown rather than click, so it closes as the press lands rather\n // than when it is let go, and the trigger is left out: it does its own\n // toggling, and closing here first would make a second press reopen.\n useEffect(() => {\n if (!isOpen) return;\n\n function handlePointerDown(event: PointerEvent) {\n const target = event.target;\n if (!(target instanceof Node)) return;\n if (panelRef.current?.contains(target)) return;\n if (triggerRef.current?.contains(target)) return;\n onClose();\n }\n document.addEventListener(\"pointerdown\", handlePointerDown);\n return () => document.removeEventListener(\"pointerdown\", handlePointerDown);\n }, [isOpen, onClose, triggerRef]);\n\n useEffect(() => {\n if (wasOpen.current === isOpen) return;\n wasOpen.current = isOpen;\n\n if (isOpen) {\n const active = document.activeElement;\n opener.current = active instanceof HTMLElement ? active : null;\n // Straight into whatever the panel says takes focus, which is the box\n // they are here to type in. Unlike the support widget's launcher this\n // is never opened by accident: somebody chose it from a menu or a\n // button, and landing anywhere but the field would cost them a Tab.\n const field = panelRef.current?.querySelector<HTMLElement>(\n `[${AUTOFOCUS_ATTRIBUTE}]`,\n );\n (field ?? panelRef.current)?.focus();\n return;\n }\n\n const back = triggerRef.current ?? opener.current;\n opener.current = null;\n // Only somewhere focus can actually land, and only if it is still on the\n // page: a menu that closed behind the panel is not there to take it back.\n if (back && back !== document.body && back.isConnected) back.focus();\n }, [isOpen, triggerRef]);\n\n if (!isHydrated) return null;\n\n return createPortal(\n <div\n ref={panelRef}\n tabIndex={-1}\n inert={!isOpen}\n // The hook the published stylesheet scopes its resets to. On a page\n // without Tailwind there is no preflight, so the package ships one\n // that reaches no further than this box.\n data-median-widget=\"\"\n style={{\n zIndex: LAYER,\n width,\n top: 0,\n left: 0,\n // Grows out of the edge nearest its trigger rather than out of its\n // own middle, which is what makes it read as the trigger opening.\n transformOrigin: side === \"top\" ? \"bottom center\" : \"top center\",\n }}\n className={cn(\n \"fixed max-w-[calc(100vw-2rem)] outline-none\",\n // The shadow is on the box rather than inside the panel: a filter on\n // a parent of anything that scrolls makes the browser repaint it on\n // every frame, and there is nothing scrolling in here to protect.\n \"drop-shadow-xl\",\n MOTION,\n isOpen\n ? [OPEN, \"pointer-events-auto scale-100 opacity-100 translate-y-0\"]\n : [\n SHUT,\n \"pointer-events-none scale-[0.97] opacity-0\",\n // Towards its trigger, which is above it or below it.\n side === \"top\" ? \"translate-y-1\" : \"-translate-y-1\",\n ],\n )}\n >\n {children}\n </div>,\n document.body,\n );\n}\n","\"use client\";\n\nimport { useLayoutEffect, useState, type RefObject } from \"react\";\n\n/**\n * Keeping the panel under whatever opened it.\n *\n * The host owns the trigger: it is a nav item, a menu row, a button in a\n * toolbar, and it can be anywhere on their page. So the panel is measured\n * against it rather than pinned to a corner, and it follows it: a page that\n * scrolls or resizes under an open panel keeps the two together.\n *\n * The edges are written straight onto the node rather than held in state,\n * because scrolling fires far more often than anything worth re-rendering\n * for, and the panel's own opening animation is a transform. Position and\n * motion on two different properties is what keeps them from fighting.\n */\n\n/** Between the trigger and the panel. */\nconst GAP = 8;\n\n/** How close to the edge of the window the panel is allowed to sit. */\nconst MARGIN = 16;\n\n/** Which side of the trigger the panel ended up on, for the motion. */\nexport type AnchorSide = \"top\" | \"bottom\";\n\n/**\n * Whatever the selector finds, or nothing.\n *\n * Read every time it is placed rather than once, because the point of taking\n * a selector at all is that the thing it names may not have been on the page\n * when the panel opened. A menu item that opens this closes itself doing it;\n * the button the menu hangs off is still there, and that is what a selector\n * is for.\n */\nfunction findAnchor(selector: string | undefined): HTMLElement | null {\n if (selector === undefined || typeof document === \"undefined\") return null;\n try {\n return document.querySelector<HTMLElement>(selector);\n } catch {\n console.error(\n `Median: the anchor passed to <MedianFeedback> is ${selector}, which is not a valid CSS selector.`,\n );\n return null;\n }\n}\n\nexport function useAnchor({\n triggerRef,\n anchor,\n panelRef,\n isOpen,\n}: {\n /** The host's own trigger, when they wrapped one. */\n triggerRef: RefObject<HTMLElement | null>;\n /** Something on their page to hang off instead. Both absent is the corner. */\n anchor?: string;\n panelRef: RefObject<HTMLDivElement | null>;\n isOpen: boolean;\n}): AnchorSide {\n const [side, setSide] = useState<AnchorSide>(\"bottom\");\n\n useLayoutEffect(() => {\n if (!isOpen) return;\n\n let frame = 0;\n\n function place() {\n const panel = panelRef.current;\n const trigger = triggerRef.current ?? findAnchor(anchor);\n if (panel === null) return;\n\n const width = panel.offsetWidth;\n const height = panel.offsetHeight;\n const room = {\n width: window.innerWidth,\n height: window.innerHeight,\n };\n\n // Nothing to hang off, so it sits in the corner every widget sits in.\n if (trigger === null) {\n panel.style.left = `${Math.max(MARGIN, room.width - width - MARGIN)}px`;\n panel.style.top = `${Math.max(MARGIN, room.height - height - MARGIN)}px`;\n setSide(\"bottom\");\n return;\n }\n\n const rect = trigger.getBoundingClientRect();\n\n // Under the trigger, unless the panel would not fit and there is more\n // room above it. Flipping only when it actually helps: a panel that\n // jumped over its trigger the moment the page got short would move\n // for nothing most of the time.\n const below = room.height - rect.bottom - GAP - MARGIN;\n const above = rect.top - GAP - MARGIN;\n const onTop = height > below && above > below;\n setSide(onTop ? \"top\" : \"bottom\");\n\n const top = onTop ? rect.top - GAP - height : rect.bottom + GAP;\n\n // Aligned to whichever edge of the trigger keeps the panel on screen:\n // its left edge normally, its right edge when the trigger is over\n // towards the right, which is where a nav item usually is.\n const fromStart = rect.left;\n const fromEnd = rect.right - width;\n const left =\n fromStart + width + MARGIN <= room.width ? fromStart : fromEnd;\n\n panel.style.left = `${clamp(left, MARGIN, room.width - width - MARGIN)}px`;\n panel.style.top = `${clamp(top, MARGIN, room.height - height - MARGIN)}px`;\n }\n\n /** One placement per frame at most, however hard the page is scrolling. */\n function schedule() {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(place);\n }\n\n place();\n window.addEventListener(\"scroll\", schedule, true);\n window.addEventListener(\"resize\", schedule);\n\n // The panel changes height as the box grows and as the thank-you takes\n // over, and a panel flipped above its trigger has to move when it does.\n const observer = new ResizeObserver(schedule);\n if (panelRef.current !== null) observer.observe(panelRef.current);\n\n return () => {\n cancelAnimationFrame(frame);\n window.removeEventListener(\"scroll\", schedule, true);\n window.removeEventListener(\"resize\", schedule);\n observer.disconnect();\n };\n }, [isOpen, anchor, panelRef, triggerRef]);\n\n return side;\n}\n\nfunction clamp(value: number, low: number, high: number): number {\n // High can be below low on a window shorter than the panel, and then the\n // top edge wins: the way out is up there.\n return Math.max(low, Math.min(value, Math.max(low, high)));\n}\n","\"use client\";\n\nimport { useSyncExternalStore } from \"react\";\nimport {\n createOpenStore,\n useOpenState,\n useRegister,\n} from \"./open-store\";\n\n/**\n * Driving the feedback panel from anywhere on the page.\n *\n * The panel usually hangs off a trigger the host wrapped it around, and for\n * that this is not needed at all. It is here for the other way of putting\n * feedback on a page: a row in a menu somewhere else entirely, a keyboard\n * shortcut, a link at the foot of an error screen. Same shape as\n * `medianSupport`, and a store of its own, so opening one never opens the\n * other.\n */\nconst store = createOpenStore(\"MedianFeedback\");\n\nexport const medianFeedback = {\n /** What the panel is doing right now. */\n get isOpen() {\n return store.readEffective();\n },\n open() {\n store.request(true);\n store.warnIfNothingIsMounted(\"the feedback panel was asked to open\");\n },\n close() {\n store.request(false);\n },\n toggle() {\n const next = !store.readEffective();\n store.request(next);\n if (next) {\n store.warnIfNothingIsMounted(\"the feedback panel was asked to open\");\n }\n },\n} satisfies MedianFeedbackControl;\n\n/** The shape of {@link medianFeedback}, and of what the hook returns. */\nexport type MedianFeedbackControl = {\n /** Whether the panel is open. */\n isOpen: boolean;\n open: () => void;\n close: () => void;\n toggle: () => void;\n};\n\n/**\n * The feedback panel, from anywhere else on the page:\n *\n * const feedback = useMedianFeedback();\n *\n * <button onClick={feedback.open}>Share feedback</button>\n *\n * `isOpen` follows the panel however it was opened, so a menu row can render\n * itself as pressed while it is up.\n */\nexport function useMedianFeedback(): MedianFeedbackControl {\n const isOpen = useSyncExternalStore(\n store.subscribe,\n store.readEffective,\n store.readServerEffective,\n );\n\n return {\n isOpen,\n open: medianFeedback.open,\n close: medianFeedback.close,\n toggle: medianFeedback.toggle,\n };\n}\n\n/** The panel's own settled open state. See {@link useOpenState}. */\nexport function useMedianFeedbackOpenState(options: {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n}): [boolean, (next: boolean) => void] {\n return useOpenState(store, options);\n}\n\n/** One panel per page, and a word in the console when there are two. */\nexport function useRegisterMedianFeedback() {\n useRegister(store);\n}\n","\"use client\";\n\nimport { useWidgetMutation } from \"./use-widget-mutation\";\nimport { api } from \"@median/backend\";\nimport { MAX_REPORT_IMAGES } from \"@median/backend/reportImages\";\nimport { useCallback, useState, useSyncExternalStore } from \"react\";\nimport { errorMessage } from \"@/lib/errors\";\nimport { uploadAttachments } from \"@/lib/upload\";\nimport { collectContext, collectPage } from \"./context\";\nimport { restoreCustomData, takeCustomData } from \"./custom-data\";\nimport { useDiagnostics, type MedianDiagnostics } from \"./diagnostics\";\nimport { capturePage } from \"./screenshot\";\nimport { getOrCreateSessionId, subscribeSession } from \"./session\";\nimport { splitIdentity, type MedianUser } from \"./use-median-thread\";\n\n/**\n * Sending one note, and everything that rides along with it.\n *\n * The same session token the support widget uses, so a person who has\n * written in and then left feedback is one customer with both to their name\n * rather than two strangers who happen to share a browser. The same\n * telemetry, the same diagnostics ring buffer, and the same attached data\n * too: a bug report filed off a note deserves what a bug report filed off a\n * conversation gets.\n *\n * Nothing is read here. A note is a thing you hand over, not a thread you\n * watch, so there is no subscription and nothing to keep in step.\n */\n\n/** What the panel is told about a note that could not be taken. */\nconst REFUSALS: Record<string, string> = {\n off: \"Feedback is currently unavailable. Try again later.\",\n empty: \"Enter your feedback before sending.\",\n};\n\nexport function useFeedbackNote({\n apiKey,\n user,\n telemetry = true,\n screenshot = true,\n diagnostics,\n appState,\n}: {\n apiKey: string;\n user?: MedianUser;\n telemetry?: boolean;\n /** Whether a note carries a picture of the page it was written on. */\n screenshot?: boolean;\n diagnostics?: MedianDiagnostics;\n appState?: Record<string, string | number | boolean>;\n}) {\n // The session token is external state (localStorage), read as a store: the\n // server snapshot is null, so SSR and the first client paint agree, and\n // `reset()` on sign out lands here like it lands on the thread.\n const sessionId = useSyncExternalStore(\n subscribeSession,\n useCallback(() => getOrCreateSessionId(apiKey), [apiKey]),\n () => null,\n );\n\n const submitNote = useWidgetMutation(api.feedback.submitV2);\n // The widget's own upload door, which the panel may use whether or not the\n // support widget is mounted: it is guarded by the same key and the same\n // toll, and a screenshot is a screenshot either way.\n const getUploadUrl = useWidgetMutation(api.widget.generateUploadUrlV2);\n const takeDiagnostics = useDiagnostics(diagnostics, appState);\n const [notice, setNotice] = useState<string | null>(null);\n\n /**\n * Rejects when the note did not land, which is what keeps the draft in the\n * box. The reason is in `notice` by then, where the panel shows it.\n */\n const submit = useCallback(\n async (body: string, images: File[] = []) => {\n setNotice(null);\n if (sessionId === null) {\n setNotice(\"Still connecting. Try again in a moment.\");\n throw new Error(\"No session yet.\");\n }\n\n // Whatever the host attached since the last send rides this note.\n // Taken outside the try so a failed send hands it back too.\n const custom = takeCustomData();\n // Whether a refusal has already said its own piece. State cannot\n // answer that here: it is set and read inside one tick.\n let explained = false;\n try {\n // Read now rather than on mount: all of it is wanted as \"what was\n // true when they wrote\", and a panel can sit closed on a page for an\n // hour before anybody opens it.\n const context = telemetry ? collectContext() : undefined;\n const page = telemetry ? collectPage() : undefined;\n const collected = takeDiagnostics();\n const { facts, claim } = splitIdentity(user ?? null);\n // The page they were looking at, taken now rather than when the panel\n // opened: a note written over a minute is about the page as it stands\n // when they press send. Only when their own pictures left room for it,\n // since those were chosen and this one was not.\n const shot =\n screenshot && images.length < MAX_REPORT_IMAGES\n ? await capturePage()\n : null;\n // Before the note, so a picture that will not upload stops the send\n // rather than landing as a report with a missing screenshot.\n const attached = await uploadAttachments(\n () => getUploadUrl({ apiKey, sessionId }),\n shot === null ? images : [...images, shot],\n );\n\n const outcome = await submitNote({\n apiKey,\n sessionId,\n body,\n ...(user ? { user: facts } : {}),\n ...(claim ? { identity: claim } : {}),\n ...(context ? { context } : {}),\n ...(page ? { page } : {}),\n ...(collected ? { diagnostics: collected } : {}),\n ...(custom ? { customData: custom } : {}),\n ...(attached.length > 0 ? { images: attached } : {}),\n });\n\n if (outcome !== \"taken\") {\n explained = true;\n setNotice(\n REFUSALS[outcome] ?? \"Could not send your feedback. Try again.\",\n );\n throw new Error(outcome);\n }\n } catch (caught) {\n // Back in the queue, so the retry carries them like it keeps the draft.\n if (custom) restoreCustomData(custom);\n if (!explained) {\n console.error(\n \"Sending feedback from the Median panel failed\",\n caught,\n );\n setNotice(errorMessage(caught));\n }\n // Rethrown so the panel keeps the draft: a failed send should cost a\n // retry, never a retype.\n throw caught;\n }\n },\n [\n apiKey,\n sessionId,\n user,\n telemetry,\n screenshot,\n takeDiagnostics,\n submitNote,\n getUploadUrl,\n ],\n );\n\n return { submit, notice, clearNotice: () => setNotice(null) };\n}\n","\"use client\";\n\nimport {\n MedianFeedback,\n type MedianFeedbackMountOptions,\n type MedianFeedbackProps,\n} from \"./median-feedback\";\nimport { mountMedian, type MedianMountInstance } from \"./mount-into\";\n\n/**\n * What a mounted panel hands back: the two things a host without React has\n * no re-render to do for them. Opening and closing go through\n * `medianFeedback`, same as anywhere.\n */\nexport type MedianFeedbackInstance = MedianMountInstance<MedianFeedbackProps>;\n\n/**\n * The same panel, for a page that has never heard of React: Vue, Svelte,\n * Rails, a script on plain HTML.\n *\n * const feedback = mountMedianFeedback({ apiKey: \"median_pk_...\" });\n *\n * document.querySelector(\"#feedback\").onclick = () => medianFeedback.open();\n *\n * There is no trigger to wrap out here, so the panel opens in the bottom\n * right corner and your own button says when. Everything else is the\n * component's, unchanged.\n */\nexport function mountMedianFeedback(\n options: MedianFeedbackMountOptions,\n): MedianFeedbackInstance {\n return mountMedian<MedianFeedbackProps>({\n caller: \"mountMedianFeedback\",\n what: \"the panel\",\n marker: \"medianFeedback\",\n into: null,\n options,\n render: (current) => <MedianFeedback {...current} />,\n });\n}\n","\"use client\";\n\nimport { useEffect, useMemo } from \"react\";\nimport { ContactForm } from \"../contact/contact-form\";\nimport { fieldComplaints, type MedianContactField } from \"../contact/fields\";\nimport type { MedianDiagnostics } from \"./diagnostics\";\nimport { MedianErrorBoundary } from \"./error-boundary\";\nimport { useMedianContact } from \"./use-median-contact\";\nimport type { MedianUser } from \"./use-median-thread\";\n\ntype MedianContactFormOptions = {\n /**\n * What the form asks, beyond an email and a message, which it always asks:\n *\n * <MedianContactForm\n * apiKey=\"median_pk_...\"\n * fields={[\n * { name: \"name\", label: \"Name\", required: true },\n * { name: \"topic\", type: \"select\", options: [\"Billing\", \"A bug\", \"Something else\"] },\n * ]}\n * />\n *\n * Shown in this order. Email goes first, or right after a `name` field,\n * and the message goes last, unless either is placed here by name. A\n * `name` field lands on the customer record; every other answer goes\n * above the message as a line of its own.\n */\n fields?: MedianContactField[];\n /**\n * Who is signed in on your site, if anyone. Their email and name fill the\n * boxes in, and the rest is shown to your team.\n */\n user?: MedianUser;\n /**\n * Whether the message carries what the browser already knows: time zone,\n * language, browser, OS, device, screen, and the page it was sent from.\n * On by default. Pass `false` and none of it is read at all.\n */\n telemetry?: boolean;\n /**\n * Watch what goes wrong on the page, so a bug report filed from the\n * message arrives with the error already attached. The same switch\n * `<MedianSupport>` takes, and the same buffer behind it.\n */\n diagnostics?: MedianDiagnostics;\n /**\n * Anything about your own app worth having next to a bug report: a build,\n * a route, a flag. Read when the form is sent.\n */\n appState?: Record<string, string | number | boolean>;\n /** The heading, for a page that calls this something else. */\n title?: string;\n /** The line under it. */\n description?: string;\n /**\n * The message landed. For whatever your page does next: a thank-you of its\n * own, a redirect, an event. The form has already said thank you itself.\n */\n onSent?: () => void;\n};\n\n/**\n * Two ways in, the same two `<MedianSupport>` takes.\n *\n * `apiKey` is the publishable id on its own. `identity` is the path to a\n * route built with `medianIdentity`, which answers with the key and with\n * whoever is signed in, already signed. Pass both and the form is up on the\n * key from the prop while the route is still answering.\n */\nexport type MedianContactFormProps = MedianContactFormOptions &\n (\n | { apiKey: string; identity?: string }\n | { apiKey?: string; identity: string }\n );\n\n/**\n * A contact form, for the page where a chat panel is the wrong shape.\n *\n * <MedianContactForm apiKey=\"median_pk_...\" />\n *\n * An email and a message, on a card the shape of the support panel, with\n * whatever else your team wants asked first. What is sent starts a\n * conversation in your inbox exactly as the widget would: the agent answers\n * first, your team picks it up, and the reply goes to the email given.\n *\n * Built on `useMedianContact`, which is also what a form of your own uses.\n * It needs no connection of its own, and it is independent of\n * `<MedianSupport>`: mount either, both, or neither. Mounted together they\n * share a visitor, so somebody who writes here and later opens the widget\n * finds the same thread.\n */\nexport function MedianContactForm(props: MedianContactFormProps) {\n return (\n <MedianErrorBoundary component=\"MedianContactForm\">\n <ConnectedContactForm {...props} />\n </MedianErrorBoundary>\n );\n}\n\n/**\n * A list of fields that will not do says so once, in the console, while the\n * person who wrote it is looking at the page that runs it. Once per list\n * rather than per render, since a host builds the array inline.\n */\nfunction useFieldComplaints(fields: MedianContactField[] | undefined) {\n const key = fields === undefined ? null : JSON.stringify(fields);\n useEffect(() => {\n if (key === null) return;\n for (const complaint of fieldComplaints(JSON.parse(key))) {\n console.error(\n `Median: in the fields passed to <MedianContactForm>, ${complaint}`,\n );\n }\n }, [key]);\n}\n\n/**\n * While an identity route is still answering and no key was passed, the\n * form is on the page and switched off rather than absent. A form that\n * arrives a beat after the page around it shoves that page down, and a page\n * that shoves is a page that reads as broken.\n */\nfunction ConnectedContactForm({\n fields,\n title,\n description,\n onSent,\n ...options\n}: MedianContactFormProps) {\n const contact = useMedianContact(options, \"<MedianContactForm>\");\n useFieldComplaints(fields);\n\n // What is already known about who is typing, for the boxes.\n const knownEmail = contact.user?.email || undefined;\n const knownName = contact.user?.name || undefined;\n const initialValues = useMemo(\n () => ({\n ...(knownEmail ? { email: knownEmail } : {}),\n ...(knownName ? { name: knownName } : {}),\n }),\n [knownEmail, knownName],\n );\n\n return (\n <ContactForm\n showBadge={contact.showBranding}\n fields={fields}\n onSubmit={async (message) => {\n await contact.send(message);\n // The message landed whatever the host's callback does. A throw here\n // must not read as a failed send, or a retry sends it twice.\n try {\n onSent?.();\n } catch (caught) {\n console.error(\n \"Median: onSent threw after the message was sent.\",\n caught,\n );\n }\n }}\n notice={contact.error}\n initialValues={initialValues}\n isDisabled={!contact.isReady}\n {...(title !== undefined ? { title } : {})}\n {...(description !== undefined ? { description } : {})}\n />\n );\n}\n","\"use client\";\n\nimport {\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type FormEvent,\n} from \"react\";\nimport { ArrowsClockwise } from \"@/components/icons\";\nimport { cn } from \"@/lib/utils\";\nimport { PoweredByMedian } from \"../widget-badge\";\nimport { WidgetButton } from \"../widget-button\";\nimport { WIDGET_CORNER } from \"../widget-corner\";\nimport { ContactField } from \"./contact-field\";\nimport { composeMessage, type ContactValues } from \"./contact-body\";\nimport {\n resolveFields,\n type ContactField as ContactFieldShape,\n type MedianContactField,\n} from \"./fields\";\nimport type { MedianContactMessage } from \"./message\";\nimport { SentMark, type SentFrom } from \"./sent-mark\";\n\n/**\n * Loose on purpose, and the same rule the composer's question uses: it\n * decides whether the answer can be sent, not whether the address is real.\n */\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\n/**\n * How the form steps aside and the thank-you takes its place.\n *\n * The two share one cell of a grid, so the card keeps the form's height\n * throughout: nothing on the host's page moves because a message went. The\n * form lifts a touch as it fades, since a message that went is a thing that\n * left, and the send button is gone the instant it does: the mark starts as\n * that button, at its spot, and carries it to the middle of the card. The\n * thank-you layer is there at once for that, and only fades on the way\n * back to the form.\n */\nconst FORM_SWAP =\n \"transition-[opacity,translate] duration-250 ease-out-quint motion-reduce:transition-none\";\nconst THANKS_SWAP =\n \"transition-opacity ease-out motion-reduce:transition-none\";\n\n/** How the words and the button arrive, once the mark has landed. */\nconst ARRIVES =\n \"animate-in duration-500 ease-out fade-in-0 slide-in-from-bottom-2 fill-mode-backwards motion-reduce:animate-none\";\n\n/** Where the button was when the message went, for the mark to start from. */\nfunction placeOf(button: HTMLElement | null): SentFrom | null {\n if (button === null) return null;\n const { left, top, width, height } = button.getBoundingClientRect();\n return { left, top, width, height };\n}\n\n/** What each field says when its answer will not do. */\nfunction complaintFor(field: ContactFieldShape, value: string): string | null {\n const trimmed = value.trim();\n if (field.role === \"email\") {\n if (trimmed.length === 0) return \"Enter your email.\";\n if (!EMAIL_PATTERN.test(trimmed)) return \"Enter a valid email address.\";\n return null;\n }\n if (field.role === \"message\") {\n return trimmed.length === 0 ? \"Write a message.\" : null;\n }\n return field.required && trimmed.length === 0 ? \"Required.\" : null;\n}\n\nfunction emptyValues(fields: readonly ContactFieldShape[]): ContactValues {\n return Object.fromEntries(fields.map((field) => [field.name, \"\"]));\n}\n\n/** Every box empty, apart from the ones something is already known for. */\nfunction startingValues(\n fields: readonly ContactFieldShape[],\n known: Partial<ContactValues> | undefined,\n): ContactValues {\n const values = emptyValues(fields);\n for (const [name, value] of Object.entries(known ?? {})) {\n if (typeof value === \"string\" && name in values) values[name] = value;\n }\n return values;\n}\n\n/**\n * The contact form: an email, a message, and whatever else the host asked\n * for, on a card the shape of the support panel.\n *\n * A form rather than a conversation, for the page where a conversation is\n * the wrong shape: a contact page, a pricing page's \"talk to us\", a footer.\n * What is typed here starts the same thread the widget would, so the team\n * answers it from the same inbox and the reply goes to the email given.\n *\n * Complaints wait for the send button. A form that argues with every\n * keystroke is a form people give up on, and one that says what is missing\n * once, in place, when they ask it to send, is not.\n *\n * Presentational, like `SupportWidget`: the filled in form goes out through\n * `onSubmit` as the same message a host's own form sends, and nothing here\n * holds a thread. A rejection keeps every answer where it was, so a send\n * that fails costs a retry rather than a retype.\n */\nexport function ContactForm({\n fields: hostFields,\n onSubmit,\n notice,\n initialValues,\n title = \"Contact us\",\n description = \"We’ll reply by email.\",\n isDisabled = false,\n showBadge = true,\n className,\n}: {\n /** The host's fields. Email and message are added when not among them. */\n fields?: readonly MedianContactField[];\n /** Awaited. A rejection keeps the answers in place. */\n onSubmit?: (message: MedianContactMessage) => unknown;\n /** One line above the button, for a send that went wrong. */\n notice?: string | null;\n /**\n * Answers already known, by field name: the signed in person's email and\n * name, usually. Filled into an empty box and never over something typed.\n */\n initialValues?: Partial<ContactValues>;\n title?: string;\n description?: string;\n /**\n * Nothing can be sent yet. For the beat before whoever mounted this knows\n * where a message would go, so the form is on the page rather than\n * arriving a moment after it.\n */\n isDisabled?: boolean;\n /** The \"Powered by Median\" line. Hosts on a paid plan can turn it off. */\n showBadge?: boolean;\n className?: string;\n}) {\n const fields = useMemo(() => resolveFields(hostFields), [hostFields]);\n const prefix = useId();\n const formRef = useRef<HTMLFormElement>(null);\n const sendRef = useRef<HTMLButtonElement>(null);\n\n const [values, setValues] = useState<ContactValues>(() =>\n startingValues(fields, initialValues),\n );\n const [errors, setErrors] = useState<Record<string, string>>({});\n const [isSending, setSending] = useState(false);\n // The message that went, while the thank-you is up: the address it names,\n // where the button was when it went, and which send this is, so the\n // thank-you plays from the start each time rather than once for the life\n // of the card.\n const [sent, setSent] = useState<{\n email: string;\n from: SentFrom | null;\n attempt: number;\n } | null>(null);\n const isSent = sent !== null;\n\n // A known answer arriving late, which is what an identity route does: the\n // form is on the page before the route has said who is signed in. Into an\n // empty box only. Somebody halfway through typing their name is not asking\n // to have it replaced. Settled during render rather than in an effect, so\n // the frame the answer lands is the frame the box fills.\n const knownEmail = initialValues?.email;\n const knownName = initialValues?.name;\n const [known, setKnown] = useState({ email: knownEmail, name: knownName });\n if (known.email !== knownEmail || known.name !== knownName) {\n setKnown({ email: knownEmail, name: knownName });\n setValues((current) => {\n const next = { ...current };\n if (knownEmail && \"email\" in next && !next.email) next.email = knownEmail;\n if (knownName && \"name\" in next && !next.name) next.name = knownName;\n return next;\n });\n }\n\n // The first complaint takes the caret, so a keyboard lands on the thing to\n // fix rather than on the button that said so. After the render that draws\n // the complaint, since the box is only marked invalid then.\n const [focusRequest, setFocusRequest] = useState(0);\n useEffect(() => {\n if (focusRequest === 0) return;\n formRef.current\n ?.querySelector<HTMLElement>('[aria-invalid=\"true\"]')\n ?.focus({ preventScroll: false });\n }, [focusRequest]);\n\n function setValue(name: string, value: string) {\n setValues((current) => ({ ...current, [name]: value }));\n // Fixed as it is typed, once it has been complained about. Nothing new is\n // complained about until the next send.\n setErrors((current) => {\n if (!(name in current)) return current;\n return Object.fromEntries(\n Object.entries(current).filter(([key]) => key !== name),\n );\n });\n }\n\n async function send() {\n if (isDisabled || isSending || isSent) return;\n\n const found: Record<string, string> = {};\n for (const field of fields) {\n const complaint = complaintFor(field, values[field.name] ?? \"\");\n if (complaint !== null) found[field.name] = complaint;\n }\n if (Object.keys(found).length > 0) {\n setErrors(found);\n setFocusRequest((count) => count + 1);\n return;\n }\n\n const message = composeMessage(fields, values);\n setSending(true);\n try {\n await onSubmit?.(message);\n } catch {\n // The notice above the button says why. The answers stay where they are.\n return;\n } finally {\n setSending(false);\n }\n // Measured before anything moves: the button is still where they\n // pressed it, and that is where the mark sets off from.\n const from = placeOf(sendRef.current);\n setSent((last) => ({\n email: message.email,\n from,\n attempt: (last?.attempt ?? 0) + 1,\n }));\n // The next message starts clean, apart from who it is from: the address\n // and the name are still theirs.\n setValues(\n startingValues(fields, {\n email: message.email,\n ...(message.name ? { name: message.name } : {}),\n }),\n );\n }\n\n function handleSubmit(event: FormEvent<HTMLFormElement>) {\n event.preventDefault();\n void send();\n }\n\n return (\n <section\n aria-label={title}\n // The hook the published stylesheet scopes its resets to, and the\n // widget's own radius scale, so the app's token cannot reshape a form\n // that sits on somebody else's site.\n data-median-widget=\"\"\n className={cn(\n `flex w-full flex-col overflow-hidden ${WIDGET_CORNER} border bg-card p-5 text-card-foreground [--radius:0.625rem]`,\n className,\n )}\n >\n <header className=\"flex flex-col gap-0.5 pb-4\">\n <h2 className=\"text-lg font-semibold tracking-tight\">{title}</h2>\n <p className=\"text-sm text-muted-foreground\">{description}</p>\n </header>\n\n {/* One cell, two occupants. The form sets the height and keeps it\n whether or not it is the one showing, so the card never changes\n size under the page it sits on. */}\n <div className=\"grid\">\n <form\n ref={formRef}\n noValidate\n inert={isSent}\n onSubmit={handleSubmit}\n className={cn(\n \"col-start-1 row-start-1 flex flex-col gap-4\",\n FORM_SWAP,\n isSent && \"pointer-events-none -translate-y-1.5 opacity-0\",\n )}\n >\n {fields.map((field) => (\n <ContactField\n key={field.name}\n field={field}\n id={`${prefix}-${field.name}`}\n value={values[field.name] ?? \"\"}\n error={errors[field.name] ?? null}\n isDisabled={isDisabled || isSending}\n onChange={(value) => setValue(field.name, value)}\n onSubmit={() => formRef.current?.requestSubmit()}\n />\n ))}\n\n {notice && (\n <p className=\"animate-in px-1 text-sm text-destructive fade-in duration-150\">\n {notice}\n </p>\n )}\n\n {/* Our name and the one action, on the same line. On a card too\n narrow for both, the button drops to a line of its own rather\n than running off the edge. */}\n <div className=\"flex min-h-10 flex-wrap items-center gap-3 pt-1\">\n {showBadge && <PoweredByMedian className=\"justify-start\" />}\n\n <WidgetButton\n ref={sendRef}\n type=\"submit\"\n size=\"xl\"\n disabled={isDisabled || isSending}\n aria-busy={isSending}\n className={cn(\n \"relative ml-auto shrink-0 rounded-full px-5\",\n // Gone at once, not faded with the form: the mark is\n // standing exactly here from the same frame, and a button\n // showing through its edges as the form lifts would read\n // as two of them.\n isSent && \"opacity-0 transition-none\",\n )}\n >\n {/* The label stays in the box while the spinner is over it,\n so it goes on setting the width and a send never moves\n the button it is happening in. */}\n <span\n className={cn(\n \"transition-opacity duration-150 motion-reduce:transition-none\",\n isSending && \"opacity-0\",\n )}\n >\n Send message\n </span>\n\n {isSending && (\n <ArrowsClockwise\n aria-hidden\n className=\"absolute inset-0 m-auto animate-spin motion-reduce:animate-none\"\n />\n )}\n </WidgetButton>\n </div>\n </form>\n\n {/* The thank-you, over the form's footprint. Remounted per send, so\n the mark pops, the tick draws and the words arrive every time\n rather than only the first. Announced, since nothing else on the\n page says the message went. */}\n <div\n inert={!isSent}\n role=\"status\"\n aria-live=\"polite\"\n className={cn(\n \"col-start-1 row-start-1 flex flex-col items-center justify-center gap-5 py-4 text-center\",\n THANKS_SWAP,\n isSent ? \"duration-0\" : \"pointer-events-none opacity-0 duration-200\",\n )}\n >\n {sent !== null && (\n <div key={sent.attempt} className=\"contents\">\n <SentMark from={sent.from} />\n\n <div\n className={cn(\"flex max-w-72 flex-col gap-1\", ARRIVES, \"delay-[400ms]\")}\n >\n <p className=\"text-xl font-semibold tracking-tight\">Message sent</p>\n <p className=\"text-base text-balance text-muted-foreground\">\n Replies go to {sent.email}.\n </p>\n </div>\n\n <div\n className={cn(\n \"flex flex-col items-center gap-4\",\n ARRIVES,\n \"delay-[600ms]\",\n )}\n >\n <WidgetButton\n type=\"button\"\n variant=\"outline\"\n size=\"xl\"\n onClick={() => setSent(null)}\n className=\"rounded-full px-5\"\n >\n Send another\n </WidgetButton>\n\n {showBadge && <PoweredByMedian />}\n </div>\n </div>\n )}\n </div>\n </div>\n </section>\n );\n}\n","\"use client\";\n\nimport { MESSAGE_MAX_LENGTH } from \"@median/backend/support\";\nimport { useState, type KeyboardEvent } from \"react\";\nimport { CaretDown } from \"@/components/icons\";\nimport { Collapse } from \"@/components/ui/collapse\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { cn } from \"@/lib/utils\";\nimport { WIDGET_TEXT_FIELD_SHELL as TEXT_FIELD_SHELL } from \"../widget-field\";\nimport type { ContactField as ContactFieldShape } from \"./fields\";\n\n/** The most a line answer can hold. The message box has the widget's own limit. */\nconst LINE_MAX_LENGTH = 200;\n\n/**\n * The same shell every widget field wears, at the panel's scale: 16px words\n * on the card's own surface. In pixels on phones, since any focused field\n * smaller than that makes iOS zoom the host's whole page in.\n */\nconst SHELL = cn(\n TEXT_FIELD_SHELL,\n \"w-full bg-card text-base max-sm:text-[16px] dark:bg-card\",\n);\n\n/** A line is a pill, the way the composer is. */\nconst LINE = \"h-11 rounded-full px-4\";\n\n/**\n * A paragraph keeps the pill's corner, which is half the line's height, so\n * the two shapes read as one family rather than a pill beside a box.\n */\nconst PARAGRAPH = \"max-h-64 min-h-32 rounded-[1.375rem] px-4 py-2.5\";\n\n/**\n * One labelled field on the contact form: a line, a paragraph, or one of a\n * list, with a complaint underneath when there is one.\n *\n * The complaint opens and closes rather than appearing, and holds its words\n * on the way out, so a field being fixed does not jump the form and does\n * not go blank mid sentence.\n */\nexport function ContactField({\n field,\n id,\n value,\n error,\n isDisabled = false,\n onChange,\n onSubmit,\n}: {\n field: ContactFieldShape;\n /** Unique on the page, since a page may carry two forms. */\n id: string;\n value: string;\n /** What is wrong with the answer, or null. */\n error: string | null;\n isDisabled?: boolean;\n onChange: (value: string) => void;\n /** Meta and enter in the paragraph box, since enter there is a new line. */\n onSubmit?: () => void;\n}) {\n // The last complaint, held for the collapse on the way out.\n const [held, setHeld] = useState(error);\n if (error !== null && error !== held) setHeld(error);\n const shown = error ?? held;\n\n const errorId = `${id}-error`;\n const control = {\n id,\n name: field.name,\n value,\n disabled: isDisabled,\n \"aria-invalid\": error !== null,\n \"aria-describedby\": error !== null ? errorId : undefined,\n };\n\n function handleKeyDown(event: KeyboardEvent<HTMLTextAreaElement>) {\n if (event.key !== \"Enter\") return;\n if (!event.metaKey && !event.ctrlKey) return;\n event.preventDefault();\n onSubmit?.();\n }\n\n return (\n <div className=\"flex flex-col\">\n <label\n htmlFor={id}\n className=\"flex items-baseline gap-2 px-1 pb-1.5 text-sm font-medium\"\n >\n {field.label}\n {!field.required && (\n <span className=\"text-xs font-normal text-muted-foreground\">\n Optional\n </span>\n )}\n </label>\n\n {field.type === \"textarea\" ? (\n <Textarea\n {...control}\n rows={4}\n maxLength={MESSAGE_MAX_LENGTH}\n placeholder={field.placeholder}\n onChange={(event) => onChange(event.target.value)}\n onKeyDown={handleKeyDown}\n className={cn(SHELL, PARAGRAPH)}\n />\n ) : field.type === \"select\" ? (\n <div className=\"relative\">\n <select\n {...control}\n onChange={(event) => onChange(event.target.value)}\n className={cn(\n SHELL,\n LINE,\n \"appearance-none pr-10\",\n // Nothing chosen yet reads as a placeholder, not an answer.\n value === \"\" && \"text-muted-foreground\",\n )}\n >\n <option value=\"\">{field.placeholder ?? \"Choose one\"}</option>\n {field.options.map((option) => (\n <option key={option} value={option}>\n {option}\n </option>\n ))}\n </select>\n <CaretDown\n aria-hidden\n className=\"pointer-events-none absolute top-1/2 right-4 size-4 -translate-y-1/2 text-muted-foreground\"\n />\n </div>\n ) : (\n <input\n {...control}\n type={field.type === \"email\" ? \"email\" : \"text\"}\n maxLength={LINE_MAX_LENGTH}\n placeholder={field.placeholder}\n autoComplete={\n field.role === \"email\"\n ? \"email\"\n : field.role === \"name\"\n ? \"name\"\n : undefined\n }\n spellCheck={field.role === \"custom\" ? undefined : false}\n onChange={(event) => onChange(event.target.value)}\n className={cn(SHELL, LINE)}\n />\n )}\n\n <Collapse isOpen={error !== null}>\n <p id={errorId} className=\"px-1 pt-1.5 text-sm text-destructive\">\n {shown}\n </p>\n </Collapse>\n </div>\n );\n}\n","import type { ContactField } from \"./fields\";\nimport type { MedianContactMessage } from \"./message\";\n\n/** What every field on our own form holds, keyed by its name. */\nexport type ContactValues = Record<string, string>;\n\n/**\n * Our form's answers, as the message every contact form sends.\n *\n * The email and the name go on the customer record, because that is where\n * the team looks for who somebody is. Everything else the host asked for\n * becomes a labelled answer, and `composeBody` puts those above the message.\n */\nexport function composeMessage(\n fields: readonly ContactField[],\n values: ContactValues,\n): MedianContactMessage {\n const name = (values.name ?? \"\").trim();\n const answers: Record<string, string> = {};\n for (const field of fields) {\n if (field.role !== \"custom\") continue;\n answers[field.label] = values[field.name] ?? \"\";\n }\n\n return {\n email: (values.email ?? \"\").trim(),\n message: (values.message ?? \"\").trim(),\n ...(name ? { name } : {}),\n ...(Object.keys(answers).length > 0 ? { fields: answers } : {}),\n };\n}\n\n/**\n * One message to the team out of the words and the extra answers. The\n * answers go first, a line each, so the thread reads the way the form was\n * filled in and the agent reads it too. Empty answers are left off, and a\n * form with none sends the words alone.\n */\nexport function composeBody(message: MedianContactMessage): string {\n const lines = Object.entries(message.fields ?? {})\n .map(([label, value]) => ({ label: label.trim(), value: value.trim() }))\n .filter((line) => line.label.length > 0 && line.value.length > 0)\n .map((line) => `${line.label}: ${line.value}`);\n\n const words = message.message.trim();\n return lines.length > 0 ? `${lines.join(\"\\n\")}\\n\\n${words}` : words;\n}\n","/**\n * What a host can put on the contact form, and what the form always has.\n *\n * Two fields are on every form whatever the host says: an email, because a\n * message nobody can reply to is a message nobody sent, and the message\n * itself. Everything else is the host's to add: a name, a topic, an order\n * number, whatever their team wants to know before they read.\n */\n\n/** A line, a paragraph, or one of a list. */\nexport type MedianContactFieldKind = \"text\" | \"textarea\" | \"select\";\n\n/**\n * One field a host adds to the form.\n *\n * { name: \"topic\", label: \"Topic\", type: \"select\", options: [\"Billing\", \"A bug\"] }\n *\n * Three names are reserved. `email` and `message` are the two the form always\n * has, and naming either here only changes its label and placeholder. `name`\n * lands on the customer record beside the email rather than in the message,\n * so the team sees who wrote rather than a line saying so.\n */\nexport type MedianContactField = {\n /** Its key. Rides the message as the label on its line. */\n name: string;\n /** Shown above the box. Made from `name` when left out. */\n label?: string;\n /** `text` by default. Ignored on `email` and `message`, which know their shape. */\n type?: MedianContactFieldKind;\n /** What a `select` offers. A select with none falls back to a text box. */\n options?: string[];\n placeholder?: string;\n /** Off by default. `email` and `message` are required whatever this says. */\n required?: boolean;\n};\n\n/** What the built-in fields do with a value, and what a custom one does. */\nexport type ContactRole = \"email\" | \"message\" | \"name\" | \"custom\";\n\n/** A field with every choice made, which is what the form renders. */\nexport type ContactField = {\n name: string;\n label: string;\n type: MedianContactFieldKind | \"email\";\n options: string[];\n placeholder: string | undefined;\n required: boolean;\n role: ContactRole;\n};\n\n/** The two the form brings, when the host does not mention them. */\nconst DEFAULT_EMAIL: MedianContactField = {\n name: \"email\",\n label: \"Email\",\n placeholder: \"you@example.com\",\n};\n\nconst DEFAULT_MESSAGE: MedianContactField = {\n name: \"message\",\n label: \"Message\",\n placeholder: \"How can we help?\",\n};\n\nconst RESERVED_ROLES: Record<string, ContactRole> = {\n email: \"email\",\n message: \"message\",\n name: \"name\",\n};\n\n/** \"order_number\" and \"orderNumber\" both read as \"Order number\". */\nfunction labelOf(name: string): string {\n const spaced = name\n .replace(/[_-]+/g, \" \")\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n .trim()\n .toLowerCase();\n return spaced.charAt(0).toUpperCase() + spaced.slice(1);\n}\n\nfunction resolveOne(field: MedianContactField): ContactField {\n const role = RESERVED_ROLES[field.name] ?? \"custom\";\n const options = field.options ?? [];\n const label = field.label?.trim() || labelOf(field.name);\n\n if (role === \"email\") {\n return {\n name: \"email\",\n label,\n type: \"email\",\n options: [],\n placeholder: field.placeholder ?? DEFAULT_EMAIL.placeholder,\n required: true,\n role,\n };\n }\n if (role === \"message\") {\n return {\n name: \"message\",\n label,\n type: \"textarea\",\n options: [],\n placeholder: field.placeholder ?? DEFAULT_MESSAGE.placeholder,\n required: true,\n role,\n };\n }\n\n // A list with nothing in it is a text box that forgot to say so.\n const type =\n field.type === \"select\" && options.length === 0\n ? \"text\"\n : (field.type ?? \"text\");\n\n return {\n name: field.name,\n label,\n type,\n options: type === \"select\" ? options : [],\n placeholder: field.placeholder,\n required: field.required ?? false,\n role,\n };\n}\n\n/**\n * The form, in the order it is shown.\n *\n * The host's fields keep their order. Email goes first when they did not\n * place it, since it is the first thing the team needs, except that a name\n * is let in front of it: that is the order people introduce themselves in.\n * The message goes last when they did not place it, since it is the box\n * everything else is about. A name repeated twice is the first one: a form\n * with two boxes for one answer would send the second and lose the first.\n */\nexport function resolveFields(\n fields: readonly MedianContactField[] = [],\n): ContactField[] {\n const seen = new Set<string>();\n const resolved: ContactField[] = [];\n\n for (const field of fields) {\n const name = field.name?.trim();\n if (!name || seen.has(name)) continue;\n seen.add(name);\n resolved.push(resolveOne({ ...field, name }));\n }\n\n if (!seen.has(\"email\")) {\n const afterName = resolved.findIndex((field) => field.role === \"name\") + 1;\n resolved.splice(afterName, 0, resolveOne(DEFAULT_EMAIL));\n }\n if (!seen.has(\"message\")) resolved.push(resolveOne(DEFAULT_MESSAGE));\n\n return resolved;\n}\n\n/**\n * What is wrong with a list of fields, said the way the console should say\n * it. Empty when nothing is. Checked at mount rather than on send, so the\n * person who wrote the list is still looking at the page that runs it.\n */\nexport function fieldComplaints(\n fields: readonly MedianContactField[] | undefined,\n): string[] {\n if (fields === undefined) return [];\n const complaints: string[] = [];\n const seen = new Set<string>();\n\n for (const field of fields) {\n const name = typeof field.name === \"string\" ? field.name.trim() : \"\";\n if (!name) {\n complaints.push(\n \"a field has no name, so it was left off. Give every field a name, which is the key its answer rides the message under.\",\n );\n continue;\n }\n if (seen.has(name)) {\n complaints.push(\n `two fields are named \"${name}\", so only the first is shown. Give each field its own name.`,\n );\n }\n seen.add(name);\n if (field.type === \"select\" && (field.options?.length ?? 0) === 0) {\n complaints.push(\n `\"${name}\" is a select with no options, so it is shown as a text box. Pass options, or drop the type.`,\n );\n }\n }\n\n return complaints;\n}\n","\"use client\";\n\nimport { useLayoutEffect, useRef } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { prefersReducedMotion } from \"../motion\";\n\n/** Matches --ease-out-quint. JS cannot read a theme token. */\nconst EASE_OUT = \"cubic-bezier(0.22, 1, 0.36, 1)\";\n\n/**\n * How long the button takes to become the mark: across the card and from a\n * pill to a square. A travel rather than a resize, so it leaves at speed\n * and settles, the way everything else in the widget arrives.\n */\nconst TRAVEL_MS = 550;\n\n/** When the tick starts drawing: as the square is settling, not after. */\nconst DRAW_LEAD_MS = 150;\nconst DRAW_MS = 420;\n\n/** The ring leaves as the tick lands, and takes its time fading. */\nconst RING_LEAD_MS = 100;\nconst RING_MS = 800;\n\n/** For a mark that pops in place rather than travelling. */\nconst POP_DRAW_DELAY_MS = 180;\nconst POP_RING_DELAY_MS = 220;\n\n/** Where the button was, in the viewport, when the message went. */\nexport type SentFrom = {\n left: number;\n top: number;\n width: number;\n height: number;\n};\n\n/**\n * The mark a sent message gets: the widget's square with a tick that draws\n * itself across it and one ring that rolls out and fades.\n *\n * Given `from`, the square starts as the send button, at the button's own\n * spot and shape, and travels to its place while it squares off. The button\n * is what was pressed, so it is the thing that should turn into the answer;\n * a mark appearing somewhere else while the button fades is two events\n * where there was one. Without `from` it pops in place on the spring.\n *\n * The tick is drawn rather than faded because a shape arriving stroke by\n * stroke reads as something completing, and a tick that appears reads as\n * something switched on. It runs through the animation API rather than a\n * keyframe, since the stroke's length is measured off the path and a\n * stylesheet cannot ask for it. Under reduced motion the square is simply\n * there with its tick drawn, and nothing travels or rings.\n *\n * Mount it when the message lands. Everything here plays once, from the\n * start, and a fresh key is how the next send gets its own.\n */\nexport function SentMark({\n from = null,\n className,\n}: {\n from?: SentFrom | null;\n className?: string;\n}) {\n const frame = useRef<HTMLSpanElement>(null);\n const square = useRef<HTMLSpanElement>(null);\n const tick = useRef<SVGPathElement>(null);\n const ring = useRef<HTMLSpanElement>(null);\n\n // A layout effect, so the square is already at the button when the first\n // frame paints rather than flashing at rest and jumping back.\n useLayoutEffect(() => {\n const rest = frame.current;\n const box = square.current;\n const path = tick.current;\n const halo = ring.current;\n if (!rest || !box || !path || !halo || prefersReducedMotion()) return;\n\n const animations: Animation[] = [];\n const travels = from !== null;\n\n if (from !== null) {\n const seat = rest.getBoundingClientRect();\n animations.push(\n box.animate(\n [\n {\n left: `${from.left - seat.left}px`,\n top: `${from.top - seat.top}px`,\n width: `${from.width}px`,\n height: `${from.height}px`,\n // The pill's corner is half its height, whatever its class\n // says. Starting from the class's own number would keep the\n // corners round until the last frames and then snap them.\n borderRadius: `${from.height / 2}px`,\n },\n {\n left: \"0px\",\n top: \"0px\",\n width: `${seat.width}px`,\n height: `${seat.height}px`,\n borderRadius: getComputedStyle(box).borderRadius,\n },\n ],\n { duration: TRAVEL_MS, easing: EASE_OUT, fill: \"both\" },\n ),\n );\n }\n\n const drawDelay = travels ? TRAVEL_MS - DRAW_LEAD_MS : POP_DRAW_DELAY_MS;\n const ringDelay = travels ? TRAVEL_MS - RING_LEAD_MS : POP_RING_DELAY_MS;\n\n const length = path.getTotalLength();\n animations.push(\n path.animate(\n [{ strokeDashoffset: length }, { strokeDashoffset: 0 }],\n { duration: DRAW_MS, delay: drawDelay, easing: EASE_OUT, fill: \"both\" },\n ),\n );\n // The ring starts where the mark's edge is and rolls outward, so it\n // reads as leaving the mark rather than arriving from nowhere. Filled\n // forwards only: a backwards fill would hold its first frame through\n // the delay, and that is a hollow square waiting at the seat while the\n // button is still on its way there.\n animations.push(\n halo.animate(\n [\n { transform: \"scale(1)\", opacity: 0.6 },\n { transform: \"scale(1.9)\", opacity: 0 },\n ],\n { duration: RING_MS, delay: ringDelay, easing: EASE_OUT, fill: \"forwards\" },\n ),\n );\n // The dash is set beside the animation rather than in the markup, so a\n // browser with motion turned off never sees an undrawn tick.\n path.style.strokeDasharray = `${length}`;\n\n return () => {\n for (const animation of animations) animation.cancel();\n };\n }, [from]);\n\n return (\n <span\n ref={frame}\n className={cn(\"relative grid size-16 place-items-center\", className)}\n >\n <span\n ref={ring}\n aria-hidden\n className=\"absolute inset-0 rounded-2xl border-2 border-primary opacity-0\"\n />\n {/* Absolute at rest as well as in flight, so the travel is only ever\n four numbers changing and the seat underneath never reflows. */}\n <span\n ref={square}\n className={cn(\n \"absolute inset-0 grid place-items-center rounded-2xl bg-primary text-primary-foreground\",\n from === null &&\n \"animate-in duration-500 ease-spring fade-in-0 zoom-in-50 motion-reduce:animate-none\",\n )}\n >\n <svg\n viewBox=\"0 0 24 24\"\n aria-hidden\n className=\"size-8\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path ref={tick} d=\"M5 12.5 10 17.5 19 7\" />\n </svg>\n </span>\n </span>\n );\n}\n","\"use client\";\n\nimport { useCallback, useMemo, useState, useSyncExternalStore } from \"react\";\nimport type { MedianContactMessage } from \"../contact/message\";\nimport { deliverContact } from \"./contact\";\nimport { useDiagnostics, type MedianDiagnostics } from \"./diagnostics\";\nimport { useRememberInstall } from \"./install\";\nimport { getOrCreateSessionId, subscribeSession } from \"./session\";\nimport { useApiKeyShapeWarning } from \"./use-api-key-warning\";\nimport { useCustomDataSender } from \"./custom-data\";\nimport { useSignedInUser } from \"./signed-out\";\nimport { useMedianIdentity, useMissingCredentials } from \"./use-identity\";\nimport type { MedianUser } from \"./use-median-thread\";\nimport { useVisitorEmail } from \"./visitor-email\";\nimport { useMedianBranding } from \"./use-median-branding\";\n\ntype MedianContactHookOptions = {\n /** Who is signed in on your site, if anyone. */\n user?: MedianUser;\n /** Browser context on the message. On by default. */\n telemetry?: boolean;\n /**\n * Watch what goes wrong on the page, so a bug report filed from the\n * message arrives with the error already attached. The same switch\n * `<MedianSupport>` takes, and the same buffer behind it.\n */\n diagnostics?: MedianDiagnostics;\n /** Anything about your own app worth having next to a bug report. */\n appState?: Record<string, string | number | boolean>;\n};\n\n/**\n * Two ways in, the same two every component takes: the publishable key, or\n * the path of a route built with `medianIdentity`, which answers with the\n * key and whoever is signed in.\n */\nexport type UseMedianContactOptions = MedianContactHookOptions &\n (\n | { apiKey: string; identity?: string }\n | { apiKey?: string; identity: string }\n );\n\n/** What the hook hands back: one call, and the state around it. */\nexport type MedianContactControl = {\n showBranding: boolean;\n /**\n * Send what the form holds. Resolves once the message is in the inbox.\n * Rejects with an `Error` whose `message` is worth showing, and `error`\n * holds the same words until the next send.\n */\n send: (message: MedianContactMessage) => Promise<void>;\n /** A send is in flight. Hold the button. */\n isSending: boolean;\n /** Why the last send failed, or null. Cleared by the next send. */\n error: string | null;\n /**\n * Whether a send has somewhere to go. False on the server, and for the\n * beat before an identity route has answered with the key.\n */\n isReady: boolean;\n /**\n * Who is typing, as far as the page knows: what you passed, what the\n * identity route said, and the email the widget's own question was\n * answered with on this browser. For filling the boxes in.\n */\n user: MedianUser | undefined;\n};\n\n/**\n * A contact form of your own, on Median's inbox:\n *\n * const contact = useMedianContact({ apiKey: \"median_pk_...\" });\n *\n * <form\n * onSubmit={async (event) => {\n * event.preventDefault();\n * await contact.send({ email, message });\n * }}\n * >\n * ...\n * {contact.error && <p>{contact.error}</p>}\n * <button disabled={contact.isSending}>Send</button>\n * </form>\n *\n * Everything about the form is yours. What is sent starts a conversation in\n * your inbox exactly as `<MedianContactForm>` and the widget do, and the\n * reply goes to the email given. No provider, no connection to manage.\n */\nexport function useMedianContact(\n options: UseMedianContactOptions,\n /** Who to name in the console. `<MedianContactForm>` passes itself. */\n caller = \"useMedianContact()\",\n): MedianContactControl {\n const { apiKey, identity, user, telemetry, diagnostics, appState } = options;\n const resolved = useMedianIdentity(identity);\n useMissingCredentials(caller, apiKey, identity, \"it cannot send\");\n const key = apiKey ?? resolved.apiKey;\n const showBranding = useMedianBranding(key);\n useApiKeyShapeWarning(caller, key);\n useCustomDataSender();\n const person = useSignedInUser(user ?? resolved.user);\n // So a crash screen has a key to report with, the same as a mounted panel.\n useRememberInstall(key, person);\n\n // The session token is external state (localStorage), read as a store: the\n // server snapshot is null, so SSR and the first client paint agree, and\n // `reset()` on sign out lands here like it lands on the thread.\n const sessionId = useSyncExternalStore(\n subscribeSession,\n useCallback(\n () => (key === undefined ? null : getOrCreateSessionId(key)),\n [key],\n ),\n () => null,\n );\n const takeDiagnostics = useDiagnostics(diagnostics, appState);\n const [knownEmail] = useVisitorEmail(key ?? \"\");\n\n const [isSending, setSending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const send = useCallback(\n async (message: MedianContactMessage) => {\n setError(null);\n if (key === undefined || sessionId === null) {\n const reason =\n key === undefined\n ? \"This form is not connected yet. Try again in a moment.\"\n : \"Still connecting. Try again in a moment.\";\n setError(reason);\n throw new Error(reason);\n }\n\n setSending(true);\n try {\n const collected = takeDiagnostics();\n await deliverContact(\n {\n apiKey: key,\n sessionId,\n ...(person ? { user: person } : {}),\n ...(telemetry !== undefined ? { telemetry } : {}),\n ...(collected ? { diagnostics: collected } : {}),\n },\n message,\n );\n } catch (caught) {\n setError(caught instanceof Error ? caught.message : String(caught));\n throw caught;\n } finally {\n setSending(false);\n }\n },\n [key, sessionId, person, telemetry, takeDiagnostics],\n );\n\n // The host's word first, then the address this browser already gave.\n const known = useMemo<MedianUser | undefined>(() => {\n const email = person?.email || knownEmail || undefined;\n if (person === undefined && email === undefined) return undefined;\n return { ...person, ...(email ? { email } : {}) };\n }, [person, knownEmail]);\n\n return {\n send,\n showBranding,\n isSending,\n error,\n isReady: key !== undefined && sessionId !== null,\n user: known,\n };\n}\n","\"use client\";\n\nimport { unwrapMutationResult } from \"@median/backend/mutationResult\";\n\nimport { api } from \"@median/backend\";\nimport type { ClientDiagnostics } from \"@median/backend/diagnostics\";\nimport { ConvexHttpClient } from \"convex/browser\";\nimport { errorMessage } from \"@/lib/errors\";\nimport { composeBody } from \"../contact/contact-body\";\nimport type { MedianContactMessage } from \"../contact/message\";\nimport { collectContext, collectPage } from \"./context\";\nimport { restoreCustomData, takeCustomData } from \"./custom-data\";\nimport { MEDIAN_DEPLOYMENT_URL } from \"./deployment\";\nimport { getOrCreateSessionId } from \"./session\";\nimport { splitIdentity, type MedianUser } from \"./use-median-thread\";\nimport { writeVisitorEmail } from \"./visitor-email\";\n\n/**\n * Sending a contact message, for a form of the host's own.\n *\n * The same door the widget's composer uses, on the same session token, so\n * what a host's form sends starts the thread the corner would have started:\n * the team answers it from the inbox, the agent goes first, and with no\n * panel open to watch, the reply goes to the email given. Somebody who\n * writes here and later opens the widget finds their own conversation.\n *\n * A stateless HTTP client rather than the subscribing one, because nothing\n * is read back: a form is a thing you hand over, not a thread you watch.\n */\n\n/** What a send is told about who is sending and from where. */\nexport type MedianContactOptions = {\n /** The browser-safe median_pk_ id. */\n apiKey: string;\n /**\n * Who is signed in on your site, if anyone. What the form says about the\n * email and the name wins over this; the rest is shown to your team.\n */\n user?: MedianUser;\n /**\n * Whether the message carries what the browser already knows: time zone,\n * language, browser, OS, device, screen, and the page it was sent from.\n * On by default. Pass `false` and none of it is read at all.\n */\n telemetry?: boolean;\n};\n\nlet client: ConvexHttpClient | null = null;\n\n/**\n * The delivery itself, shared by the plain function and the hook.\n *\n * Rejects with an `Error` whose message is worth showing, so a form keeps\n * its answers and says why. Anything the host attached through\n * `medianSupport.attach` rides along, and is handed back on a failure so\n * the retry still carries it.\n */\nexport async function deliverContact(\n options: MedianContactOptions & {\n sessionId: string;\n /** What the page has been recording, if the caller collects it. */\n diagnostics?: ClientDiagnostics;\n },\n message: MedianContactMessage,\n): Promise<void> {\n const { apiKey, sessionId, user, telemetry = true } = options;\n const email = message.email.trim();\n const name = message.name?.trim();\n\n const custom = takeCustomData();\n try {\n if (!MEDIAN_DEPLOYMENT_URL) {\n throw new Error(\n \"The Median SDK was built without a deployment URL. Set NEXT_PUBLIC_CONVEX_URL.\",\n );\n }\n client ??= new ConvexHttpClient(MEDIAN_DEPLOYMENT_URL);\n\n // Read now rather than earlier: all of it is wanted as \"what was true\n // when they wrote\", and a form can sit on a page for an hour first.\n const context = telemetry ? collectContext() : undefined;\n const page = telemetry ? collectPage() : undefined;\n const { facts, claim } = splitIdentity(user ?? null);\n\n await client\n .mutation(api.widget.sendV2, {\n apiKey,\n sessionId,\n body: composeBody(message),\n // What was typed wins over what the host passed. They are looking at\n // the box, and the box is what they just filled in.\n user: { ...facts, email, ...(name ? { name } : {}) },\n ...(claim ? { identity: claim } : {}),\n ...(context ? { context } : {}),\n ...(page ? { page } : {}),\n ...(options.diagnostics ? { diagnostics: options.diagnostics } : {}),\n ...(custom ? { customData: custom } : {}),\n // Replies go to the address they typed: nothing on the page shows one.\n fromContactForm: true,\n })\n .then(unwrapMutationResult);\n } catch (caught) {\n // Back in the queue, so the retry carries them like it keeps the answers.\n if (custom) restoreCustomData(custom);\n console.error(\"Median: sending a contact message failed\", caught);\n throw new Error(errorMessage(caught), { cause: caught });\n }\n\n // So the widget on the same site never asks for the address again.\n writeVisitorEmail(apiKey, email);\n}\n\n/**\n * Send a contact message from a form of your own, on any page in the\n * browser:\n *\n * await sendMedianContact(\n * { apiKey: \"median_pk_...\" },\n * { email, message, name, fields: { Topic: topic } },\n * );\n *\n * It resolves once the message is in your inbox and rejects with an `Error`\n * whose `message` is worth showing above your button. Nothing about the\n * form is ours: any `<form>`, any framework, any styling.\n *\n * In React, `useMedianContact()` is the same call with `isSending` and\n * `error` kept for you, and an identity route resolved.\n */\nexport async function sendMedianContact(\n options: MedianContactOptions,\n message: MedianContactMessage,\n): Promise<void> {\n const sessionId = getOrCreateSessionId(options.apiKey);\n if (sessionId === null) {\n throw new Error(\n \"sendMedianContact() was called where there is no browser, so nothing was sent. Call it from the page, after it has loaded.\",\n );\n }\n await deliverContact({ ...options, sessionId }, message);\n}\n","\"use client\";\n\nimport {\n MedianContactForm,\n type MedianContactFormProps,\n} from \"./median-contact-form\";\nimport { mountMedian, nothingMounted, type MedianMountInstance } from \"./mount-into\";\n\n/**\n * The component's props, plus where on the page the form goes.\n *\n * Spelled out rather than `Omit`ped off the props, because omitting a key\n * from a union of two shapes flattens it into one and loses the \"either,\n * but not neither\" the union is there to say.\n */\nexport type MedianContactFormMountOptions = MedianContactFormProps & {\n /**\n * The element the form is rendered into, or a selector for it:\n *\n * mountMedianContactForm({ target: \"#contact\", apiKey: \"median_pk_...\" });\n *\n * Unlike the widget, a form has no corner to go to, so this is required.\n */\n target: string | Element;\n};\n\nexport type MedianContactFormInstance = MedianMountInstance<MedianContactFormProps>;\n\n/** What a selector or an element is called in the console. */\nfunction describe(target: string | Element): string {\n return typeof target === \"string\" ? `an element matching \"${target}\"` : \"the element passed as target\";\n}\n\n/**\n * The same form, for a page that has never heard of React: Vue, Svelte,\n * Rails, a script on plain HTML.\n *\n * const form = mountMedianContactForm({\n * target: \"#contact\",\n * apiKey: \"median_pk_...\",\n * fields: [{ name: \"name\", label: \"Name\" }],\n * });\n *\n * It takes the component's props as one options object, plus `target`, and\n * renders into a container of its own inside that element with the\n * package's copy of React. Whatever else the element holds is left alone.\n */\nexport function mountMedianContactForm({\n target,\n ...options\n}: MedianContactFormMountOptions): MedianContactFormInstance {\n let into: Element | null = null;\n\n if (typeof document !== \"undefined\") {\n into = typeof target === \"string\" ? document.querySelector(target) : target;\n if (into === null) {\n console.error(\n `Median: mountMedianContactForm() could not find ${describe(target)}, so nothing was mounted. Pass a selector that matches something on the page, or the element itself, once it exists.`,\n );\n return nothingMounted;\n }\n }\n\n return mountMedian({\n caller: \"mountMedianContactForm\",\n what: \"the form\",\n marker: \"medianContactForm\",\n into,\n options,\n render: (current) => <MedianContactForm {...current} />,\n });\n}\n"],"mappings":"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","names":["useEffect","useCallback","useEffect","useRef","useState","l","s","r","a","t","o","i","n","c","m","e","e","t","r","e","a","o","t","e","a","r","e","a","o","e","t","a","o","a","e","r","e","s","r","a","e","o","e","s","r","a","e","o","t","e","o","a","e","e","r","t","o","e","e","r","t","t","e","t","e","r","o","t","t","a","e","o","e","r","e","e","a","o","r","t","a","e","e","a","o","m","p","a","e","e","a","o","r","t","a","e","a","e","a","r","o","e","t","a","e","o","e","r","o","a","e","e","m","r","o","e","a","a","e","r","o","e","a","e","r","s","a","e","a","r","o","a","r","a","l","t","o","a","l","e","e","a","r","o","s","a","r","e","a","e","o","a","o","e","a","e","r","t","a","e","o","o","File","a","o","e","a","r","t","r","e","o","r","o","e","e","o","e","at","buttonY","listeners","useState","jsx","React","i","a","r","t","n","e","o","s","f","l","p","c","jsx","e","t","r","n","o","i","S","l","d","b","p","m","c","f","s","a","w","I","X","e","t","r","n","o","i","S","l","jsx","X","d","b","p","m","f","param","React","i","Slot","__name","Slottable","child","React","jsx","Fragment","jsx","jsxs","useCallback","useLayoutEffect","useRef","prefersReducedMotion","useRef","useCallback","useLayoutEffect","prefersReducedMotion","useEffect","useRef","useState","forwardRef","jsx","Image","jsx","jsx","jsxs","File","useEffect","useRef","useState","useRef","useState","jsx","jsx","useCallback","useEffect","useRef","useLayoutEffect","useRef","d","jsx","jsxs","d","jsx","jsx","jsxs","Fragment","jsx","jsxs","useState","useRef","useEffect","jsx","jsxs","useState","jsx","jsx","useState","shape","jsx","shape","jsx","jsxs","jsx","jsxs","jsx","jsx","jsxs","useState","jsx","useLayoutEffect","useRef","jsx","prefersReducedMotion","release","useRef","useLayoutEffect","useState","Fragment","jsx","jsxs","useState","File","jsx","useLayoutEffect","useRef","useState","useSyncExternalStore","jsx","jsxs","node","a","b","useRef","play","useState","useLayoutEffect","jsx","jsx","jsxs","Fragment","jsx","jsxs","useState","useEffect","useRef","useState","jsx","jsxs","useState","useRef","useEffect","jsx","jsxs","jsx","jsxs","useState","useCallback","useEffect","useLayoutEffect","useRef","useState","useSyncExternalStore","enabled","useSyncExternalStore","useState","useRef","useCallback","useLayoutEffect","useEffect","held","clamp","prefersReducedMotion","useLayoutEffect","useState","enabled","useLayoutEffect","useState","useSyncExternalStore","useEffect","held","useSyncExternalStore","useState","useLayoutEffect","useRef","useSyncExternalStore","subscribe","onServer","useEffect","held","jsx","jsxs","useRef","useState","Fragment","jsx","jsxs","WIDTH","buttonY","held","useState","Fragment","jsx","jsxs","SHEET","useState","useRef","useCallback","useEffect","current","useEffect","useRef","useState","createPortal","jsx","jsxs","LAYER","prefersReducedMotion","useSyncExternalStore","useEffect","useCallback","useEffect","useRef","useSyncExternalStore","listeners","at","store","useSyncExternalStore","read","MESSAGE_MAX_LENGTH","trim","withoutQuery","MESSAGE_MAX_LENGTH","events","useCallback","useEffect","useRef","MESSAGE_MAX_LENGTH","withoutQuery","useEffect","listeners","listeners","i","useMutation","useCallback","useCallback","useEffect","useMemo","useState","useSyncExternalStore","useEffect","useState","ConvexError","ConvexError","STORAGE_PREFIX","read","useEffect","useRef","enabled","useRef","useEffect","useCallback","useSyncExternalStore","STORAGE_PREFIX","inMemory","listeners","i","useSyncExternalStore","useCallback","useMutation","useMemo","useEffect","useState","current","client","useSyncExternalStore","useEffect","useRef","useState","useSyncExternalStore","useSyncExternalStore","useState","useRef","useEffect","person","useSyncExternalStore","listeners","useSyncExternalStore","ConvexError","useEffect","useState","useSyncExternalStore","inFlight","read","useEffect","useState","useSyncExternalStore","ConvexHttpClient","useEffect","useState","useState","useEffect","client","ConvexHttpClient","useState","jsx","client","useState","useEffect","i","useEffect","useRef","useCallback","useEffect","useLayoutEffect","useRef","useState","isDevelopment","at","count","useCallback","useState","inMemory","at","at","useState","useCallback","at","useEffect","count","current","Fragment","jsx","jsxs","useEffect","current","jsx","current","jsx","jsx","current","useCallback","useEffect","useRef","useState","useEffect","useRef","useState","useEffect","useRef","createPortal","useLayoutEffect","useState","GAP","MARGIN","clamp","jsx","LAYER","MOTION","OPEN","SHUT","useRef","useEffect","createPortal","Fragment","jsx","jsxs","THANKS_MS","useState","useRef","useEffect","draft","useSyncExternalStore","store","useSyncExternalStore","useCallback","useState","useSyncExternalStore","useSyncExternalStore","useCallback","useState","Fragment","jsx","jsxs","DEFAULT_WIDTH","useRef","useCallback","useState","useEffect","count","jsx","current","useEffect","useMemo","useEffect","useMemo","useRef","useState","useState","jsx","jsxs","held","useState","useLayoutEffect","useRef","jsx","jsxs","useRef","useLayoutEffect","prefersReducedMotion","jsx","jsxs","EMAIL_PATTERN","ARRIVES","useMemo","useRef","useState","current","useEffect","count","useCallback","useMemo","useState","useSyncExternalStore","ConvexHttpClient","client","ConvexHttpClient","useSyncExternalStore","useCallback","useState","useMemo","jsx","useEffect","useMemo","jsx","current"]}
1
+ {"version":3,"sources":["../../../apps/web/src/components/support-widget/sdk/median-support.tsx","../../../apps/web/src/components/support-widget/markdown-chunk.tsx","../../../apps/web/src/components/support-widget/widget-launcher.tsx","../../../apps/web/src/components/support-widget/launcher-position.ts","../../../apps/web/src/components/support-widget/support-widget.tsx","../../../apps/web/src/components/ui/collapse.tsx","../../../apps/web/src/components/ui/message-scroller.tsx","../../../node_modules/.bun/@shadcn+react@0.3.0+03aa47d54245a271/node_modules/@shadcn/react/dist/chunk-HBS6WEDP.js","../../../node_modules/.bun/@shadcn+react@0.3.0+03aa47d54245a271/node_modules/@shadcn/react/dist/message-scroller/index.js","../../../node_modules/.bun/class-variance-authority@0.7.1/node_modules/class-variance-authority/dist/index.mjs","../../../node_modules/.bun/@radix-ui+react-slot@1.3.3+0f58469d5b3bd39f/node_modules/@radix-ui/react-slot/src/slot.tsx","../../../node_modules/.bun/@radix-ui+react-compose-refs@1.1.5+0f58469d5b3bd39f/node_modules/@radix-ui/react-compose-refs/src/compose-refs.tsx","../../../apps/web/src/components/ui/button.tsx","../../../apps/web/src/lib/message-groups.ts","../../../apps/web/src/components/support-widget/use-stack-shift.ts","../../../apps/web/src/components/support-widget/motion.ts","../../../apps/web/src/components/support-widget/widget-settle.ts","../../../apps/web/src/components/support-widget/widget-composer.tsx","../src/next-image-shim.tsx","../../../apps/web/src/components/attachments/attachment-image.tsx","../../../apps/web/src/components/attachments/attachment-draft-chip.tsx","../../../apps/web/src/components/attachments/use-attachment-drafts.ts","../../../apps/web/src/components/attachments/use-attachment-target.ts","../../../apps/web/src/components/support-widget/widget-button.tsx","../../../apps/web/src/components/support-widget/widget-field.ts","../../../apps/web/src/components/ui/textarea.tsx","../../../apps/web/src/hooks/use-typing-signal.ts","../../../apps/web/src/hooks/use-eased-height.ts","../../../apps/web/src/components/brand-art.ts","../../../apps/web/src/components/brand.tsx","../../../apps/web/src/components/support-widget/widget-badge.tsx","../../../apps/web/src/components/support-widget/widget-corner.ts","../../../apps/web/src/components/support-widget/widget-destination.tsx","../../../apps/web/src/components/avatar.tsx","../../../apps/web/src/components/initial-badge.tsx","../../../apps/web/src/components/support-widget/widget-avatar.tsx","../../../apps/web/src/components/support-widget/widget-empty.tsx","../../../apps/web/src/components/support-widget/widget-header.tsx","../../../apps/web/src/components/support-widget/widget-history.tsx","../../backend/convex/shared/tickets.ts","../../../apps/web/src/components/support-widget/use-recovery-flow.ts","../../../apps/web/src/lib/errors.ts","../../../apps/web/src/components/support-widget/widget-recovery-note.tsx","../../../apps/web/src/components/support-widget/widget-recovery-foot.tsx","../../../apps/web/src/components/support-widget/widget-recovery-page.tsx","../../../apps/web/src/components/support-widget/widget-code-field.tsx","../../../apps/web/src/components/support-widget/widget-ticket-row.tsx","../../../apps/web/src/components/support-widget/widget-loading.tsx","../../../apps/web/src/components/support-widget/widget-locked.tsx","../../../apps/web/src/components/support-widget/widget-message.tsx","../../../apps/web/src/components/ui/badge.tsx","../../../apps/web/src/components/ui/morph-bubble.tsx","../../../apps/web/src/components/support-widget/widget-attachments.tsx","../../../apps/web/src/components/support-widget/widget-bubble.tsx","../../../apps/web/src/components/support-widget/widget-message-body.tsx","../../../apps/web/src/components/ui/line-reveal.tsx","../../../apps/web/src/hooks/use-media-query.ts","../../../apps/web/src/lib/motion.ts","../../../apps/web/src/components/site-icon.tsx","../../../apps/web/src/components/support-widget/widget-sources.tsx","../../../apps/web/src/components/support-widget/widget-typing.tsx","../../backend/convex/shared/support.ts","../../../apps/web/src/components/support-widget/widget-rating.tsx","../../../apps/web/src/components/support-widget/widget-screen-request.tsx","../../../apps/web/src/components/support-widget/widget-status.tsx","../../../apps/web/src/components/support-widget/use-launcher-drag.ts","../../../apps/web/src/components/support-widget/use-teaser-placement.ts","../../../apps/web/src/components/support-widget/use-widget-sheet.ts","../../../apps/web/src/components/support-widget/use-page-lock.ts","../../../apps/web/src/components/support-widget/widget-modal.tsx","../../../apps/web/src/components/support-widget/use-hydrated.ts","../../../apps/web/src/components/support-widget/use-scroll-lock.ts","../../../apps/web/src/components/support-widget/widget-sheet-styles.ts","../../../apps/web/src/components/support-widget/widget-teaser.tsx","../../../apps/web/src/components/support-widget/plain-text.ts","../../../apps/web/src/components/support-widget/widget-spotlight.tsx","../../../apps/web/src/components/support-widget/sdk/control.ts","../../backend/convex/shared/customData.ts","../../../apps/web/src/components/support-widget/sdk/custom-data.ts","../../../apps/web/src/components/support-widget/sdk/open-store.ts","../../backend/convex/shared/mutationResult.ts","../../backend/client/mutationResult.ts","../../backend/convex/_generated/api.js","../../backend/convex/shared/crashes.ts","../../../apps/web/src/components/support-widget/sdk/report-error.ts","../../../apps/web/src/lib/upload.ts","../../../apps/web/src/components/support-widget/sdk/context.ts","../../../apps/web/src/components/support-widget/sdk/deployment.ts","../../backend/convex/shared/diagnostics.ts","../../../apps/web/src/components/support-widget/sdk/diagnostics.ts","../../../apps/web/src/components/support-widget/sdk/install.ts","../../../apps/web/src/components/support-widget/sdk/screenshot.ts","../../../apps/web/src/components/support-widget/sdk/session.ts","../../../apps/web/src/components/support-widget/sdk/use-median-thread.ts","../../../apps/web/src/components/support-widget/sdk/use-widget-mutation.ts","../../../apps/web/src/hooks/use-now.ts","../../../apps/web/src/components/support-widget/sdk/rating-skips.ts","../../backend/convex/shared/widgetReadState.ts","../../../apps/web/src/lib/time.ts","../../../apps/web/src/components/support-widget/sdk/thread-messages.ts","../../../apps/web/src/components/support-widget/sdk/ticket-list.ts","../../../apps/web/src/components/support-widget/sdk/use-screenshot-ask.ts","../../../apps/web/src/components/support-widget/sdk/screen-capture.ts","../../../apps/web/src/components/support-widget/sdk/use-ticket-view.ts","../../../apps/web/src/components/support-widget/sdk/visitor-email.ts","../../../apps/web/src/components/support-widget/sdk/signed-out.ts","../../../apps/web/src/components/support-widget/sdk/unread-store.ts","../../../apps/web/src/components/support-widget/sdk/error-boundary.tsx","../../../apps/web/src/components/support-widget/sdk/use-identity.ts","../../../apps/web/src/components/support-widget/sdk/use-median-branding.ts","../../../apps/web/src/components/support-widget/sdk/median-connection.tsx","../../../apps/web/src/components/support-widget/sdk/use-api-key-warning.ts","../../../node_modules/.bun/cuelume@0.2.2/node_modules/cuelume/dist/sounds/recipes.js","../../../node_modules/.bun/cuelume@0.2.2/node_modules/cuelume/dist/audio/engine.js","../../../apps/web/src/components/support-widget/sdk/use-message-sound.ts","../../../apps/web/src/components/support-widget/sdk/use-navigation.ts","../../../apps/web/src/components/support-widget/sdk/use-teaser.ts","../../../apps/web/src/components/support-widget/sdk/stamps.ts","../../../apps/web/src/components/support-widget/sdk/teaser-state.ts","../../../apps/web/src/components/support-widget/sdk/use-unread-title.ts","../../../apps/web/src/components/support-widget/sdk/mount-into.tsx","../../../apps/web/src/components/support-widget/sdk/mount.tsx","../../../apps/web/src/components/support-widget/sdk/median-support-modal.tsx","../../../apps/web/src/components/support-widget/sdk/mount-support-modal.tsx","../../../apps/web/src/components/support-widget/sdk/median-feedback.tsx","../../backend/convex/shared/feedback.ts","../../backend/convex/shared/reportImages.ts","../../../apps/web/src/components/support-widget/feedback/feedback-panel.tsx","../../../apps/web/src/components/support-widget/feedback/feedback-popover.tsx","../../../apps/web/src/components/support-widget/feedback/use-anchor.ts","../../../apps/web/src/components/support-widget/sdk/feedback-control.ts","../../../apps/web/src/components/support-widget/sdk/use-feedback-note.ts","../../../apps/web/src/components/support-widget/sdk/mount-feedback.tsx","../../../apps/web/src/components/support-widget/sdk/median-contact-form.tsx","../../../apps/web/src/components/support-widget/contact/contact-form.tsx","../../../apps/web/src/components/support-widget/contact/contact-field.tsx","../../../apps/web/src/components/support-widget/contact/contact-body.ts","../../../apps/web/src/components/support-widget/contact/fields.ts","../../../apps/web/src/components/support-widget/contact/sent-mark.tsx","../../../apps/web/src/components/support-widget/sdk/use-median-contact.ts","../../../apps/web/src/components/support-widget/sdk/contact.ts","../../../apps/web/src/components/support-widget/sdk/mount-contact-form.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useState } from \"react\";\nimport { threadRendersMarkdown, warmMarkdownWhenIdle } from \"../markdown-chunk\";\nimport { SupportLauncher, type SupportPresentation } from \"../widget-launcher\";\nimport { WidgetSpotlight } from \"../widget-spotlight\";\nimport type { SupportLauncherKind } from \"../types\";\nimport { useMedianOpenState, useRegisterMedianSupport } from \"./control\";\nimport type { MedianDiagnostics } from \"./diagnostics\";\nimport { MedianErrorBoundary } from \"./error-boundary\";\nimport { useCustomDataSender } from \"./custom-data\";\nimport { useSignedInUser } from \"./signed-out\";\nimport { useMedianIdentity, useMissingCredentials } from \"./use-identity\";\nimport { useRememberInstall } from \"./install\";\nimport { useMedianBranding } from \"./use-median-branding\";\nimport { MedianConnection } from \"./median-connection\";\nimport { useApiKeyShapeWarning } from \"./use-api-key-warning\";\nimport { useMessageSound } from \"./use-message-sound\";\nimport { useSupportNavigation } from \"./use-navigation\";\nimport { unreadStore } from \"./unread-store\";\nimport { useTeaser } from \"./use-teaser\";\nimport { useUnreadTitle } from \"./use-unread-title\";\nimport {\n isSignedIn,\n useMedianThread,\n type MedianUser,\n} from \"./use-median-thread\";\n\n/**\n * How often the open panel says so. Well inside the server's window for\n * counting somebody as present, so one dropped beat changes nothing.\n */\nconst PRESENCE_BEAT_MS = 45_000;\n\ntype MedianSupportOptions = {\n /** Who is signed in on the host's site, if anyone. Shown to the team. */\n user?: MedianUser;\n /**\n * Whether a message carries what the browser already knows: time zone,\n * language, browser, OS, device, screen, and the page it was sent from.\n * On by default, because a team that has to ask for all six spends its\n * first reply asking. Pass `false` and none of it is read at all.\n */\n telemetry?: boolean;\n /**\n * Whether the agent may ask to see the visitor's screen. It asks in the\n * thread, the panel offers a button to share it, and the browser asks the\n * visitor to allow it. The picture lands in the thread where they can see\n * what was sent. On by default. Pass `false` on a page showing somebody\n * else's data and the agent is told no picture is coming.\n *\n * Never the panel itself or anything else Median renders.\n */\n screenshot?: boolean;\n /**\n * Take questions from signed in people only.\n *\n * Until `user` says somebody is there, the panel asks them to sign in\n * instead of offering a composer, and no conversation is opened or read.\n * Pass `user` the moment you have it and the thread takes over.\n *\n * A rule about your own page, kept on your own page: it decides what this\n * widget offers, not what the API accepts. `user.id` with a signed `hash`\n * is the part nobody can talk their way around.\n */\n requireSignIn?: boolean;\n /**\n * Where your sign in lives, for the button on that panel.\n *\n * <MedianSupport\n * apiKey=\"median_pk_...\"\n * requireSignIn\n * onSignIn={() => router.push(\"/login\")}\n * />\n *\n * Without it the panel says the same thing and offers no button, which is\n * right for a site whose sign in is already the first thing on the page.\n *\n * The history offers it too, above finding a conversation by email, to\n * somebody not signed in yet. Signed `user` ids are what make their\n * conversations follow them there.\n */\n onSignIn?: () => void;\n /**\n * Ask for an email before a visitor's first message. On by default: an\n * anonymous thread cannot be followed up, and half of support is getting\n * back to somebody. Pass `false` and the composer opens straight away.\n *\n * The composer asks once per browser; the answer rides their first message\n * and lands with their details on your side. A visitor your `user` already\n * names an email for is never asked, and `medianSupport.reset()` asks the\n * next person instead.\n *\n * Your team can turn the question off from the dashboard without a\n * redeploy, and either side saying no is enough to keep it away.\n */\n requireEmail?: boolean;\n /**\n * Show the first lines of an unread reply on the closed button. On by\n * default: the count says something arrived, and the card is what says\n * whether it is worth opening.\n *\n * <MedianSupport apiKey=\"median_pk_...\" messagePreview={false} />\n *\n * Pass `false` and the corner stays a button with a count on it. Nothing\n * else changes: the reply is still there, still counted, still announced.\n * For a page where a customer's words should never appear unprompted,\n * over somebody's shoulder or in a screen share.\n *\n * Your team can turn it off from the dashboard without a redeploy, and\n * either side saying no is enough to keep the card away.\n */\n messagePreview?: boolean;\n /**\n * Put the unread count in the tab's title, as `(1) Your page`, while a\n * reply is waiting. On by default: it is the one part of your page a\n * visitor can see from another tab. Your own title is kept and put back\n * once the reply has been read.\n *\n * <MedianSupport apiKey=\"median_pk_...\" titleCount={false} />\n *\n * Pass `false` and the title is never touched.\n */\n titleCount?: boolean;\n /**\n * Watch what goes wrong on the page, so a bug report arrives with the\n * error already attached instead of after four messages of asking.\n *\n * <MedianSupport apiKey=\"median_pk_...\" diagnostics />\n *\n * `true` listens for uncaught errors and rejected promises, which changes\n * nothing about how your page runs. The object form adds the two that wrap\n * something of yours:\n *\n * diagnostics={{ network: true, console: true }}\n *\n * `network` wraps window.fetch to note requests that failed, `console`\n * wraps console.error. Both call through to what they replaced, and both\n * are put back when the widget leaves the page.\n *\n * Off by default: this reads your program's error stream, which is yours\n * to offer rather than ours to take. Nothing is sent on its own. The\n * newest snapshot rides the next message the customer sends, and only when\n * something new is in it.\n */\n diagnostics?: MedianDiagnostics;\n /**\n * Anything about your own app worth having next to a bug report: a build,\n * a route, a flag. Read when a message is sent, so it can change as they\n * move around.\n *\n * <MedianSupport\n * apiKey=\"median_pk_...\"\n * diagnostics\n * appState={{ version: BUILD_SHA, plan: user.plan }}\n * />\n *\n * Twenty entries at most, and each one a line rather than a document.\n */\n appState?: Record<string, string | number | boolean>;\n /** Our button in the corner while it is closed, or nothing at all. */\n launcher?: SupportLauncherKind;\n /** Pass it, with `onOpenChange`, to hold the open state yourself. */\n open?: boolean;\n /** Every open and close, whoever asked for it. */\n onOpenChange?: (open: boolean) => void;\n /**\n * Take the visitor to a page, when one of your own tools offers one.\n *\n * <MedianSupport apiKey=\"median_pk_...\" onNavigate={router.push} />\n *\n * A tool that answers with a destination puts a button under the reply\n * that explains it, and this is what the button does. Nothing moves on its\n * own: the visitor presses it, or they stay where they are.\n *\n * Called with a path on your own site, already checked against this page's\n * origin. Your router decides what happens: pass `router.push` and the\n * conversation stays open across the move.\n *\n * Without it there is nowhere for a press to go, so no button is shown and\n * the console says so while you are building. Nothing else needs it.\n */\n onNavigate?: (path: string) => void;\n};\n\n/**\n * Two ways in, and every app has one of them.\n *\n * `apiKey` is the publishable id, and on its own it opens an anonymous\n * thread. `identity` is the path to a route built with `medianIdentity`,\n * which answers with the key and with whoever is signed in, already signed.\n * A framework that renders its root layout on the server cannot hand this\n * component a session any other way, so that is most of them.\n *\n * Passing both is worth it on a slow session: the button is up on the key\n * from the prop straight away, and the person arrives when the route answers.\n */\nexport type MedianSupportProps = MedianSupportOptions &\n (\n | { apiKey: string; identity?: string }\n | { apiKey?: string; identity: string }\n );\n\n/**\n * The whole SDK, as one component. Drop it on a page with your publishable API\n * key and the launcher, the thread, the AI and the inbox on the other side are\n * all wired up:\n *\n * <MedianSupport apiKey=\"median_pk_...\" />\n *\n * Optionally say who is signed in, and the team sees a person rather than\n * another anonymous visitor:\n *\n * <MedianSupport\n * apiKey=\"median_pk_...\"\n * user={{ name: \"Ada\", email: \"ada@example.com\" }}\n * />\n *\n * There are three states rather than two: open, closed as a button in the\n * corner, and not on the page at all. Take the corner back and open it from\n * your own nav, from anywhere in your app, with no state to thread through:\n *\n * <MedianSupport apiKey=\"median_pk_...\" launcher=\"hidden\" />\n *\n * const support = useMedianSupport();\n * <button onClick={support.open}>Contact us</button>\n *\n * On a site nobody uses signed out, say so and the panel asks them to sign in\n * rather than opening a conversation with a stranger:\n *\n * <MedianSupport\n * apiKey=\"median_pk_...\"\n * user={session?.user}\n * requireSignIn\n * onSignIn={() => router.push(\"/login\")}\n * />\n *\n * When somebody signs out, `medianSupport.reset()` forgets the conversation\n * along with them, so the next person at the keyboard starts fresh.\n *\n * That is the surface, on purpose. Everything else a support widget could be\n * asked to do is either our decision or a setting on the dashboard, so it can\n * change without every host having to redeploy.\n *\n * It brings its own connection, so it works on pages (and sites) that have\n * never heard of Convex.\n */\nexport function MedianSupport(props: MedianSupportProps) {\n return <MedianSupportPreview {...props} />;\n}\n\n/**\n * The same widget with the handful of knobs Median's own pages need: the\n * gallery pins it inside a frame rather than to the viewport, and `defaultOpen`\n * is there for a screenshot or a page that has room for nothing else.\n *\n * Nothing uses `defaultOpen` today, on purpose. Everywhere the widget is shown\n * it starts as the button, because that is the state a host's visitors meet it\n * in and the morph out of the corner is most of what there is to look at.\n *\n * `presentation` is the one knob a host does reach, through a component of\n * its own: `MedianSupportModal` is this with the panel drawn in the middle of\n * the screen. It is a separate component rather than a prop so the two read\n * as two things to install, not one thing with a setting.\n *\n * Not exported from the package. A host gets `MedianSupport` and nothing else,\n * which is why these can exist at all without becoming promises we have to\n * keep.\n */\nexport function MedianSupportPreview({\n defaultOpen = false,\n presentation = \"corner\",\n className,\n ...props\n}: MedianSupportProps & {\n /** Where it starts, until anything opens or closes it. */\n defaultOpen?: boolean;\n /** Out of the corner, or in the middle of the screen. */\n presentation?: SupportPresentation;\n className?: string;\n}) {\n return (\n <MedianErrorBoundary component={componentName(presentation)}>\n <MedianIdentity\n {...props}\n defaultOpen={defaultOpen}\n presentation={presentation}\n className={className}\n />\n </MedianErrorBoundary>\n );\n}\n\n/**\n * Settles who the widget is for before anything is connected.\n *\n * Both halves can come from a prop or from the host's identity route, and a\n * prop wins: a host who has the key to hand should not wait on a fetch to put\n * the button up, and one who already knows the signed in person is not told\n * otherwise by a route that answers later.\n *\n * Nothing is rendered until there is a key from one of the two. Without one\n * there is nobody to talk to, and a button that opens onto an error is worse\n * on somebody's page than no button for the moment it takes to answer.\n */\ntype GateProps = MedianSupportOptions & {\n apiKey?: string;\n identity?: string;\n defaultOpen: boolean;\n presentation: SupportPresentation;\n className?: string;\n};\n\n/** What the host wrote in their page, for anything said in the console. */\nfunction componentName(presentation: SupportPresentation): string {\n return presentation === \"modal\" ? \"MedianSupportModal\" : \"MedianSupport\";\n}\n\nfunction MedianIdentity({ apiKey, identity, user, ...rest }: GateProps) {\n const resolved = useMedianIdentity(identity);\n useMissingCredentials(\n `<${componentName(rest.presentation)}>`,\n apiKey,\n identity,\n );\n const key = apiKey ?? resolved.apiKey;\n const person = useSignedInUser(user ?? resolved.user);\n\n if (key === undefined) return null;\n\n return (\n <MedianConnection what=\"widget\">\n <MedianLauncher\n {...rest}\n apiKey={key}\n user={person}\n isResolvingIdentity={resolved.isLoading}\n />\n </MedianConnection>\n );\n}\n\ntype ConnectionProps = MedianSupportOptions & {\n apiKey: string;\n /** The host's identity route has been asked and has not answered yet. */\n isResolvingIdentity: boolean;\n defaultOpen: boolean;\n presentation: SupportPresentation;\n className?: string;\n};\n\nfunction MedianLauncher({\n apiKey,\n user,\n telemetry,\n screenshot,\n diagnostics,\n appState,\n requireSignIn = false,\n onSignIn,\n requireEmail = true,\n messagePreview = true,\n titleCount = true,\n launcher,\n presentation,\n defaultOpen,\n open,\n onOpenChange,\n onNavigate,\n isResolvingIdentity,\n className,\n}: ConnectionProps) {\n useRegisterMedianSupport(componentName(presentation));\n useCustomDataSender();\n useApiKeyShapeWarning(`<${componentName(presentation)}>`, apiKey);\n // So a crash screen can file a report from a tree this widget is no\n // longer in. See `install.ts`.\n useRememberInstall(apiKey, user);\n const showBranding = useMedianBranding(apiKey);\n\n const [isOpen, setOpen] = useMedianOpenState({\n open,\n defaultOpen,\n onOpenChange,\n });\n\n // Whoever is at the keyboard has not said who they are, and this host only\n // takes questions from people who have.\n //\n // Not while the identity route is still being read. Nobody has said there\n // is no one signed in yet, and inviting somebody who is already signed in\n // to sign in, for the half second before their own name arrives, reads as a\n // widget that has lost them.\n const isLocked = requireSignIn && !isResolvingIdentity && !isSignedIn(user);\n\n const {\n presence,\n messages,\n ask,\n screenRequest,\n conversationId,\n isLoading,\n isSending,\n askForEmail,\n setVisitorEmail,\n awaitingHuman,\n rating,\n unreadCount,\n latestUnread,\n latestUnreadTicketId,\n replyElsewhere,\n reportPresence,\n tickets,\n continuesByEmail,\n sendError,\n send,\n setTyping,\n } = useMedianThread({\n apiKey,\n user,\n telemetry,\n screenshot,\n isLocked,\n diagnostics,\n appState,\n requireEmail,\n messagePreview,\n onSignIn,\n });\n\n // The card on the closed button, and the cross that waves it away. Held\n // per browser, so a dismissed reply stays dismissed\n // across the host's pages.\n const { teaser, dismiss: dismissTeaser } = useTeaser(apiKey, latestUnread);\n\n // Nothing here is settled yet: either the thread is on its way, or the\n // route that says whose thread it should be is. The panel shows the same\n // shimmer for both, because from the visitor's side they are one wait.\n const isSettling = isLoading || isResolvingIdentity;\n\n // Somebody who has been answered here before, with the answers still behind\n // the button. They are the likeliest person on the page to open it, so the\n // renderer is fetched once the page has finished its own work rather than\n // waited for on the click. Everybody else pays nothing until they reach for\n // it. See `markdown-chunk.ts`.\n useEffect(() => {\n if (!threadRendersMarkdown(messages)) return;\n return warmMarkdownWhenIdle();\n }, [messages]);\n\n // Opening the panel on a reply that landed somewhere else opens that\n // ticket: the badge and the card both said a reply was waiting, and that\n // is the one they came for. Only on the way open, so a reply arriving on\n // another ticket while they read never moves the screen.\n const [wasOpen, setWasOpen] = useState(isOpen);\n if (isOpen !== wasOpen) {\n setWasOpen(isOpen);\n if (\n isOpen &&\n latestUnreadTicketId !== null &&\n latestUnreadTicketId !== tickets.currentId\n ) {\n tickets.open(latestUnreadTicketId);\n }\n }\n\n // The same fact, told to the server as a heartbeat: on open, on coming\n // back to the tab, and every so often in between. It is what keeps a\n // reply they are watching arrive from also landing in their email.\n useEffect(() => {\n if (!isOpen) return;\n\n const beat = () => {\n if (document.visibilityState === \"visible\") reportPresence();\n };\n beat();\n const interval = setInterval(beat, PRESENCE_BEAT_MS);\n document.addEventListener(\"visibilitychange\", beat);\n window.addEventListener(\"focus\", beat);\n return () => {\n clearInterval(interval);\n document.removeEventListener(\"visibilitychange\", beat);\n window.removeEventListener(\"focus\", beat);\n };\n }, [isOpen, reportPresence]);\n\n // Whether the panel is up or not: a ring is drawn over the host's page\n // rather than in here, and an offer has to be waiting when they open it.\n const { spotlight, offer, follow } = useSupportNavigation({\n messages,\n ask,\n // Signing in is the case this exists for. A page that has been watching\n // an empty query is handed the account's whole history the moment the\n // widget can say who they are, and an instruction from a conversation\n // they had last week is not an instruction to anybody now.\n conversationId,\n // Locked, there is no thread being read at all, so the first one to\n // arrive after somebody signs in is history like any other first look.\n isLoading: isSettling || isLocked,\n onNavigate,\n });\n\n // Also whether the panel is up or not, and for the same reason as the ring:\n // a reply landing behind a closed panel is the one most worth hearing.\n useMessageSound({\n messages,\n conversationId,\n isLoading: isSettling || isLocked,\n });\n // And a reply on one of their other tickets, which is not on screen to\n // be heard there.\n const elsewhere = useMemo(\n () => (replyElsewhere === null ? [] : [replyElsewhere]),\n [replyElsewhere],\n );\n useMessageSound({\n messages: elsewhere,\n conversationId: \"elsewhere\",\n isLoading: isSettling || isLocked,\n });\n\n // And the count in the tab's title, for the reply that lands while they\n // are reading some other tab: the only part of this page they can see\n // from there.\n useUnreadTitle(titleCount ? unreadCount : 0);\n\n // And on whatever the host put in its own nav: the same count, read back\n // through `useMedianSupport()`, for a badge on a button of theirs. Taken\n // back to zero when the panel leaves the page.\n useEffect(() => {\n unreadStore.set(unreadCount);\n }, [unreadCount]);\n useEffect(() => () => unreadStore.set(0), []);\n\n // Until the config lands there is no name to put on the header, so the\n // launcher waits as a button and the panel shimmers if opened.\n const resolvedPresence = presence ?? {\n name: \"Support\",\n status: \"Connecting\",\n team: [],\n };\n\n return (\n <>\n {/* Keyed on the ask and on how many rings there have been, so a second\n one is a second ring rather than a stale one carrying on, and the\n same offer pressed twice points twice. */}\n {spotlight !== null && (\n <WidgetSpotlight key={spotlight.key} selector={spotlight.selector} />\n )}\n\n <SupportLauncher\n showBadge={showBranding}\n presence={resolvedPresence}\n messages={messages}\n // Passed through as a promise, so the composer can hold the draft on\n // a failed send and clear it on a delivered one.\n onSend={send}\n onTypingChange={setTyping}\n isLoading={isSettling}\n isLocked={isLocked}\n onSignIn={onSignIn}\n askForEmail={askForEmail}\n onSubmitEmail={setVisitorEmail}\n isSending={isSending}\n awaitingHuman={awaitingHuman}\n rating={rating}\n offer={offer}\n // Nowhere for a press to go without a handler from the host, so the\n // panel is told there is nothing to offer rather than offering\n // something that would do nothing.\n onFollow={onNavigate === undefined ? undefined : follow}\n screenRequest={screenRequest}\n unreadCount={unreadCount}\n teaser={teaser}\n onDismissTeaser={dismissTeaser}\n notice={sendError}\n // Not while locked: nobody signed in has any conversations to list.\n tickets={isLocked ? null : tickets}\n continuesByEmail={continuesByEmail}\n launcher={launcher}\n presentation={presentation}\n // Always controlled from here: the panel answers to the host's own\n // prop, to their trigger anywhere on the page, and to its own button,\n // and this is where those three are settled into one answer.\n open={isOpen}\n onOpenChange={setOpen}\n className={className}\n />\n </>\n );\n}\n","\"use client\";\n\nimport { lazy, useSyncExternalStore } from \"react\";\nimport type { SupportMessage } from \"./types\";\n\n/**\n * The markdown renderer, fetched when there is a reason to and not before.\n *\n * It is the heaviest thing the widget can reach: a parser, a sanitiser and an\n * HTML tokeniser, and together they outweigh everything else in the package\n * put together. They also cannot render anything until the panel is open, and\n * most visitors on most pages never open it. Loading them with the page\n * spends that on everybody to serve the few.\n *\n * So the import is a call rather than a statement, and the bundle splits\n * where the call is. What a host's page pays for on load is the button. The\n * rest arrives on the first sign that somebody wants it, which is early\n * enough to be there before they do:\n *\n * - the pointer reaching the launcher, or a tab landing on it\n * - the panel opening, whoever opened it\n * - a page going idle with an already answered thread behind the button\n *\n * Until it lands, a reply renders as the text it was written as, which is the\n * same paragraph a visitor's own message gets. A failed fetch leaves that in\n * place rather than leaving a hole where the answer was.\n */\n\nconst load = () => import(\"@/components/ui/message-markdown\");\n\n/**\n * The renderer itself, and the only place it is rendered from.\n *\n * `lazy` rather than a component held in a variable, so this is one component\n * for the life of the page rather than a new one per render. It is only ever\n * put on screen once `useMarkdownReady` says the module is already in hand,\n * so the suspense it can throw is a guard rather than a state anybody waits\n * in.\n */\nexport const MessageMarkdown = lazy(async () => ({\n default: (await load()).MessageMarkdown,\n}));\n\nlet ready = false;\nlet inFlight = false;\nconst listeners = new Set<() => void>();\n\n/**\n * Fetch it, unless it is already here or already on its way.\n *\n * Safe to call from anywhere, as often as you like: everything after the\n * first call in a page's life is a no-op. A fetch that fails clears the flag\n * so a later trigger can try again, which covers the tunnel a phone goes\n * through between the page loading and the visitor asking a question.\n */\nexport function warmMarkdown(): void {\n if (ready || inFlight) return;\n inFlight = true;\n\n void load().then(\n () => {\n ready = true;\n for (const listener of listeners) listener();\n },\n () => {\n inFlight = false;\n },\n );\n}\n\n/** How long to wait for an idle moment that may never come. */\nconst IDLE_FALLBACK_MS = 2_000;\n\n/**\n * The same, once the page has nothing better to do. Returns the cancel, so an\n * effect torn down before the browser gets round to it does not go on to\n * fetch a megabyte for a widget that has left the page.\n *\n * `requestIdleCallback` is missing on Safari before 17, so a timeout stands\n * in. The exact delay does not matter; being after the page's own work does.\n */\nexport function warmMarkdownWhenIdle(): () => void {\n if (typeof window === \"undefined\") return () => {};\n\n if (typeof window.requestIdleCallback === \"function\") {\n const handle = window.requestIdleCallback(() => warmMarkdown());\n return () => window.cancelIdleCallback(handle);\n }\n\n const timer = setTimeout(warmMarkdown, IDLE_FALLBACK_MS);\n return () => clearTimeout(timer);\n}\n\n/**\n * Whether there is anything in this thread that markdown would render\n * differently from a plain paragraph.\n *\n * Only the house voice writes markdown. What a visitor types is quoted\n * verbatim wherever it lands, so a thread of nothing but questions never\n * needs any of this and never fetches it.\n */\nexport function threadRendersMarkdown(messages: SupportMessage[]): boolean {\n return messages.some(\n (message) => message.sender !== \"visitor\" && message.body.length > 0,\n );\n}\n\nconst subscribe = (listener: () => void) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n};\n\nconst read = () => ready;\n// False on the server and through hydration both, so the first client pass\n// renders what the server sent rather than a tree it has to throw away.\nconst onServer = () => false;\n\n/** Whether the module is in hand, so rendering it will not suspend. */\nexport function useMarkdownReady(): boolean {\n return useSyncExternalStore(subscribe, read, onServer);\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { ChatCircleDots } from \"@/components/icons\";\nimport { cn } from \"@/lib/utils\";\nimport {\n clipOrigin,\n closedClip,\n type LauncherSide,\n} from \"./launcher-position\";\nimport { warmMarkdown } from \"./markdown-chunk\";\nimport { SupportWidget } from \"./support-widget\";\nimport { BUTTON_Y, useLauncherDrag } from \"./use-launcher-drag\";\nimport { useTeaserPlacement } from \"./use-teaser-placement\";\nimport { useWidgetSheet } from \"./use-widget-sheet\";\nimport { WIDGET_RADIUS } from \"./widget-corner\";\nimport { SupportModal } from \"./widget-modal\";\nimport { SHEET_PANEL } from \"./widget-sheet-styles\";\nimport { WidgetTeaser } from \"./widget-teaser\";\nimport type {\n SupportDestination,\n SupportFollow,\n SupportScreenRequest,\n SupportLauncherKind,\n SupportMessage,\n SupportPresence,\n SupportRating,\n SupportTickets,\n} from \"./types\";\n\n/**\n * Sized for 16px body text: room for a reply of a few lines at forty odd\n * characters a line, and a composer you can see from across the room.\n */\nconst DEFAULT_WIDTH = \"28rem\";\nconst DEFAULT_HEIGHT = \"40rem\";\n/** The closed button. Smaller than the panel's scale: it is a way in, not the room. */\nconst DEFAULT_BUTTON_SIZE = \"3rem\";\n\n/**\n * Bigger, for a thread you are actually reading rather than glancing at.\n *\n * More of the growth goes into the height than the width, because that is the\n * axis a conversation runs out of: room to see what was said, rather than room\n * per line. The width still moves, and moving it re-wraps every bubble as it\n * goes, so it is kept to the smaller number of the two.\n */\nconst EXPANDED_WIDTH = \"34rem\";\nconst EXPANDED_HEIGHT = \"52rem\";\n\n/**\n * The morph, as a clip rather than a resize.\n *\n * The panel is always laid out at its full size. Closed, it is clipped down to\n * a circle in the corner the size of the button; open, the clip is the panel\n * itself. Only the clip travels.\n *\n * This matters. Animating width and height means the browser lays out the whole\n * panel again on every frame, and the message scroller measures its viewport\n * every time that happens, so it fights the animation the entire way. Clipping\n * touches no layout at all: nothing inside ever moves, reflows or gets measured,\n * it is simply revealed.\n *\n * Where it grows out of is `launcher-position.ts`. The button can be dragged\n * anywhere up or down either edge, and the panel has to follow it there.\n */\n/**\n * The panel's own corner, from the one place it is written down.\n *\n * The clip used to be a hair rounder than the panel inside it, which is why\n * the panel had to give up its border: two curves that nearly agree meet in a\n * hairline, and a hairline that moves is the thing that makes an animation\n * look broken. Same number, one curve, and the border can come back. The\n * shadow layer wears this clip too, so it has to agree as well: cut squarer\n * than the panel, its fill shows past each corner as a second corner.\n */\nconst OPEN_CLIP = `inset(0px 0px 0px 0px round ${WIDGET_RADIUS})`;\n\n/**\n * The button's height inside the box.\n *\n * Measured and written by the drag before paint, because it is the difference\n * between two lengths taken against different boxes and no amount of `calc`\n * gets at that. The fallback is the resting place of a widget nobody has\n * moved, which is the one the server rendered and the only one on screen\n * before the measurement lands.\n */\nconst buttonY = (buttonSize: string) =>\n `var(${BUTTON_Y}, calc(100% - ${buttonSize}))`;\n\n/**\n * How far off the edges the whole thing sits. Close enough to read as parked\n * against the side rather than floating near it, and still clear of the corner\n * radius on a phone.\n */\nconst MARGIN = \"1rem\";\n\n/**\n * The bounce rides on a scale, not on the clip.\n *\n * A clip cannot show an overshoot: carrying past \"fully revealed\" only means\n * clipping outside the box, which looks like nothing at all. So the panel comes\n * up the last few percent of its size as it opens, and that is what carries a\n * touch too far and settles back. Around four pixels on a panel this wide,\n * which is about right for something you feel rather than watch.\n *\n * Scale is composited, so this costs the same as the clip does: nothing.\n */\nconst CLOSED_SCALE = \"scale(0.94)\";\nconst OPEN_SCALE = \"scale(1)\";\n\n/**\n * Where the panel comes from when there is no button for it to come from.\n *\n * A morph needs two shapes and one of them has gone. Clipping down to a circle\n * nobody can see is not an origin, it is a place the panel happens to vanish\n * at, and growing out of it reads as the corner deciding to produce a panel.\n * So it rises the last few pixels into place instead, which is what everything\n * that arrives out of nothing does.\n */\nconst RISEN = \"translateY(0.5rem) scale(0.98)\";\n\n/**\n * The shape, shared by the panel and the shadow layer behind it. One function\n * so the two cannot drift a frame apart.\n */\nconst shape = (\n isOpen: boolean,\n buttonSize: string,\n side: LauncherSide,\n hasBubble: boolean,\n) =>\n hasBubble\n ? {\n clipPath: isOpen\n ? OPEN_CLIP\n : closedClip(side, buttonY(buttonSize), buttonSize),\n transform: isOpen ? OPEN_SCALE : CLOSED_SCALE,\n // The point it grows out of, so the closed circle stays put under the\n // button rather than shrinking towards the middle.\n transformOrigin: clipOrigin(side, buttonY(buttonSize), buttonSize),\n }\n : { clipPath: OPEN_CLIP, transform: isOpen ? \"none\" : RISEN };\n\n/**\n * The two properties the morph is actually about, named rather than `all`.\n *\n * `transform-origin` is the one deliberately left out. It moves whenever the\n * button does, and easing a shape's origin across a panel while the shape\n * itself holds still is a swim, not a morph.\n */\nconst MORPH =\n \"transition-[clip-path,transform] duration-450 ease-spring motion-reduce:transition-none\";\n\n/**\n * The other one, for a widget with no button. Shorter and a pure deceleration:\n * a spring's overshoot is the button's swagger, and a panel that simply\n * appeared has not earned it.\n */\nconst ARRIVE =\n \"transition-transform duration-200 ease-out-quint motion-reduce:transition-none\";\n\n/**\n * The phone's version of the morph: none. A panel about to be the whole\n * screen has no corner to grow out of, so it rises into place as a sheet,\n * which is the shape every thumb has already been taught. A touch longer than\n * ARRIVE because it travels further, and the same pure deceleration.\n */\nconst SHEET =\n \"fixed transition-[opacity,transform] duration-300 ease-out-quint motion-reduce:transition-none\";\n\n/** Where the sheet waits: a little below its feet, ready to come up. */\nconst SHEET_RISEN = \"translateY(2rem)\";\n\n/**\n * Where the open panel is drawn.\n *\n * `corner` grows out of the button, which is what a widget is. `modal` keeps\n * the button and opens in the middle of the screen over a scrim instead,\n * for a site where the conversation deserves the room. Both are the same\n * panel with the same everything; only the box it is drawn in changes, and\n * on a phone the two are one sheet.\n */\nexport type SupportPresentation = \"corner\" | \"modal\";\n\n/**\n * The three things about the box itself that move: whether it is there at all,\n * and how wide and tall it is.\n *\n * One declaration rather than two named ones, because they are one property\n * list. Two classes reads better and does not work: both set\n * `transition-property`, so the second simply replaces the first.\n *\n * With no button in the corner there is nothing to show while it is closed, so\n * the whole thing fades with the morph. Quicker than the morph either way: the\n * clip still carries the panel out of the corner and back into it, but the few\n * frames where that is an empty circle are spent at zero.\n *\n * What is not in the list is the travel between corners. That is an animation\n * on the node rather than a transition on this class, so a drag can move the\n * box every frame without a transition trying to catch up with it.\n *\n * `ease-out` rather than the app's usual `ease-out-quint`, which is the one\n * exception in here and is about what is moving. Quint spends about seven\n * tenths of the distance in the first quarter of the time, which is what makes\n * it feel quick on something composited. This is a size, so every one of those\n * frames lays the panel out again and re-wraps the text in it, and thirty odd\n * pixels of that per frame reads as the panel stuttering rather than growing.\n * A gentler curve over slightly longer moves the same distance in far more\n * even steps.\n */\nconst BOX =\n \"transition-[opacity,width,height] duration-300 ease-out motion-reduce:transition-none\";\n\n/**\n * The button, and the panel it turns into.\n *\n * The shadow sits on a layer above the clip rather than on it, because a filter\n * is painted before a clip and would otherwise be cut away with everything\n * else. On the parent it reads the clipped shape's alpha instead, so the shadow\n * follows the corner round as it opens.\n *\n * Uncontrolled by default. Pass `open` and `onOpenChange` to drive it yourself,\n * for a host that opens support from its own menu. Pass `launcher=\"hidden\"`\n * too and the corner is theirs again: nothing is on the page until they ask\n * for it.\n */\nexport function SupportLauncher({\n presence,\n messages,\n onSend,\n onTypingChange,\n isLoading = false,\n isLocked = false,\n onSignIn,\n askForEmail = false,\n onSubmitEmail,\n isSending = false,\n awaitingHuman = false,\n rating,\n offer = null,\n onFollow,\n screenRequest = null,\n unreadCount = 0,\n teaser = null,\n onDismissTeaser,\n notice,\n tickets = null,\n continuesByEmail = false,\n showBadge = true,\n width = DEFAULT_WIDTH,\n height = DEFAULT_HEIGHT,\n expandedWidth = EXPANDED_WIDTH,\n expandedHeight = EXPANDED_HEIGHT,\n buttonSize = DEFAULT_BUTTON_SIZE,\n launcher = \"bubble\",\n presentation = \"corner\",\n defaultOpen = false,\n open,\n onOpenChange,\n className,\n}: {\n presence: SupportPresence;\n messages: SupportMessage[];\n /** Awaited by the composer, which keeps the draft if it rejects. */\n onSend?: (body: string, files: File[]) => unknown;\n /** The visitor's own typing, throttled by the composer. */\n onTypingChange?: (isTyping: boolean) => unknown;\n isLoading?: boolean;\n /** Nobody is signed in, on a host that asks people to be. */\n isLocked?: boolean;\n /** Takes the visitor to the host's own sign in. */\n onSignIn?: () => void;\n /**\n * The composer asks for an email before a first message. Off here by\n * default: only the SDK decides it, from its prop and the team's switch.\n */\n askForEmail?: boolean;\n /** What the composer's question does with a good answer. */\n onSubmitEmail?: (email: string) => void;\n /** Holds the send button while a message is on its way. */\n isSending?: boolean;\n /** The agent has called for a person and nobody has picked it up yet. */\n awaitingHuman?: boolean;\n /** The finished conversation's one question, while it is worth asking. */\n rating?: SupportRating | null;\n /** A page a tool offered with no message of its own to hang it on. */\n offer?: SupportDestination | null;\n /** What the button on an offer does. Without one, no offer is shown. */\n onFollow?: SupportFollow;\n /** The agent asking to see their screen, offered over the composer. */\n screenRequest?: SupportScreenRequest | null;\n /** Replies that arrived while the panel was closed. Sits on the button. */\n unreadCount?: number;\n /**\n * The newest of those, shown as a card on the closed button so the reply\n * can be read without opening anything. Null for no card.\n */\n teaser?: SupportMessage | null;\n /** The card's cross. Whoever owns `teaser` decides what a dismissal is. */\n onDismissTeaser?: () => void;\n /** One line above the composer, for a send that went wrong. */\n notice?: string | null;\n /** The visitor's other conversations, for the header's history. */\n tickets?: SupportTickets | null;\n /** The conversation on screen is answered by email. */\n continuesByEmail?: boolean;\n showBadge?: boolean;\n /** Any CSS length. The panel never grows past the viewport whatever this says. */\n width?: string;\n height?: string;\n /** What the header's own control grows it to, and back from. */\n expandedWidth?: string;\n expandedHeight?: string;\n /** The closed circle, and the corner the panel opens out of. */\n buttonSize?: string;\n /** Our button while it is closed, or nothing at all. */\n launcher?: SupportLauncherKind;\n /** Out of the corner, or in the middle of the screen. */\n presentation?: SupportPresentation;\n /** Uncontrolled starting state. Ignored when `open` is passed. */\n defaultOpen?: boolean;\n /** Pass it to control the panel yourself. */\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n /** Lands on the outer box. Override the default corner from here. */\n className?: string;\n}) {\n const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);\n const isOpen = open ?? uncontrolledOpen;\n const hasBubble = launcher === \"bubble\";\n const isModal = presentation === \"modal\";\n\n // Kept while it is closed rather than reset, so a panel that was too small\n // once is not too small again the next time it is opened. Nothing is stored\n // between visits: it is a reach for more room right now, not a setting.\n const [isExpanded, setExpanded] = useState(false);\n\n const launcherRef = useRef<HTMLButtonElement>(null);\n const panelRef = useRef<HTMLDivElement>(null);\n const boxRef = useRef<HTMLDivElement>(null);\n\n // Where it lives, and how it gets moved. The button while it is closed, the\n // panel by its own header once it is open: one position, held by whichever\n // end of the widget is on the page at the time. A modal is never dragged,\n // since it is nowhere in particular: only its button is.\n const drag = useLauncherDrag({\n boxRef,\n buttonRef: launcherRef,\n margin: MARGIN,\n enabled: isModal ? hasBubble : isOpen || hasBubble,\n subject: !isModal && isOpen ? \"panel\" : \"button\",\n });\n\n // On a phone the open panel is the screen, not a card: it leaves the box,\n // covers the viewport, and follows the keyboard. The button and the box it\n // lives in never hear about any of this. The modal keeps its own sheet,\n // measured against its own layer rather than this box, so here the\n // question is only ever asked about the corner.\n const cornerSheet = useWidgetSheet({\n boxRef,\n panelRef,\n isOpen: isOpen && !isModal,\n });\n const isSheet = !isModal && cornerSheet;\n\n // The card over the button, when there is one to show and a button to show\n // it on. Which side of the button is measured, since a button dragged to\n // the top of the window has no room over it.\n const hasTeaser = teaser !== null && hasBubble;\n const teaserPlacement = useTeaserPlacement({\n buttonRef: launcherRef,\n enabled: hasTeaser,\n isMoving: drag.isMoving,\n });\n // Whatever had focus when the panel came up, so closing can hand it back.\n // Our own button, usually; the host's own trigger when there is no button.\n const opener = useRef<HTMLElement | null>(null);\n // Focus only moves once the panel has actually changed state. Without this a\n // widget that starts open would steal focus on page load.\n const wasOpen = useRef(isOpen);\n\n const setOpen = useCallback(\n (next: boolean) => {\n if (open === undefined) setUncontrolledOpen(next);\n onOpenChange?.(next);\n },\n [open, onOpenChange],\n );\n\n const close = useCallback(() => setOpen(false), [setOpen]);\n\n // Taking somebody to a page they cannot see is not taking them anywhere.\n // As a sheet or a modal the panel is over the whole page, so it goes down\n // on the way out; as a card in the corner it was never in the way, and\n // closing it would throw away the conversation that asked the question.\n const follow = useCallback(\n (destination: SupportDestination) => {\n onFollow?.(destination);\n if (isSheet || isModal) close();\n },\n [onFollow, isSheet, isModal, close],\n );\n\n // The panel is up, so the renderer is wanted now whether or not anything\n // touched the button on the way. A host opening it from their own nav never\n // does, and neither does a phone.\n useEffect(() => {\n if (isOpen) warmMarkdown();\n }, [isOpen]);\n\n // Escape closes, wherever focus happens to be inside the panel.\n useEffect(() => {\n if (!isOpen) return;\n\n function handleKeyDown(event: KeyboardEvent) {\n if (event.key === \"Escape\") close();\n }\n\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [isOpen, close]);\n\n // Opening moves focus into the panel rather than onto the composer, so a\n // keyboard carries on from here without a phone throwing up its keyboard.\n // Closing hands it back where it came from.\n //\n // In an effect rather than in the handlers, because whichever end focus is\n // going to is still `inert` until the state that opened or closed it has\n // rendered, and focusing an inert element does nothing at all. It also has to\n // run for a host driving `open` themselves, who never touches a handler here.\n useEffect(() => {\n if (wasOpen.current === isOpen) return;\n wasOpen.current = isOpen;\n\n if (isOpen) {\n const active = document.activeElement;\n opener.current = active instanceof HTMLElement ? active : null;\n // Without `preventScroll` the browser scrolls the host's page to bring\n // the panel into view, and a panel that covers the screen is already in\n // view: all it does is drag the page out from under the sheet.\n panelRef.current?.focus({ preventScroll: true });\n return;\n }\n\n const back = launcherRef.current ?? opener.current;\n opener.current = null;\n // Only somewhere focus can actually land, and only if it is still on the\n // page: a menu that closed behind the panel is not there to take it back,\n // and leaving focus where it is beats throwing it at the body.\n if (back && back !== document.body && back.isConnected) back.focus();\n }, [isOpen]);\n\n // The panel itself, the same whichever box it is drawn in. Told whether it\n // is a sheet right now, because the corner and the modal each answer that\n // for themselves.\n const widget = (isFullScreen: boolean) => (\n <SupportWidget\n presence={presence}\n messages={messages}\n onSend={onSend}\n onTypingChange={onTypingChange}\n onClose={close}\n isExpanded={isExpanded}\n // A sheet is already every pixel there is, so a control that asks\n // for more would be a lie with an icon. A modal has its size already.\n onToggleExpanded={\n isFullScreen || isModal\n ? undefined\n : () => setExpanded((current) => !current)\n }\n isLoading={isLoading}\n isLocked={isLocked}\n onSignIn={onSignIn}\n askForEmail={askForEmail}\n onSubmitEmail={onSubmitEmail}\n isSending={isSending}\n awaitingHuman={awaitingHuman}\n rating={rating}\n offer={offer}\n // Passed on only when there is somewhere for a press to go, so a\n // panel that cannot move anybody makes no offers.\n onFollow={onFollow === undefined ? undefined : follow}\n screenRequest={screenRequest}\n notice={notice}\n tickets={tickets}\n continuesByEmail={continuesByEmail}\n showBadge={showBadge}\n // The header is the handle. Same four handlers the button wears,\n // and only one of the two is ever reachable at a time. A sheet has\n // nowhere to be dragged to, and neither does a modal, so there it is\n // only a header.\n grabHandlers={isFullScreen || isModal ? undefined : drag.dragHandlers}\n isMoving={drag.isMoving}\n // Its own border and its own radius, which the clip is cut to match.\n // The panel is a card sitting on somebody else's page, and a card\n // here has an edge like a card anywhere else in the app. Unless it\n // is the screen, which has neither.\n className={isFullScreen ? SHEET_PANEL : undefined}\n // A modal has room to spare, so it takes the denser scale. The corner\n // keeps the phone sized one it has always had.\n compact={isModal}\n />\n );\n\n return (\n <>\n {/* The box is panel sized whether it is open or not, so it never lets a\n click through to the host's page. Only what is actually on screen\n takes pointer events back. As a modal's home it is the button's box\n and nothing else, and stays panel sized so the card over the button\n has the same room it always had. */}\n <div\n ref={boxRef}\n // The hook the published stylesheet scopes its resets to. On a page\n // without Tailwind there is no preflight, so the package ships one that\n // reaches no further than this box and its siblings below.\n data-median-widget=\"\"\n style={{\n width: isExpanded ? expandedWidth : width,\n height: isExpanded ? expandedHeight : height,\n ...drag.style,\n }}\n className={cn(\n \"pointer-events-none fixed z-50\",\n // Twice the margin, so the panel is inset by the same gap on both\n // sides however close to the edge the button has been put.\n //\n // Against whatever is positioning it as well as against the window,\n // which are the same thing for a widget on a host's page and are not\n // for one pinned inside a frame. Expanded is what finds the\n // difference: it is bigger than some of the boxes this turns up in,\n // and a panel hanging out the side of its own preview is not a panel\n // anybody is going to trust on their site.\n \"max-h-[min(calc(100svh-2rem),calc(100%-2rem))] max-w-[min(calc(100vw-2rem),calc(100%-2rem))]\",\n BOX,\n // Closed with no button on it, there is nothing here to see. The panel\n // stays mounted and stays `inert`, so the conversation is still live\n // and nothing behind it can be tabbed into by mistake.\n !hasBubble && !isOpen && \"opacity-0\",\n // The body wears the closed hand while a drag is on, which covers the\n // host's page. It does not reach in here, where the composer asks for\n // a caret and every button asks for a pointer, so this says it again\n // for everything inside.\n drag.isMoving && \"cursor-grabbing **:cursor-grabbing\",\n className,\n )}\n >\n {/* The shadow, on an empty layer of its own behind the panel.\n\n It cannot go on the panel, because a filter is painted before a clip\n and would be cut away with everything else. It also cannot go on a\n parent of the panel, which is where it used to be: a filter anywhere\n above a scrolling area stops the browser scrolling it the cheap way\n and makes it re-paint the whole thread on every frame instead, which\n is what made the thread stutter.\n\n So it goes on a sibling with nothing inside it. Same shape, same\n timing, no children to re-paint. */}\n {/* Not when the panel is a sheet: a full screen has no edges to throw a\n shadow past, and the closed circle it would leave in the corner sits\n entirely under the button, which carries a shadow of its own. */}\n {!isSheet && !isModal && (\n <div aria-hidden className=\"absolute inset-0 drop-shadow-xl\">\n <div\n style={shape(isOpen, buttonSize, drag.side, hasBubble)}\n className={cn(\n \"size-full bg-card\",\n hasBubble ? MORPH : ARRIVE,\n drag.isMoving && \"transition-none\",\n )}\n />\n </div>\n )}\n\n {!isModal && (\n <div\n ref={panelRef}\n tabIndex={-1}\n inert={!isOpen}\n // What the parts inside read to know when to settle into place.\n // See `widget-settle.ts`.\n data-state={isOpen ? \"open\" : \"closed\"}\n // As a sheet it wears no clip at all: it fades and rises as one\n // piece, and the keyboard fit writes its edges straight onto the\n // node.\n style={\n isSheet\n ? { transform: isOpen ? \"none\" : SHEET_RISEN }\n : shape(isOpen, buttonSize, drag.side, hasBubble)\n }\n className={cn(\n \"absolute inset-0 outline-none\",\n isSheet\n ? [SHEET, !isOpen && \"opacity-0\"]\n : [\n hasBubble ? MORPH : ARRIVE,\n // While it is travelling, the clip changes corners and\n // there is nothing to animate about that. Transitioning it\n // wipes the panel across the box on the way from one\n // circle to the other.\n drag.isMoving && \"transition-none\",\n ],\n isOpen && \"pointer-events-auto\",\n )}\n >\n {widget(isSheet)}\n </div>\n )}\n\n {/* A sibling rather than a child of the clip, so its focus ring is not\n clipped away with it. Gone entirely when the host is doing the\n opening: an invisible button in the corner of somebody else's page\n would still be there to tab into. */}\n {hasBubble && (\n <button\n ref={launcherRef}\n type=\"button\"\n {...drag.dragHandlers}\n draggable={false}\n // The first sign anybody wants the panel. Fetching the renderer\n // here rather than on the click buys it the time between reaching\n // for the button and pressing it, which is usually all it needs.\n onPointerEnter={warmMarkdown}\n onFocus={warmMarkdown}\n onClick={() => {\n // The pointerup that ended a drag still arrives here as a click.\n // Opening on it would mean the panel came up every time somebody\n // moved the button, which is the one thing they did not ask for.\n if (drag.swallowClick()) return;\n setOpen(true);\n }}\n aria-expanded={isOpen}\n aria-label={\n unreadCount > 0\n ? `Chat with ${presence.name} support, ${unreadCount} unread`\n : `Chat with ${presence.name} support`\n }\n inert={isOpen}\n style={{\n width: buttonSize,\n height: buttonSize,\n // The same height the clip is cut at, from the same variable, so\n // the button and the circle under it cannot end up a pixel apart.\n top: buttonY(buttonSize),\n [drag.side]: 0,\n }}\n className={cn(\n \"absolute grid place-items-center rounded-full\",\n \"bg-primary text-primary-foreground shadow-lg outline-none\",\n // Struck off its own foreground rather than the border token. The\n // button is the inverse of the page it sits on, so `border` is a\n // colour picked to separate a card from the background and lands\n // invisible here: white at a tenth, over white. This reads as a\n // rim in both, because it is a tenth of whatever the icon is.\n \"border border-primary-foreground/15\",\n // Named properties rather than `all`, and the names matter.\n //\n // Its height inside the box is a real length now, and it changes\n // the instant a drag is committed. Under `transition-all` that is\n // an animatable property, so the button eased to its new height\n // over three hundred milliseconds after a two hundred millisecond\n // delay, while the clipped circle it is supposed to be sitting on\n // moved immediately. Half a second of a button beside its own\n // shadow rather than on it.\n //\n // Nothing about where it is should ever animate. The box carries\n // it, in one animation, and that is the only travel there is.\n \"transition-[opacity,transform,filter] motion-reduce:transition-none\",\n \"hover:brightness-125 focus-visible:ring-3 focus-visible:ring-ring/50\",\n // The browser must not claim the gesture for scrolling, or a drag\n // on a phone moves the page instead of the button.\n \"touch-none\",\n isOpen\n ? // Gone before the clip has opened far enough to show what is\n // underneath it.\n \"scale-90 opacity-0 duration-150 ease-out-quint\"\n : \"pointer-events-auto scale-100 opacity-100 delay-200 duration-300 ease-spring\",\n // Nothing on it is selectable, and it is not a thing the browser\n // may pick up and drag as an image either. Both of those end the\n // same way: the gesture becomes the browser's instead of ours.\n \"select-none\",\n // No scale while it is being dragged, and not only because the\n // press-in belongs to a tap. A scaled button measures scaled, and\n // every sum here starts from where its edges are.\n // A pointer at rest, not a grab hand. The button is a button first\n // and a thing you can move second, and hovering it should say the\n // first of those. The grab shows up once one is actually underway.\n !isOpen &&\n (drag.isMoving ? \"cursor-grabbing\" : \"cursor-pointer active:scale-95\"),\n )}\n >\n <ChatCircleDots className=\"size-7\" />\n\n {/* What arrived while they were not looking. The count is already in\n the button's own label, so this is only the picture of it.\n\n Struck off `background` rather than a fixed white, so it inverts\n with the theme and stays legible on the red either way. */}\n {unreadCount > 0 && (\n <span\n aria-hidden\n className={cn(\n \"absolute -top-0.5 -right-0.5 grid min-w-5 place-items-center rounded-full px-1\",\n \"bg-destructive text-xs font-semibold text-background tabular-nums\",\n \"ring-2 ring-card\",\n \"animate-in duration-300 ease-spring fade-in-0 zoom-in-50 motion-reduce:animate-none\",\n )}\n >\n {unreadCount > 9 ? \"9+\" : unreadCount}\n </span>\n )}\n </button>\n )}\n\n {/* The newest unread reply, as a card on the button. A sibling of the\n clip for the same reason the button is, and after it so it sits\n over everything else in the box. It hides itself the moment the\n panel is opening, since the panel is about to show the same words\n in full. */}\n {hasBubble && (\n <WidgetTeaser\n message={teaser}\n isShown={hasTeaser && !isOpen}\n side={drag.side}\n placement={teaserPlacement}\n buttonY={buttonY(buttonSize)}\n buttonSize={buttonSize}\n onOpen={() => setOpen(true)}\n onDismiss={() => onDismissTeaser?.()}\n />\n )}\n </div>\n\n {/* The panel, in the middle of the screen. A sibling of the box rather\n than a child, since it answers to the window and the box answers to\n wherever the host put the button. The focus and escape handling\n above reach it through the same ref the corner's panel wears. */}\n {isModal && (\n <SupportModal\n isOpen={isOpen}\n onClose={close}\n panelRef={panelRef}\n label={`${presence.name} support`}\n >\n {widget}\n </SupportModal>\n )}\n </>\n );\n}\n","/**\n * Where the widget lives, and everything that follows from it.\n *\n * A side and a height. The button snaps to the left or the right edge, because\n * an edge is where a thing parked on somebody's page belongs, and keeps\n * whatever height it was let go at, because that is the half nobody can guess\n * for them: the button is in the way of a cart, a banner or a footer, and only\n * the person looking at it knows which.\n *\n * The height is kept as a fraction of the travel rather than a number of\n * pixels, so a window that changes size moves it proportionally instead of\n * stranding it off the bottom.\n */\n\nexport type LauncherSide = \"left\" | \"right\";\n\n/** `y` runs 0 at the top of its travel to 1 at the bottom. */\nexport type LauncherSpot = { side: LauncherSide; y: number };\n\nexport const DEFAULT_SPOT: LauncherSpot = { side: \"right\", y: 1 };\n\nexport type Bounds = { left: number; top: number; width: number; height: number };\nexport type Size = { width: number; height: number };\n\nfunction clamp(value: number, low: number, high: number): number {\n return Math.min(Math.max(value, low), Math.max(low, high));\n}\n\n/** How far the button can travel between the top margin and the bottom one. */\nexport function travelOf(bounds: Bounds, button: Size, margin: number): number {\n return Math.max(0, bounds.height - 2 * margin - button.height);\n}\n\n/** The spot a button that landed here belongs to. */\nexport function spotFrom(\n at: { x: number; y: number },\n bounds: Bounds,\n button: Size,\n margin: number,\n): LauncherSpot {\n const travel = travelOf(bounds, button, margin);\n\n return {\n side:\n at.x + button.width / 2 < bounds.left + bounds.width / 2\n ? \"left\"\n : \"right\",\n y: travel === 0 ? 0 : clamp((at.y - bounds.top - margin) / travel, 0, 1),\n };\n}\n\n/**\n * The spot a panel dropped here belongs to.\n *\n * The other way round from the one above: an open panel is dragged by its own\n * header, so what landed somewhere is the box, not the button. The box hangs\n * upward from the button, so the button is at the box's bottom edge, and that\n * is the height the spot is a fraction of.\n *\n * The two do not cover the same range, and should not. A panel dragged to the\n * top of a short window still leaves its button at the panel's bottom edge,\n * which is exactly where the bubble should be waiting when it closes.\n */\nexport function spotFromPanel(\n at: { x: number; y: number },\n bounds: Bounds,\n button: Size,\n box: Size,\n margin: number,\n): LauncherSpot {\n const travel = travelOf(bounds, button, margin);\n const buttonY = at.y + box.height - button.height;\n\n return {\n side:\n at.x + box.width / 2 < bounds.left + bounds.width / 2 ? \"left\" : \"right\",\n y: travel === 0 ? 0 : clamp((buttonY - bounds.top - margin) / travel, 0, 1),\n };\n}\n\n/**\n * Everything the widget is positioned by, worked out from a spot.\n *\n * The box is panel sized and hangs upward from the button, which is where it\n * hung when this was a corner. Clamped so it never leaves the space it is in:\n * a button near the top drags the panel down below it instead, and the button\n * simply sits further up inside the same box.\n */\nexport function metricsFor(\n spot: LauncherSpot,\n bounds: Bounds,\n button: Size,\n boxHeight: number,\n margin: number,\n): { buttonY: number; boxTop: number; buttonInBox: number } {\n const buttonY = bounds.top + margin + spot.y * travelOf(bounds, button, margin);\n\n const highest = bounds.top + margin;\n const lowest = bounds.top + bounds.height - margin - boxHeight;\n const boxTop = clamp(buttonY + button.height - boxHeight, highest, lowest);\n\n return { buttonY, boxTop, buttonInBox: buttonY - boxTop };\n}\n\n/**\n * The closed shape: a circle the size of the button, at the button's own\n * height on the side it is parked against.\n *\n * `buttonY` is handed in as a CSS length rather than a number, so the same\n * function writes the resting shape from a variable the layout has filled in\n * and the fallback shape from a plain calc.\n */\nexport function closedClip(\n side: LauncherSide,\n buttonY: string,\n buttonSize: string,\n): string {\n const far = `calc(100% - ${buttonSize})`;\n\n return [\n `inset(${buttonY}`,\n side === \"left\" ? far : \"0px\",\n `calc(100% - ${buttonY} - ${buttonSize})`,\n side === \"left\" ? \"0px\" : far,\n `round calc(${buttonSize} / 2))`,\n ].join(\" \");\n}\n\n/** The point the scale grows from, so the circle stays under the button. */\nexport function clipOrigin(\n side: LauncherSide,\n buttonY: string,\n buttonSize: string,\n): string {\n return `${side === \"left\" ? \"0%\" : \"100%\"} calc(${buttonY} + ${buttonSize} / 2)`;\n}\n\n/**\n * Where this browser last left it.\n *\n * A small store rather than a bare read, the same shape `session.ts` uses and\n * for the same two reasons. The server cannot know, so it renders the default\n * and the store hands the real answer over on the client without a mismatched\n * attribute in between; and a snapshot has to be stable across calls, which is\n * what the cache is for.\n */\nconst STORAGE_KEY = \"median:launcher-spot\";\n\nconst listeners = new Set<() => void>();\nlet current: LauncherSpot | null = null;\n\nexport function subscribeSpot(listener: () => void) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\nexport function getSpot(): LauncherSpot {\n if (current !== null) return current;\n\n current = DEFAULT_SPOT;\n try {\n const stored = window.localStorage.getItem(STORAGE_KEY);\n const parsed: unknown = stored === null ? null : JSON.parse(stored);\n if (\n typeof parsed === \"object\" &&\n parsed !== null &&\n \"side\" in parsed &&\n \"y\" in parsed\n ) {\n const { side, y } = parsed as { side: unknown; y: unknown };\n if ((side === \"left\" || side === \"right\") && typeof y === \"number\") {\n current = { side, y: clamp(y, 0, 1) };\n }\n }\n } catch {\n // Blocked storage, or something else's key with our name on it. Either\n // way the default is a perfectly good place for a support button.\n }\n return current;\n}\n\nexport function getDefaultSpot(): LauncherSpot {\n return DEFAULT_SPOT;\n}\n\nexport function setSpot(spot: LauncherSpot): void {\n current = spot;\n\n try {\n window.localStorage.setItem(STORAGE_KEY, JSON.stringify(spot));\n } catch {\n // It stays where they put it until the page goes away, which is the same\n // deal the session token gets.\n }\n\n for (const listener of listeners) listener();\n}\n","\"use client\";\n\nimport { useState } from \"react\";\nimport { Collapse } from \"@/components/ui/collapse\";\nimport {\n MessageScroller,\n MessageScrollerButton,\n MessageScrollerContent,\n MessageScrollerItem,\n MessageScrollerProvider,\n MessageScrollerViewport,\n} from \"@/components/ui/message-scroller\";\nimport { groupMessages, withinRun } from \"@/lib/message-groups\";\nimport { cn } from \"@/lib/utils\";\nimport type { DragHandlers } from \"./use-launcher-drag\";\nimport { useStackShift } from \"./use-stack-shift\";\nimport { SETTLE_BODY, SETTLE_HEADER } from \"./widget-settle\";\nimport { WidgetComposer } from \"./widget-composer\";\nimport { WIDGET_CORNER } from \"./widget-corner\";\nimport { WidgetDestination } from \"./widget-destination\";\nimport { WidgetEmpty } from \"./widget-empty\";\nimport { WidgetHeader } from \"./widget-header\";\nimport { WidgetHistory } from \"./widget-history\";\nimport { WidgetLoading } from \"./widget-loading\";\nimport { WidgetLocked } from \"./widget-locked\";\nimport { WidgetDivider, WidgetMessage } from \"./widget-message\";\nimport { WidgetRating } from \"./widget-rating\";\nimport { WidgetScreenRequest } from \"./widget-screen-request\";\nimport { WidgetAwaitingHuman, WidgetByEmail } from \"./widget-status\";\nimport type {\n SupportDestination,\n SupportFollow,\n SupportMessage,\n SupportPresence,\n SupportRating,\n SupportScreenRequest,\n SupportTickets,\n} from \"./types\";\n\n/**\n * What keeps a run going: the same voice, close together, and no divider\n * cutting in. The agent is compared by id rather than by side, so a handover\n * from the AI to a person always starts a fresh run and says so. A new\n * conversation always does too, whoever is speaking either side of it.\n */\nfunction joinsPreviousTurn(message: SupportMessage, previous: SupportMessage) {\n return (\n !message.dayLabel &&\n !message.startsConversation &&\n message.sender === previous.sender &&\n message.agent?.id === previous.agent?.id &&\n withinRun(previous.sentAt, message.sentAt)\n );\n}\n\n/**\n * The support panel a visitor sees on a host's site: who is around, the\n * thread, and a box to write in.\n *\n * It fills whatever it is given rather than picking its own size, so the same\n * component works as a floating bubble on desktop, a full screen sheet on a\n * phone, and a framed preview in the gallery. Whoever mounts it decides the\n * box; this decides what goes in it.\n *\n * Uncontrolled on purpose: `messages` come in, `onSend` goes out, and nothing\n * here holds a thread of its own. The one thing it does hold is whether the\n * history is up, which is the panel's business and nobody else's.\n */\nexport function SupportWidget({\n presence,\n messages,\n onSend,\n onTypingChange,\n onClose,\n isExpanded = false,\n onToggleExpanded,\n isLoading = false,\n isLocked = false,\n onSignIn,\n askForEmail = false,\n onSubmitEmail,\n isSending = false,\n awaitingHuman = false,\n rating,\n offer = null,\n onFollow,\n screenRequest = null,\n notice,\n tickets = null,\n continuesByEmail = false,\n showBadge = true,\n grabHandlers,\n isMoving = false,\n compact = false,\n className,\n}: {\n presence: SupportPresence;\n messages: SupportMessage[];\n /** Awaited by the composer, which keeps the draft if it rejects. */\n onSend?: (body: string, files: File[]) => unknown;\n /** The visitor's own typing, throttled by the composer. */\n onTypingChange?: (isTyping: boolean) => unknown;\n /** Omit to hide the close button, for a widget that is always on the page. */\n onClose?: () => void;\n /** Standing taller than it normally does. */\n isExpanded?: boolean;\n /**\n * Omit to hide the control. Only whoever owns the box the panel is drawn in\n * can grow it, so a widget that fills a frame it was given does not offer it.\n */\n onToggleExpanded?: () => void;\n /** Holds the mark while the conversation is on its way. */\n isLoading?: boolean;\n /**\n * Nobody is signed in, on a host that only takes questions from people who\n * are. The thread and the composer make way for the invitation to sign in.\n */\n isLocked?: boolean;\n /** Takes the visitor to the host's own sign in, and gives it its button. */\n onSignIn?: () => void;\n /**\n * The composer asks for an email before a first message, and the starter\n * rows stand down while it does: a pick would send before the question\n * was answered.\n */\n askForEmail?: boolean;\n /** What the composer's question does with a good answer. */\n onSubmitEmail?: (email: string) => void;\n /** Holds the send button while a message is. */\n isSending?: boolean;\n /** The agent has called for a person and nobody has picked it up yet. */\n awaitingHuman?: boolean;\n /**\n * The conversation is over and nobody has asked how it went yet. Null the\n * rest of the time, which is most of it.\n */\n rating?: SupportRating | null;\n /**\n * A page one of the team's tools offered with no message to hang it on,\n * which is a teammate running a tool themselves. It stands over the\n * composer while the offer is worth making.\n */\n offer?: SupportDestination | null;\n /**\n * What the buttons on those offers do. Omit it and none of them are shown:\n * this is the only way the panel can move anybody.\n */\n onFollow?: SupportFollow;\n /**\n * The agent asking to see their screen: a button to share it and one to\n * say no, over the composer while the ask is open.\n */\n screenRequest?: SupportScreenRequest | null;\n /** One line above the composer, for a send that went wrong. */\n notice?: string | null;\n /**\n * The visitor's other conversations, and moving between them. Omit for a\n * panel that only ever shows one, and the header offers neither button.\n */\n tickets?: SupportTickets | null;\n /**\n * The conversation on screen is answered from the mail client, so there\n * is no composer under it, only a line saying where the replies go.\n */\n continuesByEmail?: boolean;\n showBadge?: boolean;\n /**\n * Makes the header the thing the panel is dragged around by. Omitted by\n * anything that frames the widget rather than floats it, which is most of\n * the places it turns up outside a host's page.\n */\n grabHandlers?: DragHandlers;\n /** Being dragged right now, so the header can hold the closed hand. */\n isMoving?: boolean;\n /**\n * A denser scale: smaller type, tighter rows, the composer as a box. For\n * a panel with room to spare, where the corner's phone sized type reads\n * as oversized. The corner never sets it.\n */\n compact?: boolean;\n className?: string;\n}) {\n const stack = useStackShift(messages.length);\n\n // Whether the history is up, and the ticket it is waiting on: a pick keeps\n // it up until that ticket has arrived, then it gives way to it.\n const [isHistoryOpen, setHistoryOpen] = useState(false);\n const [openingId, setOpeningId] = useState<string | null>(null);\n // Whether it arrived or turned out to be gone, the wait is over either way.\n if (openingId !== null && tickets !== null && !tickets.isOpening) {\n setOpeningId(null);\n setHistoryOpen(false);\n }\n\n const history =\n tickets === null || isLocked\n ? undefined\n : {\n isOpen: isHistoryOpen,\n onToggle: () => {\n setOpeningId(null);\n setHistoryOpen((current) => !current);\n },\n onStartNew: () => {\n setOpeningId(null);\n setHistoryOpen(false);\n tickets.startNew();\n },\n hasUnread: tickets.list.some(\n (ticket) => ticket.id !== tickets.currentId && ticket.unreadCount > 0,\n ),\n isFresh: tickets.currentId === null && messages.length === 0,\n };\n\n function pick(id: string) {\n if (tickets === null) return;\n if (id === tickets.currentId) {\n setHistoryOpen(false);\n return;\n }\n setOpeningId(id);\n tickets.open(id);\n }\n\n return (\n <section\n aria-label={`${presence.name} support`}\n className={cn(\n // The widget keeps its own radius scale. The app's token can climb as\n // the chrome gets rounder without the widget changing shape under the\n // hosts embedding it.\n `flex h-full w-full flex-col overflow-hidden ${WIDGET_CORNER} border bg-card text-card-foreground [--radius:0.625rem]`,\n // And its own pace for a reply arriving a line at a time: a step\n // quicker than the palette's ladder, since a support answer is a\n // few short lines in a bubble rather than an essay in a column.\n \"[--duration-line-rise:360ms] [--duration-line-beat:45ms]\",\n className,\n )}\n >\n <WidgetHeader\n presence={presence}\n onClose={onClose}\n isExpanded={isExpanded}\n onToggleExpanded={onToggleExpanded}\n grabHandlers={grabHandlers}\n isMoving={isMoving}\n history={history}\n compact={compact}\n className={SETTLE_HEADER}\n />\n\n {isLoading ? (\n <WidgetLoading />\n ) : isLocked ? (\n /* After the loading rather than before it: the line names the host,\n so it waits for the name the same as everything else does. */\n <WidgetLocked\n orgName={presence.name}\n onSignIn={onSignIn}\n showBadge={showBadge}\n />\n ) : (\n // The history lies over the conversation in the same box, so the\n // conversation keeps its place and its scroll underneath.\n <div className=\"relative flex min-h-0 flex-1 flex-col\">\n <Conversation\n // Drawn afresh for another ticket or a new one, never for a reply.\n key={tickets?.viewKey ?? 0}\n presence={presence}\n messages={messages}\n onSend={onSend}\n onTypingChange={onTypingChange}\n askForEmail={askForEmail}\n onSubmitEmail={onSubmitEmail}\n isSending={isSending}\n awaitingHuman={awaitingHuman}\n rating={rating}\n offer={offer}\n onFollow={onFollow}\n screenRequest={screenRequest}\n notice={notice}\n continuesByEmail={continuesByEmail}\n showBadge={showBadge}\n compact={compact}\n stackRef={stack}\n />\n {history && tickets !== null && (\n <WidgetHistory\n tickets={tickets}\n isOpen={isHistoryOpen}\n openingId={openingId}\n onPick={pick}\n onClose={() => setHistoryOpen(false)}\n compact={compact}\n />\n )}\n </div>\n )}\n </section>\n );\n}\n\n/**\n * Everything under the header. Split out so it mounts as one thing when the\n * conversation lands, which is what gives the whole lot a single fade in\n * rather than a thread and a composer arriving separately.\n */\nfunction Conversation({\n presence,\n messages,\n onSend,\n onTypingChange,\n askForEmail = false,\n onSubmitEmail,\n isSending = false,\n awaitingHuman = false,\n rating,\n offer = null,\n onFollow,\n screenRequest = null,\n notice,\n continuesByEmail,\n showBadge,\n compact,\n stackRef,\n}: {\n presence: SupportPresence;\n messages: SupportMessage[];\n onSend?: (body: string, files: File[]) => unknown;\n onTypingChange?: (isTyping: boolean) => unknown;\n /** The composer asks for an email before a first message. */\n askForEmail?: boolean;\n /** What the composer's question does with a good answer. */\n onSubmitEmail?: (email: string) => void;\n isSending?: boolean;\n awaitingHuman?: boolean;\n rating?: SupportRating | null;\n /** A page offered with no message to hang it on. */\n offer?: SupportDestination | null;\n /** What every offer's button does, if the panel can move anybody at all. */\n onFollow?: SupportFollow;\n /** The agent asking to see their screen. */\n screenRequest?: SupportScreenRequest | null;\n notice?: string | null;\n /** Answered by email: a line where the composer would be. */\n continuesByEmail: boolean;\n showBadge: boolean;\n compact: boolean;\n stackRef: (node: HTMLDivElement | null) => void;\n}) {\n const groups = groupMessages(messages, joinsPreviousTurn);\n\n // Which messages were already here when the thread was first drawn. Those\n // sit still; anything after them landed in front of somebody, and arrives\n // like it.\n const [openedWith] = useState<ReadonlySet<string>>(\n () => new Set(messages.map((message) => message.id)),\n );\n\n // The last offer and the last notice, held through their own fold: by the\n // time either is closing, whoever passed it has stopped, and a section\n // folding shut around nothing has no height to fold.\n const [heldOffer, setHeldOffer] = useState(offer);\n if (offer !== null && offer.key !== heldOffer?.key) setHeldOffer(offer);\n const [heldRequest, setHeldRequest] = useState(screenRequest);\n if (screenRequest !== null && screenRequest !== heldRequest) {\n setHeldRequest(screenRequest);\n }\n const [heldNotice, setHeldNotice] = useState(notice ?? null);\n if (notice && notice !== heldNotice) setHeldNotice(notice);\n\n return (\n <div\n className={cn(\n \"flex min-h-0 flex-1 flex-col animate-in fade-in-0 duration-500 ease-out motion-reduce:animate-none\",\n SETTLE_BODY,\n )}\n >\n {messages.length === 0 ? (\n <WidgetEmpty\n team={presence.team}\n orgName={presence.name}\n onPick={\n onSend === undefined\n ? undefined\n : (message) => void onSend(message, [])\n }\n isSending={isSending}\n // Held rather than taken away while the email is asked for: a row\n // would send before the question above it was answered, and rows\n // that leave and come back move everything above them twice.\n isWaiting={askForEmail}\n compact={compact}\n />\n ) : (\n /* The same scroller the inbox thread uses, so both sides of a\n conversation stick to the newest message the same way, and both grow\n the same button when you scroll back up.\n\n It keeps following the bottom while the panel is resizing, and it\n has to. A panel shrinking takes its top edge down past the thread,\n so a scroller left alone slides the newest message under the\n composer and has to catch up afterwards. Following it is what the\n scroller stops easing for, so this tracks frame by frame. */\n <MessageScrollerProvider>\n {/* h-auto so the scroller takes the space the header and composer\n leave, rather than the full column its own `size-full` asks for. */}\n <MessageScroller className=\"h-auto min-h-0 flex-1\">\n <MessageScrollerViewport\n // A log with a polite live region, so a reply is announced as it\n // lands without pulling focus out of the box being typed in.\n role=\"log\"\n aria-live=\"polite\"\n aria-relevant=\"additions\"\n aria-label=\"Conversation\"\n className={compact ? \"px-5 py-3\" : \"px-4 py-3\"}\n >\n {/* justify-end so a short thread sits on the composer rather than\n hanging from the header with a field of nothing under it. The\n content is already at least a viewport tall, so this only\n decides where the slack goes: above the first message, not\n below the last.\n\n The space between messages is on the items rather than here,\n since a message carrying on the one above it sits closer to\n it than one starting a new turn. */}\n <MessageScrollerContent\n ref={stackRef}\n className=\"justify-end gap-0\"\n >\n {messages.map((message, index) => (\n <MessageScrollerItem\n key={message.id}\n messageId={message.id}\n // The scroller skips rendering off screen items and holds\n // 10rem open for each one until it arrives. That is a fair\n // guess in the inbox, where the column is wide and a\n // message is tall. Here it is roughly double: a bubble in a\n // panel this narrow is nearer 4rem, so every item resized\n // the moment it scrolled into view and the whole thread\n // jumped. A support thread is short enough that skipping\n // any of it buys nothing anyway.\n className={cn(\n \"flex flex-col gap-4 [content-visibility:visible] first:pt-0\",\n groups[index].isFirst\n ? compact\n ? \"pt-4\"\n : \"pt-5\"\n : compact\n ? \"pt-1\"\n : \"pt-1.5\",\n )}\n >\n <WidgetDivider message={message} />\n <WidgetMessage\n message={message}\n group={groups[index]}\n onFollow={onFollow}\n compact={compact}\n isLive={!openedWith.has(message.id)}\n />\n </MessageScrollerItem>\n ))}\n </MessageScrollerContent>\n </MessageScrollerViewport>\n\n {/* Outside the viewport: it is positioned against the scroller, so\n inside it would scroll away with the messages. */}\n <MessageScrollerButton />\n </MessageScroller>\n </MessageScrollerProvider>\n )}\n\n {/* The question a finished thread asks. Never at the same time as the\n line below it: one is a conversation that is over and the other is\n one waiting on a person. */}\n {rating && <WidgetRating rating={rating} />}\n\n {/* A page a teammate's own tool offered. It sits here rather than in\n the thread because there is no message it belongs under, and this\n is where the panel puts anything about where the conversation\n stands rather than what was said in it.\n\n Each of the three lines below opens out of the composer and folds\n back into it, so the thread above them moves with the fold rather\n than jumping the height of a line. Always mounted, for that: a\n section that comes and goes cannot fold. */}\n <Collapse\n isOpen={offer !== null && onFollow !== undefined}\n className=\"shrink-0\"\n >\n {heldOffer !== null && onFollow !== undefined && (\n <div className=\"px-4 pb-1.5\">\n <WidgetDestination destination={heldOffer} onFollow={onFollow} />\n </div>\n )}\n </Collapse>\n\n {/* The agent asking to see their screen. Here for the same reason as\n the offer above: it is about where the conversation stands, and\n the answer is a press rather than a message. */}\n <Collapse isOpen={screenRequest !== null} className=\"shrink-0\">\n {heldRequest !== null && (\n <div className=\"px-4 pb-1.5\">\n <WidgetScreenRequest request={heldRequest} />\n </div>\n )}\n </Collapse>\n\n {/* Above the failed send, since it is the older of the two facts and\n the one that is still true after a retry works. */}\n <Collapse isOpen={awaitingHuman} className=\"shrink-0\">\n <WidgetAwaitingHuman />\n </Collapse>\n\n {/* A send that failed says so right where the retry happens. */}\n <Collapse isOpen={Boolean(notice)} className=\"shrink-0\">\n <p className=\"px-4 pb-1.5 text-center text-sm text-destructive\">\n {notice ?? heldNotice}\n </p>\n </Collapse>\n\n {continuesByEmail ? (\n <WidgetByEmail showBadge={showBadge} compact={compact} />\n ) : (\n <WidgetComposer\n onSend={onSend}\n onTypingChange={onTypingChange}\n askForEmail={askForEmail}\n onSubmitEmail={onSubmitEmail}\n isSending={isSending}\n showBadge={showBadge}\n variant={compact ? \"stacked\" : \"pill\"}\n compact={compact}\n />\n )}\n </div>\n );\n}\n","import type { ReactNode } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * How long one takes to open or close. Anything that has to act after a\n * collapse has finished reads this rather than guessing, so the two cannot\n * drift; it matches the `duration-200` below.\n */\nexport const COLLAPSE_MS = 200;\n\n/**\n * A section that opens and closes rather than appearing and disappearing.\n *\n * The height is a grid row travelling between nothing and whatever the\n * contents want to be, which is the one way to animate to `auto` without\n * measuring anything on every frame. Sideways it is the width, a grid column\n * doing the same, for something that joins a row: its neighbours glide over\n * to make room instead of jumping. The contents keep their own size\n * throughout and are clipped rather than squashed, so words never reflow\n * mid animation.\n *\n * Closed, it is inert: a keyboard cannot land in a section that is not on\n * screen, and nothing inside one can be clicked through the clip.\n *\n * No gap between this and its neighbours. A closed section is zero pixels\n * tall but a flex gap around it is not, so spacing belongs inside the\n * contents; otherwise closing one leaves a hole where it used to be.\n */\nexport function Collapse({\n isOpen,\n id,\n direction = \"vertical\",\n children,\n className,\n}: {\n isOpen: boolean;\n /** For whatever toggles it to point at with `aria-controls`. */\n id?: string;\n /** Which way it opens. Sideways for something joining a row. */\n direction?: \"vertical\" | \"horizontal\";\n children: ReactNode;\n className?: string;\n}) {\n const isSideways = direction === \"horizontal\";\n\n return (\n <div\n id={id}\n inert={isOpen ? undefined : true}\n className={cn(\n \"grid duration-200 ease-out motion-reduce:transition-none\",\n isSideways\n ? \"transition-[grid-template-columns,opacity]\"\n : \"transition-[grid-template-rows,opacity]\",\n isSideways\n ? isOpen\n ? \"grid-cols-[1fr]\"\n : \"grid-cols-[0fr]\"\n : isOpen\n ? \"grid-rows-[1fr]\"\n : \"grid-rows-[0fr]\",\n isOpen ? \"opacity-100\" : \"opacity-0\",\n className,\n )}\n >\n <div className=\"overflow-hidden\">{children}</div>\n </div>\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport {\n MessageScroller as MessageScrollerPrimitive,\n useMessageScroller,\n useMessageScrollerScrollable,\n useMessageScrollerVisibility,\n} from \"@shadcn/react/message-scroller\";\n\n// Solar instead of the registry default, per the project icon rule.\nimport { ArrowDown } from \"@/components/icons\";\nimport { Button } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * From the shadcn registry (@shadcn/message-scroller, radix-nova style), with\n * two changes: the icon comes from our own set, and the registry's\n * `scrollbar-*` and `inset-s-*` utilities are written as plain properties,\n * since neither the scrollbar plugin nor logical inset utilities are here.\n *\n * `scrollbar-width` is said here as well as on the app's own `html`, and the\n * repetition is the point: this is the one scroller that also runs inside the\n * widget on somebody else's page, where there is no `html` of ours to inherit\n * from and a thread would otherwise wear whatever their site wears.\n */\n\nfunction MessageScrollerProvider({\n autoScroll = true,\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Provider>) {\n // Every thread here is a chat, so the chat behavior is the default: stay\n // pinned to the newest message while at the bottom, stay put while reading\n // older ones, and let the button offer the way back down. The primitive's\n // own default (off, with per-item scroll anchors) is built for pin-the-\n // question streaming UIs and yanks the viewport around in a live thread.\n return <MessageScrollerPrimitive.Provider autoScroll={autoScroll} {...props} />;\n}\n\nfunction MessageScroller({\n className,\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Root>) {\n return (\n <MessageScrollerPrimitive.Root\n data-slot=\"message-scroller\"\n className={cn(\n \"group/message-scroller relative flex size-full min-h-0 flex-col overflow-hidden\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/** Within this of the foot of the thread counts as being at it. */\nconst AT_END_PX = 8;\n/** Less than a pixel adrift is not worth correcting. */\nconst DRIFT_PX = 1;\n\n/**\n * Holds the newest message against the foot of the viewport while the viewport\n * is changing size.\n *\n * The scroller already does this, one frame late. Both of its resize observers\n * hand their work to `requestAnimationFrame`, so every frame of a resize is\n * painted with the scroll position the frame before it asked for.\n *\n * Growing hides that. A viewport that gets taller leaves its scroll offset out\n * of range, and the browser has to pull it back in as part of the same layout,\n * which lands the thread exactly where the late correction was going to put it\n * anyway. Shrinking has nothing doing that: the offset stays valid, the foot of\n * the viewport comes down past the thread, and the newest message drops a dozen\n * pixels behind and is dragged back a frame later. Every frame. That is the\n * shiver, and it is why only shrinking has it, and why it is worst on a panel\n * parked at the bottom, where the whole of the change comes off the top.\n *\n * So the same correction is made here first, in the observer itself, which is\n * before the frame is painted rather than after.\n */\nfunction usePinnedToEnd(viewport: React.RefObject<HTMLDivElement | null>) {\n React.useEffect(() => {\n const node = viewport.current;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n\n // Whether the thread was against the foot of the viewport, written down as\n // it happens: by the time a resize is reported the new size is already on\n // and the answer to that has changed.\n let wasAtEnd = true;\n const remember = () => {\n wasAtEnd =\n node.scrollHeight - node.scrollTop - node.clientHeight <= AT_END_PX;\n };\n // And how tall it was, since only a viewport that got shorter is one\n // this has to catch up.\n let height = node.clientHeight;\n\n const observer = new ResizeObserver(() => {\n const drift = node.scrollHeight - node.scrollTop - node.clientHeight;\n const shrank = node.clientHeight < height;\n height = node.clientHeight;\n\n // Only ever catching up, never restating. A viewport that grew has had\n // its offset put back in range by the browser already, to a precision\n // this cannot match: `scrollHeight` and `clientHeight` are whole pixels\n // and the box between them is not, so saying it again would replace an\n // exact answer with one up to a pixel out, differently on every frame.\n // And when it grew because the composer gave back its lines as a\n // message landed, the drift is the message, not the box: the scroller\n // is already gliding to it, and a correction here would snap instead.\n if (wasAtEnd && shrank && drift > DRIFT_PX) {\n // Not `auto`, which would take the smooth scrolling below and ease a\n // correction that is meant to be invisible.\n node.scrollTo({\n top: node.scrollHeight - node.clientHeight,\n behavior: \"instant\",\n });\n }\n\n remember();\n });\n\n node.addEventListener(\"scroll\", remember, { passive: true });\n observer.observe(node);\n return () => {\n node.removeEventListener(\"scroll\", remember);\n observer.disconnect();\n };\n }, [viewport]);\n}\n\n/**\n * How quickly the glide closes on where it is going, as a time constant:\n * every millisecond covers the same share of what is left. Short enough to\n * arrive promptly, and the tail is what makes it read as settling rather\n * than stopping. Half a screen is within a pixel in about a third of a\n * second.\n */\nconst GLIDE_MS = 70;\n\n/** A frame that took longer than this is a tab that was asleep, not a long step. */\nconst MAX_STEP_MS = 64;\n\nfunction prefersReducedMotion(): boolean {\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n}\n\n/**\n * The scroller's own scrolls, on the thread's own curve.\n *\n * The primitive pins the thread by calling `scrollTo` on the viewport, and\n * `scroll-behavior: smooth` used to answer that with the browser's animation:\n * a fixed curve that is not ours to time, restarted from rest every time the\n * target moved, which it does on every frame of a bubble growing into a\n * reply. So the method is taken over on the node. Each call says where the\n * thread should be, and one loop carries the viewport there, closing a fixed\n * share of what is left on every frame. A target that moves mid glide is\n * followed from wherever the thread has got to; the speed never resets.\n *\n * Two scrolls go straight to the browser, and neither is worth watching. The\n * first one, since a thread opens at the top and lands on the newest\n * message, and easing that drags the whole history past before you can read\n * any of it. And any one that finds the box narrower or shorter than the\n * last one did: a window dragged, a keyboard coming up, a composer growing a\n * line all re-pin the thread on every frame, and gliding towards a target\n * that moves every frame leaves the messages trailing the box they are in.\n * A box that got taller is different. The browser keeps the thread on the\n * bottom of a growing box by itself, so a request then is about something\n * else, a message landing as the composer gives its lines back, and that\n * one is worth gliding. `instant`, and anyone who asked for less motion, go\n * straight through too.\n *\n * A wheel, a touch or a key under the glide ends it: the reader has taken\n * over where the thread is, and the primitive stops asking on its own.\n */\nfunction useGlidingScroll(\n viewport: React.RefObject<HTMLDivElement | null>,\n hasLanded: boolean,\n) {\n // Read at the moment a scroll is asked for rather than captured, since the\n // first one can come on any frame.\n const landed = React.useRef(hasLanded);\n React.useLayoutEffect(() => {\n landed.current = hasLanded;\n }, [hasLanded]);\n\n React.useEffect(() => {\n const node = viewport.current;\n if (!node) return;\n\n // The prototype's, under one signature, so it can be called back\n // without arguing with the overloads.\n const scrollNatively = Element.prototype.scrollTo as (\n this: Element,\n options?: ScrollToOptions,\n ) => void;\n\n let target: number | null = null;\n let frame = 0;\n let last = 0;\n // The box as the last request found it. See `glide`.\n let box = { width: node.clientWidth, height: node.clientHeight };\n\n const stop = () => {\n if (frame !== 0) cancelAnimationFrame(frame);\n frame = 0;\n target = null;\n };\n\n const step = (now: number) => {\n frame = 0;\n if (target === null) return;\n\n // Never below zero. A frame's timestamp is the frame's start, and a\n // request made partway through that frame is stamped later than it,\n // which would make the first step a step backwards.\n const elapsed = Math.max(0, Math.min(now - last, MAX_STEP_MS));\n last = now;\n // Against the box as it is now, not as it was when asked: a box that\n // grew since then has less room to scroll than the request thought,\n // and a goal past the end is a goal that is never reached.\n const goal = Math.min(target, node.scrollHeight - node.clientHeight);\n const left = goal - node.scrollTop;\n if (Math.abs(left) < 0.5) {\n node.scrollTop = goal;\n target = null;\n return;\n }\n node.scrollTop += left * (1 - Math.exp(-elapsed / GLIDE_MS));\n frame = requestAnimationFrame(step);\n };\n\n const glide = (options?: ScrollToOptions | number, y?: number) => {\n const asked: ScrollToOptions | undefined =\n typeof options === \"number\" ? { left: options, top: y } : options;\n const size = { width: node.clientWidth, height: node.clientHeight };\n const shrank = size.width !== box.width || size.height < box.height;\n box = size;\n const glides =\n asked !== undefined &&\n typeof asked.top === \"number\" &&\n asked.behavior !== \"instant\" &&\n landed.current &&\n !shrank &&\n !prefersReducedMotion();\n\n if (!glides) {\n stop();\n scrollNatively.call(node, asked);\n return;\n }\n\n const furthest = node.scrollHeight - node.clientHeight;\n target = Math.min(Math.max(0, asked.top as number), furthest);\n if (frame === 0) {\n last = performance.now();\n frame = requestAnimationFrame(step);\n }\n };\n\n // On the node itself, in front of the prototype's, and taken off again\n // below.\n Object.defineProperty(node, \"scrollTo\", {\n value: glide,\n configurable: true,\n writable: true,\n });\n\n node.addEventListener(\"wheel\", stop, { passive: true });\n node.addEventListener(\"touchstart\", stop, { passive: true });\n node.addEventListener(\"keydown\", stop);\n return () => {\n stop();\n node.removeEventListener(\"wheel\", stop);\n node.removeEventListener(\"touchstart\", stop);\n node.removeEventListener(\"keydown\", stop);\n // Back to the prototype's, for a node that outlives this.\n Reflect.deleteProperty(node, \"scrollTo\");\n };\n }, [viewport]);\n}\n\n/**\n * True from the frame after the viewport mounts, which is a frame after the\n * scroller has put itself at the newest message.\n */\nfunction useHasLanded() {\n const [hasLanded, setHasLanded] = React.useState(false);\n\n React.useEffect(() => {\n const frame = requestAnimationFrame(() => setHasLanded(true));\n\n return () => cancelAnimationFrame(frame);\n }, []);\n\n return hasLanded;\n}\n\nfunction MessageScrollerViewport({\n className,\n ref,\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Viewport>) {\n const viewport = React.useRef<HTMLDivElement | null>(null);\n\n // Whether the first scroll has happened, which is the one that lands. See\n // `useGlidingScroll` for the rest.\n const hasLanded = useHasLanded();\n\n usePinnedToEnd(viewport);\n useGlidingScroll(viewport, hasLanded);\n\n // Ours as well as whoever else asked for it. The primitive keeps its own\n // separately, so this is only the two of us.\n const holdViewport = React.useCallback(\n (node: HTMLDivElement | null) => {\n viewport.current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref) ref.current = node;\n },\n [ref],\n );\n\n return (\n <MessageScrollerPrimitive.Viewport\n ref={holdViewport}\n data-slot=\"message-scroller-viewport\"\n // Every scroll the scroller makes for itself asks for `auto`, and the\n // glide is what answers: a reply landing while you are at the bottom\n // carries you down rather than teleporting you, and the button travels\n // the distance it is offering to cover.\n //\n // All of them are a pin to the newest message today. The one to watch\n // for is the correction that holds your place when older messages are\n // loaded in above: that one wants to be instant, and it would have to\n // ask for it by name to stay that way.\n className={cn(\n \"size-full min-h-0 min-w-0 overflow-y-auto overscroll-contain contain-content [scrollbar-gutter:stable] [scrollbar-width:thin]\",\n // The fade says \"there is more below\", so it only belongs to a\n // viewport that has more below.\n //\n // The registry hangs `scroll-fade-b` on unconditionally and retracts\n // it with a scroll driven animation. That leaves the newest message\n // dimmed at the foot of every thread: a browser without scroll\n // timelines takes the utility's own fallback and pins the fade on\n // forever, and even where they work the mask is still painted over a\n // thread too short to scroll at all. `data-scrollable` is the same\n // question answered by measurement, and the primitive already writes\n // it here. It drops \"end\" as soon as you are within a few pixels of\n // the bottom, and while it is following the bottom it never claims it.\n \"data-[scrollable~=end]:scroll-fade-b\",\n // The registry also blanks the scrollbar while the scroller is moving\n // on its own. That was covering for the jump: the bar flicked from one\n // place to another with nothing in between. Now it slides, so hiding it\n // is the flicker, and it holds the thumb back for a fixed 180ms that\n // has nothing to do with how far the thread actually travelled.\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction MessageScrollerContent({\n className,\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Content>) {\n return (\n <MessageScrollerPrimitive.Content\n data-slot=\"message-scroller-content\"\n className={cn(\"flex h-max min-h-full flex-col gap-6\", className)}\n {...props}\n />\n );\n}\n\nfunction MessageScrollerItem({\n className,\n scrollAnchor = false,\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Item>) {\n return (\n <MessageScrollerPrimitive.Item\n data-slot=\"message-scroller-item\"\n scrollAnchor={scrollAnchor}\n className={cn(\n \"min-w-0 shrink-0 [contain-intrinsic-size:auto_10rem] [content-visibility:auto]\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction MessageScrollerButton({\n direction = \"end\",\n className,\n children,\n render,\n variant = \"secondary\",\n size = \"icon-sm\",\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Button> &\n Pick<React.ComponentProps<typeof Button>, \"variant\" | \"size\">) {\n return (\n <MessageScrollerPrimitive.Button\n data-slot=\"message-scroller-button\"\n data-direction={direction}\n data-variant={variant}\n data-size={size}\n direction={direction}\n className={cn(\n \"absolute start-1/2 -translate-x-1/2 border-border bg-background text-foreground transition-[translate,scale,opacity] duration-200 hover:bg-muted hover:text-foreground data-[active=false]:pointer-events-none data-[active=false]:scale-95 data-[active=false]:opacity-0 data-[active=false]:duration-400 data-[active=false]:ease-[cubic-bezier(0.7,0,0.84,0)] data-[active=true]:translate-y-0 data-[active=true]:scale-100 data-[active=true]:opacity-100 data-[active=true]:ease-[cubic-bezier(0.23,1,0.32,1)] data-[direction=end]:bottom-4 data-[direction=end]:data-[active=false]:translate-y-full data-[direction=start]:top-4 data-[direction=start]:data-[active=false]:-translate-y-full rtl:translate-x-1/2 data-[direction=start]:[&_svg]:rotate-180\",\n className,\n )}\n render={render ?? <Button variant={variant} size={size} />}\n {...props}\n >\n {children ?? (\n <>\n <ArrowDown />\n <span className=\"sr-only\">\n {direction === \"end\" ? \"Scroll to end\" : \"Scroll to start\"}\n </span>\n </>\n )}\n </MessageScrollerPrimitive.Button>\n );\n}\n\nexport {\n MessageScrollerProvider,\n MessageScroller,\n MessageScrollerViewport,\n MessageScrollerContent,\n MessageScrollerItem,\n MessageScrollerButton,\n useMessageScroller,\n useMessageScrollerScrollable,\n useMessageScrollerVisibility,\n};\n","import*as i from'react';function S({defaultTagName:a,props:r,render:t,state:n={},stateAttributesMapping:e}){let o=d(R(n,e),r);if(!t)return i.createElement(a,o);if(typeof t==\"function\")return t(o,n);if(!i.isValidElement(t))return null;let s=t.props,u={...d(o,s),ref:f(o.ref,s.ref)};return i.cloneElement(t,u)}function d(...a){let r={};for(let t of a){if(!t)continue;let n=t;for(let e of Object.keys(n)){let o=n[e];if(o===void 0)continue;let s=r[e];e===\"className\"?r[e]=[s,o].filter(Boolean).join(\" \"):e===\"style\"?r[e]={...s,...o}:e===\"ref\"?r[e]=f(s,o):l(e)&&typeof s==\"function\"&&typeof o==\"function\"?r[e]=p(o,s):r[e]=o;}}return r}function R(a,r){let t={};for(let n of Object.keys(a)){let e=a[n],o=r?.[n]?.(e);if(o){Object.assign(t,o);continue}if(n===\"slot\"){t[\"data-slot\"]=e;continue}let s=`data-${String(n).replace(/[A-Z]/g,c=>`-${c.toLowerCase()}`)}`;typeof e==\"boolean\"?t[s]=e?\"\":void 0:e!=null&&(t[s]=String(e));}return t}function p(a,r){return function(n){a(n),n.defaultPrevented||r(n);}}function l(a){return /^on[A-Z]/.test(a)}function f(...a){let r=a.filter(Boolean);if(r.length!==0)return t=>{for(let n of r)typeof n==\"function\"?n(t):n&&(n.current=t);}}export{S as a,d as b,f as c};","\"use client\";\nimport {c,a,b}from'../chunk-HBS6WEDP.js';import*as g from'react';import {jsx,jsxs}from'react/jsx-runtime';var xe=8,Ve=64,_e=0,J=.5,ke=180,Be=new Set([\"ArrowDown\",\"ArrowUp\",\"End\",\"Home\",\"PageDown\",\"PageUp\",\" \"]),ge={start:false,end:false},dt=[],Q={currentAnchorId:null,visibleMessageIds:dt};function De({content:e,scrollEdgeThreshold:t,spacer:r,viewport:n}){if(!n||!e)return ge;let o=Se({content:e,spacer:r,viewport:n});return {start:n.scrollTop>t,end:o-n.scrollTop-n.clientHeight>t}}function Fe({content:e,scrollMargin:t,scrollPreviousItemPeek:r,spacer:n,viewport:o,visibleMessageIds:i}){if(!e||!o)return Q;let S=o.getBoundingClientRect(),l=S.top+t+r,d=typeof IntersectionObserver>\"u\",E=[],b=null;for(let C of ee(e,n)){let p=C.dataset.messageId;if(!p)continue;let m=C.dataset.scrollAnchor===\"true\",L=m||d?C.getBoundingClientRect():null;(d&&L?L.bottom>l&&L.top<S.bottom:i.has(p))&&E.push(p),m&&L&&L.top<=l+J&&(b=p);}return E.length===0&&b===null?Q:{currentAnchorId:b,visibleMessageIds:E}}function ee(e,t){return Array.from(e.children).filter(r=>r instanceof HTMLElement&&r!==t)}function je(e,t){for(let r=t;r<e.length;r++){let n=e[r];if(n?.dataset.scrollAnchor===\"true\")return n}return null}function Ge(e,t){for(let r of e)if(r.dataset.scrollAnchor===\"true\"&&!t.has(r))return r;return null}function Ne(e,t){let r=0;for(let n=t;n<e.length;n++)if(e[n]?.dataset.scrollAnchor===\"true\"&&(r+=1,r>1))return true;return false}function Ue(e){for(let t=e.length-1;t>=0;t--){let r=e[t];if(r?.dataset.scrollAnchor===\"true\")return r}return null}function Ye({content:e,spacer:t,viewport:r}){let n=r.getBoundingClientRect();for(let o of ee(e,t)){if(!o.dataset.messageId)continue;let i=o.getBoundingClientRect();if(i.bottom>n.top&&i.top<n.bottom)return o}return null}function ze({align:e,element:t,scrollMargin:r,spacer:n,viewport:o}){let i=pe(t,o),S=t.getBoundingClientRect().height,l=bt(n);if(e===\"center\"){let d=Math.max(0,o.clientHeight-l.start-l.end);return i-l.start-(d-S)/2-r}if(e===\"end\")return i-o.clientHeight+S+l.end+r;if(e===\"nearest\"){let d=i+S,E=o.scrollTop+l.start,b=o.scrollTop+o.clientHeight-l.end;return i>=E&&d<=b?o.scrollTop:i<E?i-l.start-r:d-o.clientHeight+l.end+r}return i-l.start-r}function pe(e,t){let r=e.getBoundingClientRect(),n=t.getBoundingClientRect();return r.top-n.top+t.scrollTop}function te(e,t){return e.getBoundingClientRect().top-t.getBoundingClientRect().top}function qe({content:e,scrollTop:t,spacer:r,viewport:n}){let o=Se({content:e,spacer:r,viewport:n});return t+n.clientHeight-o}function Se({content:e,spacer:t,viewport:r}){let n=ee(e,t),o=We(e),i=r.getBoundingClientRect(),S=r.scrollTop,l=o.start+o.end;for(let d of n){let E=d.getBoundingClientRect();l=Math.max(l,E.bottom-i.top+S+o.end);}return l}function Ke(e){return Math.max(0,e.scrollHeight-e.clientHeight)}function We(e){let t=window.getComputedStyle(e);return {end:Re(t.paddingBlockEnd||t.paddingBottom),start:Re(t.paddingBlockStart||t.paddingTop)}}function bt(e){let t=e?.parentElement;return t?We(t):{end:0,start:0}}function $e(e){if(!e)return 0;let t=window.getComputedStyle(e),r=t.rowGap===\"normal\"?t.gap:t.rowGap;return Re(r)}function Re(e){if(!e)return 0;let t=Number.parseFloat(e);return Number.isFinite(t)?t:0}function Je({refs:e,commitScrollState:t,scheduleStateCommit:r,scheduleVisibilitySync:n}){let{streamingTurnRef:o,autoScrollRef:i,autoscrollingRef:S,autoscrollingTimeoutRef:l,contentRef:d,defaultScrollPositionAppliedRef:E,itemCountRef:b,messageElementsRef:C,modeRef:p,pendingScrollToMessageRef:m,prependRestoreRef:L,scrollMarginRef:y,scrollPreviousItemPeekRef:u,spacerGapRef:x,spacerHeightRef:Y,spacerRef:k,viewportRef:_}=e,G=g.useCallback(c=>{l.current!==null&&(window.clearTimeout(l.current),l.current=null),S.current!==c&&(S.current=c,t()),c&&(l.current=window.setTimeout(()=>{l.current=null,S.current=false,t();},ke));},[t]),v=g.useCallback(c=>{let f=k.current;if(!f)return;let h=Math.max(0,Math.ceil(c));Y.current!==h&&(Y.current=h,f.hidden=h===0,f.style.height=`${h}px`,f.style.marginTop=h>0?`${-x.current}px`:\"\");},[]),H=g.useCallback((c,{behavior:f=\"auto\",autoscrolling:h=false}={})=>{let P=_.current;if(!P)return;let V=Math.max(0,c);if(Math.abs(P.scrollTop-V)<=J){P.scrollTop=V,t();return}h&&G(true),P.scrollTo({top:V,behavior:f}),r();},[t,r,G]),N=g.useCallback(({behavior:c=\"auto\"}={})=>_.current?(v(0),o.current=null,p.current=\"free-scrolling\",H(0,{behavior:c}),n(),true):false,[n,H,v]),ne=g.useCallback(({behavior:c=\"auto\"}={})=>{let f=_.current;return f?(v(0),o.current=null,p.current=i.current?\"following-bottom\":\"free-scrolling\",H(Ke(f),{autoscrolling:true,behavior:c}),n(),true):false},[n,H,v]),A=g.useCallback((c,{align:f=\"start\",behavior:h=\"auto\",scrollMargin:P=y.current}={},{keepPreviousPeek:V=false}={})=>{let z=d.current,q=_.current;if(!z||!q||!z.contains(c))return false;let K=ze({align:f,element:c,scrollMargin:V?P+u.current:P,spacer:k.current,viewport:q}),se=qe({content:z,scrollTop:K,spacer:k.current,viewport:q});return v(se),L.current={element:c,viewportTop:te(c,q)},p.current=V?\"anchored-to-message\":\"settling-jump\",o.current=V?c:null,H(K,{behavior:h}),n(),true},[n,H,v]),B=g.useCallback(()=>{let c=o.current;return !c||!c.isConnected||p.current!==\"anchored-to-message\"?false:A(c,{align:\"start\"},{keepPreviousPeek:true})},[A]),D=g.useCallback((c,f)=>{let h=C.current.get(c);return h?(E.current=true,A(h,f)?(m.current=null,true):(m.current={messageId:c,options:f},true)):b.current===0?(m.current={messageId:c,options:f},E.current=true,true):false},[A]);return {flushPendingScrollToMessage:g.useCallback(()=>{let c=m.current;if(!c)return false;let f=C.current.get(c.messageId);return !f||!A(f,c.options)?false:(m.current=null,E.current=true,true)},[A]),reanchorToAnchoredMessage:B,scrollToElement:A,scrollToEnd:ne,scrollToMessage:D,scrollToStart:N}}function Qe(e,t){let r=e,n=new Set;return {getSnapshot:()=>r,hasListeners:()=>n.size>0,setSnapshot:o=>{t(r,o)||(r=o,n.forEach(i=>i()));},subscribe:(o,i,S)=>{let l=n.size===0;return n.add(o),l&&i?.(),()=>{n.delete(o),n.size===0&&S?.();}}}}function Xe(e,t){return Qe(e,t)}function Ze(){return Qe(Q,Tt)}function et(e,t){return e.start===t.start&&e.end===t.end}function Tt(e,t){return e.currentAnchorId!==t.currentAnchorId||e.visibleMessageIds.length!==t.visibleMessageIds.length?false:e.visibleMessageIds.every((r,n)=>r===t.visibleMessageIds[n])}function tt({autoScroll:e,scrollEdgeThreshold:t,scrollMargin:r,scrollPreviousItemPeek:n}){let o=g.useRef(e),i=g.useRef(false),S=g.useRef(null),l=g.useRef(false),d=g.useRef(t),E=g.useRef(0),b=g.useRef(null),C=g.useRef(0),p=g.useRef(e?\"following-bottom\":\"free-scrolling\"),m=g.useRef(new Map),L=g.useRef(null),y=g.useRef(null),u=g.useRef(null),x=g.useRef(n),Y=g.useRef(true),k=g.useRef(null),_=g.useRef(r),G=g.useRef(null),v=g.useRef(0),H=g.useRef(0),N=g.useRef(null),ne=g.useRef(null),A=g.useRef(null),B=g.useRef(null),D=g.useRef(null),U=g.useRef(null),c=g.useRef(null),f=g.useRef(null),h=g.useRef(new Set),P=g.useRef(new WeakSet);return A.current===null&&(A.current=Xe(ge,et)),f.current===null&&(f.current=Ze()),o.current=e,d.current=t,_.current=r,x.current=n,{autoScrollRef:o,autoscrollingRef:i,autoscrollingTimeoutRef:B,streamingTurnRef:u,contentRef:S,defaultScrollPositionAppliedRef:l,firstItemRef:b,itemCountRef:E,lastScrollTopRef:C,messageElementsRef:m,modeRef:p,pendingScrollFrameRef:G,pendingScrollToMessageRef:L,prependRestoreRef:y,preserveScrollOnPrependRef:Y,rootRef:k,scrollEdgeThresholdRef:d,scrollMarginRef:_,scrollPreviousItemPeekRef:x,spacerGapRef:v,spacerHeightRef:H,spacerRef:N,stateFrameRef:ne,stateStore:A.current,viewportRef:D,visibilityFrameRef:U,visibilityObserverRef:c,visibilityStore:f.current,visibleMessageIdsRef:h,handledScrollAnchorsRef:P}}function rt(e,t){return g.useCallback(r=>{e.current=r,r&&t();},[e,t])}function nt({autoScroll:e=false,defaultScrollPosition:t=\"end\",scrollEdgeThreshold:r=xe,scrollPreviousItemPeek:n=Ve,scrollMargin:o=_e}){let i=tt({autoScroll:e,scrollEdgeThreshold:r,scrollMargin:o,scrollPreviousItemPeek:n}),{streamingTurnRef:S,autoScrollRef:l,autoscrollingRef:d,autoscrollingTimeoutRef:E,contentRef:b,defaultScrollPositionAppliedRef:C,firstItemRef:p,itemCountRef:m,lastScrollTopRef:L,messageElementsRef:y,modeRef:u,pendingScrollFrameRef:x,pendingScrollToMessageRef:Y,prependRestoreRef:k,preserveScrollOnPrependRef:_,rootRef:G,scrollEdgeThresholdRef:v,scrollMarginRef:H,scrollPreviousItemPeekRef:N,spacerGapRef:ne,spacerHeightRef:A,spacerRef:B,stateFrameRef:D,stateStore:U,viewportRef:c,visibilityFrameRef:f,visibilityObserverRef:h,visibilityStore:P,visibleMessageIdsRef:V,handledScrollAnchorsRef:z}=i,q=g.useRef(t);q.current!==t&&(q.current=t,C.current=false);let K=g.useCallback(s=>{let a=G.current,T=c.current,O=[s.start&&\"start\",s.end&&\"end\"].filter(Boolean).join(\" \"),ue=d.current;for(let W of [a,T])W&&(O?W.setAttribute(\"data-scrollable\",O):W.removeAttribute(\"data-scrollable\"),W.toggleAttribute(\"data-autoscrolling\",ue));},[]),se=g.useCallback(s=>{let a=c.current?.scrollTop??0,T=a<L.current-J;L.current=a,l.current&&!s.end&&u.current!==\"settling-jump\"&&u.current!==\"anchored-to-message\"?u.current=\"following-bottom\":u.current===\"following-bottom\"&&s.end&&T&&!d.current&&(u.current=\"free-scrolling\");},[]),F=g.useCallback(()=>{let s=De({content:b.current,scrollEdgeThreshold:v.current,spacer:B.current,viewport:c.current});se(s);let a=u.current===\"following-bottom\"?{...s,end:false}:s;K(a),U.setSnapshot(a);},[se,U,K]),oe=g.useCallback(()=>{D.current===null&&(D.current=window.requestAnimationFrame(()=>{D.current=null,F();}));},[F]),w=g.useCallback(()=>{P.hasListeners()&&f.current===null&&(f.current=window.requestAnimationFrame(()=>{f.current=null,P.hasListeners()&&P.setSnapshot(Fe({content:b.current,scrollMargin:H.current,scrollPreviousItemPeek:N.current,spacer:B.current,viewport:c.current,visibleMessageIds:V.current}));}));},[P]),{flushPendingScrollToMessage:ce,reanchorToAnchoredMessage:me,scrollToElement:le,scrollToEnd:I,scrollToMessage:Me,scrollToStart:ae}=Je({refs:i,commitScrollState:F,scheduleStateCommit:oe,scheduleVisibilitySync:w}),de=g.useCallback(()=>{let s=k.current,a=c.current;if(!s||!a||!s.element.isConnected)return false;let O=te(s.element,a)-s.viewportTop;return Math.abs(O)<=J?false:(a.scrollTop+=O,s.viewportTop=te(s.element,a),oe(),w(),true)},[oe,w]),Z=g.useCallback(()=>{let s=b.current,a=c.current;if(!s||!a){k.current=null;return}let T=Ye({content:s,spacer:B.current,viewport:a});k.current=T?{element:T,viewportTop:te(T,a)}:null;},[]),be=g.useCallback(()=>{x.current===null&&(x.current=window.requestAnimationFrame(()=>{x.current=null,ce()&&Z();}));},[Z,ce]),ie=g.useCallback(()=>{if(!t||C.current||m.current===0)return false;let s=false;if(t===\"last-anchor\"){let a=b.current,T=c.current,O=a&&T?Ue(ee(a,B.current)):null;if(!a||!T||!O)s=I({behavior:\"auto\"});else {let ue=pe(O,T);s=Se({content:a,spacer:B.current,viewport:T})-ue<=T.clientHeight?I({behavior:\"auto\"}):le(O,{align:\"start\"},{keepPreviousPeek:true});}}else s=t===\"end\"?I({behavior:\"auto\"}):ae({behavior:\"auto\"});return s?(C.current=true,true):false},[t,le,I,ae]),Te=g.useCallback(()=>{let s=b.current;if(!s)return;let a=ee(s,B.current),T=m.current,O=p.current;m.current=a.length,p.current=a[0]??null,(()=>{if(ce())return;if(T===0){if(ie()||a.length>0&&l.current&&I({behavior:\"auto\"}))return;F(),w();return}let W=O?a.indexOf(O):-1;if(_.current&&W>0){de();return}if(a.length>T){let $=je(a,T);if($){if(l.current&&u.current===\"following-bottom\"&&Ne(a,T)){I({behavior:\"auto\"});return}le($,{align:\"start\"},{keepPreviousPeek:true}),z.current.add($);return}}if(a.length===T){let $=Ge(a,z.current);if($){le($,{align:\"start\"},{keepPreviousPeek:true}),z.current.add($);return}}u.current===\"following-bottom\"&&l.current?I({behavior:\"auto\"}):(F(),w());})(),Z();},[ie,Z,F,ce,de,w,le,I]),Ee=g.useCallback(()=>{if(u.current===\"following-bottom\"&&l.current){I({behavior:\"auto\"});return}let s=A.current;if(me()){l.current&&s>0&&A.current===0&&I({behavior:\"auto\"});return}oe(),w();},[me,oe,w,I]),ve=g.useCallback(()=>{let s=c.current;if(!(!s||!P.hasListeners())){if(typeof IntersectionObserver>\"u\"){w();return}h.current||(h.current=new IntersectionObserver(a=>{for(let T of a){let O=T.target.dataset.messageId;O&&(T.isIntersecting?V.current.add(O):V.current.delete(O));}w();},{root:s,rootMargin:`${-(H.current+N.current)}px 0px 0px 0px`,threshold:[0,.01,.5,1]})),y.current.forEach(a=>{h.current?.observe(a);}),w();}},[w,P]),he=g.useCallback(()=>{f.current!==null&&(window.cancelAnimationFrame(f.current),f.current=null),h.current?.disconnect(),h.current=null,V.current.clear(),P.setSnapshot(Q);},[P]),pt=g.useCallback((s,a,T)=>{if(a){y.current.set(s,a),h.current?.observe(a),w(),Y.current?.messageId===s&&be();return}T&&y.current.get(s)===T&&(y.current.delete(s),V.current.delete(s),h.current?.unobserve(T),w());},[be,w]),Le=g.useCallback(()=>{(u.current===\"following-bottom\"||u.current===\"anchored-to-message\"||u.current===\"settling-jump\")&&(S.current=null,u.current=\"free-scrolling\");},[]),Ce=g.useCallback(()=>K(U.getSnapshot()),[U,K]),Pe=rt(G,Ce),we=rt(c,Ce),Oe=g.useCallback(s=>{b.current=s;},[]),ye=g.useCallback(s=>{B.current=s,ne.current=$e(s?.parentElement??null);},[]),He=g.useCallback(()=>{F(),w(),Z();},[Z,F,w]),mt=g.useMemo(()=>({handleContentChange:Te,handleResize:Ee,observeVisibility:ve,preserveScrollOnPrependRef:_,scrollToEnd:I,scrollToMessage:Me,scrollToStart:ae,setContentElement:Oe,setRootElement:Pe,setSpacerElement:ye,setViewportElement:we,stateStore:U,syncAfterScroll:He,unobserveVisibility:he,userScrollIntent:Le,viewportRef:c,visibilityStore:P}),[Te,Ee,ve,I,Me,ae,Oe,Pe,ye,we,U,He,he,Le,P]);return g.useLayoutEffect(()=>{ie();},[ie]),g.useEffect(()=>()=>{D.current!==null&&(window.cancelAnimationFrame(D.current),D.current=null),f.current!==null&&(window.cancelAnimationFrame(f.current),f.current=null),E.current!==null&&(window.clearTimeout(E.current),E.current=null),x.current!==null&&(window.cancelAnimationFrame(x.current),x.current=null),h.current?.disconnect(),h.current=null;},[]),g.useLayoutEffect(()=>{if(e&&u.current===\"following-bottom\"&&m.current>0){I({behavior:\"auto\"});return}F();},[e,F,I]),{context:mt,registerMessage:pt}}function lt(e){let t=g.useRef(e);return t.current=e,t}var st=g.createContext(null),ct=g.createContext(null);function X(){let e=g.useContext(st);if(!e)throw new Error(\"useMessageScroller must be used within a MessageScroller.\");return e}function Et(){let e=g.useContext(ct);if(!e)throw new Error(\"MessageScrollerItem must be used within a MessageScroller.\");return e}function vt(){let{scrollToEnd:e,scrollToMessage:t,scrollToStart:r}=X();return g.useMemo(()=>({scrollToEnd:e,scrollToMessage:t,scrollToStart:r}),[e,t,r])}function ht(){let{stateStore:e}=X();return g.useSyncExternalStore(e.subscribe,e.getSnapshot,e.getSnapshot)}function Lt(){let{observeVisibility:e,unobserveVisibility:t,visibilityStore:r}=X(),n=g.useCallback(o=>r.subscribe(o,e,t),[e,t,r]);return g.useSyncExternalStore(n,r.getSnapshot,r.getSnapshot)}function at({autoScroll:e=false,children:t,defaultScrollPosition:r=\"end\",scrollEdgeThreshold:n,scrollPreviousItemPeek:o,scrollMargin:i}){let{context:S,registerMessage:l}=nt({autoScroll:e,defaultScrollPosition:r,scrollEdgeThreshold:n,scrollPreviousItemPeek:o,scrollMargin:i});return jsx(st.Provider,{value:S,children:jsx(ct.Provider,{value:l,children:t})})}function it({children:e,...t}){let{setRootElement:r}=X();return jsx(\"div\",{ref:r,...t,children:e})}function ut({\"aria-label\":e,children:t,onKeyDown:r,onScroll:n,onTouchMove:o,onWheel:i,preserveScrollOnPrepend:S=true,ref:l,role:d,tabIndex:E,...b}){let{handleResize:C,preserveScrollOnPrependRef:p,setViewportElement:m,syncAfterScroll:L,userScrollIntent:y,viewportRef:u}=X();p.current=S;let x=g.useCallback(v=>{m(v),c(l)?.(v);},[l,m]);function Y(v){L(),n?.(v);}function k(v){y(),i?.(v);}function _(v){y(),o?.(v);}function G(v){Be.has(v.key)&&y(),r?.(v);}return g.useEffect(()=>{let v=u.current;if(!v||typeof ResizeObserver>\"u\")return;let H=0,N=new ResizeObserver(()=>{window.cancelAnimationFrame(H),H=window.requestAnimationFrame(C);});return N.observe(v),()=>{window.cancelAnimationFrame(H),N.disconnect();}},[C,u]),jsx(\"div\",{ref:x,role:d??\"region\",\"aria-label\":e??\"Messages\",tabIndex:E??0,onKeyDown:G,onScroll:Y,onTouchMove:_,onWheel:k,...b,children:t})}function ft({\"aria-relevant\":e,children:t,ref:r,role:n,spacerClassName:o,...i}){let{handleContentChange:S,handleResize:l,setContentElement:d,setSpacerElement:E}=X(),b=g.useRef(null),C=g.useCallback(p=>{b.current=p,d(p),c(r)?.(p);},[r,d]);return g.useLayoutEffect(()=>{let p=b.current;if(!p||(S(),typeof MutationObserver>\"u\"))return;let m=new MutationObserver(()=>{S();});return m.observe(p,{childList:true}),()=>m.disconnect()},[S]),g.useEffect(()=>{let p=b.current;if(!p||typeof ResizeObserver>\"u\")return;let m=0,L=new ResizeObserver(()=>{window.cancelAnimationFrame(m),m=window.requestAnimationFrame(l);});return L.observe(p),()=>{window.cancelAnimationFrame(m),L.disconnect();}},[l]),jsxs(\"div\",{ref:C,role:n??\"log\",\"aria-relevant\":e??\"additions\",...i,children:[t,jsx(\"div\",{ref:E,\"aria-hidden\":\"true\",\"data-message-scroller-spacer\":\"\",hidden:true,className:o})]})}function gt({messageId:e,ref:t,scrollAnchor:r=false,...n}){let o=Et(),i=g.useRef(null),S=g.useCallback(l=>{let d=i.current;i.current=l,e&&o(e,l,d),c(t)?.(l);},[e,t,o]);return jsx(\"div\",{ref:S,\"data-message-id\":e,\"data-scroll-anchor\":r?\"true\":\"false\",...n})}function St({behavior:e=\"smooth\",children:t,direction:r=\"end\",onClick:n,render:o,tabIndex:i,type:S=\"button\",...l}){let{scrollToEnd:d,scrollToStart:E,stateStore:b$1}=X(),C=lt(n),p=g.useCallback(u=>b$1.subscribe(u),[b$1]),m=g.useCallback(()=>{let u=b$1.getSnapshot();return r===\"start\"?u.start:u.end},[r,b$1]),L=g.useSyncExternalStore(p,m,m),y=g.useCallback(u=>{L&&(C.current?.(u),u.defaultPrevented||(u.currentTarget.blur(),r===\"start\"?E({behavior:e}):d({behavior:e})));},[e,r,L,C,d,E]);return a({defaultTagName:\"button\",props:b({type:S,inert:!L,tabIndex:L?i:-1,children:t??jsxs(\"span\",{children:[\"Scroll to \",r]}),onClick:y},l),render:o,state:{active:L,direction:r},stateAttributesMapping:{active:u=>({\"data-active\":u?\"true\":\"false\"})}})}var $t={Provider:at,Root:it,Viewport:ut,Content:ft,Item:gt,Button:St};export{$t as MessageScroller,vt as useMessageScroller,ht as useMessageScrollerScrollable,Lt as useMessageScrollerVisibility};","/**\n * Copyright 2022 Joe Bell. All rights reserved.\n *\n * This file is licensed to you under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with the\n * License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */ import { clsx } from \"clsx\";\nconst falsyToString = (value)=>typeof value === \"boolean\" ? `${value}` : value === 0 ? \"0\" : value;\nexport const cx = clsx;\nexport const cva = (base, config)=>(props)=>{\n var _config_compoundVariants;\n if ((config === null || config === void 0 ? void 0 : config.variants) == null) return cx(base, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);\n const { variants, defaultVariants } = config;\n const getVariantClassNames = Object.keys(variants).map((variant)=>{\n const variantProp = props === null || props === void 0 ? void 0 : props[variant];\n const defaultVariantProp = defaultVariants === null || defaultVariants === void 0 ? void 0 : defaultVariants[variant];\n if (variantProp === null) return null;\n const variantKey = falsyToString(variantProp) || falsyToString(defaultVariantProp);\n return variants[variant][variantKey];\n });\n const propsWithoutUndefined = props && Object.entries(props).reduce((acc, param)=>{\n let [key, value] = param;\n if (value === undefined) {\n return acc;\n }\n acc[key] = value;\n return acc;\n }, {});\n const getCompoundVariantClassNames = config === null || config === void 0 ? void 0 : (_config_compoundVariants = config.compoundVariants) === null || _config_compoundVariants === void 0 ? void 0 : _config_compoundVariants.reduce((acc, param)=>{\n let { class: cvClass, className: cvClassName, ...compoundVariantOptions } = param;\n return Object.entries(compoundVariantOptions).every((param)=>{\n let [key, value] = param;\n return Array.isArray(value) ? value.includes({\n ...defaultVariants,\n ...propsWithoutUndefined\n }[key]) : ({\n ...defaultVariants,\n ...propsWithoutUndefined\n })[key] === value;\n }) ? [\n ...acc,\n cvClass,\n cvClassName\n ] : acc;\n }, []);\n return cx(base, getVariantClassNames, getCompoundVariantClassNames, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);\n };\n\n","import * as React from 'react';\nimport { useComposedRefs } from '@radix-ui/react-compose-refs';\n\ndeclare module 'react' {\n interface ReactElement {\n $$typeof?: symbol | string;\n }\n}\n\n/* -------------------------------------------------------------------------------------------------\n * Slot\n * -----------------------------------------------------------------------------------------------*/\n\nexport type Usable<T> = PromiseLike<T> | React.Context<T>;\n\ntype SlotProps<Elem extends Element = HTMLElement, Props = React.HTMLAttributes<Elem>> = Props & {\n children?: React.ReactNode;\n};\n\n/* @__NO_SIDE_EFFECTS__ */ export function createSlot<\n Elem extends Element = HTMLElement,\n Props = React.HTMLAttributes<Elem>,\n>(ownerName: string) {\n const Slot = React.forwardRef<Elem, SlotProps<Elem, Props>>((props, forwardedRef) => {\n let { children, ...slotProps } = props;\n let slottableElement: React.ReactElement | null = null;\n let hasSlottable = false;\n const newChildren: React.ReactNode[] = [];\n\n if (isLazyComponent(children) && typeof use === 'function') {\n children = use(children._payload);\n }\n\n React.Children.forEach(children, (maybeSlottable) => {\n if (isSlottable(maybeSlottable)) {\n hasSlottable = true;\n const slottable = maybeSlottable;\n let child = 'child' in slottable.props ? slottable.props.child : slottable.props.children;\n\n if (isLazyComponent(child) && typeof use === 'function') {\n child = use(child._payload);\n }\n\n slottableElement = getSlottableElementFromSlottable(slottable, child);\n newChildren.push((slottableElement?.props as any)?.children);\n } else {\n newChildren.push(maybeSlottable);\n }\n });\n\n if (slottableElement) {\n slottableElement = React.cloneElement(slottableElement, undefined, newChildren);\n } else if (\n // A `Slottable` was found but it didn't resolve to a single element (e.g.\n // it wrapped multiple elements, text, or a render-prop `child` that\n // wasn't an element). Don't fall back to treating the `Slottable` wrapper\n // itself as the slot target — throw a descriptive error below instead.\n !hasSlottable &&\n React.Children.count(children) === 1 &&\n React.isValidElement(children)\n ) {\n slottableElement = children;\n }\n\n const slottableElementRef = slottableElement ? getElementRef(slottableElement) : undefined;\n const composedRef = useComposedRefs(forwardedRef, slottableElementRef);\n\n if (!slottableElement) {\n // Empty/falsy children (`null`, `undefined`, `false`, no children, etc.) are valid and\n // render nothing. Anything else is content we couldn't slot onto a single element, which\n // is a usage error, so we fail loudly with a clear message.\n if (children || children === 0) {\n throw new Error(\n hasSlottable ? createSlottableError(ownerName) : createSlotError(ownerName),\n );\n }\n return children;\n }\n\n const mergedProps = mergeProps(slotProps, slottableElement.props ?? {});\n\n // do not pass ref to React.Fragment for React 19 compatibility\n if (slottableElement.type !== React.Fragment) {\n mergedProps.ref = forwardedRef ? composedRef : slottableElementRef;\n }\n\n return React.cloneElement(slottableElement, mergedProps);\n });\n\n Slot.displayName = `${ownerName}.Slot`;\n return Slot;\n}\n\nconst Slot = createSlot('Slot');\n\n/* -------------------------------------------------------------------------------------------------\n * Slottable\n * -----------------------------------------------------------------------------------------------*/\n\nconst SLOTTABLE_IDENTIFIER = Symbol.for('radix.slottable');\n\ntype SlottableChildrenProps = { children: React.ReactNode };\ntype SlottableRenderFnProps = {\n child: React.ReactNode;\n children: (slottable: React.ReactNode) => React.ReactNode;\n};\n\ntype SlottableProps = SlottableRenderFnProps | SlottableChildrenProps;\ninterface SlottableComponent extends React.FC<SlottableProps> {\n __radixId: symbol;\n}\n\n/* @__NO_SIDE_EFFECTS__ */ export function createSlottable(ownerName: string) {\n const Slottable: SlottableComponent = (props) =>\n 'child' in props ? props.children(props.child) : props.children;\n\n Slottable.displayName = `${ownerName}.Slottable`;\n Slottable.__radixId = SLOTTABLE_IDENTIFIER;\n return Slottable;\n}\n\nconst Slottable = createSlottable('Slottable');\n\n/* -------------------------------------------------------------------------------------------------\n * getSlottableElementFromSlottable\n * -----------------------------------------------------------------------------------------------*/\n\nconst getSlottableElementFromSlottable = (slottable: SlottableElement, child: React.ReactNode) => {\n if ('child' in slottable.props) {\n const child = slottable.props.child;\n if (!React.isValidElement<React.PropsWithChildren>(child)) return null;\n return React.cloneElement(child, undefined, slottable.props.children(child.props.children));\n }\n\n return React.isValidElement(child) ? child : null;\n};\n\n/* -------------------------------------------------------------------------------------------------\n * mergeProps\n * -----------------------------------------------------------------------------------------------*/\n\ntype AnyProps = Record<string, any>;\n\nfunction mergeProps(slotProps: AnyProps, childProps: AnyProps) {\n // all child props should override\n const overrideProps = { ...childProps };\n\n for (const propName in childProps) {\n const slotPropValue = slotProps[propName];\n const childPropValue = childProps[propName];\n\n const isHandler = /^on[A-Z]/.test(propName);\n if (isHandler) {\n // if the handler exists on both, we compose them\n if (slotPropValue && childPropValue) {\n overrideProps[propName] = (...args: unknown[]) => {\n const result = childPropValue(...args);\n slotPropValue(...args);\n return result;\n };\n }\n // but if it exists only on the slot, we use only this one\n else if (slotPropValue) {\n overrideProps[propName] = slotPropValue;\n }\n }\n // if it's `style`, we merge them\n else if (propName === 'style') {\n overrideProps[propName] = { ...slotPropValue, ...childPropValue };\n } else if (propName === 'className') {\n overrideProps[propName] = [slotPropValue, childPropValue].filter(Boolean).join(' ');\n }\n }\n\n return { ...slotProps, ...overrideProps };\n}\n\n/* -------------------------------------------------------------------------------------------------\n * getElementRef\n * -----------------------------------------------------------------------------------------------*/\n\n// Before React 19 accessing `element.props.ref` will throw a warning and suggest using `element.ref`\n// After React 19 accessing `element.ref` does the opposite.\n// https://github.com/facebook/react/pull/28348\n//\n// Access the ref using the method that doesn't yield a warning.\nfunction getElementRef(element: React.ReactElement) {\n // React <=18 in DEV\n let getter = Object.getOwnPropertyDescriptor(element.props, 'ref')?.get;\n let mayWarn = getter && 'isReactWarning' in getter && getter.isReactWarning;\n if (mayWarn) {\n return (element as any).ref;\n }\n\n // React 19 in DEV\n getter = Object.getOwnPropertyDescriptor(element, 'ref')?.get;\n mayWarn = getter && 'isReactWarning' in getter && getter.isReactWarning;\n if (mayWarn) {\n return (element.props as { ref?: React.Ref<unknown> }).ref;\n }\n\n // Not DEV\n return (element.props as { ref?: React.Ref<unknown> }).ref || (element as any).ref;\n}\n\n/* ---------------------------------------------------------------------------------------------- */\n\ntype SlottableElement = React.ReactElement<SlottableProps, SlottableComponent>;\n\nfunction isSlottable(\n child: React.ReactNode,\n): child is React.ReactElement<SlottableProps, typeof Slottable> {\n return (\n React.isValidElement(child) &&\n typeof child.type === 'function' &&\n '__radixId' in child.type &&\n child.type.__radixId === SLOTTABLE_IDENTIFIER\n );\n}\n\nconst REACT_LAZY_TYPE = Symbol.for('react.lazy');\n\ninterface LazyReactElement extends React.ReactElement {\n $$typeof: typeof REACT_LAZY_TYPE;\n _payload: PromiseLike<Exclude<React.ReactNode, PromiseLike<any>>>;\n}\n\nfunction isLazyComponent(element: React.ReactNode): element is LazyReactElement {\n return (\n element != null &&\n typeof element === 'object' &&\n '$$typeof' in element &&\n element.$$typeof === REACT_LAZY_TYPE &&\n '_payload' in element &&\n isPromiseLike(element._payload)\n );\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return typeof value === 'object' && value !== null && 'then' in value;\n}\n\nconst createSlotError = (ownerName: string) => {\n return `${ownerName} failed to slot onto its children. Expected a single React element child or \\`Slottable\\`.`;\n};\n\nconst createSlottableError = (ownerName: string) => {\n return `${ownerName} failed to slot onto its \\`Slottable\\`. Expected \\`Slottable\\` to receive a single React element child.`;\n};\n\nconst use: typeof React.use | undefined = (React as any)[' use '.trim().toString()];\n\nexport {\n Slot,\n Slottable,\n //\n Slot as Root,\n};\nexport type { SlotProps };\n","import * as React from 'react';\n\ntype PossibleRef<T> = React.Ref<T> | undefined;\n\n/**\n * Set a given ref to a given value\n * This utility takes care of different types of refs: callback refs and RefObject(s)\n */\nfunction setRef<T>(ref: PossibleRef<T>, value: T) {\n if (typeof ref === 'function') {\n return ref(value);\n } else if (ref !== null && ref !== undefined) {\n ref.current = value;\n }\n}\n\n/**\n * A utility to compose multiple refs together\n * Accepts callback refs and RefObject(s)\n */\nfunction composeRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {\n return (node) => {\n let hasCleanup = false;\n const cleanups = refs.map((ref) => {\n const cleanup = setRef(ref, node);\n if (!hasCleanup && typeof cleanup == 'function') {\n hasCleanup = true;\n }\n return cleanup;\n });\n\n // React <19 will log an error to the console if a callback ref returns a\n // value. We don't use ref cleanups internally so this will only happen if a\n // user's ref callback returns a value, which we only expect if they are\n // using the cleanup functionality added in React 19.\n if (hasCleanup) {\n return () => {\n for (let i = 0; i < cleanups.length; i++) {\n const cleanup = cleanups[i];\n if (typeof cleanup == 'function') {\n cleanup();\n } else {\n setRef(refs[i], null);\n }\n }\n };\n }\n };\n}\n\n/**\n * A custom hook that composes multiple refs\n * Accepts callback refs and RefObject(s)\n */\nfunction useComposedRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n return React.useCallback(composeRefs(...refs), refs);\n}\n\nexport { composeRefs, useComposedRefs };\n","import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { Slot } from \"radix-ui\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n \"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-[background-color,border-color,color,box-shadow,translate] duration-(--duration-quick) ease-(--ease-smooth-out) motion-reduce:transition-none outline-none select-none touch-manipulation focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/25 motion-safe:active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n {\n variants: {\n variant: {\n default: \"bg-primary bg-clip-border text-primary-foreground hover:bg-primary/90\",\n outline:\n \"border-input/80 bg-card text-foreground hover:border-input hover:bg-muted/60 aria-expanded:bg-muted aria-expanded:text-foreground\",\n secondary:\n \"border-border bg-secondary text-secondary-foreground hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground\",\n ghost:\n \"text-muted-foreground hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground\",\n destructive:\n \"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default:\n \"h-9 gap-1.5 px-3.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3\",\n xs: \"h-7 gap-1 px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3\",\n sm: \"h-8 gap-1 px-3 text-[0.8rem] has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5 [&_svg:not([class*='size-'])]:size-3.5\",\n lg: \"h-10 gap-1.5 px-4 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3\",\n icon: \"size-9\",\n \"icon-xs\": \"size-7 [&_svg:not([class*='size-'])]:size-3\",\n \"icon-sm\": \"size-8\",\n \"icon-lg\": \"size-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean\n }) {\n const Comp = asChild ? Slot.Root : \"button\"\n\n return (\n <Comp\n data-slot=\"button\"\n data-variant={variant}\n data-size={size}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n )\n}\n\nexport { Button, buttonVariants }\n","/**\n * Runs of consecutive messages from the same voice, so a burst of three reads\n * as one turn rather than as three separate arrivals. A run says who is\n * speaking once, at the top, and carries the time and the face at the foot;\n * everything in between is just a bubble.\n *\n * What joins what is the caller's rule, since the inbox and the widget hold\n * different shapes. All this does is walk the list and mark the edges.\n */\n\n/** How long a run stays open. A message later than this starts a new one. */\nconst RUN_MS = 5 * 60_000;\n\n/** Where a message sits in its run. Both true when it is a run of one. */\nexport type MessageGroup = {\n /** Carries the name. */\n isFirst: boolean;\n /** Carries the face and the time. */\n isLast: boolean;\n};\n\n/** A message standing on its own, for anywhere nothing is grouped. */\nexport const LONE_MESSAGE: MessageGroup = { isFirst: true, isLast: true };\n\n/**\n * Marks each message with where it sits in its run.\n *\n * `joinsPrevious` is only ever asked about neighbours, so it can compare\n * senders, timestamps, day labels, or whatever else the shape at hand knows.\n */\nexport function groupMessages<T>(\n messages: T[],\n joinsPrevious: (message: T, previous: T) => boolean,\n): MessageGroup[] {\n /** Whether the message at `index` carries on the one above it. */\n const continues = (index: number) => {\n const message = messages[index];\n const previous = messages[index - 1];\n if (message === undefined || previous === undefined) return false;\n return joinsPrevious(message, previous);\n };\n\n return messages.map((_, index) => ({\n isFirst: !continues(index),\n isLast: !continues(index + 1),\n }));\n}\n\n/**\n * Whether two messages are close enough together to belong to one run. A\n * missing timestamp means canned content with no clock behind it, which groups\n * on who is speaking alone.\n */\nexport function withinRun(previous?: number, next?: number): boolean {\n if (previous === undefined || next === undefined) return true;\n return next - previous <= RUN_MS;\n}\n","\"use client\";\n\nimport { useCallback, useLayoutEffect, useRef } from \"react\";\nimport { prefersReducedMotion } from \"./motion\";\n\nconst SHIFT_MS = 420;\n/** Matches --ease-spring. Kept in step by hand, since JS cannot read a theme token. */\nconst SHIFT_EASING = \"cubic-bezier(0.34, 1.45, 0.64, 1)\";\n\n/** How far the first message sits from the top of the stack, or null if there is none. */\nfunction topOfStack(element: HTMLElement | null): number | null {\n const first = element?.firstElementChild as HTMLElement | null;\n\n return first ? first.offsetTop : null;\n}\n\n/**\n * Slides a bottom anchored stack up as it grows, rather than letting it jump.\n *\n * A short thread sits on the composer, so a new message does not push the ones\n * above it down: it lifts them. Left alone that happens between two frames and\n * the whole conversation teleports.\n *\n * So it is measured instead. The first message's offset from the top of the\n * stack says how far everything moved, the stack is put straight back where it\n * was, and it travels the distance properly. Nothing moves once the thread is\n * long enough to scroll, because then the first message stops moving, the\n * distance is zero, and the scroller has it.\n *\n * The starting measurement is taken as the element attaches rather than in the\n * effect below, because the effect only runs when the message count changes. A\n * thread that arrives after a loading state would otherwise have nothing to\n * compare against until its second message, and the first one would jump.\n */\nexport function useStackShift(dependency: number) {\n const stack = useRef<HTMLDivElement | null>(null);\n const previousTop = useRef<number | null>(null);\n const watcher = useRef<ResizeObserver | null>(null);\n\n // Attaching also starts watching the stack for a change of size, and letting\n // go stops. Both here rather than in an effect, because the stack arrives\n // whenever the first message does: a thread that starts empty has nothing to\n // attach to on mount, and an effect that ran then would have found nothing\n // and never looked again.\n const ref = useCallback((node: HTMLDivElement | null) => {\n stack.current = node;\n previousTop.current = topOfStack(node);\n\n watcher.current?.disconnect();\n watcher.current = null;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n\n // The stack moves when the panel changes size too, and that is not a shift\n // worth animating: nothing arrived, the box around it grew. It does have to\n // be written down though. The effect below only runs on a new message, so\n // without this the measurement from before a resize is the one the next\n // message is compared against, and it travels the difference between two\n // panel heights on its way in.\n watcher.current = new ResizeObserver(() => {\n previousTop.current = topOfStack(node);\n });\n watcher.current.observe(node);\n }, []);\n\n useLayoutEffect(() => {\n const element = stack.current;\n if (!element) return;\n\n const top = topOfStack(element);\n const from = previousTop.current;\n previousTop.current = top;\n\n if (from === null || top === null) return;\n\n const distance = from - top;\n if (distance <= 0) return;\n if (prefersReducedMotion()) return;\n\n element.animate(\n [\n { transform: `translateY(${distance}px)` },\n { transform: \"translateY(0)\" },\n ],\n { duration: SHIFT_MS, easing: SHIFT_EASING },\n );\n }, [dependency]);\n\n return ref;\n}\n","/**\n * CSS carries `motion-reduce:` for anything animated in a stylesheet. The two\n * animations driven from JavaScript cannot read that, so they ask here.\n *\n * Guarded for the server, where there is no window to ask.\n */\nexport function prefersReducedMotion(): boolean {\n if (typeof window === \"undefined\") return false;\n\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n}\n","import { cn } from \"@/lib/utils\";\n\n/**\n * How the inside of the panel arrives once the panel has.\n *\n * The corner opens as a clip growing out of the button, which reveals a\n * picture that was already painted: the header, the thread and the composer\n * are simply there behind the edge as it passes. This is what makes them\n * arrive instead. Each part sits a few pixels low and faded while the panel\n * is closed, and rises into place once it opens, the header a beat ahead of\n * the rest, on the same curve everything else in the widget settles on.\n *\n * Read off the panel rather than passed down: the panel wrapper says\n * `data-state=\"open\"` or `\"closed\"`, and the parts answer. A panel that is\n * framed rather than floated, as in the gallery, has no such wrapper and\n * shows the parts at rest, which is exactly what a panel that never opens\n * or closes should do.\n *\n * Closing does not move the parts. The clip closes over them, and their\n * low pose is taken up silently once it has: no motion, after a delay the\n * length of the corner's own morph. Reopened before then, they are already\n * home and simply stay.\n */\nconst SETTLE = cn(\n \"transition-[translate,opacity] ease-out-quint motion-reduce:transition-none\",\n \"[[data-state=open]_&]:duration-500\",\n \"[[data-state=closed]_&]:translate-y-2 [[data-state=closed]_&]:opacity-0\",\n \"[[data-state=closed]_&]:duration-0 [[data-state=closed]_&]:[transition-delay:450ms]\",\n);\n\n/** The header, first. */\nexport const SETTLE_HEADER = cn(\n SETTLE,\n \"[[data-state=open]_&]:[transition-delay:60ms]\",\n);\n\n/** Everything under it, a beat behind. */\nexport const SETTLE_BODY = cn(\n SETTLE,\n \"[[data-state=open]_&]:[transition-delay:120ms]\",\n);\n","\"use client\";\n\nimport {\n useEffect,\n useRef,\n useState,\n type ChangeEvent,\n type KeyboardEvent,\n} from \"react\";\nimport {\n AttachmentDraftChip,\n useAttachmentDrafts,\n useAttachmentTarget,\n} from \"@/components/attachments\";\nimport { ArrowUp, Check, EnvelopeSimple, Plus } from \"@/components/icons\";\nimport { WidgetButton } from \"./widget-button\";\nimport { WIDGET_TEXT_FIELD_SHELL as TEXT_FIELD_SHELL } from \"./widget-field\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { useTypingSignal } from \"@/hooks/use-typing-signal\";\nimport { cn } from \"@/lib/utils\";\nimport { useEasedHeight } from \"@/hooks/use-eased-height\";\nimport { PoweredByMedian } from \"./widget-badge\";\n\n/**\n * Loose on purpose: it decides whether the arrow lights up, not whether the\n * address is real. Only a mail server can answer that, and a visitor locked\n * out by a suspicious regex has nobody to appeal to.\n */\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\n/**\n * How the pill changes its mind.\n *\n * `translate` rather than `transform`, because that is the property Tailwind's\n * `translate-y-*` actually sets. Named as `transform` it transitions nothing:\n * the opacity eases and the travel lands in one frame, which is the jump this\n * was written to avoid.\n */\nconst SWAP =\n \"transition-[opacity,translate] duration-300 ease-out motion-reduce:transition-none\";\n\n/**\n * The notch coming up out of the composer, with the spring's small overshoot\n * at the top of the travel: it is a thing being pushed up from underneath, and\n * a thing pushed up passes where it lands.\n */\nconst RISE =\n \"transition-transform duration-450 ease-spring motion-reduce:transition-none\";\n\n/**\n * And going back into it. Quicker, and accelerating rather than easing off,\n * which is the difference between something leaving and something being taken\n * back: it is not travelling to a new place, it is going home.\n */\nconst SINK =\n \"transition-transform duration-250 ease-in motion-reduce:transition-none\";\n\n/**\n * The two phrases passing each other, both moving the same way, like a word on\n * a wheel. No fade in it: two things at half opacity on top of each other is\n * what makes a swap look like a smudge.\n *\n * A pure deceleration rather than the spring above, because this one runs\n * inside a box exactly one line tall. Anything that overshoots its landing has\n * its ascenders shaved off on the way past.\n */\nconst ROLL =\n \"transition-transform duration-350 ease-out-quint motion-reduce:transition-none\";\n\n/**\n * The box growing a line, or giving one back, while there is a draft to\n * size it. Quick and decelerating: the caret is already on the new line,\n * and the box has to show it before the next word lands there.\n *\n * An emptied box is a sent message, and that one snaps. The message is\n * landing in the thread at that moment, and the composer stepping aside\n * behind it is a second motion for the eye to follow, against the first.\n */\nconst GROW =\n \"transition-[height] duration-200 ease-out-quint motion-reduce:transition-none\";\n\n/**\n * How long the tab holds its answer before tucking away. Long enough to read\n * two words, and short enough that nobody is waiting on it: the box to write\n * in is already underneath by then.\n */\nconst SAVED_MS = 1200;\n\n/**\n * The composer's two shapes.\n *\n * `pill` is one round row, add on the left, write in the middle, send on the\n * right, which is the corner panel's and always has been. `stacked` is a\n * box: the words on top and a row of controls underneath, add on the left\n * and send on the right. For the modal, where there is room for a box and\n * a pill that wide reads as a search bar.\n */\nexport type ComposerVariant = \"pill\" | \"stacked\";\n\n/**\n * One pill at the foot of the panel: add on the left, write in the middle,\n * send on the right. The shape is round rather than square so the composer\n * reads as a control rather than as another message, and it holds that shape\n * as the box grows because the corners are already as round as they get.\n *\n * Enter sends, shift and enter starts a new line. Files arrive from the plus\n * button, from a drop anywhere along the foot of the panel, or from a paste,\n * which is how anybody who has just taken a screenshot expects to send it.\n * They stack above the pill as chips, each with a thumbnail and a way out, and\n * one that is too big says so and holds the send button until it goes. Nothing\n * is uploaded yet.\n *\n * The draft clears when the send lands rather than when the button goes down,\n * so a send that fails costs a retry, not a retype.\n *\n * The box grows a line at a time as the words wrap, and it travels the line\n * rather than jumping it. See `useEasedHeight`.\n *\n * Before a first message, the same pill can ask who they are instead: a tab\n * rises from behind its top edge and says what is being asked, while the\n * writing box makes way for an email field underneath. Nothing remounts, so\n * both movements are transitions rather than things appearing.\n *\n * An answer is taken here rather than waited on. The tab says it has it, the\n * box to write in comes back under it, and the caret is already there before\n * the tab has finished tucking away, so a phone keeps the keyboard it has\n * instead of losing it and asking for it again.\n */\nexport function WidgetComposer({\n onSend,\n onTypingChange,\n askForEmail = false,\n onSubmitEmail,\n placeholder = \"Type your message...\",\n showBadge = true,\n isSending = false,\n variant = \"pill\",\n compact = false,\n}: {\n /** Awaited if it returns a promise. A rejection keeps the draft in place. */\n onSend?: (body: string, files: File[]) => unknown;\n /** Best effort, throttled here. */\n onTypingChange?: (isTyping: boolean) => unknown;\n /**\n * Ask for an email before a first message. The pill asks instead of the\n * composer: no attachments, no typing dots, nothing to send but the\n * address.\n */\n askForEmail?: boolean;\n /** Called with the address once it looks like one. */\n onSubmitEmail?: (email: string) => void;\n placeholder?: string;\n /** The \"Powered by Median\" line. Hosts on a paid plan can turn it off. */\n showBadge?: boolean;\n isSending?: boolean;\n /** A round row, or a box with its controls underneath. */\n variant?: ComposerVariant;\n /** The denser scale's padding. See `SupportWidget`. */\n compact?: boolean;\n}) {\n const isStacked = variant === \"stacked\";\n const [draft, setDraft] = useState(\"\");\n const [email, setEmail] = useState(\"\");\n // Where the notch has got to. `none` is the question, `saved` is the word\n // that answers it, and `settled` is that same word on its way down: the\n // notch goes back to asking only if the question itself comes back.\n const [answer, setAnswer] = useState<\"none\" | \"saved\" | \"settled\">(\"none\");\n const fileInputRef = useRef<HTMLInputElement>(null);\n const draftRef = useRef<HTMLTextAreaElement>(null);\n const draftBoxRef = useEasedHeight(draftRef);\n const attachments = useAttachmentDrafts();\n const target = useAttachmentTarget({ onFiles: attachments.add });\n const typing = useTypingSignal(onTypingChange);\n\n const hasContent = draft.trim().length > 0 || attachments.drafts.length > 0;\n const canSend = hasContent && !attachments.hasBlocker && !isSending;\n\n // True once what they typed could be an address. It decides the arrow, not\n // whether the address is real.\n const isValid = EMAIL_PATTERN.test(email.trim());\n\n // A question that has come back outranks an answer already given, which is\n // `medianSupport.reset()` and nothing else: the stored address went, so the\n // notch has something to ask again rather than something to hold. Worked out\n // here rather than written back into the state, since the two facts are the\n // prop and the beat, and this is only what they add up to.\n const stage = askForEmail && answer === \"settled\" ? \"none\" : answer;\n\n // What the notch says, and whether it is up at all. It holds the word it\n // answered with the whole way down: a notch that changed its mind back to\n // the question as it left would be asking on its way out the door.\n const isAsking = askForEmail && stage === \"none\";\n const isSaved = stage !== \"none\";\n const showTab = isAsking || stage === \"saved\";\n\n function submitEmail() {\n if (!isValid || !onSubmitEmail) return;\n setAnswer(\"saved\");\n onSubmitEmail(email.trim());\n }\n\n // The beat itself. Nothing waits on it: the composer is already back\n // underneath, so this only decides how long the word stays up before the\n // notch takes it down.\n useEffect(() => {\n if (answer !== \"saved\") return;\n const timer = setTimeout(() => setAnswer(\"settled\"), SAVED_MS);\n return () => clearTimeout(timer);\n }, [answer]);\n\n // Straight on to the message, since that is what the address was in the way\n // of. On a phone it also keeps the keyboard where it is: sending it away and\n // asking for it back is the whole screen bouncing twice for nothing.\n // `preventScroll`, because the browser's idea of bringing a field into view\n // is to scroll a page that is holding a sheet still.\n useEffect(() => {\n if (answer !== \"saved\" || askForEmail) return;\n draftRef.current?.focus({ preventScroll: true });\n }, [answer, askForEmail]);\n\n async function send() {\n if (!canSend) return;\n // Quiet locally only: the message itself is the stop, server side.\n typing.settle();\n\n const body = draft.trim();\n try {\n await onSend?.(body, attachments.files);\n } catch {\n // The notice above the composer says why it failed. The draft stays\n // where it was written.\n return;\n }\n\n // Only what was sent is cleared: anything typed while the message was in\n // flight is the next draft, not part of the one that just went.\n setDraft((now) => (now.trim() === body ? \"\" : now));\n attachments.clear();\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLTextAreaElement>) {\n if (event.key !== \"Enter\") return;\n // Shift for a new line, and never while an IME is open, where Enter is\n // accepting a candidate rather than finishing the message.\n if (event.shiftKey || event.nativeEvent.isComposing) return;\n\n event.preventDefault();\n void send();\n }\n\n function handlePick(event: ChangeEvent<HTMLInputElement>) {\n if (event.target.files?.length) attachments.add(event.target.files);\n // Cleared so picking the same file twice in a row still fires a change.\n event.target.value = \"\";\n }\n\n // The controls, built once and laid out by whichever shape this is. The\n // box's buttons are a size down from the pill's, since they sit on a row\n // of their own rather than bracketing a line of text.\n const controlSize = isStacked ? \"icon-lg\" : \"icon-xl\";\n\n const fileInput = (\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n tabIndex={-1}\n onChange={handlePick}\n className=\"sr-only\"\n />\n );\n\n const addButton = (\n <WidgetButton\n type=\"button\"\n variant=\"ghost\"\n size={controlSize}\n onClick={() => fileInputRef.current?.click()}\n aria-label=\"Add an attachment\"\n className=\"rounded-full text-muted-foreground hover:rotate-90 motion-reduce:hover:rotate-0\"\n >\n <Plus className={isStacked ? \"size-5\" : undefined} />\n </WidgetButton>\n );\n\n const draftBox = (\n // The field sizes itself to its words and sits at the foot of the box,\n // so a line that wraps stays where it was typed and the box's top edge\n // rises to take the first line in: the composer grows upward, and the\n // words hold still under the caret. The box follows the field on a\n // curve and clips whatever it is behind by.\n <div\n ref={draftBoxRef}\n className={cn(\n \"flex flex-col justify-end overflow-clip\",\n isStacked ? \"w-full\" : \"min-w-0 flex-1\",\n draft.length > 0 && GROW,\n )}\n >\n <Textarea\n ref={draftRef}\n rows={1}\n value={draft}\n onChange={(event) => {\n setDraft(event.target.value);\n typing.ping();\n }}\n onKeyDown={handleKeyDown}\n // The same swap the inbox does, so both ends of a conversation\n // confirm a drop the same way.\n placeholder={target.isDropTarget ? \"Drop to attach\" : placeholder}\n aria-label=\"Message\"\n // 16px on phones, and in pixels: any focused field smaller than\n // that makes iOS zoom the host's whole page in, and a host who has\n // resized their root rem would smuggle the zoom back past `text-base`.\n //\n // `shrink-0`, or the box's pinned height would size the field\n // instead of the other way round, and it would never grow at all.\n className={cn(\n \"max-h-40 shrink-0 max-sm:text-[16px]\",\n isStacked\n ? \"min-h-9 px-2 pt-1.5 pb-1 text-sm\"\n : \"min-h-10 px-2 py-2 text-base\",\n )}\n />\n </div>\n );\n\n // Pops in as the first character lands, rather than sitting there greyed\n // out waiting to be earned.\n const sendButton = (\n <WidgetButton\n type=\"button\"\n size={controlSize}\n disabled={!canSend}\n onClick={() => void send()}\n aria-label=\"Send message\"\n className={cn(\n \"rounded-full\",\n canSend\n ? \"scale-100 opacity-100\"\n : \"scale-90 opacity-40 motion-reduce:scale-100\",\n )}\n >\n <ArrowUp className={isStacked ? \"size-5\" : undefined} />\n </WidgetButton>\n );\n\n const emailBox = (\n <input\n type=\"email\"\n value={email}\n onChange={(event) => setEmail(event.target.value)}\n onKeyDown={(event) => {\n if (event.key !== \"Enter\" || event.nativeEvent.isComposing) return;\n event.preventDefault();\n submitEmail();\n }}\n placeholder=\"you@example.com\"\n aria-label=\"Email address\"\n autoComplete=\"email\"\n spellCheck={false}\n // The same rule as the message field, for the same reason.\n className={cn(\n \"min-w-0 flex-1 px-2 outline-none placeholder:text-muted-foreground max-sm:text-[16px]\",\n isStacked ? \"h-9 text-sm\" : \"h-10 text-base\",\n )}\n />\n );\n\n const badge = showBadge ? <PoweredByMedian /> : null;\n\n const emailButton = (\n <WidgetButton\n type=\"button\"\n size={controlSize}\n disabled={!isValid}\n onClick={() => submitEmail()}\n aria-label=\"Continue with this email\"\n className={cn(\n \"rounded-full\",\n isValid\n ? \"scale-100 opacity-100\"\n : \"scale-90 opacity-40 motion-reduce:scale-100\",\n )}\n >\n <ArrowUp className={isStacked ? \"size-5\" : undefined} />\n </WidgetButton>\n );\n\n return (\n // The whole foot of the panel takes a drop, not just the pill: the pill is\n // a thin thing to aim a file at, and the chips that appear sit up here\n // anyway. Paste rides on the same element, so it is caught wherever the\n // caret happens to be inside the composer. None of that while the email\n // is asked for: there is no message yet to attach anything to.\n <div\n className={cn(\n \"flex shrink-0 flex-col gap-2 pt-1\",\n compact ? \"px-5 pb-4\" : \"px-4 pb-4\",\n )}\n {...(isAsking ? {} : target.targetProps)}\n >\n {!isAsking && attachments.drafts.length > 0 && (\n <ul className=\"flex flex-wrap gap-1.5 px-1.5\">\n {attachments.drafts.map((item) => (\n <li key={item.id} className=\"min-w-0\">\n <AttachmentDraftChip\n draft={item}\n onRemove={() => attachments.remove(item.id)}\n />\n </li>\n ))}\n </ul>\n )}\n\n <div className=\"relative\">\n {/* The slot the notch lives in: the full width of the pill, ending two\n pixels inside its top edge, and clipped.\n\n That clip is the whole trick. The notch does not fade and does not\n need to: it slides down the height of itself and the slot cuts it\n off exactly where the pill begins, so it goes into the composer\n rather than off it. Coming back it comes up out of the same seam.\n The two pixels are what guarantee no hairline of daylight between\n the two shapes, and they are behind the pill, which paints over\n them. */}\n <div\n aria-hidden={!showTab}\n inert={!showTab}\n // The headroom is for the spring: the notch passes where it lands on\n // the way up, and a clip tight to its resting height would flatten\n // the top edge for those few frames.\n className=\"pointer-events-none absolute inset-x-0 bottom-full z-0 -mb-0.5 flex items-end justify-center overflow-hidden pt-2\"\n >\n {/* Square at the bottom and no bottom hairline: that edge is the\n seam with the pill, not an edge of its own. */}\n <div\n className={cn(\n \"grid max-w-[calc(100%-2.5rem)] select-none\",\n \"rounded-t-2xl border border-b-0 bg-muted px-4 pt-2 pb-2.5 text-muted-foreground\",\n isStacked ? \"text-sm\" : \"text-base\",\n showTab ? RISE : SINK,\n !showTab && \"translate-y-full\",\n )}\n >\n {/* One line tall and clipped, so the phrases roll through it.\n\n Both share the cell, so the notch is as wide as the longer of\n the two and holds that width while they change: the words move\n inside a shape that does not. Each is centred, so the short one\n sits in the middle of the notch rather than off to its left. */}\n <span className=\"grid overflow-hidden\">\n <span\n aria-hidden={isSaved}\n className={cn(\n \"col-start-1 row-start-1 flex items-center gap-2 justify-self-center\",\n ROLL,\n isSaved && \"-translate-y-full\",\n )}\n >\n <EnvelopeSimple aria-hidden className=\"size-5 shrink-0\" />\n <span className=\"truncate\">Enter your email</span>\n </span>\n\n <span\n aria-hidden={!isSaved}\n className={cn(\n \"col-start-1 row-start-1 flex items-center gap-2 justify-self-center\",\n ROLL,\n !isSaved && \"translate-y-full\",\n )}\n >\n <Check aria-hidden className=\"size-5 shrink-0\" />\n <span className=\"truncate\">Saved</span>\n </span>\n </span>\n </div>\n </div>\n\n {/* The same surface as the panel behind it, so the composer is a shape\n rather than a slab. Painted rather than left transparent, since it\n is what hides the foot of the tab. A grid with both composers in\n one cell: they sit exactly on top of each other, same height, so\n the swap moves nothing. Whichever one is waiting wears `inert`,\n which takes it out of the tab order and the accessibility tree\n along with the pointer events. */}\n <div\n className={cn(\n TEXT_FIELD_SHELL,\n \"relative z-10 grid bg-card p-2 transition-colors dark:bg-card\",\n // Half the pill's one line height, so it is a full pill at one\n // line and keeps those corners as it grows. The box keeps a\n // card's corner instead: its height is never one line.\n isStacked ? \"rounded-2xl\" : \"items-center rounded-[1.75rem]\",\n // The pill lights up rather than the whole foot of the panel, so a\n // file being dragged in points at the thing it is about to join.\n !isAsking &&\n target.isDropTarget &&\n \"border-ring bg-muted/60 ring-1 ring-ring dark:bg-muted/60\",\n )}\n >\n {/* The message half. Both halves travel down together, the same way\n the notch above them goes, so an answer settles the whole thing\n rather than sending three pieces of it in three directions. */}\n <div\n aria-hidden={isAsking}\n inert={isAsking}\n className={cn(\n \"col-start-1 row-start-1\",\n isStacked ? \"flex flex-col gap-1\" : \"flex items-end gap-1.5\",\n SWAP,\n isAsking && \"-translate-y-1 opacity-0 pointer-events-none\",\n )}\n >\n {/* The same four pieces in both shapes. The pill lays them in a\n row; the box puts the words first and the buttons on a row\n of their own underneath. */}\n {isStacked ? (\n <>\n {draftBox}\n <div className=\"flex items-center gap-1.5\">\n {fileInput}\n {addButton}\n {/* Our name rides the controls row, beside the send, so\n the box is the whole foot of the panel and nothing\n hangs under it. */}\n <div className=\"ml-auto flex items-center gap-2\">\n {badge}\n {sendButton}\n </div>\n </div>\n </>\n ) : (\n <>\n {fileInput}\n {addButton}\n {draftBox}\n {sendButton}\n </>\n )}\n </div>\n\n {/* The answer half. */}\n <div\n aria-hidden={!isAsking}\n inert={!isAsking}\n className={cn(\n \"col-start-1 row-start-1\",\n isStacked ? \"flex flex-col gap-1\" : \"flex items-center gap-1.5\",\n SWAP,\n !isAsking && \"pointer-events-none translate-y-1 opacity-0\",\n )}\n >\n {isStacked ? (\n <>\n {emailBox}\n <div className=\"flex items-center justify-end gap-2\">\n {badge}\n {emailButton}\n </div>\n </>\n ) : (\n <>\n {emailBox}\n {emailButton}\n </>\n )}\n </div>\n </div>\n </div>\n\n {/* Under the pill. The box carries it inside instead. */}\n {!isStacked && badge}\n </div>\n );\n}\n","import { forwardRef, type ImgHTMLAttributes } from \"react\";\n\n/**\n * Stands in for `next/image` when the widget is bundled for npm, via the\n * `paths` entry in this package's tsconfig. The app renders avatars and\n * marks through the framework's image pipeline; the package has no framework,\n * and everything in the widget's closure is an avatar, a mark, or an object\n * URL that never left the browser, all of which a plain img carries the same.\n *\n * Next-only props are swallowed here so they do not land on a DOM element\n * that has never heard of them.\n */\ntype NextOnlyProps = {\n unoptimized?: boolean;\n priority?: boolean;\n quality?: number | string;\n placeholder?: string;\n blurDataURL?: string;\n fill?: boolean;\n loader?: unknown;\n};\n\nconst Image = forwardRef<\n HTMLImageElement,\n ImgHTMLAttributes<HTMLImageElement> & NextOnlyProps\n>(function Image(\n {\n unoptimized: _unoptimized,\n priority: _priority,\n quality: _quality,\n placeholder: _placeholder,\n blurDataURL: _blurDataURL,\n fill: _fill,\n loader: _loader,\n ...props\n },\n ref,\n) {\n return <img ref={ref} {...props} />;\n});\n\nexport default Image;\n","\"use client\";\n\nimport Image from \"next/image\";\n\n/**\n * One picture, drawn by whichever loader is actually allowed to fetch it.\n *\n * A file we hold goes through next/image, which is what keeps a four megabyte\n * screenshot from arriving whole inside a tile the size of a stamp. A linked\n * one lives on somebody else's machine, and the optimizer is deliberately not\n * allowed to fetch arbitrary hosts: `/_next/image` is public and billed per\n * distinct source, so an open host list is somebody else's loop running up\n * our bill. Those are drawn plainly instead.\n *\n * No referrer goes with an outside fetch. Opening a bug report should not\n * tell whoever is hosting the picture which page it was opened from.\n */\nexport function AttachmentImage({\n src,\n alt,\n external = false,\n width,\n height,\n className,\n onError,\n}: {\n src: string;\n alt: string;\n /** True for an address we do not serve, which changes how it is loaded. */\n external?: boolean;\n width: number;\n height: number;\n className?: string;\n onError?: () => void;\n}) {\n if (external) {\n return (\n // eslint-disable-next-line @next/next/no-img-element -- an outside host the optimizer is not allowed to fetch.\n <img\n src={src}\n alt={alt}\n width={width}\n height={height}\n loading=\"lazy\"\n referrerPolicy=\"no-referrer\"\n onError={onError}\n draggable={false}\n className={className}\n />\n );\n }\n\n return (\n <Image\n src={src}\n alt={alt}\n width={width}\n height={height}\n onError={onError}\n draggable={false}\n className={className}\n />\n );\n}\n","\"use client\";\n\nimport Image from \"next/image\";\nimport { File, ImageSquare, Play, WarningCircle, X } from \"@/components/icons\";\nimport { Button } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\nimport type { AttachmentDraft } from \"./use-attachment-drafts\";\n\nconst KIND_ICONS = {\n image: ImageSquare,\n video: Play,\n file: File,\n};\n\n/**\n * One staged file, with a thumbnail where there is one and a way out.\n *\n * `max-w-full` so the chip gives up its preferred width inside something\n * narrow, like the support widget, rather than pushing out of the box.\n */\nexport function AttachmentDraftChip({\n draft,\n onRemove,\n className,\n}: {\n draft: AttachmentDraft;\n onRemove: () => void;\n className?: string;\n}) {\n const hasError = draft.error !== null;\n const Glyph = hasError ? WarningCircle : KIND_ICONS[draft.kind];\n\n return (\n <div\n className={cn(\n \"flex w-48 max-w-full items-center gap-2 rounded-lg border bg-card/60 p-1.5\",\n \"animate-in fade-in-0 zoom-in-95 duration-200 ease-out motion-reduce:animate-none\",\n hasError && \"border-destructive/40\",\n className,\n )}\n >\n {draft.previewUrl ? (\n // Object URLs cannot be optimised, and there is nothing to optimise:\n // the file has not left the browser.\n <Image\n src={draft.previewUrl}\n alt=\"\"\n width={64}\n height={64}\n unoptimized\n className=\"size-8 shrink-0 rounded-md object-cover\"\n />\n ) : (\n <span className=\"grid size-8 shrink-0 place-items-center rounded-md bg-muted\">\n <Glyph\n className={cn(\n \"size-4\",\n hasError ? \"text-destructive\" : \"text-muted-foreground\",\n )}\n />\n </span>\n )}\n\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <p className=\"truncate text-xs font-medium\">{draft.name}</p>\n <p\n className={cn(\n \"truncate text-xs\",\n hasError ? \"text-destructive\" : \"text-muted-foreground\",\n )}\n >\n {draft.error ?? draft.size}\n </p>\n </div>\n\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n onClick={onRemove}\n aria-label={`Remove ${draft.name}`}\n className=\"shrink-0 rounded-full text-muted-foreground\"\n >\n <X />\n </Button>\n </div>\n );\n}\n","\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport type { AttachmentKind } from \"./types\";\n\nconst MAX_BYTES = 25 * 1024 * 1024;\n\n/**\n * Types the backend turns away, checked here too so the chip says so before\n * anything is uploaded rather than after the send fails. The list that counts\n * is `UNSAFE_ATTACHMENT_TYPES` in the backend's `shared/support.ts`; this one\n * is the same list wearing a friendlier moment.\n */\nconst UNSAFE_TYPES = [\n \"text/html\",\n \"application/xhtml+xml\",\n \"image/svg+xml\",\n \"text/xsl\",\n \"application/xslt+xml\",\n];\n\nfunction isUnsafeType(type: string): boolean {\n return UNSAFE_TYPES.includes(type.split(\";\")[0].trim().toLowerCase());\n}\n\nexport type AttachmentDraft = {\n id: string;\n /** The file itself, kept so a caller can hand it on once it is sent. */\n file: File;\n name: string;\n size: string;\n kind: AttachmentKind;\n /** An object URL for pictures, so the chip can show what was picked. */\n previewUrl: string | null;\n /** Set when the file cannot be sent. Shown on the chip in place of the size. */\n error: string | null;\n};\n\nlet nextDraftId = 0;\n\n/**\n * Bytes, written the way a person would say them. Exported because whoever\n * receives a sent file has to label it the same way the chip that staged it\n * did, or the same file changes size on its way into the thread.\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n\n const units = [\"KB\", \"MB\", \"GB\"];\n let value = bytes / 1024;\n let unit = 0;\n\n while (value >= 1024 && unit < units.length - 1) {\n value /= 1024;\n unit += 1;\n }\n\n return `${value < 10 ? value.toFixed(1) : Math.round(value)} ${units[unit]}`;\n}\n\n/** The kinds in the plural, for the line on a chip that has to say no. */\nconst KIND_NAMES: Record<AttachmentKind, string> = {\n image: \"pictures\",\n video: \"videos\",\n file: \"files\",\n};\n\n/** What a file is, from its MIME type. Anything unrecognised is just a file. */\nexport function attachmentKindOf(type: string): AttachmentKind {\n if (type.startsWith(\"image/\")) return \"image\";\n if (type.startsWith(\"video/\")) return \"video\";\n return \"file\";\n}\n\nfunction release(draft: AttachmentDraft) {\n if (draft.previewUrl) URL.revokeObjectURL(draft.previewUrl);\n}\n\n/**\n * Files staged on a composer, from either the picker or a drop. Shared by the\n * agent's composer in the inbox, the visitor's in the support widget, and the\n * feedback panel, so all three agree on what is too big and what a size looks\n * like written down.\n *\n * Nothing is uploaded: a draft is checked, previewed and then thrown away when\n * the message is sent. A file that fails the check stays in the list wearing\n * its reason, so it gets read rather than silently dropped.\n *\n * `files` hands back the originals in the same order, which is what a caller\n * passes on once there is somewhere to send them.\n */\nexport function useAttachmentDrafts({\n accept,\n max,\n}: {\n /**\n * The one kind this composer takes. Anything else still appears, wearing\n * the reason it cannot go, because a picture that vanished on the way into\n * the box reads as the box being broken.\n */\n accept?: AttachmentKind;\n /** The most that may be staged at once. Unbounded when left off. */\n max?: number;\n} = {}) {\n const [drafts, setDrafts] = useState<AttachmentDraft[]>([]);\n\n // Object URLs outlive the elements that used them, so any still open when\n // the composer goes away get revoked on the way out.\n const draftsRef = useRef(drafts);\n useEffect(() => {\n draftsRef.current = drafts;\n }, [drafts]);\n useEffect(() => () => draftsRef.current.forEach(release), []);\n\n function add(files: FileList | File[]) {\n const picked = Array.from(files).map((file, index): AttachmentDraft => {\n const isTooBig = file.size > MAX_BYTES;\n const isUnsafe = isUnsafeType(file.type);\n const kind = attachmentKindOf(file.type);\n const isWrongKind = accept !== undefined && kind !== accept;\n const isOverCap = max !== undefined && drafts.length + index >= max;\n const error = isTooBig\n ? `Larger than the ${formatFileSize(MAX_BYTES)} limit`\n : isUnsafe\n ? \"This kind of file cannot be sent\"\n : isWrongKind\n ? `Only ${KIND_NAMES[accept]} can go here`\n : isOverCap\n ? `Only ${max} at a time`\n : null;\n\n return {\n id: `draft-${(nextDraftId += 1)}`,\n file,\n name: file.name,\n size: formatFileSize(file.size),\n kind,\n previewUrl:\n kind === \"image\" && error === null ? URL.createObjectURL(file) : null,\n error,\n };\n });\n\n setDrafts([...drafts, ...picked]);\n }\n\n function remove(id: string) {\n const going = drafts.find((draft) => draft.id === id);\n if (going) release(going);\n setDrafts(drafts.filter((draft) => draft.id !== id));\n }\n\n function clear() {\n drafts.forEach(release);\n setDrafts([]);\n }\n\n return {\n drafts,\n add,\n remove,\n clear,\n /** The originals, in the order they were picked. */\n files: drafts.map((draft) => draft.file),\n /** True while something in the list cannot be sent. */\n hasBlocker: drafts.some((draft) => draft.error !== null),\n /** True once the cap is reached, for a picker that should go quiet. */\n isFull: max !== undefined && drafts.length >= max,\n };\n}\n","\"use client\";\n\nimport { useRef, useState, type ClipboardEvent, type DragEvent } from \"react\";\n\n/**\n * The two ways a file arrives that are not the picker: dragged onto the\n * composer, or pasted into it. Both are how somebody who has just taken a\n * screenshot expects to send it, and neither should need the plus button.\n *\n * Shared by the inbox and the widget, so a screenshot lands the same way on\n * both sides of a conversation. This only notices and collects; what becomes\n * of the files is the caller's business, which is what lets an internal note\n * turn them away.\n */\nexport function useAttachmentTarget({\n onFiles,\n disabled = false,\n}: {\n onFiles: (files: File[]) => void;\n /** Somewhere a file is not welcome, like a note the backend keeps to words. */\n disabled?: boolean;\n}) {\n const [isDropTarget, setIsDropTarget] = useState(false);\n\n // Dragging over a child element fires leave on the parent, so the highlight\n // counts its way in and out rather than trusting a single leave to mean the\n // pointer has actually gone.\n const depth = useRef(0);\n\n function handleDragEnter(event: DragEvent<HTMLElement>) {\n // Dragging a selection of text across the page is not an attachment.\n if (disabled || !event.dataTransfer.types.includes(\"Files\")) return;\n depth.current += 1;\n setIsDropTarget(true);\n }\n\n function handleDragLeave() {\n depth.current = Math.max(0, depth.current - 1);\n if (depth.current === 0) setIsDropTarget(false);\n }\n\n function handleDrop(event: DragEvent<HTMLElement>) {\n event.preventDefault();\n depth.current = 0;\n setIsDropTarget(false);\n if (disabled) return;\n\n const files = Array.from(event.dataTransfer.files);\n if (files.length > 0) onFiles(files);\n }\n\n function handlePaste(event: ClipboardEvent<HTMLElement>) {\n if (disabled) return;\n\n // Ordinary text carries no files, and is left alone entirely: caret,\n // selection, undo history and all. A screenshot does carry one, and\n // taking it means the markup the clipboard came with does not also end\n // up in the message.\n const files = Array.from(event.clipboardData.files);\n if (files.length === 0) return;\n\n event.preventDefault();\n onFiles(files);\n }\n\n return {\n /** True while something is being dragged over it, for the highlight. */\n isDropTarget,\n /** Spread onto whatever counts as the target. */\n targetProps: {\n onDragEnter: handleDragEnter,\n onDragLeave: handleDragLeave,\n // Without this the browser refuses the drop and opens the file instead,\n // taking the page, and whatever was being written, with it.\n onDragOver: (event: DragEvent<HTMLElement>) => event.preventDefault(),\n onDrop: handleDrop,\n onPaste: handlePaste,\n },\n };\n}\n","import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Slot } from \"radix-ui\";\n\nimport { cn } from \"@/lib/utils\";\n\n/**\n * The widget's own button, deliberately forked from `ui/button`.\n *\n * The widget ships on other people's sites, so its look is a contract with\n * them, not with our chrome. When the app restyles its buttons, the widget\n * must not come along for the ride; this file is where it stays put.\n */\nconst widgetButtonVariants = cva(\n \"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/80\",\n outline:\n \"border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary,oklch(0.97_0_0)),var(--foreground,oklch(0.145_0_0))_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground\",\n ghost:\n \"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50\",\n destructive:\n \"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default:\n \"h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2\",\n xs: \"h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3\",\n sm: \"h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5\",\n lg: \"h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2\",\n // The support panel's scale: 16px words, 20px glyphs.\n xl: \"h-10 gap-2 px-4 text-base has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3 [&_svg:not([class*='size-'])]:size-5\",\n icon: \"size-8\",\n \"icon-xs\":\n \"size-6 rounded-[min(var(--radius-md),10px)] [&_svg:not([class*='size-'])]:size-3\",\n \"icon-sm\": \"size-7 rounded-[min(var(--radius-md),12px)]\",\n \"icon-lg\": \"size-9\",\n \"icon-xl\": \"size-10 [&_svg:not([class*='size-'])]:size-5\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nfunction WidgetButton({\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof widgetButtonVariants> & {\n asChild?: boolean;\n }) {\n const Comp = asChild ? Slot.Root : \"button\";\n\n return (\n <Comp\n data-slot=\"widget-button\"\n data-variant={variant}\n data-size={size}\n className={cn(widgetButtonVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\n\nexport { WidgetButton, widgetButtonVariants };\n","/** The widget keeps its original fields when the app's controls change. */\nexport const WIDGET_TEXT_FIELD_SHELL = [\n \"rounded-lg border border-input bg-transparent text-sm max-sm:text-base transition-[color,box-shadow] outline-none dark:bg-input/30\",\n \"placeholder:text-muted-foreground\",\n \"focus-within:border-ring\",\n \"aria-invalid:border-destructive\",\n \"disabled:pointer-events-none disabled:opacity-50\",\n].join(\" \");\n","import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/**\n * Grows with its content up to `max-h`, courtesy of `field-sizing-content`.\n * Browsers without it fall back to the `rows` the caller sets.\n */\nfunction Textarea({ className, ...props }: React.ComponentProps<\"textarea\">) {\n return (\n <textarea\n data-slot=\"textarea\"\n className={cn(\n \"field-sizing-content w-full resize-none bg-transparent text-sm outline-none max-sm:text-base\",\n \"placeholder:text-muted-foreground\",\n \"disabled:pointer-events-none disabled:opacity-50\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { Textarea };\n","\"use client\";\n\nimport { useCallback, useEffect, useRef } from \"react\";\n\n/** Refreshed well inside the server's TTL, without spamming a mutation. */\nconst REFRESH_MS = 2500;\n/**\n * This long without a keystroke means the sentence stalled. Generous on\n * purpose: dots that vanish at every thinking pause and pop back at the next\n * key read as flicker, not presence.\n */\nconst IDLE_MS = 8000;\n\n/**\n * Turns keystrokes into a calm typing signal: `ping` on every change sends\n * \"typing\" at most every couple of seconds, going quiet after a pause, on\n * `stop`, and on unmount.\n *\n * `settle` goes quiet locally without announcing it. That is for sending:\n * the message itself is the stop, and racing a \"stopped typing\" mutation in\n * ahead of it would delete the pending bubble the reply is about to fill,\n * which is the difference between a bubble that morphs and one that flashes.\n *\n * Failures are swallowed on purpose. A typing indicator is decoration, and\n * decoration does not get to put errors in front of anyone.\n */\nexport function useTypingSignal(onChange?: (isTyping: boolean) => unknown) {\n // Held in a ref so ping and stop keep stable identities while the caller\n // re-renders with fresh closures.\n const onChangeRef = useRef(onChange);\n useEffect(() => {\n onChangeRef.current = onChange;\n }, [onChange]);\n\n const isTypingRef = useRef(false);\n const lastSentRef = useRef(0);\n const idleTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const send = useCallback((isTyping: boolean) => {\n try {\n void Promise.resolve(onChangeRef.current?.(isTyping)).catch(() => {});\n } catch {\n // Decorative; see above.\n }\n }, []);\n\n /** Goes quiet without telling anyone. The send that follows says it. */\n const settle = useCallback(() => {\n if (idleTimer.current) clearTimeout(idleTimer.current);\n idleTimer.current = null;\n isTypingRef.current = false;\n lastSentRef.current = 0;\n }, []);\n\n const stop = useCallback(() => {\n const wasTyping = isTypingRef.current;\n settle();\n if (wasTyping) send(false);\n }, [send, settle]);\n\n const ping = useCallback(() => {\n if (idleTimer.current) clearTimeout(idleTimer.current);\n idleTimer.current = setTimeout(stop, IDLE_MS);\n\n const now = Date.now();\n if (!isTypingRef.current || now - lastSentRef.current > REFRESH_MS) {\n isTypingRef.current = true;\n lastSentRef.current = now;\n send(true);\n }\n }, [send, stop]);\n\n useEffect(() => () => stop(), [stop]);\n\n return { ping, stop, settle };\n}\n","\"use client\";\n\nimport { useLayoutEffect, useRef, type RefObject } from \"react\";\n\n/**\n * A box whose height follows the field inside it, on a curve.\n *\n * The field sizes itself to its own words, which is what `field-sizing:\n * content` does, and it does it between one frame and the next: a line\n * wraps, and the box around it is a line taller with nothing in between.\n * So the field goes in a box with a height of its own, written from the\n * field's every time the field changes, and a transition on the box does\n * the travel. The field keeps its natural size underneath, which means the\n * caret and the words are already where they will end up; the box is what\n * catches them up.\n *\n * The box clips rather than scrolls while it is behind. A box that can\n * scroll is a box the browser will scroll to show a caret in, and that would\n * drag the words up inside it on every new line.\n *\n * The first height is written in the same layout pass as the first paint,\n * so the first keystroke that wraps has a number to travel from. A\n * transition from `auto` is not a transition at all.\n *\n * `offsetHeight` rather than the client rect, since the modal's panel is\n * scaled down while it is closed and a rect read then is scaled with it.\n * The field's height is whole lines of whole pixels anyway.\n */\nexport function useEasedHeight(field: RefObject<HTMLElement | null>) {\n const box = useRef<HTMLDivElement>(null);\n\n useLayoutEffect(() => {\n const outer = box.current;\n const inner = field.current;\n if (!outer || !inner) return;\n\n const follow = () => {\n outer.style.height = `${inner.offsetHeight}px`;\n };\n follow();\n if (typeof ResizeObserver === \"undefined\") return;\n\n const observer = new ResizeObserver(follow);\n observer.observe(inner);\n return () => {\n observer.disconnect();\n outer.style.height = \"\";\n };\n }, [field]);\n\n return box;\n}\n","/**\n * The Median marks, as geometry rather than as files.\n *\n * The same brand components render inside the widget package, which is\n * installed into someone else's app and has no way to reach our public\n * directory. Keeping the paths here is what lets a mark draw itself\n * anywhere: in the app, in the npm bundle, and through a CSS mask, all from\n * one copy.\n *\n * `public/` still carries the SVGs, for the things outside the app that want\n * a file to point at: favicons, OG images, the docs site.\n */\n\n/** What the artwork is drawn in. Dark mode inverts it rather than restating it. */\nexport const BRAND_INK = \"#191919\";\n\n/** The mark, in its own square box. */\nexport const LOGO = {\n viewBox: \"0 0 58 58\",\n paths: [\n \"M48.9788 0H8.97304C4.01742 0 0 4.11877 0 9.22508V20.7384C0 21.5573 0.577354 22.0631 1.37122 21.8223L46.068 10.1885C47.2227 9.87541 47.8963 10.7666 47.5595 11.9228L35.8922 56.5789C35.6757 57.446 36.2049 58 37.0709 58H48.7383C53.7661 58 58 53.6885 58 48.799V9.22508C58 4.11877 53.9585 0 48.9788 0Z\",\n \"M0 26.976V49.0151C0 53.9287 3.99336 57.9993 8.97304 57.9993H30.8884C31.7545 57.9993 32.1634 57.2767 31.9229 56.4337L28.579 44.3182C28.4347 43.8606 28.4587 43.4511 28.6993 42.9935L39.3803 21.5084C39.9818 20.3041 38.5384 19.4129 37.6723 20.3282L10.4164 47.3531C9.64662 48.1721 8.41974 47.2327 8.85276 46.1488L13.9527 30.9744C14.2173 30.1073 13.8565 29.2642 12.8221 29.0716L1.4915 25.9644C0.60141 25.6513 0 26.133 0 26.976Z\",\n ],\n} as const;\n\n/** The word, sharing the mark's 58 unit height so the two set together. */\nexport const WORDMARK = {\n viewBox: \"0 0 253 58\",\n width: 253,\n height: 58,\n paths: [\n \"M10.1999 2.52174L29.0138 35.0522L47.8637 2.52174H57.8834V56.9913H47.7916V21.1106L33.1586 46.2199H24.6527L9.98362 20.9665V56.9913H0V2.52174H10.1999Z\",\n \"M102.616 40.6H71.5115C72.1363 43.4099 73.5058 45.5834 75.6203 47.1205C77.7588 48.6335 80.3778 49.3901 83.4775 49.3901C85.664 49.3901 87.6823 48.9458 89.5325 48.0571C91.4067 47.1445 92.8363 45.9317 93.8215 44.4186L101.57 49.318C99.8165 51.9598 97.3176 54.0613 94.0738 55.6224C90.83 57.2075 87.2979 58 83.4775 58C79.2966 58 75.5362 57.0874 72.1963 55.2621C68.8805 53.4609 66.2975 50.9752 64.4473 47.805C62.6212 44.6108 61.7081 41.0563 61.7081 37.1416C61.7081 34.2116 62.2247 31.4617 63.2579 28.8919C64.3152 26.2981 65.7448 24.0766 67.5469 22.2273C69.349 20.3781 71.5235 18.9251 74.0705 17.8683C76.6175 16.7876 79.3326 16.2472 82.216 16.2472C88.1509 16.2472 93.0526 18.1085 96.9211 21.8311C100.814 25.5536 102.76 30.3329 102.76 36.1689C102.76 36.8654 102.712 38.3424 102.616 40.6ZM71.5115 33.5031H93.0646C92.8003 30.8373 91.695 28.6998 89.7487 27.0907C87.8265 25.4816 85.3396 24.677 82.2881 24.677C79.5969 24.677 77.2662 25.4576 75.2959 27.0186C73.3497 28.5797 72.0882 30.7412 71.5115 33.5031Z\",\n \"M136.608 1.54907H146.735V56.9913H136.608V51.3714C135.286 53.5329 133.508 55.1781 131.273 56.3068C129.039 57.4356 126.516 58 123.705 58C118.202 58 113.601 55.9946 109.901 51.9839C106.2 47.9731 104.35 43.0257 104.35 37.1416C104.326 34.2836 104.807 31.5578 105.792 28.964C106.777 26.3702 108.122 24.1366 109.828 22.2634C111.558 20.366 113.625 18.877 116.028 17.7963C118.454 16.7155 121.013 16.1992 123.705 16.2472C126.516 16.2472 129.039 16.8116 131.273 17.9404C133.508 19.0451 135.286 20.6663 136.608 22.8037V1.54907ZM133.436 45.8957C135.55 43.6861 136.608 40.7681 136.608 37.1416C136.608 33.5151 135.55 30.5971 133.436 28.3876C131.345 26.154 128.642 25.0373 125.326 25.0373C122.011 25.0373 119.271 26.1901 117.109 28.4957C115.042 30.7772 114.009 33.6592 114.009 37.1416C114.009 40.6 115.067 43.482 117.181 45.7876C119.295 48.0692 122.011 49.2099 125.326 49.2099C128.642 49.2099 131.345 48.1052 133.436 45.8957Z\",\n \"M150.957 5.98012C150.957 4.29896 151.557 2.88199 152.759 1.72919C153.984 0.576398 155.486 0 157.264 0C159.018 0 160.496 0.588406 161.697 1.76522C162.923 2.91801 163.535 4.32298 163.535 5.98012C163.535 7.70932 162.935 9.1383 161.733 10.2671C160.556 11.3959 159.066 11.9602 157.264 11.9602C155.462 11.9602 153.96 11.3959 152.759 10.2671C151.557 9.1383 150.957 7.70932 150.957 5.98012ZM162.274 17.2919V56.9913H152.218V17.2919H162.274Z\",\n \"M198.176 17.2919H208.304V56.9913H198.176V51.3714C196.854 53.5329 195.076 55.1781 192.842 56.3068C190.607 57.4356 188.084 58 185.273 58C179.77 58 175.169 55.9946 171.469 51.9839C167.768 47.9731 165.918 43.0257 165.918 37.1416C165.894 34.2836 166.375 31.5578 167.36 28.964C168.345 26.3702 169.691 24.1366 171.397 22.2634C173.127 20.366 175.193 18.877 177.596 17.7963C180.023 16.7155 182.582 16.1992 185.273 16.2472C188.084 16.2472 190.607 16.8116 192.842 17.9404C195.076 19.0451 196.854 20.6663 198.176 22.8037V17.2919ZM195.004 45.8957C197.119 43.6861 198.176 40.7681 198.176 37.1416C198.176 33.5151 197.119 30.5971 195.004 28.3876C192.914 26.154 190.211 25.0373 186.895 25.0373C183.579 25.0373 180.84 26.1901 178.677 28.4957C176.611 30.7772 175.578 33.6592 175.578 37.1416C175.578 40.6 176.635 43.482 178.749 45.7876C180.864 48.0692 183.579 49.2099 186.895 49.2099C190.211 49.2099 192.914 48.1052 195.004 45.8957Z\",\n \"M253 34.5478V56.9913H242.872V35.2683C242.872 31.882 242.115 29.3362 240.602 27.6311C239.088 25.9019 236.865 25.0373 233.934 25.0373C231.002 25.0373 228.588 26.082 226.689 28.1714C224.791 30.2369 223.842 32.9267 223.842 36.241V56.9913H213.786V17.2919H223.842V22.9478C224.995 20.8824 226.665 19.2493 228.852 18.0484C231.062 16.8476 233.549 16.2472 236.313 16.2472C241.503 16.2472 245.575 17.8563 248.531 21.0745C251.51 24.2928 253 28.7839 253 34.5478Z\",\n ],\n} as const;\n\n/**\n * The mark as a `url()`, for CSS that paints through its shape rather than\n * painting the artwork itself. Inline, because a mask is fetched the way an\n * image is and a host has nothing at `/median-logo.svg` to answer with.\n *\n * A mask reads only coverage, so the paths go in without a fill and come out\n * opaque.\n */\nexport const LOGO_MASK_URL = `url(\"data:image/svg+xml,${encodeURIComponent(\n `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"${LOGO.viewBox}\">` +\n LOGO.paths.map((d) => `<path d=\"${d}\"/>`).join(\"\") +\n `</svg>`,\n)}\")`;\n","import { BRAND_INK, LOGO, LOGO_MASK_URL, WORDMARK } from \"@/components/brand-art\";\nimport { cn } from \"@/lib/utils\";\n\n/** The artwork is solid black, so it gets inverted for dark mode. */\nconst INVERT_IN_DARK = \"dark:invert\";\n\n/**\n * The marks, drawn inline.\n *\n * Inline rather than fetched, because these render inside the widget package\n * too, where there is no public directory of ours to ask. See\n * `brand-art.ts` for the geometry.\n */\n\nexport function Wordmark({\n width = 132,\n className = \"\",\n}: {\n width?: number;\n className?: string;\n}) {\n return (\n <svg\n viewBox={WORDMARK.viewBox}\n width={width}\n height={Math.round((width * WORDMARK.height) / WORDMARK.width)}\n role=\"img\"\n aria-label=\"Median\"\n className={`${INVERT_IN_DARK} ${className}`}\n >\n {WORDMARK.paths.map((d) => (\n <path key={d} d={d} fill={BRAND_INK} />\n ))}\n </svg>\n );\n}\n\nexport function Logo({\n size = 104,\n tone = \"brand\",\n className = \"\",\n}: {\n size?: number;\n /**\n * `brand` is the mark in its own ink. `current` takes the text colour\n * around it, for a mark standing in a row of icons and wearing their grey.\n */\n tone?: \"brand\" | \"current\";\n className?: string;\n}) {\n const isBrand = tone === \"brand\";\n\n return (\n <svg\n viewBox={LOGO.viewBox}\n width={size}\n height={size}\n aria-hidden\n className={`${isBrand ? INVERT_IN_DARK : \"\"} ${className}`}\n >\n {LOGO.paths.map((d) => (\n <path key={d} d={d} fill={isBrand ? BRAND_INK : \"currentColor\"} />\n ))}\n </svg>\n );\n}\n\n/**\n * The mark and the word set together on one line, which is the logo as it is\n * signed: for the foot of a page outside the app, where the name has to be\n * said rather than assumed.\n */\nexport function FullLogo({\n /** How tall the mark stands. The word is scaled to match it. */\n height = 20,\n className = \"\",\n}: {\n height?: number;\n className?: string;\n}) {\n return (\n <span\n role=\"img\"\n aria-label=\"Median\"\n className={`inline-flex items-center gap-2 ${className}`}\n >\n {/* `role=\"img\"` above takes the whole lockup out of the tree, so the\n word's own label inside it is never read twice. */}\n <Logo size={height} />\n <Wordmark width={Math.round((height * WORDMARK.width) / WORDMARK.height)} />\n </span>\n );\n}\n\n/**\n * The mark with a band of light travelling through it, for a wait that has\n * no shape yet: a conversation loading, an answer on its way.\n *\n * Painted by a gradient showing through a mask cut in the logo's shape,\n * rather than by the artwork itself, so the band travels across the mark\n * instead of over the top of it.\n */\nexport function ShimmerLogo({\n label,\n className,\n}: {\n label: string;\n className?: string;\n}) {\n return (\n <span\n role=\"status\"\n aria-label={label}\n style={{\n maskImage: LOGO_MASK_URL,\n maskSize: \"contain\",\n maskRepeat: \"no-repeat\",\n maskPosition: \"center\",\n WebkitMaskImage: LOGO_MASK_URL,\n WebkitMaskSize: \"contain\",\n WebkitMaskRepeat: \"no-repeat\",\n WebkitMaskPosition: \"center\",\n backgroundImage:\n // With the widget's own light values behind the host's, since this\n // mark also draws on pages that define neither.\n \"linear-gradient(115deg, var(--muted, oklch(0.955 0 0)) 34%, var(--muted-foreground, oklch(0.556 0 0)) 50%, var(--muted, oklch(0.955 0 0)) 66%)\",\n backgroundSize: \"300% 100%\",\n }}\n className={cn(\n \"size-12 animate-shimmer motion-reduce:animate-none motion-reduce:bg-muted\",\n className,\n )}\n />\n );\n}\n","import { Logo, Wordmark } from \"@/components/brand\";\nimport { cn } from \"@/lib/utils\";\n\nconst MEDIAN_URL = \"https://median.sh\";\n\n/**\n * Our name on somebody else's site, so it sits under the composer rather than\n * inside it: a quiet line at the foot of the panel, not a control.\n *\n * It opens in a new tab, since a visitor who taps it is halfway through asking\n * for help and should not lose the page they were on.\n */\nexport function PoweredByMedian({ className }: { className?: string }) {\n return (\n <a\n href={MEDIAN_URL}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className={cn(\n // It shares the foot of the feedback panel with two buttons, so it is\n // held at its own width and kept on one line: a flex item that can be\n // squeezed folds \"Powered by\" onto a second row.\n \"flex shrink-0 items-center justify-center gap-1.5 rounded-md py-1 text-xs whitespace-nowrap text-muted-foreground\",\n \"transition-opacity hover:opacity-70\",\n \"focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-none\",\n className,\n )}\n >\n Powered by\n <Logo size={12} className=\"opacity-80\" />\n <Wordmark width={40} className=\"opacity-80\" />\n </a>\n );\n}\n","/**\n * The corner every widget surface is cut to: the panel, the teaser, the\n * feedback panel, the contact form, and the clip and shadow the corner\n * launcher draws the panel through.\n *\n * Written out rather than `rounded-xl`, because the app's radius scale is\n * inlined when the stylesheet is built, and pinning `--radius` on the widget\n * cannot pin the multiplier. When the app's scale moved, the panel grew\n * rounder than the clip behind it, and the clip's fill showed at each\n * corner. This is 0.625rem at the 1.4 the widget's published stylesheet\n * uses, which is the corner the panel had before that.\n *\n * Two spellings of one number: the class where the Tailwind compiler can\n * read it, and the length for a clip path. Keep them together.\n */\nexport const WIDGET_RADIUS = \"0.875rem\";\nexport const WIDGET_CORNER = \"rounded-[0.875rem]\";\n","import { ArrowRight } from \"@/components/icons\";\nimport { WidgetButton } from \"./widget-button\";\nimport type { SupportDestination, SupportFollow } from \"./types\";\n\n/**\n * The button a tool's destination becomes.\n *\n * One of the team's tools has worked out which page answers the question,\n * and this is the offer of it. The page changes when this is pressed and at\n * no other time, so nobody is carried off a form they were halfway through\n * by a reply they had not finished reading.\n *\n * A filled pill, because it is the one thing in a thread to press rather\n * than read, and an outline of it would sit under a bubble looking like more\n * of the bubble. The arrow leans into the press on the way. The label says\n * what happens rather than where, since the message above it has just said\n * where in the team's own words; the address goes to a screen reader, which\n * has no message above it to read from.\n */\nexport function WidgetDestination({\n destination,\n onFollow,\n}: {\n destination: SupportDestination;\n onFollow: SupportFollow;\n}) {\n return (\n <WidgetButton\n type=\"button\"\n onClick={() => onFollow(destination)}\n aria-label={`Take me to ${destination.path}`}\n // The same shape wherever it turns up. It is one button doing one\n // thing, so it hugs its label under a message and over the composer\n // alike, rather than stretching to whatever it happens to sit in.\n //\n // The side padding is the bubble's own, to the rem, so the label starts\n // exactly under the words that explain it. A pill and a bubble stacked\n // with their text on two different left edges is the thing that reads\n // as crooked.\n className=\"group/go h-auto max-w-full justify-start gap-2.5 rounded-full px-4 py-2.5 text-left text-base\"\n >\n <span className=\"min-w-0 flex-1 truncate\">Take me there</span>\n <ArrowRight\n aria-hidden\n className=\"size-4 transition-transform group-hover/go:translate-x-0.5\"\n />\n </WidgetButton>\n );\n}\n","\"use client\";\n\nimport Image from \"next/image\";\nimport { useState } from \"react\";\nimport { InitialBadge } from \"@/components/initial-badge\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * A profile photo, falling back to a letter if there is no image or it fails\n * to load. `className` lands last on both branches, so a caller can override\n * the size and the letter's type scale together.\n */\nexport function Avatar({\n src,\n name,\n sourceSize = 48,\n unoptimized = false,\n className,\n}: {\n src: string | null;\n name: string;\n /** Intrinsic pixels to request. Aim for twice the rendered size. */\n sourceSize?: number;\n /**\n * Serve the picture as it came rather than through the image optimizer.\n * For faces from somebody else's site: the optimizer only fetches hosts\n * next.config names, and a customer's picture could be on any of them.\n */\n unoptimized?: boolean;\n className?: string;\n}) {\n // Which picture failed, rather than whether one did. A plain boolean never\n // cleared itself, so one bad response left the letter in place for the life\n // of the component: switch organizations and the next face inherited the\n // last one's failure, and a picture that was fine again stayed a letter\n // until the whole page reloaded.\n const [failedSrc, setFailedSrc] = useState<string | null>(null);\n const hasFailed = failedSrc !== null && failedSrc === src;\n const shape = \"size-4.5 shrink-0 rounded-full\";\n\n if (!src || hasFailed) {\n return (\n <InitialBadge\n value={name}\n className={cn(shape, \"border-none text-[10px]\", className)}\n />\n );\n }\n\n return (\n <Image\n src={src}\n alt=\"\"\n width={sourceSize}\n height={sourceSize}\n unoptimized={unoptimized}\n onError={() => setFailedSrc(src)}\n draggable={false}\n className={cn(shape, \"bg-muted object-cover ring-1 ring-foreground/5\", className)}\n />\n );\n}\n","import { cn } from \"@/lib/utils\";\n\nconst TONES = [\n \"bg-[var(--avatar-blue,#e0edfa)] text-[var(--avatar-blue-foreground,#37628c)]\",\n \"bg-[var(--avatar-violet,#eae4f7)] text-[var(--avatar-violet-foreground,#705894)]\",\n \"bg-[var(--avatar-amber,#f8ead6)] text-[var(--avatar-amber-foreground,#8b6434)]\",\n \"bg-[var(--avatar-teal,#dbefea)] text-[var(--avatar-teal-foreground,#3c766d)]\",\n \"bg-[var(--avatar-rose,#f5e2e8)] text-[var(--avatar-rose-foreground,#92586c)]\",\n];\n\n/** A name keeps its color across lists, menus, and both themes. */\nfunction toneFor(value: string): string {\n let hash = 0;\n for (const character of value.trim().toLowerCase()) {\n hash = (hash * 31 + character.charCodeAt(0)) >>> 0;\n }\n return TONES[hash % TONES.length];\n}\n\n/**\n * A letter stand in for an avatar. Decorative: the readable name always sits\n * next to it, so this stays out of the accessibility tree.\n */\nexport function InitialBadge({\n value,\n className,\n}: {\n value: string;\n className?: string;\n}) {\n return (\n <span\n aria-hidden\n className={cn(\n // Sized to match UserAvatar, since the two sit side by side in the nav.\n \"grid size-4.5 shrink-0 place-items-center rounded border border-foreground/5 text-[10px] font-medium\",\n toneFor(value),\n className,\n )}\n >\n {value.trim().charAt(0).toUpperCase() || \"?\"}\n </span>\n );\n}\n","import { Avatar } from \"@/components/avatar\";\nimport { Sparkle } from \"@/components/icons\";\nimport { cn } from \"@/lib/utils\";\nimport type { SupportAgent } from \"./types\";\n\n/**\n * An agent's face, or a mark where the AI is concerned. Squares rather than\n * the circles the inbox uses: the widget sits on somebody else's site next to\n * their own chrome, and a rounded square reads as a product mark there instead\n * of as another person's photo.\n *\n * Sizing comes from `className` so a caller can pick one scale for the header\n * and a smaller one for a read receipt. `label` overrides the name a screen\n * reader hears, which is how a receipt says \"Seen by Median AI\" from the same\n * one element rather than announcing the name twice.\n */\nexport function WidgetAvatar({\n agent,\n label,\n className,\n}: {\n agent: SupportAgent;\n label?: string;\n className?: string;\n}) {\n const shape = \"size-7 shrink-0 rounded-lg\";\n\n // The AI wears the mark until somebody gives it a face in settings; a\n // configured picture takes over and renders like anyone else's.\n if (agent.kind === \"ai\" && !agent.avatarUrl) {\n return (\n <span\n role=\"img\"\n aria-label={label ?? agent.name}\n className={cn(\n \"grid place-items-center bg-foreground text-background\",\n shape,\n className,\n )}\n >\n <Sparkle className=\"size-[55%]\" />\n </span>\n );\n }\n\n // Wrapped rather than labelled directly, because Avatar renders a decorative\n // image and has no name of its own to override.\n return (\n <span\n role=\"img\"\n aria-label={label ?? agent.name}\n className={cn(\"inline-flex\", shape, className)}\n >\n <Avatar\n src={agent.avatarUrl}\n name={agent.name}\n className=\"size-full rounded-[inherit] bg-muted bg-none font-semibold text-muted-foreground shadow-none ring-0\"\n />\n </span>\n );\n}\n\n/**\n * Who is around, in the corner of the header. A row rather than an overlap,\n * since two or three faces read more clearly side by side at this size.\n */\nexport function WidgetAvatarStack({\n team,\n className,\n avatarClassName,\n}: {\n team: SupportAgent[];\n className?: string;\n /** Each face's size. The header's default, unless it is compact. */\n avatarClassName?: string;\n}) {\n if (team.length === 0) return null;\n\n return (\n <div className={cn(\"flex items-center gap-1.5\", className)}>\n {team.map((agent) => (\n <WidgetAvatar\n key={agent.id}\n agent={agent}\n className={avatarClassName ?? \"size-9\"}\n />\n ))}\n </div>\n );\n}\n","import type { ComponentType } from \"react\";\nimport { Bug, CaretRight, ChatCircleDots, Lightbulb } from \"@/components/icons\";\nimport { cn } from \"@/lib/utils\";\nimport { WidgetAvatar } from \"./widget-avatar\";\nimport { WidgetButton } from \"./widget-button\";\nimport type { SupportAgent } from \"./types\";\n\n/**\n * The three things people open a support widget to do, and the message each\n * row sends.\n *\n * The messages are written as something a person would actually type, since\n * that is exactly what they become: the row sends it and the agent takes it\n * from there.\n */\nconst STARTERS: {\n label: string;\n glyph: ComponentType<{ className?: string }>;\n message: string;\n}[] = [\n {\n label: \"Ask a question\",\n glyph: ChatCircleDots,\n message: \"I have a question\",\n },\n {\n label: \"Report a bug\",\n glyph: Bug,\n message: \"I want to report a bug\",\n },\n {\n label: \"Make a suggestion\",\n glyph: Lightbulb,\n message: \"I have a suggestion\",\n },\n];\n\n/**\n * A conversation with nothing in it yet.\n *\n * Who is about to answer, and three ways to start. The AI goes first because\n * it is the one that always replies; a team of only people falls back to\n * whoever is at the front.\n *\n * The invitation names the organization, because that is the whole of what\n * the agent will talk about. Saying so here is cheaper than declining later.\n *\n * The rows send rather than fill the box in. Somebody who knows what they\n * want types it; somebody who is looking at three rows wants the shortest\n * way to the first reply.\n */\nexport function WidgetEmpty({\n team,\n orgName,\n onPick,\n isSending = false,\n isWaiting = false,\n compact = false,\n}: {\n team: SupportAgent[];\n /** Whose site this is, so the invitation can say what it covers. */\n orgName?: string;\n /** Omit to leave the rows off, for a widget with nowhere to send them. */\n onPick?: (message: string) => void;\n /** Holds the rows while a message is on its way. */\n isSending?: boolean;\n /**\n * Holds them for something else the visitor has to do first, which is the\n * composer's question. The rows stay where they are while it is up rather\n * than arriving once it is answered: three rows appearing in a column that\n * centres itself would shove everything above them up the panel, and an\n * answer should not move the furniture.\n */\n isWaiting?: boolean;\n /** The denser scale. See `SupportWidget`. */\n compact?: boolean;\n}) {\n const agent = team.find((member) => member.kind === \"ai\") ?? team[0];\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col items-center justify-center gap-5 px-6 pb-2 text-center\">\n {agent && (\n <WidgetAvatar\n agent={agent}\n className={cn(\n \"animate-in rounded-2xl duration-500 ease-out fade-in-0 zoom-in-95 motion-reduce:animate-none\",\n compact ? \"size-14\" : \"size-16\",\n )}\n />\n )}\n\n {/* A beat behind the mark, so the two read as one thing settling rather\n than as a block appearing. Backwards fill holds it out of sight\n through the delay instead of showing it and taking it away. */}\n <div className=\"flex max-w-72 animate-in flex-col gap-1 delay-150 duration-500 ease-out fade-in-0 slide-in-from-bottom-2 fill-mode-backwards motion-reduce:animate-none\">\n {agent && (\n <p\n className={cn(\n \"font-semibold tracking-tight\",\n compact ? \"text-lg\" : \"text-xl\",\n )}\n >\n {agent.name}\n </p>\n )}\n <p\n className={cn(\n \"text-balance text-muted-foreground\",\n compact ? \"text-sm\" : \"text-base\",\n )}\n >\n {orgName ? `How can we help you with ${orgName}?` : \"How can we help?\"}\n </p>\n </div>\n\n {/* Held to a column the corner panel's width, so the same three rows\n sit in the middle of the modal rather than stretching across it. */}\n {onPick && (\n <div className=\"flex w-full max-w-md flex-col gap-2 pt-1\">\n {STARTERS.map((starter, index) => (\n <WidgetButton\n key={starter.label}\n type=\"button\"\n variant=\"outline\"\n size=\"xl\"\n disabled={isSending || isWaiting}\n onClick={() => onPick(starter.message)}\n // Each one a beat behind the last, so the three deal out rather\n // than land as a block. Backwards fill keeps them out of sight\n // until their turn.\n style={{ animationDelay: `${300 + index * 60}ms` }}\n className={cn(\n \"group/row h-auto w-full animate-in justify-start gap-3 text-left\",\n compact ? \"px-3.5 py-2.5 text-sm\" : \"px-4 py-3\",\n \"duration-500 ease-out fade-in-0 slide-in-from-bottom-2 fill-mode-backwards motion-reduce:animate-none\",\n \"hover:border-foreground/15 hover:shadow-sm\",\n )}\n >\n <starter.glyph\n className={cn(\n \"text-muted-foreground\",\n compact ? \"size-4\" : \"size-5\",\n )}\n />\n <span className=\"min-w-0 flex-1 truncate\">{starter.label}</span>\n <CaretRight\n aria-hidden\n className=\"size-3.5 text-muted-foreground/50 transition-transform group-hover/row:translate-x-0.5\"\n />\n </WidgetButton>\n ))}\n </div>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { ArrowsIn, ArrowsOut, History, NotePencil, X } from \"@/components/icons\";\nimport { WidgetButton } from \"./widget-button\";\nimport { cn } from \"@/lib/utils\";\nimport type { DragHandlers } from \"./use-launcher-drag\";\nimport { WidgetAvatarStack } from \"./widget-avatar\";\nimport type { SupportPresence } from \"./types\";\n\n/**\n * Who you are talking to, the visitor's other conversations, and the one way\n * out. The history and a fresh start sit with the panel's own controls, so\n * the faces stay the faces and the right edge is everything you can press.\n *\n * It is also the handle. A panel floating on somebody's page is in the way of\n * something eventually, and the strip along the top is where anyone would\n * reach for to move it. The close button and the faces stay their own: a press\n * that lands on a control belongs to that control, which the drag checks\n * before it takes hold of anything.\n */\nexport function WidgetHeader({\n presence,\n onClose,\n isExpanded = false,\n onToggleExpanded,\n grabHandlers,\n isMoving = false,\n history,\n compact = false,\n className,\n}: {\n presence: SupportPresence;\n onClose?: () => void;\n /** Standing taller than it normally does. */\n isExpanded?: boolean;\n /** Omit to hide the control, for a widget whose size is not its own. */\n onToggleExpanded?: () => void;\n /** Omit for a widget that is framed rather than floating. */\n grabHandlers?: DragHandlers;\n isMoving?: boolean;\n /**\n * The history's button and the one that starts a new conversation. Omit\n * both for a panel that only ever shows one conversation.\n */\n history?: {\n isOpen: boolean;\n onToggle: () => void;\n onStartNew: () => void;\n /** A reply waits on a ticket other than the one on screen. */\n hasUnread: boolean;\n /** On a fresh start already, with nothing written yet. */\n isFresh: boolean;\n };\n /** The denser scale. See `SupportWidget`. */\n compact?: boolean;\n className?: string;\n}) {\n return (\n <header\n {...grabHandlers}\n className={cn(\n \"flex shrink-0 items-start gap-3\",\n compact ? \"px-5 pt-4 pb-2\" : \"px-4 pt-4 pb-3\",\n className,\n grabHandlers && [\n // Nothing here is selectable while it is being used to drag, and the\n // browser must not claim the gesture for scrolling either. Both end\n // the same way: the drag becomes the page's instead of ours.\n \"touch-none select-none\",\n isMoving ? \"cursor-grabbing\" : \"cursor-grab\",\n ],\n )}\n >\n <div className=\"flex min-w-0 flex-1 flex-col pt-0.5\">\n <h2\n className={cn(\n \"truncate font-semibold tracking-tight\",\n compact ? \"text-base\" : \"text-lg\",\n )}\n >\n {presence.name}\n </h2>\n <p\n className={cn(\n \"truncate text-muted-foreground\",\n compact ? \"text-xs\" : \"text-sm\",\n )}\n >\n {presence.status}\n </p>\n </div>\n\n <div className=\"flex shrink-0 items-center gap-3\">\n <WidgetAvatarStack\n team={presence.team}\n avatarClassName={compact ? \"size-8\" : undefined}\n />\n\n {/* The controls sit closer to each other than to the faces, so they\n read as a pair rather than as the end of the row. */}\n <div className=\"flex items-center gap-0.5\">\n {history && (\n <>\n <WidgetButton\n variant=\"ghost\"\n size=\"icon-lg\"\n onClick={history.onToggle}\n aria-pressed={history.isOpen}\n aria-label={\n history.hasUnread\n ? \"Your conversations, with unread replies\"\n : \"Your conversations\"\n }\n className={cn(\n \"relative text-muted-foreground\",\n history.isOpen && \"bg-muted text-foreground dark:bg-muted/50\",\n )}\n >\n <History className=\"size-5\" />\n {history.hasUnread && (\n <span\n aria-hidden\n className=\"absolute top-1.5 right-1.5 size-2 rounded-full bg-destructive ring-2 ring-card animate-in duration-300 ease-spring fade-in-0 zoom-in-50 motion-reduce:animate-none\"\n />\n )}\n </WidgetButton>\n\n <WidgetButton\n variant=\"ghost\"\n size=\"icon-lg\"\n onClick={history.onStartNew}\n // Already there, with nothing written: a second fresh start\n // would only redraw the same greeting.\n disabled={history.isFresh && !history.isOpen}\n aria-label=\"New conversation\"\n className=\"text-muted-foreground\"\n >\n <NotePencil className=\"size-5\" />\n </WidgetButton>\n </>\n )}\n\n {onToggleExpanded && (\n <WidgetButton\n variant=\"ghost\"\n size=\"icon-lg\"\n onClick={onToggleExpanded}\n aria-pressed={isExpanded}\n aria-label={isExpanded ? \"Shrink the panel\" : \"Expand the panel\"}\n className=\"text-muted-foreground\"\n >\n {isExpanded ? (\n <ArrowsIn className=\"size-5\" />\n ) : (\n <ArrowsOut className=\"size-5\" />\n )}\n </WidgetButton>\n )}\n\n {onClose && (\n <WidgetButton\n variant=\"ghost\"\n size=\"icon-lg\"\n onClick={onClose}\n aria-label=\"Close support\"\n // Pulled out by the glyph's own inset, so the cross ends on the\n // same line the name starts on.\n className=\"-mr-2 text-muted-foreground\"\n >\n <X className=\"size-5\" />\n </WidgetButton>\n )}\n </div>\n </div>\n </header>\n );\n}\n","\"use client\";\n\nimport { useState, type KeyboardEvent } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { useRecoveryFlow } from \"./use-recovery-flow\";\nimport { WidgetRecoveryFoot } from \"./widget-recovery-foot\";\nimport { WidgetRecoveryPage } from \"./widget-recovery-page\";\nimport { WidgetTicketRow } from \"./widget-ticket-row\";\nimport type { SupportTickets } from \"./types\";\n\n/**\n * Two pages in one place, trading with a fade and a little travel. One\n * coming in is visible at once, so its field can take the caret on the\n * first frame; one going out stays visible until its fade is done.\n */\nfunction pageClass(isShown: boolean, from: \"above\" | \"below\") {\n return cn(\n \"col-start-1 row-start-1 duration-300 ease-out-quint motion-reduce:transition-none\",\n isShown\n ? \"visible transition-[opacity,translate]\"\n : \"invisible opacity-0 transition-[opacity,translate,visibility]\",\n !isShown && (from === \"above\" ? \"-translate-y-1\" : \"translate-y-1\"),\n );\n}\n\n/**\n * The visitor's conversations, laid over the one on screen.\n *\n * Over it rather than instead of it: the conversation underneath keeps its\n * place and its scroll, so closing the history puts them back exactly where\n * they were. It rises a few pixels as it fades in and sinks as it goes, the\n * same small travel the panel's own parts settle with, and it is inert\n * whenever it is not up.\n *\n * Picking a ticket keeps the list up until that ticket has arrived, with\n * its row held, so the history gives way to the conversation itself rather\n * than to a loading mark. Finding one by email takes the whole of it: the\n * list steps aside for a page of its own, and comes back once it is done.\n */\nexport function WidgetHistory({\n tickets,\n isOpen,\n openingId,\n onPick,\n onClose,\n compact = false,\n className,\n}: {\n tickets: SupportTickets;\n isOpen: boolean;\n /** The row picked and still on its way. */\n openingId: string | null;\n onPick: (id: string) => void;\n /** Escape, from anywhere in the list. */\n onClose: () => void;\n /** The denser scale. See `SupportWidget`. */\n compact?: boolean;\n className?: string;\n}) {\n // Read once it is first up, not before: a panel nobody opens the history\n // on draws none of it.\n const [hasOpened, setHasOpened] = useState(isOpen);\n if (isOpen && !hasOpened) setHasOpened(true);\n\n const flow = useRecoveryFlow(tickets.recovery);\n const isFinding = flow.step !== \"idle\";\n\n // Escape steps back one page at a time: out of the steps, then away.\n function handleKeyDown(event: KeyboardEvent) {\n if (event.key !== \"Escape\") return;\n event.stopPropagation();\n if (isFinding) flow.cancel();\n else onClose();\n }\n\n return (\n <div\n aria-hidden={!isOpen}\n inert={!isOpen}\n onKeyDown={handleKeyDown}\n className={cn(\n \"absolute inset-0 z-20 flex flex-col bg-card\",\n \"transition-[opacity,translate,visibility] ease-out-quint motion-reduce:transition-none\",\n isOpen\n ? \"visible translate-y-0 opacity-100 duration-300\"\n : // Away quicker than it came, and hidden once it has gone so\n // nothing under a finished fade can still be found.\n \"invisible translate-y-2 opacity-0 duration-200\",\n className,\n )}\n >\n {hasOpened && (\n <div className=\"grid min-h-0 flex-1 grid-cols-1 grid-rows-[minmax(0,1fr)]\">\n <div\n aria-hidden={isFinding}\n inert={isFinding}\n className={cn(\n pageClass(!isFinding, \"above\"),\n \"flex min-h-0 flex-col\",\n )}\n >\n <nav\n aria-label=\"Your conversations\"\n className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain\"\n >\n {tickets.list.length === 0 ? (\n <p\n className={cn(\n \"grid h-full place-items-center px-6 text-center text-muted-foreground\",\n compact ? \"text-sm\" : \"text-base\",\n )}\n >\n No conversations yet\n </p>\n ) : (\n <ul\n className={cn(\n \"flex flex-col gap-0.5 py-2\",\n compact ? \"px-2\" : \"px-1.5\",\n )}\n >\n {tickets.list.map((ticket) => (\n <li\n key={ticket.id}\n // A ticket that turns up while the list is open, from an\n // emailed code or a reply, arrives rather than appears.\n className=\"animate-in duration-300 ease-out fade-in-0 slide-in-from-bottom-1 motion-reduce:animate-none\"\n >\n <WidgetTicketRow\n ticket={ticket}\n isCurrent={ticket.id === tickets.currentId}\n isOpening={ticket.id === openingId}\n onOpen={() => onPick(ticket.id)}\n compact={compact}\n />\n </li>\n ))}\n </ul>\n )}\n </nav>\n\n {tickets.recovery && (\n <div className=\"shrink-0 pt-2 pb-3\">\n <WidgetRecoveryFoot\n recovery={tickets.recovery}\n flow={flow}\n compact={compact}\n />\n </div>\n )}\n </div>\n\n {tickets.recovery && (\n <WidgetRecoveryPage\n flow={flow}\n isShown={isFinding}\n compact={compact}\n className={pageClass(isFinding, \"below\")}\n />\n )}\n </div>\n )}\n </div>\n );\n}\n","/**\n * The customer's side of their tickets, as the widget and the support site\n * both show them: a list of their conversations, one open at a time, and an\n * emailed code that brings back the ones a lost browser took with it.\n */\n\n/** How many conversations the history lists, newest first. */\nexport const TICKET_LIST_LIMIT = 25;\n\n/** Digits in an emailed code. */\nexport const HISTORY_CODE_LENGTH = 6;\n\n/** How long an emailed code works for. */\nexport const HISTORY_CODE_TTL_MS = 10 * 60 * 1000;\n\n/** Wrong guesses a code survives before it is spent. */\nexport const HISTORY_CODE_ATTEMPTS = 5;\n\n/** How long a right code keeps those conversations on this browser. */\nexport const HISTORY_GRANT_TTL_MS = 30 * 24 * 60 * 60 * 1000;\n\n/** Addresses one browser can hold at once. Asking for another drops the oldest. */\nexport const HISTORY_GRANTS_PER_SESSION = 3;\n\n/**\n * Where a conversation continues. A chat one is answered from the panel; an\n * email one shows in the history but is answered from the mail client, since\n * that is where the replies go.\n */\nexport type TicketChannel = \"chat\" | \"email\";\n\n/** A code as somebody might paste it: spaces and dashes are not digits. */\nexport function cleanHistoryCode(input: string): string {\n return input.replace(/\\D/g, \"\").slice(0, HISTORY_CODE_LENGTH);\n}\n","\"use client\";\n\nimport { HISTORY_CODE_LENGTH } from \"@median/backend/tickets\";\nimport { useState, type FormEvent } from \"react\";\nimport { errorMessage } from \"@/lib/errors\";\nimport type { SupportRecovery } from \"./types\";\n\n/** Loose on purpose, like the composer's: it wakes the button, nothing more. */\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\nexport type RecoveryStep = \"idle\" | \"email\" | \"code\";\nexport type RecoveryNote = { tone: \"error\" | \"quiet\"; text: string };\n\nexport type RecoveryFlow = {\n /** Idle is the list; the other two are the page laid over it. */\n step: RecoveryStep;\n email: string;\n setEmail: (email: string) => void;\n /** Digits only: the code field keeps everything else out. */\n code: string;\n setCode: (code: string) => void;\n /** The address as it was sent, trimmed. */\n address: string;\n isEmailValid: boolean;\n isCodeComplete: boolean;\n isBusy: boolean;\n /** How the last step went, or null. */\n note: RecoveryNote | null;\n /** The same, held on the way out so the line folds away with its words. */\n shownNote: RecoveryNote | null;\n begin: () => void;\n cancel: () => void;\n changeEmail: () => void;\n sendCode: (event?: FormEvent) => Promise<void>;\n checkCode: (event?: FormEvent) => Promise<void>;\n};\n\n/**\n * Getting older conversations back with an emailed code: which step they\n * are on, what they have typed, the request on its way, and the one line\n * that says how it went. Shared by the history's foot, where it starts and\n * where it ends, and the page the steps fill in between, which puts the\n * caret in each step's field as it comes up.\n */\nexport function useRecoveryFlow(recovery: SupportRecovery | null): RecoveryFlow {\n const [step, setStep] = useState<RecoveryStep>(\"idle\");\n const [email, setEmail] = useState(\"\");\n const [code, setCode] = useState(\"\");\n const [isBusy, setBusy] = useState(false);\n const [note, setNote] = useState<RecoveryNote | null>(null);\n const [heldNote, setHeldNote] = useState<RecoveryNote | null>(null);\n if (note !== null && note !== heldNote) setHeldNote(note);\n\n const address = email.trim();\n const isEmailValid = EMAIL_PATTERN.test(address);\n const isCodeComplete = code.length === HISTORY_CODE_LENGTH;\n\n function begin() {\n setStep(\"email\");\n setNote(null);\n }\n\n function cancel() {\n setStep(\"idle\");\n setCode(\"\");\n setNote(null);\n }\n\n function changeEmail() {\n setStep(\"email\");\n setCode(\"\");\n setNote(null);\n }\n\n async function sendCode(event?: FormEvent) {\n event?.preventDefault();\n if (recovery === null || !isEmailValid || isBusy) return;\n setBusy(true);\n setNote(null);\n try {\n await recovery.request(address);\n if (step === \"code\") {\n setNote({ tone: \"quiet\", text: \"Sent a new code.\" });\n } else {\n setCode(\"\");\n setStep(\"code\");\n }\n } catch (caught) {\n setNote({ tone: \"error\", text: errorMessage(caught) });\n } finally {\n setBusy(false);\n }\n }\n\n async function checkCode(event?: FormEvent) {\n event?.preventDefault();\n if (recovery === null || !isCodeComplete || isBusy) return;\n setBusy(true);\n setNote(null);\n try {\n const answer = await recovery.verify(address, code);\n if (!answer.ok) {\n setNote({ tone: \"error\", text: answer.message });\n return;\n }\n setStep(\"idle\");\n setCode(\"\");\n setNote({\n tone: \"quiet\",\n text: answer.found\n ? `Showing conversations for ${address}.`\n : `No conversations for ${address}.`,\n });\n } catch (caught) {\n setNote({ tone: \"error\", text: errorMessage(caught) });\n } finally {\n setBusy(false);\n }\n }\n\n return {\n // Somebody signed in partway through has nothing left to find this way.\n step: recovery === null ? \"idle\" : step,\n email,\n setEmail,\n code,\n setCode,\n address,\n isEmailValid,\n isCodeComplete,\n isBusy,\n note,\n shownNote: note ?? heldNote,\n begin,\n cancel,\n changeEmail,\n sendCode,\n checkCode,\n };\n}\n","import { ConvexError } from \"convex/values\";\n\nconst FALLBACK = \"Something went wrong on our end. Please try again.\";\n\nconst OUT_OF_DATE =\n \"This page is out of date. Refresh to get the latest version.\";\n\n/**\n * A call to a function the backend no longer has: the tab is running a\n * bundle from before a release. Convex words it this way in development and\n * whenever it does not redact the reason.\n */\nfunction isMissingFunction(error: unknown): boolean {\n return (\n error instanceof Error &&\n error.message.includes(\"Could not find public function\")\n );\n}\n\ntype ErrorPayload = { code: string; message: string };\n\nfunction isErrorPayload(data: unknown): data is ErrorPayload {\n return (\n typeof data === \"object\" &&\n data !== null &&\n \"message\" in data &&\n typeof (data as ErrorPayload).message === \"string\"\n );\n}\n\n/** The `code` on an error we raised on purpose, or null for anything else. */\nexport function errorCode(error: unknown): string | null {\n if (\n error instanceof ConvexError &&\n typeof error.data === \"object\" &&\n error.data !== null &&\n typeof (error.data as Partial<ErrorPayload>).code === \"string\"\n )\n return (error.data as ErrorPayload).code;\n return null;\n}\n\n/**\n * Turns anything thrown by a Convex call into a sentence worth showing.\n * Only errors we raised on purpose are surfaced; everything else falls back so\n * we never paste a stack trace into the UI.\n */\nexport function errorMessage(error: unknown): string {\n if (error instanceof ConvexError) {\n if (typeof error.data === \"string\") return error.data;\n if (isErrorPayload(error.data)) return error.data.message;\n }\n if (isMissingFunction(error)) return OUT_OF_DATE;\n return FALLBACK;\n}\n","\"use client\";\n\nimport { Collapse } from \"@/components/ui/collapse\";\nimport { cn } from \"@/lib/utils\";\nimport type { RecoveryFlow } from \"./use-recovery-flow\";\n\n/**\n * How the last step went, in one line that folds open and holds its words\n * on the way out. Shown on the page while the steps are up, and at the\n * history's foot once they are done.\n */\nexport function WidgetRecoveryNote({\n flow,\n isOpen,\n compact,\n className,\n}: {\n flow: RecoveryFlow;\n isOpen: boolean;\n compact: boolean;\n className?: string;\n}) {\n const shown = flow.shownNote;\n return (\n <Collapse isOpen={isOpen}>\n <p\n role={shown?.tone === \"error\" ? \"alert\" : \"status\"}\n className={cn(\n \"text-center\",\n compact ? \"text-xs\" : \"text-sm\",\n shown?.tone === \"error\" ? \"text-destructive\" : \"text-muted-foreground\",\n className,\n )}\n >\n {shown?.text}\n </p>\n </Collapse>\n );\n}\n","\"use client\";\n\nimport { EnvelopeSimple, UserCircle } from \"@/components/icons\";\nimport { cn } from \"@/lib/utils\";\nimport { WidgetButton } from \"./widget-button\";\nimport { WidgetRecoveryNote } from \"./widget-recovery-note\";\nimport type { SupportRecovery } from \"./types\";\nimport type { RecoveryFlow } from \"./use-recovery-flow\";\n\n/**\n * The way back to older conversations, at the foot of the history. One row\n * to find them by email, or, where the host's sign in is offered too, the\n * two side by side under a label, as two halves of the same choice.\n *\n * Either opens the page the steps fill. Once a code has gone through, the\n * line that says what it found folds open here, under the list it changed.\n */\nexport function WidgetRecoveryFoot({\n recovery,\n flow,\n compact,\n}: {\n recovery: SupportRecovery;\n flow: RecoveryFlow;\n compact: boolean;\n}) {\n const size = compact ? \"default\" : \"lg\";\n\n return (\n <div className={cn(\"flex flex-col\", compact ? \"px-5\" : \"px-4\")}>\n {recovery.signIn ? (\n <div className=\"flex flex-col gap-2 pt-1 pb-1\">\n <p\n className={cn(\n \"text-center text-muted-foreground\",\n compact ? \"text-xs\" : \"text-sm\",\n )}\n >\n Find an older conversation\n </p>\n <div className=\"grid grid-cols-2 gap-2\">\n <WidgetButton\n type=\"button\"\n variant=\"secondary\"\n size={size}\n onClick={recovery.signIn}\n className=\"w-full min-w-0 gap-2\"\n >\n <UserCircle aria-hidden className=\"size-4\" />\n Sign in\n </WidgetButton>\n <WidgetButton\n type=\"button\"\n variant=\"secondary\"\n size={size}\n onClick={flow.begin}\n className=\"w-full min-w-0 gap-2\"\n >\n <EnvelopeSimple aria-hidden className=\"size-4\" />\n Email a code\n </WidgetButton>\n </div>\n </div>\n ) : (\n <div className=\"pb-1\">\n <WidgetButton\n type=\"button\"\n variant=\"ghost\"\n size={size}\n onClick={flow.begin}\n className=\"w-full gap-2 text-muted-foreground\"\n >\n <EnvelopeSimple aria-hidden className=\"size-4\" />\n Find a conversation by email\n </WidgetButton>\n </div>\n )}\n\n <WidgetRecoveryNote\n flow={flow}\n isOpen={flow.step === \"idle\" && flow.note !== null}\n compact={compact}\n className=\"pt-1 pb-1\"\n />\n </div>\n );\n}\n","\"use client\";\n\nimport { HISTORY_CODE_LENGTH } from \"@median/backend/tickets\";\nimport { useEffect, useRef, type FormEvent, type ReactNode } from \"react\";\nimport { CaretLeft, EnvelopeSimple } from \"@/components/icons\";\nimport { cn } from \"@/lib/utils\";\nimport { WidgetButton } from \"./widget-button\";\nimport { WidgetCodeField } from \"./widget-code-field\";\nimport { WIDGET_TEXT_FIELD_SHELL } from \"./widget-field\";\nimport { WidgetRecoveryNote } from \"./widget-recovery-note\";\nimport type { RecoveryFlow } from \"./use-recovery-flow\";\n\n/** The two steps trading places, the way the composer's two halves do. */\nconst SWAP =\n \"transition-[opacity,translate] duration-300 ease-out motion-reduce:transition-none\";\n\n/**\n * Finding a conversation by email, given the whole of the history to do it\n * in: the email, then the code that was sent to it, each with room to read\n * and a field big enough to type in without squinting.\n *\n * The two steps share one place, so moving from the email to the code moves\n * nothing around them. Back returns to the list, as Escape does.\n */\nexport function WidgetRecoveryPage({\n flow,\n isShown,\n compact,\n className,\n}: {\n flow: RecoveryFlow;\n isShown: boolean;\n compact: boolean;\n className?: string;\n}) {\n const emailRef = useRef<HTMLInputElement>(null);\n const codeRef = useRef<HTMLInputElement>(null);\n\n // The caret goes to each step's field as it comes up, once the page has\n // started in and the field can take it.\n useEffect(() => {\n const field =\n flow.step === \"email\" ? emailRef : flow.step === \"code\" ? codeRef : null;\n if (field === null) return;\n const frame = requestAnimationFrame(() => field.current?.focus());\n return () => cancelAnimationFrame(frame);\n }, [flow.step]);\n\n return (\n <div\n aria-hidden={!isShown}\n inert={!isShown}\n className={cn(\n \"flex min-h-0 flex-col overflow-y-auto overscroll-contain\",\n className,\n )}\n >\n <div className={cn(\"shrink-0 pt-2\", compact ? \"px-3\" : \"px-2.5\")}>\n <WidgetButton\n type=\"button\"\n variant=\"ghost\"\n size={compact ? \"sm\" : \"default\"}\n onClick={flow.cancel}\n className=\"gap-1 text-muted-foreground\"\n >\n <CaretLeft aria-hidden className=\"size-4\" />\n Back\n </WidgetButton>\n </div>\n\n <div\n className={cn(\n \"m-auto flex w-full max-w-sm flex-col items-center pt-2 pb-8\",\n compact ? \"px-5\" : \"px-6\",\n )}\n >\n <div\n aria-hidden\n className={cn(\n \"mb-4 grid place-items-center rounded-full bg-muted text-muted-foreground\",\n compact ? \"size-11\" : \"size-12\",\n )}\n >\n <EnvelopeSimple className={compact ? \"size-5\" : \"size-6\"} />\n </div>\n\n {/* Both steps in one cell: the cell is as tall as the taller one,\n so trading them moves nothing above or below. */}\n <div className=\"grid w-full\">\n <Step\n isShown={flow.step === \"email\"}\n from=\"above\"\n onSubmit={flow.sendCode}\n >\n <Heading compact={compact}>Find a conversation by email</Heading>\n <Lede compact={compact}>\n Enter the email you used before. We&apos;ll send you a{\" \"}\n {HISTORY_CODE_LENGTH} digit code.\n </Lede>\n <input\n ref={emailRef}\n type=\"email\"\n value={flow.email}\n onChange={(event) => flow.setEmail(event.target.value)}\n placeholder=\"you@example.com\"\n aria-label=\"Email address\"\n autoComplete=\"email\"\n spellCheck={false}\n className={cn(\n WIDGET_TEXT_FIELD_SHELL,\n \"w-full bg-card px-4 dark:bg-card max-sm:text-[16px]\",\n compact ? \"h-11\" : \"h-12 text-base\",\n )}\n />\n <Submit isReady={flow.isEmailValid && !flow.isBusy} compact={compact}>\n Send code\n </Submit>\n </Step>\n\n <Step\n isShown={flow.step === \"code\"}\n from=\"below\"\n onSubmit={flow.checkCode}\n >\n <Heading compact={compact}>Check your email</Heading>\n <Lede compact={compact}>\n Enter the code sent to{\" \"}\n <span className=\"font-medium text-foreground\">{flow.address}</span>.\n </Lede>\n <WidgetCodeField\n value={flow.code}\n length={HISTORY_CODE_LENGTH}\n onChange={flow.setCode}\n inputRef={codeRef}\n compact={compact}\n />\n <Submit\n isReady={flow.isCodeComplete && !flow.isBusy}\n compact={compact}\n >\n Check code\n </Submit>\n <div className=\"flex items-center justify-center gap-5 pt-1\">\n <LinkButton\n onClick={() => void flow.sendCode()}\n disabled={flow.isBusy}\n compact={compact}\n >\n Send again\n </LinkButton>\n <LinkButton onClick={flow.changeEmail} compact={compact}>\n Use another email\n </LinkButton>\n </div>\n </Step>\n </div>\n\n <WidgetRecoveryNote\n flow={flow}\n isOpen={flow.step !== \"idle\" && flow.note !== null}\n compact={compact}\n className=\"pt-4\"\n />\n </div>\n </div>\n );\n}\n\nfunction Step({\n isShown,\n from,\n onSubmit,\n children,\n}: {\n isShown: boolean;\n /** Where it waits while the other step is up. */\n from: \"above\" | \"below\";\n onSubmit: (event: FormEvent) => void;\n children: ReactNode;\n}) {\n return (\n <form\n onSubmit={onSubmit}\n aria-hidden={!isShown}\n inert={!isShown}\n className={cn(\n \"col-start-1 row-start-1 flex flex-col gap-3\",\n SWAP,\n !isShown && \"pointer-events-none opacity-0\",\n !isShown && (from === \"above\" ? \"-translate-y-1\" : \"translate-y-1\"),\n )}\n >\n {children}\n </form>\n );\n}\n\nfunction Heading({ compact, children }: { compact: boolean; children: ReactNode }) {\n return (\n <h2\n className={cn(\n \"text-center font-semibold text-foreground\",\n compact ? \"text-base\" : \"text-lg\",\n )}\n >\n {children}\n </h2>\n );\n}\n\nfunction Lede({ compact, children }: { compact: boolean; children: ReactNode }) {\n return (\n <p\n className={cn(\n \"-mt-2 mb-2 text-center text-pretty text-muted-foreground\",\n compact ? \"text-sm\" : \"text-base\",\n )}\n >\n {children}\n </p>\n );\n}\n\n/** The step's one button, the width of the field above it. */\nfunction Submit({\n isReady,\n compact,\n children,\n}: {\n isReady: boolean;\n compact: boolean;\n children: ReactNode;\n}) {\n return (\n <WidgetButton\n type=\"submit\"\n size={compact ? \"lg\" : \"xl\"}\n disabled={!isReady}\n className={cn(\"w-full\", compact ? \"h-10\" : \"h-11\")}\n >\n {children}\n </WidgetButton>\n );\n}\n\nfunction LinkButton({\n onClick,\n disabled = false,\n compact,\n children,\n}: {\n onClick: () => void;\n disabled?: boolean;\n compact: boolean;\n children: ReactNode;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n className={cn(\n \"rounded-sm font-medium text-muted-foreground underline-offset-4 outline-none transition-colors hover:text-foreground hover:underline focus-visible:ring-3 focus-visible:ring-ring/50 disabled:opacity-50\",\n compact ? \"text-xs\" : \"text-sm\",\n )}\n >\n {children}\n </button>\n );\n}\n","\"use client\";\n\nimport {\n useState,\n type ClipboardEvent,\n type Ref,\n type SyntheticEvent,\n} from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { WIDGET_TEXT_FIELD_SHELL } from \"./widget-field\";\n\n/**\n * A code typed into a box per digit.\n *\n * One real input lies over the boxes, unseen, so typing, pasting, deleting\n * and the phone's suggested code all work the way they do in any field. The\n * boxes only show what it holds, and ring the one the next digit goes in.\n * The caret is kept at the end, so a press on any box carries on from the\n * last digit rather than wedging one in between, and a paste is a whole\n * new code rather than more digits on the end of the last one.\n */\nexport function WidgetCodeField({\n value,\n length,\n onChange,\n inputRef,\n compact = false,\n}: {\n value: string;\n length: number;\n /** Digits only, never more than `length`. A pasted \"123 456\" arrives whole. */\n onChange: (value: string) => void;\n inputRef?: Ref<HTMLInputElement>;\n /** The denser scale. See `SupportWidget`. */\n compact?: boolean;\n}) {\n const [isFocused, setFocused] = useState(false);\n const next = Math.min(value.length, length - 1);\n\n // Everything selected stays selected, so select all and delete clears it.\n function keepCaretAtEnd(event: SyntheticEvent<HTMLInputElement>) {\n const input = event.currentTarget;\n const end = input.value.length;\n const start = input.selectionStart;\n const isAll = start === 0 && input.selectionEnd === end;\n const isAtEnd = start === end && input.selectionEnd === end;\n if (!isAll && !isAtEnd) input.setSelectionRange(end, end);\n }\n\n function paste(event: ClipboardEvent<HTMLInputElement>) {\n const pasted = event.clipboardData.getData(\"text\").replace(/\\D/g, \"\");\n if (pasted.length === 0) return;\n event.preventDefault();\n onChange(pasted.slice(0, length));\n }\n\n return (\n <div className=\"relative\">\n <div\n aria-hidden\n className=\"grid gap-2\"\n style={{ gridTemplateColumns: `repeat(${length}, minmax(0, 1fr))` }}\n >\n {Array.from({ length }, (_, index) => (\n <div\n key={index}\n className={cn(\n WIDGET_TEXT_FIELD_SHELL,\n \"grid place-items-center bg-card font-medium text-foreground tabular-nums dark:bg-card\",\n compact ? \"h-12 text-xl\" : \"h-14 text-2xl\",\n // The focus every widget field shows, on the box that is next.\n isFocused && index === next && \"border-ring\",\n )}\n >\n {value[index] ?? \"\"}\n </div>\n ))}\n </div>\n <input\n ref={inputRef}\n value={value}\n onChange={(event) =>\n onChange(event.target.value.replace(/\\D/g, \"\").slice(0, length))\n }\n onFocus={(event) => {\n setFocused(true);\n keepCaretAtEnd(event);\n }}\n onBlur={() => setFocused(false)}\n onSelect={keepCaretAtEnd}\n onPaste={paste}\n aria-label=\"Code\"\n inputMode=\"numeric\"\n autoComplete=\"one-time-code\"\n spellCheck={false}\n // 16px, unseen as it is: a smaller focused field zooms iOS in.\n className=\"absolute inset-0 size-full cursor-text bg-transparent text-[16px] text-transparent caret-transparent outline-none selection:bg-transparent\"\n />\n </div>\n );\n}\n","import { EnvelopeSimple } from \"@/components/icons\";\nimport { cn } from \"@/lib/utils\";\nimport type { SupportTicket } from \"./types\";\n\n/**\n * One conversation in the history: what it was about and when it last\n * moved, then the last thing said in it and the team's name for it.\n *\n * Two lines and no more, so a long list scans as a column of subjects. A\n * reply they have not seen makes the subject heavier and puts the count at\n * the end, in the same red the closed button wears for it. A finished one\n * says so beside its name and steps back a shade, so open ones lead.\n */\nexport function WidgetTicketRow({\n ticket,\n isCurrent = false,\n isOpening = false,\n onOpen,\n compact = false,\n}: {\n ticket: SupportTicket;\n /** The one on screen behind the history. */\n isCurrent?: boolean;\n /** Picked, and on its way. */\n isOpening?: boolean;\n onOpen: () => void;\n /** The denser scale. See `SupportWidget`. */\n compact?: boolean;\n}) {\n const isUnread = ticket.unreadCount > 0;\n const isResolved = ticket.status === \"resolved\";\n\n return (\n <button\n type=\"button\"\n onClick={onOpen}\n aria-current={isCurrent ? \"true\" : undefined}\n aria-busy={isOpening || undefined}\n className={cn(\n \"flex w-full min-w-0 flex-col gap-0.5 rounded-xl text-left outline-none\",\n \"transition-[background-color,opacity] duration-150 ease-out motion-reduce:transition-none\",\n \"hover:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 dark:hover:bg-muted/50\",\n compact ? \"px-3 py-2\" : \"px-3.5 py-2.5\",\n (isCurrent || isOpening) && \"bg-muted dark:bg-muted/50\",\n )}\n >\n <span className=\"flex min-w-0 items-center gap-2\">\n {ticket.channel === \"email\" && (\n <EnvelopeSimple\n aria-label=\"By email\"\n className=\"size-4 shrink-0 text-muted-foreground\"\n />\n )}\n <span\n className={cn(\n \"min-w-0 flex-1 truncate\",\n compact ? \"text-sm\" : \"text-base\",\n isUnread ? \"font-semibold\" : \"font-medium\",\n isResolved && !isUnread && \"text-muted-foreground\",\n )}\n >\n {ticket.subject}\n </span>\n <span className=\"shrink-0 text-xs text-muted-foreground tabular-nums\">\n {ticket.time}\n </span>\n </span>\n\n <span\n className={cn(\n \"flex min-w-0 items-center gap-2 text-muted-foreground\",\n compact ? \"text-xs\" : \"text-sm\",\n )}\n >\n <span className=\"min-w-0 flex-1 truncate\">{ticket.preview}</span>\n {(isResolved || ticket.threadId) && (\n <span className=\"shrink-0 text-xs\">\n {[isResolved ? \"Resolved\" : null, ticket.threadId]\n .filter(Boolean)\n .join(\" · \")}\n </span>\n )}\n {isUnread && (\n <span\n aria-label={`${ticket.unreadCount} unread`}\n className={cn(\n \"grid h-4.5 min-w-4.5 shrink-0 place-items-center rounded-full px-1\",\n \"bg-destructive text-[0.6875rem] font-semibold text-background tabular-nums\",\n \"animate-in duration-300 ease-spring fade-in-0 zoom-in-50 motion-reduce:animate-none\",\n )}\n >\n {ticket.unreadCount > 9 ? \"9+\" : ticket.unreadCount}\n </span>\n )}\n </span>\n </button>\n );\n}\n","import { ShimmerLogo } from \"@/components/brand\";\n\n/**\n * Waiting on the conversation.\n *\n * One mark and nothing else. Skeleton bubbles would be guessing at a shape\n * nobody knows yet, and they always guess wrong.\n */\nexport function WidgetLoading() {\n return (\n <div className=\"grid min-h-0 flex-1 place-items-center p-6\">\n <ShimmerLogo label=\"Loading the conversation\" className=\"size-14\" />\n </div>\n );\n}\n","import { LockSimple } from \"@/components/icons\";\nimport { WidgetButton } from \"./widget-button\";\nimport { PoweredByMedian } from \"./widget-badge\";\n\n/**\n * What a visitor meets when the host only takes questions from people who are\n * signed in.\n *\n * The same shape as the empty conversation it stands in for, and it arrives\n * the same way: the mark, then the words a beat behind it, then the one thing\n * to do about it. The button is only there when the host said where their\n * sign in lives, because a button that goes nowhere is worse than none.\n */\nexport function WidgetLocked({\n orgName,\n onSignIn,\n showBadge = true,\n}: {\n /** Whose site this is, so the line can name who is waiting. */\n orgName?: string;\n onSignIn?: () => void;\n /** The line the composer usually carries, which is not here to carry it. */\n showBadge?: boolean;\n}) {\n return (\n <div className=\"flex min-h-0 flex-1 flex-col\">\n <div className=\"flex min-h-0 flex-1 flex-col items-center justify-center gap-5 px-8 text-center\">\n <span className=\"grid size-16 animate-in place-items-center rounded-2xl bg-muted duration-500 ease-out fade-in-0 zoom-in-95 motion-reduce:animate-none\">\n <LockSimple className=\"size-7 text-muted-foreground\" />\n </span>\n\n <div className=\"flex max-w-72 animate-in flex-col gap-1 delay-150 duration-500 ease-out fade-in-0 slide-in-from-bottom-2 fill-mode-backwards motion-reduce:animate-none\">\n <p className=\"text-xl font-semibold tracking-tight\">\n Sign in to chat\n </p>\n <p className=\"text-base text-balance text-muted-foreground\">\n {orgName\n ? `Ask ${orgName} anything once you are signed in.`\n : \"Sign in to contact our support team.\"}\n </p>\n </div>\n\n {onSignIn && (\n <WidgetButton\n size=\"xl\"\n onClick={onSignIn}\n className=\"animate-in delay-300 duration-500 ease-out fade-in-0 slide-in-from-bottom-2 fill-mode-backwards motion-reduce:animate-none\"\n >\n Sign in\n </WidgetButton>\n )}\n </div>\n\n {showBadge && <PoweredByMedian className=\"shrink-0 px-4 pb-4\" />}\n </div>\n );\n}\n","\"use client\";\n\nimport { useState } from \"react\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { MORPH_MS } from \"@/components/ui/morph-bubble\";\nimport { LONE_MESSAGE, type MessageGroup } from \"@/lib/message-groups\";\nimport { cn } from \"@/lib/utils\";\nimport { WidgetAttachments } from \"./widget-attachments\";\nimport { WidgetAvatar } from \"./widget-avatar\";\nimport { WidgetBubble } from \"./widget-bubble\";\nimport { WidgetDestination } from \"./widget-destination\";\nimport { WidgetMessageBody, type BodyArrival } from \"./widget-message-body\";\nimport { WidgetSources } from \"./widget-sources\";\nimport { WidgetTyping } from \"./widget-typing\";\nimport type { SupportFollow, SupportMessage } from \"./types\";\n\n/**\n * Both bubbles keep the page's text colour and separate by surface instead.\n * The visitor's own is a step further from the panel than the agent's, which\n * tells the two apart without a slab of pure black in the light, or pure white\n * in the dark.\n *\n * Four fifths of the thread, and never wider than a line worth reading. The\n * corner panel is narrow enough that the fraction is the whole rule; the\n * modal is twice as wide, and a bubble stretched across it would be a\n * paragraph of ninety character lines. Thirty six rem is about seventy.\n */\nconst BUBBLE = \"max-w-[min(80%,36rem)] rounded-2xl px-4 py-2.5 text-base\";\n\n/** The same bubble a size down, for a panel on the denser scale. */\nconst COMPACT_BUBBLE = \"max-w-[min(80%,36rem)] rounded-2xl px-3.5 py-2 text-sm\";\n/**\n * The fallbacks are the widget's own light values, the ones its stylesheet\n * puts behind every other colour: a host page without `--muted` or\n * `--foreground` would otherwise get a bubble with no background at all.\n */\nconst VISITOR_BUBBLE =\n \"bg-[color-mix(in_oklch,var(--muted,oklch(0.955_0_0)),var(--foreground,oklch(0.145_0_0))_12%)]\";\n\n/**\n * New messages arrive rather than appear. Small on purpose: the scroller is\n * gliding down to the newest message at the same time, and a bubble\n * travelling any further than this reads as a wobble against it rather than\n * as one thing arriving. The curve is the app's, a pure deceleration, so\n * the bubble and the glide it rides in on settle the same way. Off for\n * anyone who asked.\n */\nconst ARRIVES =\n \"animate-in fade-in-0 slide-in-from-bottom-1 duration-300 ease-out-quint motion-reduce:animate-none\";\n\n/**\n * A message the visitor just sent lands rather than appears: it comes up\n * out of the composer's corner, a little further and a touch smaller to\n * start than a reply's arrival, on the same curve. The origin is the\n * bottom right, which is where the send button is, so it grows out of the\n * press that sent it. The thread is gliding up to make room at the same\n * time, and both movements decelerate the same way, so the two read as one\n * thing settling rather than two things happening. Still small: a bubble\n * that travels much further than this reads as thrown.\n */\nconst SENT =\n \"animate-in fade-in-0 slide-in-from-bottom-3 zoom-in-95 origin-bottom-right duration-400 ease-out-quint motion-reduce:animate-none\";\n\n/**\n * How far into the bubble's growth the first line starts rising. Not all of\n * it: the box eases into its last stretch, and a line that starts then is\n * still faint when the box stops, so the two read as one thing landing\n * rather than a box, a pause, and some words.\n */\nconst LADDER_DELAY_MS = Math.round(MORPH_MS * 0.7);\n\n/**\n * The break above a message, when there is one: a new day, or a conversation\n * that starts here because the one before it was resolved.\n *\n * One line either way, and never two stacked. A conversation starting is the\n * bigger of the two breaks, so it stands in for the date rather than sitting\n * under it.\n */\nexport function WidgetDivider({ message }: { message: SupportMessage }) {\n const label = message.startsConversation\n ? \"New conversation\"\n : message.dayLabel;\n if (!label) return null;\n\n return (\n <div className=\"flex items-center gap-3 py-1\">\n <span className=\"h-px flex-1 bg-border\" />\n <span className=\"text-sm text-muted-foreground\">{label}</span>\n <span className=\"h-px flex-1 bg-border\" />\n </div>\n );\n}\n\n/**\n * One message. The two sides are built differently on purpose.\n *\n * A visitor's own message is only the bubble. They know what they said and\n * when, so a name, a time and a read receipt underneath are three things that\n * earn nothing. An agent's turn gets one line above it saying who is speaking\n * and when, because you want both before you read what they said. One line per\n * turn, not per message: a reply that comes in three parts says it once.\n */\nexport function WidgetMessage({\n message,\n group = LONE_MESSAGE,\n onFollow,\n compact = false,\n isLive = false,\n}: {\n message: SupportMessage;\n /** Where it sits in a run from the same voice. Alone, by default. */\n group?: MessageGroup;\n /**\n * What the button under a message does, when one of the team's tools has\n * offered a page. Omit it and the offer is not shown at all, which is the\n * right answer for a panel with no way to move anybody.\n */\n onFollow?: SupportFollow;\n /** The denser scale. See `SupportWidget`. */\n compact?: boolean;\n /**\n * Landed while the thread was already on screen, rather than being part\n * of the history it opened with. Only a reply somebody watched arrive is\n * worth arriving a line at a time, and only a message they just sent is\n * worth seeing land.\n */\n isLive?: boolean;\n}) {\n return message.sender === \"visitor\" ? (\n <VisitorMessage message={message} compact={compact} isLive={isLive} />\n ) : (\n <AgentMessage\n message={message}\n group={group}\n onFollow={onFollow}\n compact={compact}\n isLive={isLive}\n />\n );\n}\n\n/**\n * What goes in a bubble: the files first, then what was said about them. The\n * text is skipped entirely when there is none, so a picture sent on its own\n * gets a bubble that hugs it rather than one with an empty line underneath.\n *\n * A reply that has not landed yet holds three bouncing dots in the same place\n * the words are about to be, which is what the bubble grows out of.\n */\nfunction BubbleContent({\n message,\n arrival = null,\n}: {\n message: SupportMessage;\n /** How the words come in, if they are coming in now. */\n arrival?: BodyArrival | null;\n}) {\n const hasAttachments = (message.attachments?.length ?? 0) > 0;\n\n if (message.pending) {\n return (\n <WidgetTyping\n label={`${message.agent?.name ?? \"Support\"} is writing a reply`}\n />\n );\n }\n\n return (\n <>\n {hasAttachments && (\n <WidgetAttachments attachments={message.attachments ?? []} />\n )}\n\n {message.body && (\n <WidgetMessageBody\n body={message.body}\n sender={message.sender}\n arrival={arrival}\n />\n )}\n </>\n );\n}\n\nfunction VisitorMessage({\n message,\n compact,\n isLive,\n}: {\n message: SupportMessage;\n compact: boolean;\n /** Just sent from the composer underneath, rather than read back. */\n isLive: boolean;\n}) {\n return (\n <div className={cn(\"flex justify-end\", isLive ? SENT : ARRIVES)}>\n <WidgetBubble\n morphKey=\"settled\"\n className={cn(compact ? COMPACT_BUBBLE : BUBBLE, VISITOR_BUBBLE)}\n >\n <BubbleContent message={message} />\n </WidgetBubble>\n </div>\n );\n}\n\nfunction AgentMessage({\n message,\n group,\n onFollow,\n compact,\n isLive,\n}: {\n message: SupportMessage;\n group: MessageGroup;\n onFollow?: SupportFollow;\n compact: boolean;\n isLive: boolean;\n}) {\n const { agent } = message;\n\n // Whether the words took the place of three dots, which is a bubble still\n // growing as the reply renders. Remembered rather than read, since by the\n // render that has the words the dots are already gone. Only ever set: the\n // arrival plays once, on the first render with words in it, and never\n // asks again.\n const [sawDots, setSawDots] = useState(message.pending === true);\n if (message.pending && !sawDots) setSawDots(true);\n\n // A reply that landed in front of somebody arrives a line at a time. One\n // that grew out of the dots gives the bubble most of its growth first, so\n // the lines climb into a box that is all but still.\n const arrival: BodyArrival | null =\n isLive && !message.pending && message.body.length > 0\n ? { key: message.id, delay: sawDots ? LADDER_DELAY_MS : 0 }\n : null;\n const destination =\n message.navigateTo === undefined\n ? null\n : {\n key: message.id,\n path: message.navigateTo,\n highlight: message.highlight ?? null,\n };\n const sources = message.pending ? [] : (message.sources ?? []);\n\n return (\n // One line saying who and when, then the thing they said. The face used to\n // sit in a gutter beside the bubble with the name above it and the time\n // below, which spread four small things across three rows and pushed every\n // bubble in off the edge. All of it is one line now, and the bubble gets\n // the full width back.\n <div className={cn(\"flex flex-col items-start gap-1\", ARRIVES)}>\n {/* Once per turn rather than once per message: a reply that arrives in\n three parts is still one agent saying one thing. */}\n {group.isFirst && (\n <div\n className={cn(\n \"flex max-w-full items-center gap-2 px-0.5\",\n compact ? \"text-xs\" : \"text-sm\",\n )}\n >\n {agent && (\n <>\n <WidgetAvatar\n agent={agent}\n className={cn(\"rounded-md\", compact ? \"size-5\" : \"size-6\")}\n />\n <span className=\"truncate font-medium\">{agent.name}</span>\n\n {/* Said plainly, because a name and a face are exactly what a\n machine can be given, and a visitor should never have to\n work out which one they got. Once per turn, since that is\n where the name is: a burst of three says it once. */}\n {agent.kind === \"ai\" && (\n <Badge variant=\"muted\" className=\"shrink-0 rounded-md bg-muted px-1 inset-ring-0\">\n AI\n </Badge>\n )}\n </>\n )}\n\n {/* No time on a reply that has not been written yet. */}\n {!message.pending && (\n <span className=\"shrink-0 text-muted-foreground tabular-nums\">\n {message.time}\n </span>\n )}\n </div>\n )}\n\n {/* The same bubble either way. It is what the dots are in, so when the\n answer replaces them it grows out of where they were rather than\n appearing somewhere near them. */}\n <WidgetBubble\n // Derived from the contents, so an answer that arrives in pieces\n // keeps growing rather than morphing once and then snapping.\n morphKey={\n message.pending\n ? \"pending\"\n : `${message.body.length}:${message.attachments?.length ?? 0}`\n }\n className={cn(compact ? COMPACT_BUBBLE : BUBBLE, \"bg-muted\")}\n // The ladder is the arrival, so the box has nothing to hold back.\n holdContent={arrival === null}\n >\n <BubbleContent message={message} arrival={arrival} />\n </WidgetBubble>\n\n {/* The pages the answer came from. A step further from the bubble than\n its name is from it, like the button below, since these are for\n pressing rather than reading. */}\n {sources.length > 0 && (\n <div className=\"max-w-full pt-1\">\n <WidgetSources\n sources={sources}\n body={message.body}\n arrival={arrival}\n compact={compact}\n />\n </div>\n )}\n\n {/* Under the words that explain it, and inside the same arrival, so the\n offer lands as part of the reply rather than after it.\n\n Twice the column's gap, which is the one place that gap is wrong: it\n is set for a name sitting on its bubble, and a button that close to\n one reads as part of it rather than as something to press. Still\n well inside the space between turns, so it stays this turn's. */}\n {destination !== null && onFollow !== undefined && (\n <div className=\"pt-1.5\">\n <WidgetDestination destination={destination} onFollow={onFollow} />\n </div>\n )}\n </div>\n );\n}\n","import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Slot } from \"radix-ui\";\n\nimport { cn } from \"@/lib/utils\";\n\n// Quiet, softly edged labels. Status colors can still be supplied by callers.\nconst badgeVariants = cva(\n \"inline-flex w-fit shrink-0 items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium whitespace-nowrap [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3\",\n {\n variants: {\n variant: {\n default: \"bg-secondary text-secondary-foreground inset-ring-1 inset-ring-foreground/5\",\n muted: \"bg-muted/70 text-muted-foreground inset-ring-1 inset-ring-foreground/5\",\n outline: \"border border-border text-muted-foreground\",\n solid: \"bg-primary text-primary-foreground\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n },\n);\n\nfunction Badge({\n className,\n variant = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"span\"> &\n VariantProps<typeof badgeVariants> & {\n asChild?: boolean;\n }) {\n const Comp = asChild ? Slot.Root : \"span\";\n\n return (\n <Comp\n data-slot=\"badge\"\n data-variant={variant}\n className={cn(badgeVariants({ variant, className }))}\n {...props}\n />\n );\n}\n\nexport { Badge, badgeVariants };\n","\"use client\";\n\nimport { useLayoutEffect, useRef, type ReactNode } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * How long the box takes to travel. `--duration-medium`, which is what the\n * palette's list resizes on too. Exported for whatever waits inside it to\n * start once there is room: see the widget's line by line arrival.\n */\nexport const MORPH_MS = 350;\n/**\n * Matches --ease-in-out. A resize wants easing at both ends: leaving at\n * speed reads as a jump with a tail, and arriving at speed reads as a snap.\n * No spring either, since an overshoot carries the box past its final size\n * and back, and a box that clips its contents makes that read as the text\n * being cut off and restored.\n */\nconst MORPH_EASING = \"ease-in-out\";\n\n/** How long the new contents take to appear once the box has room for them. */\nconst CONTENT_FADE_MS = 140;\n\n/** Sub pixel differences are not worth an animation. */\nconst EPSILON = 1;\n\nfunction prefersReducedMotion(): boolean {\n if (typeof window === \"undefined\") return false;\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n}\n\n/**\n * Hand the contents back to the layout: no held width, no held opacity.\n *\n * Also what has to happen before measuring, since a box still pinned by the\n * last morph measures at the size that morph was holding it at.\n */\nfunction release(inner: HTMLElement) {\n inner.style.width = \"\";\n inner.style.boxSizing = \"\";\n inner.style.opacity = \"\";\n}\n\n/**\n * Hold the contents at the width they just laid out at, in full precision.\n *\n * The precision is the whole point. `offsetWidth` is rounded to a whole\n * pixel, and a bubble that sizes itself to its own text sits on a fractional\n * width far more often than not, so half the time that rounding goes down and\n * pins the text a hair narrower than the width it measured at. That is enough\n * to push the last word of a line onto the next one. It happens under the\n * pin, where it is hidden, so the box travels to the height it measured\n * before the wrap and the words spring back a line the moment the pin comes\n * off. The computed width is the used width, fractions and all, and it is\n * layout space rather than screen space, so an ancestor mid animation cannot\n * skew it either.\n *\n * Held as a content box so the number means the same thing going back in as\n * it did coming out, whatever padding the contents carry.\n */\nfunction pin(inner: HTMLElement, hold: boolean) {\n const width = getComputedStyle(inner).width;\n inner.style.boxSizing = \"content-box\";\n inner.style.width = width;\n if (hold) inner.style.opacity = \"0\";\n}\n\n/**\n * A bubble that grows into its new contents rather than snapping to them.\n *\n * `morphKey` is what it watches. Change it and the bubble measures where it\n * was, where it now is, and travels between the two. That is what turns three\n * bouncing dots into an answer: same element, same node, a different size.\n *\n * The contents wait, and they wait for the whole morph. React puts the new\n * text in at its full size the moment it arrives, so without this it is\n * already sitting there while the box is still growing, and the box, which\n * clips, wipes it into view a sliver at a time. Fading it in partway through\n * only moves the problem: a multi line reply is taller than the box until the\n * very end, so it fades in already cut off. The box travels first and the\n * words arrive in it once there is room for all of them.\n *\n * They are also pinned to the width they just laid out at for the duration.\n * Without that the text re-wraps on every frame while the box around it is\n * still moving, which looks far worse than no animation at all.\n */\nexport function MorphBubble({\n morphKey,\n holdContent = true,\n className,\n contentClassName,\n children,\n}: {\n /** Derive it from the content, so any change to that content morphs. */\n morphKey: string;\n /**\n * Keep the new contents out of sight until the box has finished growing,\n * then fade them in. Off for contents that bring an arrival of their own,\n * which is the widget's reply climbing in a line at a time: the box still\n * pins their width while it travels, and shows them as they come.\n */\n holdContent?: boolean;\n className?: string;\n contentClassName?: string;\n children: ReactNode;\n}) {\n const box = useRef<HTMLDivElement>(null);\n const content = useRef<HTMLDivElement>(null);\n const previous = useRef<{ width: number; height: number } | null>(null);\n const running = useRef<Animation[]>([]);\n\n useLayoutEffect(() => {\n const outer = box.current;\n const inner = content.current;\n if (!outer || !inner) return;\n\n // Where the box is right now, mid travel included, so a change that lands\n // during a morph carries on from what is on screen rather than from where\n // that morph happened to start.\n const from = running.current.length\n ? { width: outer.offsetWidth, height: outer.offsetHeight }\n : previous.current;\n\n // Everything the last morph was holding comes off before this one\n // measures, or \"the natural size of the new contents\" is really just the\n // size the last one was pinning them at.\n const interrupted = running.current;\n running.current = [];\n release(inner);\n for (const animation of interrupted) animation.cancel();\n\n const next = { width: outer.offsetWidth, height: outer.offsetHeight };\n previous.current = next;\n\n // Nothing to travel from on the first paint, and nothing to travel to if\n // the new contents happen to be the same size.\n if (!from) return;\n if (\n Math.abs(from.width - next.width) < EPSILON &&\n Math.abs(from.height - next.height) < EPSILON\n ) {\n return;\n }\n if (prefersReducedMotion()) return;\n\n // Set before the browser paints, so the new contents are never on screen\n // at full size for even one frame while the box is still small.\n pin(inner, holdContent);\n\n const size = outer.animate(\n [\n { width: `${from.width}px`, height: `${from.height}px` },\n { width: `${next.width}px`, height: `${next.height}px` },\n ],\n { duration: MORPH_MS, easing: MORPH_EASING },\n );\n\n // Contents arriving on their own only need the width held while the box\n // moves, and get it back the moment it stops.\n if (!holdContent) {\n running.current = [size];\n size.finished\n .catch(() => {})\n .finally(() => {\n if (!running.current.includes(size)) return;\n running.current = [];\n release(inner);\n });\n return;\n }\n // Filled both ways on purpose. Backwards holds it invisible through the\n // delay, while the box is still moving. Forwards is what stops it\n // blinking at the other end: the inline opacity above is what hides it\n // before the animation starts, and without a forwards fill the finished\n // animation stops applying and that inline zero shows through again for\n // a frame before the cleanup below can clear it.\n const reveal = inner.animate([{ opacity: 0 }, { opacity: 1 }], {\n duration: CONTENT_FADE_MS,\n delay: MORPH_MS,\n easing: \"ease-out\",\n fill: \"both\",\n });\n\n running.current = [size, reveal];\n\n reveal.finished\n .catch(() => {})\n .finally(() => {\n if (!running.current.includes(reveal)) return;\n running.current = [];\n // Drop the inline hold first and only then the animation holding it\n // open, so there is never a frame where neither is keeping it up.\n release(inner);\n reveal.cancel();\n });\n // `holdContent` is read here and settled per arrival on purpose: a key\n // that stays put has nothing to morph, whatever the hold says now.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [morphKey]);\n\n return (\n <div ref={box} className={cn(\"overflow-hidden\", className)}>\n <div ref={content} className={contentClassName}>\n {children}\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport { useState, type ReactNode } from \"react\";\nimport { File, ImageSquare, Play } from \"@/components/icons\";\nimport { cn } from \"@/lib/utils\";\nimport type { SupportAttachment } from \"./types\";\n\n/** Tiles sit on the panel colour so they read as objects inside the bubble. */\nconst SURFACE = \"rounded-xl border border-foreground/10 bg-card/70\";\n\n/**\n * Opens the file, for anything that has somewhere to be opened.\n *\n * A new tab, because this is running on somebody else's page and a visitor\n * halfway through asking for help should not lose it. No `download`: the file\n * is served from another origin, where browsers ignore that attribute, so it\n * would promise a save and quietly do something else.\n *\n * Falls through to the plain contents when there is no URL, which is a file\n * still on its way or one whose link could not be signed. Better a tile that\n * does nothing than a link that goes nowhere.\n */\nfunction OpenAttachment({\n attachment,\n className,\n children,\n}: {\n attachment: SupportAttachment;\n className?: string;\n children: ReactNode;\n}) {\n if (!attachment.url) return <>{children}</>;\n\n return (\n <a\n href={attachment.url}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n aria-label={`Open ${attachment.name}`}\n className={cn(\n \"block rounded-xl transition-opacity outline-none hover:opacity-80\",\n \"focus-visible:ring-3 focus-visible:ring-ring/50\",\n className,\n )}\n >\n {children}\n </a>\n );\n}\n\nfunction MediaTile({\n attachment,\n className,\n}: {\n attachment: SupportAttachment;\n className?: string;\n}) {\n const [hasFailed, setHasFailed] = useState(false);\n\n // A preview that will not load says so in place, rather than leaving a grey\n // rectangle to puzzle over.\n if (!attachment.previewUrl || hasFailed) {\n return (\n <div\n className={cn(\n SURFACE,\n \"flex flex-col items-center justify-center gap-1 px-2 text-center\",\n className,\n )}\n >\n <ImageSquare className=\"size-6 text-muted-foreground\" />\n <p className=\"text-sm text-muted-foreground\">Preview unavailable</p>\n </div>\n );\n }\n\n return (\n <figure className={cn(SURFACE, \"relative overflow-hidden\", className)}>\n {/* A plain img, not the framework's: a freshly picked file is an object\n URL that has not left the browser, so there is nothing for an image\n pipeline to do, and this file also ships in the npm package where\n there is no framework to ask. */}\n {/* eslint-disable-next-line @next/next/no-img-element -- an object URL or signed link, nothing for the optimizer to do, and the npm bundle has no next/image. */}\n <img\n src={attachment.previewUrl}\n alt={attachment.name}\n width={640}\n height={480}\n onError={() => setHasFailed(true)}\n draggable={false}\n className=\"size-full object-cover\"\n />\n\n {attachment.kind === \"video\" && (\n <span\n aria-hidden\n className=\"absolute inset-0 grid place-items-center bg-foreground/15\"\n >\n <span className=\"grid size-10 place-items-center rounded-full bg-background/85 shadow-sm\">\n <Play className=\"size-4 translate-x-px\" />\n </span>\n </span>\n )}\n </figure>\n );\n}\n\nfunction FileRow({ attachment }: { attachment: SupportAttachment }) {\n return (\n <OpenAttachment attachment={attachment}>\n <div className={cn(SURFACE, \"flex items-center gap-2.5 px-2.5 py-2\")}>\n <File className=\"size-5 shrink-0 text-muted-foreground\" />\n\n <div className=\"flex min-w-0 flex-1 flex-col text-left\">\n <p className=\"truncate text-sm font-medium\">{attachment.name}</p>\n <p className=\"truncate text-sm text-muted-foreground\">\n {attachment.size}\n </p>\n </div>\n </div>\n </OpenAttachment>\n );\n}\n\n/**\n * Everything hanging off a message. Pictures and clips get a tile, anything\n * else gets a named row.\n *\n * Tiles go shorter once they share a row, so a pair reads as a pair rather than\n * two letterboxes stacked in a box.\n */\nexport function WidgetAttachments({\n attachments,\n}: {\n attachments: SupportAttachment[];\n}) {\n const media = attachments.filter((one) => one.kind !== \"file\");\n const files = attachments.filter((one) => one.kind === \"file\");\n\n return (\n <div className=\"flex w-64 max-w-full flex-col gap-2\">\n {media.length > 0 && (\n <div className={cn(\"grid gap-2\", media.length > 1 && \"grid-cols-2\")}>\n {media.map((attachment) => (\n <OpenAttachment\n key={attachment.id}\n attachment={attachment}\n className={media.length > 1 ? \"h-24\" : \"h-36\"}\n >\n {/* Opens in a tab rather than a lightbox. The inbox has one\n because a teammate is working through a queue; a visitor is\n looking at a picture they sent a minute ago. */}\n <MediaTile attachment={attachment} className=\"size-full\" />\n </OpenAttachment>\n ))}\n </div>\n )}\n\n {files.map((attachment) => (\n <FileRow key={attachment.id} attachment={attachment} />\n ))}\n </div>\n );\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { MorphBubble } from \"@/components/ui/morph-bubble\";\n\n/**\n * The widget's bubble. The morph itself is shared with the inbox, so both\n * sides of a conversation grow into a reply the same way; this only fixes\n * how the contents stack inside it.\n */\nexport function WidgetBubble({\n morphKey,\n holdContent,\n className,\n children,\n}: {\n morphKey: string;\n /** See `MorphBubble`. Off while the reply is arriving a line at a time. */\n holdContent?: boolean;\n className?: string;\n children: ReactNode;\n}) {\n return (\n <MorphBubble\n morphKey={morphKey}\n holdContent={holdContent}\n className={className}\n contentClassName=\"flex flex-col gap-2\"\n >\n {children}\n </MorphBubble>\n );\n}\n","import { Suspense } from \"react\";\nimport { LineReveal } from \"@/components/ui/line-reveal\";\nimport { MessageMarkdown, useMarkdownReady } from \"./markdown-chunk\";\nimport type { SupportSender } from \"./types\";\n\n/**\n * How a reply's words arrive, for one that lands while somebody is watching.\n *\n * `key` is what the arrival hangs on: the same key holds the words still\n * through every render after. `delay` is how long the first line waits,\n * which is the time the bubble around it spends growing into the room the\n * lines need.\n */\nexport type BodyArrival = { key: string; delay: number };\n\n/**\n * What was said.\n *\n * The agent's side renders markdown, because the house voice writes it: a\n * bold word, a short list, a line of code. The visitor's side is quoted\n * verbatim, asterisks and all, since nothing they type is markup.\n *\n * A reply that lands while the panel is open arrives a line at a time, each\n * rising on its own beat. The bubble around it is already growing, so the\n * ladder waits for it: the box travels first, and the words climb into it\n * once there is room for all of them. History, and anything the visitor\n * wrote themselves, sits still.\n */\nexport function WidgetMessageBody({\n body,\n sender,\n arrival = null,\n}: {\n body: string;\n sender: SupportSender;\n /** Null for words that were already here, which is most of them. */\n arrival?: BodyArrival | null;\n}) {\n // False until the renderer has been fetched. See `markdown-chunk.tsx` for\n // when that happens and why it is not simply imported.\n const isReady = useMarkdownReady();\n\n const plain = <p className=\"break-words whitespace-pre-line\">{body}</p>;\n if (sender === \"visitor\") return plain;\n\n // The same paragraph again as the fallback, so a render that beats the\n // module here by a frame shows the words rather than a gap.\n const words = isReady ? (\n <Suspense fallback={plain}>\n <MessageMarkdown body={body} />\n </Suspense>\n ) : (\n plain\n );\n if (arrival === null) return words;\n\n return (\n <LineReveal contentKey={arrival.key} delay={arrival.delay}>\n {words}\n </LineReveal>\n );\n}\n","\"use client\";\n\nimport { useLayoutEffect, useRef, useState, type ReactNode } from \"react\";\nimport { useMediaQuery } from \"@/hooks/use-media-query\";\nimport { durationMs } from \"@/lib/motion\";\nimport { cn } from \"@/lib/utils\";\n\n/** Past this, extra lines share the last beat so the wait cannot outrun reading. */\nconst LAST_BEAT = 11;\n\n/** How long the words wait for their code to be colored before rising anyway. */\nconst COLOR_WAIT_MS = 400;\n\ntype Slice = {\n top: number;\n height: number;\n};\n\ntype Delay = number | `--${string}`;\n\n/** One arrival: what it is of, how long it waited, and whether it is still on. */\ntype Play = {\n key: string;\n delay: Delay;\n isPlaying: boolean;\n};\n\n/**\n * Unique line tops inside a laid-out reply, then gapless windows from one\n * top to the next. A wrapped paragraph is several lines, and so is code: it\n * rises a line at a time like the words around it, its frame's top edge\n * riding with its first line. A table is one object. That is the\n * difference between a line arriving and a chunk.\n */\nfunction lineSlices(root: HTMLElement): Slice[] {\n const host = root.getBoundingClientRect();\n if (host.height < 1) return [];\n\n const tops: number[] = [];\n const skip = \"table, img, hr\";\n\n for (const block of root.querySelectorAll(skip)) {\n const box = block.getBoundingClientRect();\n if (box.height < 1) continue;\n tops.push(box.top - host.top);\n }\n\n // A code block's first line starts where its frame does.\n const firstLines: number[] = [];\n for (const pre of root.querySelectorAll(\"pre\")) {\n const frame = pre.closest(\"[data-code-block]\") ?? pre;\n const box = frame.getBoundingClientRect();\n if (box.height < 1) continue;\n tops.push(box.top - host.top);\n const line = pre.querySelector(\"code, span\")?.getClientRects()[0];\n if (line) firstLines.push(line.top - host.top);\n }\n\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {\n acceptNode(node) {\n if (node.textContent?.trim() === \"\") return NodeFilter.FILTER_REJECT;\n const parent = node.parentElement;\n if (parent !== null && parent.closest(skip) !== null) {\n return NodeFilter.FILTER_REJECT;\n }\n return NodeFilter.FILTER_ACCEPT;\n },\n });\n\n const range = document.createRange();\n let node = walker.nextNode();\n while (node !== null) {\n range.selectNodeContents(node);\n for (const box of range.getClientRects()) {\n if (box.height < 1 || box.width < 1) continue;\n tops.push(box.top - host.top);\n }\n node = walker.nextNode();\n }\n\n if (tops.length === 0) {\n return [{ top: 0, height: host.height }];\n }\n\n const unique = [...new Set(tops.map((top) => Math.round(top)))]\n .filter((top) => !firstLines.some((first) => Math.abs(top - first) <= 1))\n .sort((a, b) => a - b);\n\n return unique.map((top, index) => {\n const next = unique[index + 1] ?? Math.ceil(host.height);\n return { top, height: Math.max(1, next - top) };\n });\n}\n\n/**\n * Everything past the last beat rises together anyway, so it rides in one\n * window instead of one each. An essay costs the same twelve copies of the\n * reply that a paragraph does, which is what keeps a long answer from\n * arriving as a stall.\n */\nfunction beats(slices: Slice[]): Slice[] {\n if (slices.length <= LAST_BEAT + 1) return slices;\n\n const rest = slices.slice(LAST_BEAT);\n const last = rest[rest.length - 1];\n return [\n ...slices.slice(0, LAST_BEAT),\n { top: rest[0].top, height: last.top + last.height - rest[0].top },\n ];\n}\n\n/**\n * How long the arrival runs, read off the stage rather than the document:\n * inside the support widget on a host's page the tokens live on the\n * widget's own root, and the stage inherits them from there.\n */\nfunction playMs(stage: HTMLElement, delay: number): number {\n const rise = durationMs(\n \"--duration-line-rise\",\n durationMs(\"--duration-very-slow\", 500, stage),\n stage,\n );\n const beat = durationMs(\"--duration-line-beat\", 70, stage);\n // A frame's grace, so the real reply takes over after the last line has\n // landed rather than a tick inside its own arrival.\n return delay + rise + LAST_BEAT * beat + 32;\n}\n\nfunction paintSlices(\n source: HTMLElement,\n stage: HTMLElement,\n delay: number,\n): boolean {\n const slices = beats(lineSlices(source));\n if (slices.length === 0) return false;\n\n // Built off the page and hung in one go. The palette's list is watched by\n // cmdk and by the selection pill, and appending a window at a time gives\n // them a mutation each to answer with a fresh read of the layout.\n const batch = document.createDocumentFragment();\n for (const [index, slice] of slices.entries()) {\n const frame = document.createElement(\"div\");\n frame.className = \"t-line-slice\";\n frame.style.top = `${slice.top}px`;\n frame.style.height = `${slice.height}px`;\n frame.style.setProperty(\"--t-line\", String(index));\n\n const clone = source.cloneNode(true) as HTMLElement;\n clone.style.marginTop = `-${slice.top}px`;\n // Colors that came in while it waited were never seen plain.\n for (const tinted of clone.querySelectorAll(\"[data-tint]\")) {\n tinted.removeAttribute(\"data-tint\");\n }\n frame.appendChild(clone);\n batch.appendChild(frame);\n }\n stage.style.setProperty(\"--t-line-delay\", `${delay}ms`);\n stage.replaceChildren(batch);\n return true;\n}\n\n/**\n * A finished answer, read top to bottom: each visual line rises on its own\n * beat, then the real reply takes over so links and the reader land on one\n * tree rather than a stack of copies.\n */\nexport function LineReveal({\n contentKey,\n delay = 0,\n children,\n className,\n}: {\n /** Change this and the arrival plays again. Same key holds still. */\n contentKey: string;\n /**\n * How long the first line waits, in milliseconds or a duration token. For a\n * reply whose bubble is still growing into the room the lines need: the\n * ladder starts once the box has stopped. Read as the arrival starts, so\n * a change afterwards does not replay it.\n */\n delay?: Delay;\n children: ReactNode;\n className?: string;\n}) {\n const sourceRef = useRef<HTMLDivElement>(null);\n const stageRef = useRef<HTMLDivElement>(null);\n const reduce = useMediaQuery(\"(prefers-reduced-motion: reduce)\");\n // The delay rides with the key it was given for, so the one that starts\n // an arrival is the one it keeps.\n const [play, setPlay] = useState<Play>({\n key: contentKey,\n delay,\n isPlaying: !reduce,\n });\n if (play.key !== contentKey) {\n setPlay({ key: contentKey, delay, isPlaying: !reduce });\n }\n const { key: playKey, delay: hold, isPlaying } = play;\n\n useLayoutEffect(() => {\n if (reduce || !isPlaying) return;\n\n const source = sourceRef.current;\n const stage = stageRef.current;\n if (source === null || stage === null) return;\n\n const wait = typeof hold === \"number\" ? hold : durationMs(hold, 0, stage);\n\n let painted = false;\n let timer = 0;\n const started = performance.now();\n\n const end = () => setPlay((now) => ({ ...now, isPlaying: false }));\n\n // Code whose colors are on their way holds the rise, for a moment, so\n // the lines go up colored rather than turning colored after.\n const isColoring = () =>\n source.querySelector('[data-highlight=\"pending\"]') !== null &&\n performance.now() - started < COLOR_WAIT_MS;\n\n const paint = () => {\n if (painted || isColoring()) return;\n // Time spent waiting comes off the delay rather than adding to it.\n const left = Math.max(0, wait - (performance.now() - started));\n if (!paintSlices(source, stage, left)) return;\n painted = true;\n timer = window.setTimeout(end, playMs(stage, left));\n // Nothing else to wait for. Left watching, it would answer the\n // arrival's own reflows with work nobody is going to look at.\n observer.disconnect();\n colors.disconnect();\n };\n\n const observer = new ResizeObserver(paint);\n const colors = new MutationObserver(paint);\n paint();\n observer.observe(source);\n colors.observe(source, {\n subtree: true,\n attributes: true,\n attributeFilter: [\"data-highlight\"],\n });\n const giveUp = window.setTimeout(paint, COLOR_WAIT_MS);\n const coloring = source.querySelector('[data-highlight=\"pending\"]') !== null;\n const fallback = window.setTimeout(\n () => {\n if (!painted) end();\n },\n (coloring ? COLOR_WAIT_MS : 0) + 120,\n );\n\n return () => {\n observer.disconnect();\n colors.disconnect();\n window.clearTimeout(timer);\n window.clearTimeout(giveUp);\n window.clearTimeout(fallback);\n stage.replaceChildren();\n };\n }, [playKey, hold, isPlaying, reduce]);\n\n const showSlices = isPlaying && !reduce;\n\n return (\n <div className={cn(\"relative\", className)}>\n <div className={showSlices ? \"pointer-events-none opacity-0\" : undefined}>\n <div ref={sourceRef}>{children}</div>\n </div>\n {showSlices && (\n <div\n ref={stageRef}\n aria-hidden\n className=\"absolute inset-0 overflow-hidden\"\n />\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { useSyncExternalStore } from \"react\";\n\n/**\n * Whether a media query currently matches.\n *\n * `ssrValue` is what the server (and the first paint) answer, so a query that\n * is only true on a phone must default to false: the desktop layout is the\n * one the HTML is built for, and a phone hydrates into its own.\n */\nexport function useMediaQuery(query: string, ssrValue = false): boolean {\n return useSyncExternalStore(\n (onChange) => {\n const media = window.matchMedia(query);\n media.addEventListener(\"change\", onChange);\n return () => media.removeEventListener(\"change\", onChange);\n },\n () => window.matchMedia(query).matches,\n () => ssrValue,\n );\n}\n\n/**\n * Tailwind's `sm` breakpoint, written out: a phone is narrower than 40rem.\n *\n * The server answers false, matching every `max-sm:` class that hides until\n * the client knows the width.\n */\nexport function usePhone(): boolean {\n return useMediaQuery(\"(max-width: 39.999rem)\");\n}\n\n/**\n * Tailwind's `lg` breakpoint. The server answers true, so a three-pane\n * desktop layout does not collapse in the HTML and then reopen.\n */\nexport function useLgUp(): boolean {\n return useMediaQuery(\"(min-width: 64rem)\", true);\n}\n\n/**\n * Tailwind's `md` breakpoint, where the sidebar opens out beside the page.\n * The server answers true, matching every `md:` class that shows until the\n * client knows the width.\n */\nexport function useMdUp(): boolean {\n return useMediaQuery(\"(min-width: 48rem)\", true);\n}\n","/**\n * The motion scale, read from the page rather than written down twice.\n *\n * `motion.css` declares every duration in milliseconds, but a custom\n * property computes to whatever the build chose to print, and the minifier\n * prints the shorter of the two: `80ms` stays, `350ms` becomes `.35s`. So\n * the unit has to be read. Parsing the number alone turns a third of a\n * second into a third of a millisecond, and whatever was waiting on it\n * stops waiting at all.\n */\nexport function durationMs(\n token: string,\n fallback: number,\n /**\n * Where to read it from. The page's root by default; the widget passes one\n * of its own nodes, because on a host's page its tokens are scoped to the\n * widget's roots and the document's root has never heard of them.\n */\n from: Element = document.documentElement,\n): number {\n const raw = getComputedStyle(from).getPropertyValue(token).trim();\n const value = Number.parseFloat(raw);\n if (Number.isNaN(value)) return fallback;\n if (raw.endsWith(\"ms\")) return value;\n if (raw.endsWith(\"s\")) return value * 1000;\n return fallback;\n}\n","\"use client\";\n\nimport Image from \"next/image\";\nimport { useState } from \"react\";\nimport { GlobeSimple } from \"@/components/icons\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * A website's favicon, with a globe underneath it.\n *\n * The globe is the resting state rather than a fallback swapped in later:\n * the favicon fades in over it once it has actually loaded, so a slow icon\n * never pops in and a broken one never shows the browser's torn page. An\n * icon found after the chip is already on screen arrives the same way.\n *\n * Sized by `className`, like the avatars.\n *\n * The support widget wears this too, on customers' sites, and the widget's\n * look does not follow the app's. A restyle here reaches every host, so\n * keep this to behaviour and leave the look to the callers.\n */\nexport function SiteIcon({\n src,\n className,\n}: {\n /** The site's icon, or null for the globe alone. */\n src: string | null;\n className?: string;\n}) {\n // Which icon loaded or failed, rather than whether one did, so a new\n // address starts over instead of inheriting the last one's answer.\n const [loadedSrc, setLoadedSrc] = useState<string | null>(null);\n const [failedSrc, setFailedSrc] = useState<string | null>(null);\n const isShown = src !== null && loadedSrc === src;\n\n return (\n <span\n aria-hidden\n className={cn(\n \"relative inline-grid size-4 shrink-0 place-items-center overflow-hidden rounded-full\",\n className,\n )}\n >\n <GlobeSimple\n className={cn(\n \"size-full text-muted-foreground transition-opacity duration-150 motion-reduce:transition-none\",\n isShown && \"opacity-0\",\n )}\n />\n\n {src !== null && failedSrc !== src && (\n <Image\n src={src}\n alt=\"\"\n width={32}\n height={32}\n // From somebody else's site, which the optimizer will not fetch.\n unoptimized\n referrerPolicy=\"no-referrer\"\n draggable={false}\n // A cached icon can finish before React is listening for it.\n ref={(image) => {\n if (image?.complete && image.naturalWidth > 0) setLoadedSrc(src);\n }}\n onLoad={() => setLoadedSrc(src)}\n onError={() => setFailedSrc(src)}\n className={cn(\n \"absolute inset-0 size-full object-cover opacity-0 transition-opacity duration-150 motion-reduce:transition-none\",\n isShown && \"opacity-100\",\n )}\n />\n )}\n </span>\n );\n}\n","import type { CSSProperties } from \"react\";\nimport { SiteIcon } from \"@/components/site-icon\";\nimport { cn } from \"@/lib/utils\";\nimport type { BodyArrival } from \"./widget-message-body\";\nimport type { SupportSource } from \"./types\";\n\n/**\n * Where the ladder caps itself. See `LAST_BEAT` in `ui/line-reveal.tsx`: a\n * reply of any length has at most this many beats, so the chips never wait\n * longer than one past the last of them.\n */\nconst LADDER_BEATS = 12;\n\n/**\n * Roughly how many characters one line of a reply holds in the corner panel.\n * On the low side on purpose: the modal fits more, and chips a beat late\n * after the words read fine where chips a beat early rise beside them.\n */\nconst CHARACTERS_PER_LINE = 30;\n\n/**\n * Which beat of the reply's ladder the chips land on: the one after its last\n * line. Counted from the words rather than measured, since the chips render\n * before the lines are laid out.\n */\nfunction beatAfter(body: string): number {\n const lines = body\n .split(\"\\n\")\n .reduce(\n (sum, line) =>\n sum + Math.max(1, Math.ceil(line.length / CHARACTERS_PER_LINE)),\n 0,\n );\n return Math.min(lines, LADDER_BEATS);\n}\n\n/**\n * The pages an answer came from, as a row of chips under it.\n *\n * Each one is the page's favicon and its title, and opens the page in a new\n * tab so the conversation stays where it was. They wrap rather than scroll:\n * there are never more than three, and a row that hides one is a row that\n * hides the one somebody wanted.\n *\n * A reply arriving in front of somebody brings its chips in as one more line\n * of the ladder, after the words. History sits still.\n */\nexport function WidgetSources({\n sources,\n body,\n arrival = null,\n compact = false,\n}: {\n sources: SupportSource[];\n /** What the chips hang under, which decides when they arrive. */\n body: string;\n /** How the words are arriving, if they are arriving now. */\n arrival?: BodyArrival | null;\n /** The denser scale. See `SupportWidget`. */\n compact?: boolean;\n}) {\n const ladder =\n arrival === null\n ? undefined\n : ({\n \"--t-line-delay\": `${arrival.delay}ms`,\n \"--t-line\": beatAfter(body),\n } as CSSProperties);\n\n return (\n <ul\n aria-label=\"Sources\"\n className={cn(\n \"flex max-w-full flex-wrap gap-1.5\",\n ladder !== undefined && \"t-line-after\",\n )}\n style={ladder}\n >\n {sources.map((source) => (\n <li key={source.url} className=\"flex min-w-0 max-w-full\">\n <SourceChip source={source} compact={compact} />\n </li>\n ))}\n </ul>\n );\n}\n\nfunction SourceChip({\n source,\n compact,\n}: {\n source: SupportSource;\n compact: boolean;\n}) {\n return (\n <a\n href={source.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={cn(\n \"flex max-w-[min(100%,16rem)] items-center rounded-full bg-muted text-foreground transition-colors duration-150 outline-none hover:bg-[color-mix(in_oklch,var(--muted),var(--foreground)_7%)] focus-visible:ring-3 focus-visible:ring-ring/50 motion-reduce:transition-none\",\n compact\n ? \"h-6 gap-1.5 pr-2.5 pl-1.5 text-xs\"\n : \"h-7 gap-1.5 pr-3 pl-1.5 text-sm\",\n )}\n >\n <SiteIcon src={source.iconUrl} className={compact ? \"size-3.5\" : \"size-4\"} />\n <span className=\"min-w-0 truncate\">{source.title}</span>\n <span className=\"sr-only\">{\" (opens in a new tab)\"}</span>\n </a>\n );\n}\n","/** Offsets, so the bounce travels along the row instead of all three hopping together. */\nconst DOTS = [0, 160, 320];\n\n/**\n * An agent mid sentence. It lives inside the reply's own bubble rather than in\n * a bubble of its own, which is what lets the bubble stay put and grow into the\n * answer when it arrives.\n */\nexport function WidgetTyping({ label }: { label: string }) {\n return (\n <span className=\"flex items-center gap-1.5 py-2\">\n <span className=\"sr-only\">{label}</span>\n\n {DOTS.map((delay) => (\n <span\n key={delay}\n aria-hidden\n style={{ animationDelay: `${delay}ms` }}\n className=\"size-2 animate-typing-dot rounded-full bg-foreground/45 motion-reduce:animate-none\"\n />\n ))}\n </span>\n );\n}\n","/**\n * The rules the support side of Median follows: how long a message can be,\n * what an agent can be called, what an API key looks like.\n *\n * Deliberately pure, like shared/organizations.ts: no Convex imports, so the\n * web app can import this (`@median/backend/support`) and complain in the same\n * words the mutations would.\n */\n\n/** What an organization's AI agent is called until someone renames it. */\nexport const DEFAULT_AGENT_NAME = \"Median AI\";\n\n/**\n * Whether the agent may end a conversation on its own: resolving one it\n * finished, or closing one that was only ever spam.\n *\n * On unless a team says otherwise. A queue is only useful if what is left in\n * it is what somebody still has to look at, and a thread the agent answered\n * end to end is not one of those. Turning it off means every conversation\n * waits for a person to end it, which is the right call for a team that\n * reads all of them anyway.\n */\nexport const DEFAULT_AUTO_RESOLVE = true;\n\n/**\n * Whether resolved conversations get read for knowledge base suggestions.\n * On by default: the suggestions only ever land in a review queue, so the\n * cost of being wrong is a dismissal, not a bad answer.\n */\nexport const DEFAULT_LEARN_FROM_THREADS = true;\n\n/**\n * Whether the live agent may file knowledge and tool suggestions from the\n * middle of a conversation, the moment something teaches it or stumps it.\n * On by default for the same reason the reader is: everything lands in a\n * review queue, so the cost of a bad one is a dismissal.\n */\nexport const DEFAULT_SUGGEST_KNOWLEDGE = true;\n\n/**\n * Whether the nightly sweep may resolve this organization's finished\n * threads. On by default for the same reason auto-resolve is: an inbox\n * that closes what is clearly done is the behavior a team expects.\n */\nexport const DEFAULT_NIGHTLY_SWEEP = true;\n\n/**\n * Whether the agent may file a bug or a suggestion when a customer hands one\n * over. On by default: a signal is a note in a list a person reads, not a\n * change to anything, and a complaint nobody wrote down is a complaint the\n * team never hears.\n */\nexport const DEFAULT_FILE_SIGNALS = true;\n\n/**\n * Whether the widget asks for an email before a visitor's first message. On\n * by default: an anonymous thread cannot be followed up, and half of support\n * is getting back to somebody. A host can waive it in code, and this is the\n * team's side of the same switch.\n */\nexport const DEFAULT_REQUIRE_EMAIL = true;\n\n/**\n * Whether the closed widget shows the first lines of a reply nobody has read\n * yet. On by default: a count on the button says something arrived, and the\n * card is what says whether it is worth opening. A host can waive it in\n * code, and this is the team's side of the same switch.\n */\nexport const DEFAULT_MESSAGE_PREVIEW = true;\n\nexport const AGENT_NAME_MIN_LENGTH = 2;\nexport const AGENT_NAME_MAX_LENGTH = 40;\nexport const PERSONALITY_MAX_LENGTH = 2000;\n\nexport const MESSAGE_MAX_LENGTH = 4000;\n\n/**\n * How many live Discord ticket channels the bot's subscription carries. A\n * full list means there may be more, and the bot asks about the rest one\n * channel at a time.\n */\nexport const DISCORD_LIVE_CHANNEL_LIMIT = 500;\n\nexport const VISITOR_NAME_MAX_LENGTH = 80;\nexport const VISITOR_EMAIL_MAX_LENGTH = 320;\nexport const VISITOR_AVATAR_URL_MAX_LENGTH = 512;\n/** Extra facts a host attaches to a visitor. Small on purpose. */\nexport const VISITOR_METADATA_MAX_ENTRIES = 16;\nexport const VISITOR_METADATA_MAX_LENGTH = 200;\n\n/** A message can carry a few files, not a filesystem. */\nexport const MESSAGE_MAX_ATTACHMENTS = 6;\nexport const ATTACHMENT_MAX_BYTES = 25 * 1024 * 1024;\n\n/**\n * What a visitor makes of a finished conversation: stars, and whatever they\n * wanted to say about it. Both sides check the score, since the widget draws\n * exactly this many stars and the backend takes nothing else.\n */\nexport const RATING_MIN_STARS = 1;\nexport const RATING_MAX_STARS = 5;\nexport const RATING_FEEDBACK_MAX_LENGTH = 1000;\n\n/** Whether a score is one of the stars actually offered. */\nexport function isRatingStars(stars: number): boolean {\n return (\n Number.isInteger(stars) &&\n stars >= RATING_MIN_STARS &&\n stars <= RATING_MAX_STARS\n );\n}\n\n/** Their words, trimmed and capped. Empty means they had none. */\nexport function cleanFeedback(feedback: string): string {\n return feedback.trim().slice(0, RATING_FEEDBACK_MAX_LENGTH);\n}\n\n/**\n * Publishable, like a Stripe pk_ or an Intercom app id: it ships in the\n * browser on the host's site, so it grants nothing beyond opening a support\n * conversation with that organization.\n */\nexport const API_KEY_PREFIX = \"median_pk_\";\n\n/**\n * The only credential a new Median integration needs. The public id is part\n * of the value so server-side SDKs can safely hand the widget its\n * `median_pk_...` identifier without another environment variable. The\n * secret tail never leaves the server.\n */\nexport const MEDIAN_KEY_PREFIX = \"median_key_\";\n\n/** Purpose-separated signing material is derived from the Median key. */\nexport const MEDIAN_SIGNATURE_PREFIX = \"median_sig_\";\n\nexport const MEDIAN_KEY_PUBLIC_ID_LENGTH = 32;\nexport const MEDIAN_KEY_SECRET_LENGTH = 64;\n\n/** Enough separate credentials for production, staging, and local work. */\nexport const MAX_MEDIAN_KEYS_PER_ORG = 10;\nexport const MEDIAN_KEY_NAME_MAX_LENGTH = 40;\n\n/**\n * The other kind, and the opposite of the one above: this one never goes near\n * a browser. It lives on the host's server, where it signs the id of whoever\n * is signed in, and anybody holding it can claim to be any of their users.\n */\nexport const SIGNING_KEY_PREFIX = \"median_sk_\";\n\n/**\n * What the REST API takes. Secret like the signing key: it lives on the\n * host's server and can read and write their whole inbox's visitor side, so\n * shipping one in a browser is handing the room key to the lobby.\n */\nexport const SECRET_API_KEY_PREFIX = \"median_api_\";\n\n/**\n * What webhook deliveries are signed with, one per endpoint. Held by us and\n * by the receiver, so the receiver can tell our POST from anybody else's.\n */\nexport const WEBHOOK_SECRET_PREFIX = \"median_whsec_\";\n\n/**\n * What tool endpoint calls are signed with, one per organization. Held by us\n * and by the host's server, so their handler can tell our POST from anybody\n * else's. The `@mediansh/agent-tools` package mirrors this literal in\n * `src/handler.ts`; change them together.\n */\nexport const TOOL_SECRET_PREFIX = \"median_toolsec_\";\n\n/**\n * What every prefix above starts with, and what it started with when the\n * product was called Portal.\n *\n * A key issued then is still a key. The rename went all the way to the wire\n * for anything minted from here on, but nothing already in somebody's\n * environment was invalidated by it, so every prefix has a twin and one rule\n * makes it. Read through `legacyPrefix` rather than writing `portal_` out\n * again: there is one family to retire, and this is the only line that names\n * it.\n */\nconst FAMILY = \"median_\";\nconst LEGACY_FAMILY = \"portal_\";\n\n/** The same prefix under the old name. */\nexport function legacyPrefix(prefix: string): string {\n return prefix.startsWith(FAMILY)\n ? `${LEGACY_FAMILY}${prefix.slice(FAMILY.length)}`\n : prefix;\n}\n\n/** Whether a credential carries this prefix under either name. */\nexport function hasPrefix(value: string, prefix: string): boolean {\n return value.startsWith(prefix) || value.startsWith(legacyPrefix(prefix));\n}\n\n/**\n * The prefix a credential actually carries, so anything derived from it\n * comes out in the family it belongs to. A legacy key's signing material\n * was derived as `portal_sig_` and is held that way, and re-deriving it as\n * `median_sig_` would stop it matching.\n */\nexport function prefixCarriedBy(value: string, prefix: string): string | null {\n if (value.startsWith(prefix)) return prefix;\n const legacy = legacyPrefix(prefix);\n return value.startsWith(legacy) ? legacy : null;\n}\n\n/**\n * The bounds on a session token, here rather than in the widget so the REST\n * API and the SDK refuse the same strings in the same words.\n */\nexport const SESSION_ID_MIN_LENGTH = 8;\nexport const SESSION_ID_MAX_LENGTH = 128;\n\n/**\n * A host's own id for a person. Long enough for a UUID or a Stripe style id\n * and short enough that nobody is putting a document in it.\n */\nexport const EXTERNAL_ID_MAX_LENGTH = 128;\n\n/**\n * How long a typing signal counts as live. Composers refresh it well inside\n * this while keys are hit, so anything older is a browser that went away.\n */\nexport const TYPING_TTL_MS = 12_000;\n\n/**\n * The bounds on a snooze. When it comes back is worked out on the client,\n * where the clock and the timezone are, so these only catch a number that\n * cannot have been meant: one already gone, or one so far out that nobody was\n * ever going to see the conversation again.\n */\nexport const SNOOZE_MIN_MS = 60_000;\nexport const SNOOZE_MAX_MS = 365 * 24 * 60 * 60_000;\n\n/** What is wrong with this snooze, in a sentence, or null if nothing is. */\nexport function snoozeIssue(until: number, now: number): string | null {\n if (!Number.isFinite(until)) return \"Choose a valid date and time.\";\n if (until - now < SNOOZE_MIN_MS) {\n return \"Choose a time at least one minute from now.\";\n }\n if (until - now > SNOOZE_MAX_MS) {\n return \"Conversations can be snoozed for up to a year.\";\n }\n return null;\n}\n\n/**\n * A visitor's picture, or null if what we were handed cannot be one.\n *\n * https only. It ends up in an `<img>` on the inbox, and anything else is\n * either blocked as mixed content or was never a picture in the first place.\n * Whatever a host sends is theirs, so it is checked rather than trusted.\n */\nexport function cleanAvatarUrl(input: string | undefined): string | null {\n const url = input?.trim();\n if (!url || url.length > VISITOR_AVATAR_URL_MAX_LENGTH) return null;\n\n try {\n return new URL(url).protocol === \"https:\" ? url : null;\n } catch {\n return null;\n }\n}\n\n/**\n * An address, or null if what we were handed cannot be one.\n *\n * Deliberately barely a check: the shape of an address is the only thing\n * anyone can tell without sending mail to it. It is here because the agent\n * writes these down from whatever a customer typed, and \"looks like an\n * address\" should be decided in one place rather than at each caller.\n */\nexport function cleanEmail(input: string | undefined): string | null {\n const email = input?.trim();\n if (!email || email.length > VISITOR_EMAIL_MAX_LENGTH) return null;\n\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email) ? email : null;\n}\n\n/**\n * What to call a customer wherever a name is printed. Their name when we have\n * one, then their address, since a directory of \"Customer #12\"s all reads\n * the same and an address at least says who. Then the number they were\n * handed on arrival, so an anonymous customer is one person everywhere\n * rather than one of a crowd of anonymous visitors. The last fallback is for\n * rows written before numbers existed.\n */\nexport function customerDisplayName(\n name: string | null | undefined,\n customerNumber: number | null | undefined,\n email?: string | null,\n): string {\n if (name) return name;\n if (email) return email;\n if (customerNumber) return `Customer #${customerNumber}`;\n return \"Anonymous visitor\";\n}\n\n/** What a thread id starts with when the workspace's name gives no letter. */\nconst THREAD_PREFIX_FALLBACK = \"T\";\n\n/**\n * The letter a workspace's thread ids start with: the first letter or digit\n * of its name, in capitals. \"Whirl\" numbers its threads W-1, W-2. A name\n * that opens with neither, all punctuation or an emoji, falls back to T.\n */\nexport function threadPrefixOf(orgName: string): string {\n const first = /[a-z0-9]/i.exec(orgName);\n return first === null\n ? THREAD_PREFIX_FALLBACK\n : first[0].toLocaleUpperCase(\"en-US\");\n}\n\n/**\n * What a thread is called out loud: W-12, the workspace's initial and the\n * number it was handed on arrival. Null for a row that has no number, which\n * is only one written before numbers existed and not yet backfilled.\n */\nexport function threadIdOf(\n orgName: string,\n threadNumber: number | null | undefined,\n): string | null {\n if (threadNumber === null || threadNumber === undefined) return null;\n return `${threadPrefixOf(orgName)}-${threadNumber}`;\n}\n\n/** Collapses the whitespace someone pasted in without changing their words. */\nexport function normalizeAgentName(input: string): string {\n return input.replace(/\\s+/g, \" \").trim();\n}\n\n/** What is wrong with this agent name, in a sentence, or null if nothing is. */\nexport function agentNameIssue(name: string): string | null {\n if (name.length === 0) return \"Enter an agent name.\";\n if (name.length < AGENT_NAME_MIN_LENGTH) {\n return `Use at least ${AGENT_NAME_MIN_LENGTH} characters for the agent name.`;\n }\n if (name.length > AGENT_NAME_MAX_LENGTH) {\n return `Use ${AGENT_NAME_MAX_LENGTH} characters or fewer for the agent name.`;\n }\n return null;\n}\n\n/** What is wrong with this personality, or null. Empty is fine: quiet is a personality. */\nexport function personalityIssue(personality: string): string | null {\n if (personality.length > PERSONALITY_MAX_LENGTH) {\n return `Keep personality instructions to ${PERSONALITY_MAX_LENGTH} characters or fewer.`;\n }\n return null;\n}\n\n/**\n * File types an attachment may not be, whatever it says it is.\n *\n * Storage serves a file back under the type it was stored with, on its own\n * host under the same registrable domain as the app. A browser opening one of\n * these runs it there: a teammate clicking what a stranger called `invoice.pdf`\n * would land on a real Median subdomain running somebody else's page.\n *\n * A refusal list rather than an allow list, because support is where people\n * send whatever they have. Logs, archives, dumps and video are all somebody\n * trying to explain a problem; only the handful of types a browser treats as\n * a document to run have to be turned away.\n *\n * Short on purpose, and it costs something. An exported HTML error report and\n * a diagram saved as SVG are both ordinary evidence, and both are turned away\n * here, because a browser opening either one runs it. Plain XML stays: a\n * config file or a SOAP body is read, not run.\n */\nexport const UNSAFE_ATTACHMENT_TYPES = [\n \"text/html\",\n \"application/xhtml+xml\",\n \"image/svg+xml\",\n \"text/xsl\",\n \"application/xslt+xml\",\n];\n\n/** Whether a content type is one a browser would run rather than show. */\nexport function isUnsafeAttachmentType(contentType: string): boolean {\n const bare = contentType.split(\";\")[0].trim().toLowerCase();\n return UNSAFE_ATTACHMENT_TYPES.includes(bare);\n}\n\n/**\n * The session id namespaces the channels own.\n *\n * A browser mints a random token for itself, but an email or a Discord\n * account is not a browser: those doors derive a session from the address or\n * the account so that everything a person writes from it lands on one\n * visitor. Derived means workable out, and an address is not a secret.\n *\n * What makes them safe is that nothing on the outside may present one.\n * `cleanSessionId` refuses the whole namespace, so an id in it can only ever\n * have come from the ingest that made it.\n */\nexport const CHANNEL_SESSION_PREFIXES = [\"email:\", \"discord:\", \"site:\"] as const;\n\n/** Whether a session token belongs to a channel rather than to a browser. */\nexport function isChannelSessionId(sessionId: string): boolean {\n const lowered = sessionId.toLowerCase();\n return CHANNEL_SESSION_PREFIXES.some((prefix) => lowered.startsWith(prefix));\n}\n","\"use client\";\n\nimport {\n RATING_FEEDBACK_MAX_LENGTH,\n RATING_MAX_STARS,\n} from \"@median/backend/support\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { Star } from \"@/components/icons\";\nimport { WidgetButton } from \"./widget-button\";\nimport { Collapse, COLLAPSE_MS } from \"@/components/ui/collapse\";\nimport { WIDGET_TEXT_FIELD_SHELL as TEXT_FIELD_SHELL } from \"./widget-field\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { cn } from \"@/lib/utils\";\nimport type { SupportRating } from \"./types\";\n\nconst STARS = Array.from({ length: RATING_MAX_STARS }, (_, index) => index + 1);\n\n/** How long the thank you stays before the card folds itself away. */\nconst THANKS_MS = 1400;\n\n/** Between one star lighting up and the next, so a pick reads as a sweep. */\nconst STAR_STAGGER_MS = 40;\n\n/**\n * The question a resolved conversation asks, sitting on the composer.\n *\n * A star saves the moment it is pressed rather than waiting for a Send,\n * because most people stop there and four stars with nothing after them is\n * still the answer. The box for words opens underneath afterwards, so\n * somebody who has more to say has somewhere to say it and nobody is held up\n * by a form they did not want.\n *\n * Nothing here appears or vanishes. The card opens out of the composer, the\n * box for words opens under the stars, the thank you takes the question's\n * place, and the whole thing folds away when it is finished, each of them the\n * same height travelling. Which is why leaving tells the panel it is done\n * only once it has finished folding: unmounting on the click would take the\n * animation with it.\n *\n * Above the composer rather than in place of it: the thread is finished, not\n * closed, and writing again is how a visitor starts the next one.\n */\nexport function WidgetRating({ rating }: { rating: SupportRating }) {\n const [isOpen, setIsOpen] = useState(false);\n const [picked, setPicked] = useState<number | null>(rating.stars);\n const [hovered, setHovered] = useState<number | null>(null);\n const [feedback, setFeedback] = useState(\"\");\n const [isSending, setIsSending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [isDone, setIsDone] = useState(false);\n\n const timers = useRef<ReturnType<typeof setTimeout>[]>([]);\n useEffect(() => {\n // Shut for one frame, open on the next, which is what turns a card that\n // was suddenly there into one that grew out of the composer.\n const frame = requestAnimationFrame(() => setIsOpen(true));\n const pending = timers.current;\n return () => {\n cancelAnimationFrame(frame);\n for (const timer of pending) clearTimeout(timer);\n };\n }, []);\n\n function later(ms: number, run: () => void) {\n timers.current.push(setTimeout(run, ms));\n }\n\n /** Fold away first, and only then tell the panel there is nothing to ask. */\n function leave() {\n setIsOpen(false);\n later(COLLAPSE_MS, rating.skip);\n }\n\n const shown = hovered ?? picked ?? 0;\n\n async function send(stars: number, words?: string) {\n setError(null);\n setIsSending(true);\n try {\n await rating.submit(stars, words);\n return true;\n } catch (caught) {\n console.error(\"Rating the conversation failed\", caught);\n setError(\"Could not submit your rating. Try again.\");\n return false;\n } finally {\n setIsSending(false);\n }\n }\n\n async function choose(stars: number) {\n // On screen first: the star they pressed is the answer whether or not\n // the network agrees yet.\n setPicked(stars);\n await send(stars);\n }\n\n /**\n * Finishing, with or without words. The stars are already saved, so an\n * empty box is somebody who has said all they wanted to and does not owe\n * anybody a sentence about it: no round trip, straight to the thanks.\n */\n async function finish() {\n if (picked === null) return;\n const words = feedback.trim();\n if (words.length > 0 && !(await send(picked, words))) return;\n\n setIsDone(true);\n later(THANKS_MS, leave);\n }\n\n return (\n <Collapse isOpen={isOpen} className=\"shrink-0\">\n {/* The spacing lives inside each section rather than as a gap between\n them, so a section that is closed takes up nothing at all. */}\n <div className=\"mx-4 mb-3 rounded-xl border bg-muted/40 px-4 dark:bg-muted/20\">\n <Collapse isOpen={!isDone}>\n <div className=\"py-3\">\n <div className=\"flex items-start justify-between gap-2\">\n <p className=\"text-base font-medium\">\n {picked === null ? \"How did we do?\" : \"Anything we could improve?\"}\n </p>\n {/* A word rather than a cross: this is a question, and the way\n out of a question is an answer, not a close button. */}\n <WidgetButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"-mt-0.5 -mr-1.5 text-muted-foreground\"\n disabled={isSending}\n onClick={leave}\n >\n Skip\n </WidgetButton>\n </div>\n\n <div\n role=\"radiogroup\"\n aria-label=\"How did we do?\"\n className=\"flex items-center justify-center gap-1.5 pt-2\"\n onMouseLeave={() => setHovered(null)}\n >\n {STARS.map((value) => (\n <button\n key={value}\n type=\"button\"\n role=\"radio\"\n aria-checked={picked === value}\n aria-label={`${value} of ${RATING_MAX_STARS}`}\n disabled={isSending}\n onMouseEnter={() => setHovered(value)}\n onFocus={() => setHovered(value)}\n onBlur={() => setHovered(null)}\n onClick={() => void choose(value)}\n className={cn(\n \"rounded-md p-1 outline-none transition-transform duration-150 ease-out\",\n \"hover:scale-115 focus-visible:ring-3 focus-visible:ring-ring/50\",\n \"active:scale-95 disabled:pointer-events-none\",\n \"motion-reduce:transition-none\",\n )}\n >\n <Star\n // A pick lights the row up one star at a time, left to\n // right. Only a pick: a stagger under the pointer would\n // read as the widget lagging behind the mouse.\n style={{\n transitionDelay:\n hovered === null && picked !== null\n ? `${(value - 1) * STAR_STAGGER_MS}ms`\n : undefined,\n }}\n className={cn(\n \"size-7 transition-colors duration-200 ease-out motion-reduce:transition-none\",\n value <= shown\n ? \"text-foreground\"\n : \"text-muted-foreground/30\",\n )}\n />\n </button>\n ))}\n </div>\n\n <Collapse isOpen={picked !== null}>\n <div className=\"flex flex-col gap-2 pt-2\">\n <Textarea\n value={feedback}\n onChange={(event) => setFeedback(event.target.value)}\n placeholder=\"Add a comment (optional)\"\n aria-label=\"Comment (optional)\"\n rows={2}\n maxLength={RATING_FEEDBACK_MAX_LENGTH}\n disabled={isSending}\n // The field's own shell, over the card's tint rather than in\n // it, so the box to write in reads as a box to write in.\n className={cn(\n TEXT_FIELD_SHELL,\n \"max-h-28 min-h-20 bg-card px-3 py-2.5 text-base\",\n )}\n />\n {/* Never held shut by an empty box. The words are the optional\n half of this, and a button that refuses to finish without\n them is a required field in a hat. */}\n <WidgetButton\n type=\"button\"\n className=\"self-end\"\n disabled={isSending}\n onClick={() => void finish()}\n >\n {isSending\n ? \"Sending\"\n : feedback.trim().length === 0\n ? \"Done\"\n : \"Send\"}\n </WidgetButton>\n </div>\n </Collapse>\n\n <Collapse isOpen={error !== null}>\n <p className=\"pt-2 text-center text-sm text-destructive\">\n {error}\n </p>\n </Collapse>\n </div>\n </Collapse>\n\n <Collapse isOpen={isDone}>\n <p className=\"py-3.5 text-center text-base font-medium\">\n Thanks for the feedback.\n </p>\n </Collapse>\n </div>\n </Collapse>\n );\n}\n","import { Monitor } from \"@/components/icons\";\nimport { WidgetButton } from \"./widget-button\";\nimport type { SupportScreenRequest } from \"./types\";\n\n/**\n * The agent asking to see their screen, as two buttons over the composer.\n *\n * The message above has already asked in the agent's own words, so this is\n * only the answer: share it, or not. Sharing is the filled pill, shaped like\n * a destination's button, since it is the thing in the thread to press.\n * \"Not now\" sits beside it as plain text on the same line, so saying no is\n * one press and never a hunt.\n *\n * Both hold once either is pressed. The browser's own prompt comes next,\n * and the offer folds away when the answer reaches the thread.\n */\nexport function WidgetScreenRequest({\n request,\n}: {\n request: SupportScreenRequest;\n}) {\n return (\n <div className=\"flex items-center gap-1\">\n <WidgetButton\n type=\"button\"\n onClick={request.share}\n disabled={request.isSharing}\n // The side padding is the bubble's own, as the destination's is, so\n // the label starts under the words that asked.\n className=\"h-auto gap-2.5 rounded-full px-4 py-2.5 text-base\"\n >\n <Monitor aria-hidden className=\"size-4\" />\n Share my screen\n </WidgetButton>\n <WidgetButton\n type=\"button\"\n variant=\"ghost\"\n onClick={request.decline}\n disabled={request.isSharing}\n className=\"h-auto rounded-full px-4 py-2.5 text-base text-muted-foreground\"\n >\n Not now\n </WidgetButton>\n </div>\n );\n}\n","import { EnvelopeSimple, UserCircle } from \"@/components/icons\";\nimport { cn } from \"@/lib/utils\";\nimport { PoweredByMedian } from \"./widget-badge\";\n\n/**\n * A quiet line about where the conversation stands, rather than about the\n * last thing said in it.\n *\n * Above the composer rather than under the header, for the same reason the\n * inbox puts its own status line there: the question it settles is who writes\n * the next reply, and that belongs next to the box you would write it in.\n *\n * Not a live region. The handoff arrives as a message too, in a thread that\n * already announces itself, and a screen reader should hear that once.\n *\n * No arrival of its own: the panel folds it open and shut.\n */\nexport function WidgetAwaitingHuman() {\n return (\n <p className=\"flex items-center justify-center gap-1.5 px-4 pb-1.5 text-sm text-muted-foreground\">\n <UserCircle aria-hidden className=\"size-4\" />\n Waiting for the support team\n </p>\n );\n}\n\n/**\n * Where the composer would be, on a conversation that is answered by email.\n * The replies go to the mail client, so a box here would be a second place\n * to write that nobody reads in the same way. The badge stays where the\n * composer keeps it, so the foot of the panel does not change shape.\n */\nexport function WidgetByEmail({\n showBadge,\n compact,\n}: {\n showBadge: boolean;\n compact: boolean;\n}) {\n return (\n <div\n className={cn(\n \"flex shrink-0 flex-col items-center gap-2 pt-2\",\n compact ? \"px-5 pb-4\" : \"px-4 pb-4\",\n )}\n >\n <p\n className={cn(\n \"flex items-center justify-center gap-1.5 text-muted-foreground\",\n compact ? \"text-xs\" : \"text-sm\",\n )}\n >\n <EnvelopeSimple aria-hidden className=\"size-4\" />\n This conversation continues by email\n </p>\n {showBadge && <PoweredByMedian />}\n </div>\n );\n}\n","\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n useSyncExternalStore,\n type PointerEvent as ReactPointerEvent,\n type RefObject,\n} from \"react\";\nimport { flushSync } from \"react-dom\";\nimport {\n getDefaultSpot,\n getSpot,\n metricsFor,\n setSpot,\n spotFrom,\n spotFromPanel,\n subscribeSpot,\n type Bounds,\n type Size,\n} from \"./launcher-position\";\nimport { prefersReducedMotion } from \"./motion\";\n\n/** Further than this and it is a drag. Under it, it is a click that wobbled. */\nconst DRAG_THRESHOLD = 5;\n\n/**\n * What a press takes hold of: the button while it is closed, and the panel\n * itself, by its header, once it is open.\n *\n * Both move the same box. The difference is what is being kept on the page\n * while it travels, and what the spot is read back off when it lands.\n */\nexport type DragSubject = \"button\" | \"panel\";\n\n/** What a surface wears to become something the widget can be dragged by. */\nexport type DragHandlers = {\n onPointerDown: (event: ReactPointerEvent<HTMLElement>) => void;\n onPointerMove: (event: ReactPointerEvent<HTMLElement>) => void;\n onPointerUp: (event: ReactPointerEvent<HTMLElement>) => void;\n onPointerCancel: (event: ReactPointerEvent<HTMLElement>) => void;\n};\n\n/**\n * A press that landed on a control belongs to that control.\n *\n * Only the panel needs asking. The button is the handle and the whole of it,\n * so a press anywhere on it is a press on the thing being dragged; the header\n * is a strip with a close button and a row of faces on it, and grabbing the\n * panel by the close button would be a drag that shuts it.\n */\nfunction isControl(target: EventTarget | null): boolean {\n return (\n target instanceof Element &&\n target.closest(\"a, button, input, select, textarea\") !== null\n );\n}\n\n/** The flight to the edge once it is let go. Matches --ease-spring. */\nconst SNAP_MS = 420;\nconst SNAP_EASING = \"cubic-bezier(0.34, 1.45, 0.64, 1)\";\n\n/**\n * The button's height inside the box, which the clip and the button both read.\n * A custom property because it inherits: one write on the box reaches the\n * panel, the shadow and the button, and none of them has to be told twice.\n */\nexport const BUTTON_Y = \"--median-button-y\";\n\n/** Everything the drag needs, measured once, while it is still standing still. */\ntype Grip = {\n subject: DragSubject;\n grabX: number;\n grabY: number;\n startX: number;\n startY: number;\n bounds: Bounds;\n margin: number;\n /** The button, which the spot is always defined in terms of. */\n size: Size;\n box: Size;\n /** The one actually travelling: the button, or the whole box. */\n dragged: Size;\n /** Where that one was before any of this, and where it is now. */\n resting: { x: number; y: number };\n at: { x: number; y: number };\n moved: boolean;\n};\n\n/**\n * Dragging the launcher, and the edge it settles against.\n *\n * The box that holds the panel is positioned to contain the button, and the\n * button is pinned inside it, so moving one moves both. The closed panel is a\n * circle sitting exactly under the button; if only the button travelled it\n * would slide off its own shadow.\n *\n * Nothing goes through React state while a finger is down. The offset is\n * written straight onto the box, because a `setState` per pointermove renders\n * the whole panel underneath, and the panel is an entire conversation. React\n * hears the spot once, at the end.\n *\n * The resting geometry cannot be written in CSS, because the button's height\n * inside the box is the difference between two things measured against\n * different boxes. So it is measured and written, before paint, and the style\n * carries a plain calc as a fallback for the frame before that happens, which\n * is the one the server rendered.\n */\nexport function useLauncherDrag({\n boxRef,\n buttonRef,\n margin,\n enabled,\n subject,\n}: {\n boxRef: RefObject<HTMLDivElement | null>;\n buttonRef: RefObject<HTMLButtonElement | null>;\n /** Any CSS length. How far off the edges it sits. */\n margin: string;\n /** Off when there is nothing on the page to take hold of. */\n enabled: boolean;\n /** Which of the two a press is grabbing, which is to say whether it is open. */\n subject: DragSubject;\n}) {\n const spot = useSyncExternalStore(subscribeSpot, getSpot, getDefaultSpot);\n\n // Two renders a drag, at the ends of it. Never one per move. It stays true\n // through the flight as well, because the clip is keyed to the button's\n // height and that changes as it lands; transitioning between the two would\n // wipe the panel across the box on the way.\n const [isMoving, setIsMoving] = useState(false);\n\n const grip = useRef<Grip | null>(null);\n /** Set by the drag that just ended, read by the click it turns into. */\n const justDragged = useRef(false);\n\n /** The space it lives in: the page, or whatever is positioning it. */\n const measure = useCallback((): {\n bounds: Bounds;\n margin: number;\n size: Size;\n box: Size;\n } | null => {\n const box = boxRef.current;\n const button = buttonRef.current;\n if (!box) return null;\n\n const parent = box.offsetParent;\n const rect =\n parent instanceof HTMLElement\n ? parent.getBoundingClientRect()\n : new DOMRect(0, 0, window.innerWidth, window.innerHeight);\n const boxRect = box.getBoundingClientRect();\n\n return {\n bounds: {\n left: rect.left,\n top: rect.top,\n width: rect.width,\n height: rect.height,\n },\n // Asked of the resolved style rather than parsed out of the string we\n // were handed, so a margin in any unit at all comes back in pixels.\n margin: Math.round(\n Number.parseFloat(getComputedStyle(box).getPropertyValue(spot.side)) ||\n 0,\n ),\n // Offset sizes, so a button mid press is not measured through whatever\n // scale it happens to be wearing.\n //\n // No button at all is a widget the host opens themselves. Nothing\n // hangs below the panel then, so it is a button of no size: the same\n // arithmetic, with the box parked against the bottom margin.\n size: button\n ? { width: button.offsetWidth, height: button.offsetHeight }\n : { width: 0, height: 0 },\n // The box gets the fractional one instead, and the difference is the\n // whole of whether a resize is smooth. Its top is worked out as its\n // bottom edge minus its height, so a height rounded to a whole pixel\n // against a box that is mid animation puts the bottom edge up to half a\n // pixel out, in a different direction on every frame. The panel shimmers\n // in place rather than growing. Nothing scales the box, so a rect is as\n // safe here as an offset and says the same thing more precisely.\n box: { width: boxRect.width, height: boxRect.height },\n };\n }, [boxRef, buttonRef, spot.side]);\n\n /**\n * Writes the resting geometry onto the nodes. Before paint on the way in,\n * and again whenever the space it is measured against changes.\n */\n const place = useCallback(() => {\n const box = boxRef.current;\n const taken = measure();\n if (!box || taken === null) return;\n\n const metrics = metricsFor(\n spot,\n taken.bounds,\n taken.size,\n taken.box.height,\n taken.margin,\n );\n\n box.style.top = `${metrics.boxTop - taken.bounds.top}px`;\n box.style.setProperty(BUTTON_Y, `${metrics.buttonInBox}px`);\n }, [boxRef, measure, spot]);\n\n useLayoutEffect(place, [place]);\n\n useEffect(() => {\n window.addEventListener(\"resize\", place);\n return () => window.removeEventListener(\"resize\", place);\n }, [place]);\n\n // Its own size counts as the space changing too, and this is the one that\n // moves on its own. The box hangs upward from the button, so where its top\n // belongs is worked out from how tall it is, and a panel growing into its\n // expanded height has a different answer on every frame of the way there.\n // Left to the effect above it would get the final answer on the first frame,\n // and the panel would jump off the button and then grow back down onto it.\n useEffect(() => {\n const box = boxRef.current;\n if (!box || typeof ResizeObserver === \"undefined\") return;\n\n // Nothing here changes the box's size, so this cannot feed itself.\n const observer = new ResizeObserver(place);\n observer.observe(box);\n return () => observer.disconnect();\n }, [boxRef, place]);\n\n /** Puts the box back where `place` left it, with no animation. */\n const settle = useCallback(() => {\n const box = boxRef.current;\n if (box) box.style.transform = \"\";\n }, [boxRef]);\n\n /**\n * The page while something is being dragged across it: nothing selectable,\n * and the closed hand everywhere.\n *\n * Both belong on the body rather than on what is being held, because the\n * pointer spends the whole drag over everything else. Left to the handle's\n * own classes the cursor turns back into a caret the moment it crosses the\n * thread, and the browser reads the gesture as a sweep through the host's\n * text, which leaves the next press picking up that selection instead of\n * the widget.\n *\n * Written and cleared in one place because these outlive the widget: a drag\n * that ended without this running leaves somebody else's page unselectable\n * and wearing a cursor that is not theirs.\n */\n const holdPointer = useCallback((held: boolean) => {\n document.body.style.userSelect = held ? \"none\" : \"\";\n document.body.style.cursor = held ? \"grabbing\" : \"\";\n if (held) window.getSelection()?.removeAllRanges();\n }, []);\n\n const handlePointerDown = useCallback(\n (event: ReactPointerEvent<HTMLElement>) => {\n justDragged.current = false;\n\n // A press starts from nothing held. If the last one never heard its own\n // release, this is where the page gets its cursor and its selection\n // back, rather than the next drag inheriting them.\n if (grip.current !== null) {\n grip.current = null;\n holdPointer(false);\n setIsMoving(false);\n }\n\n if (!enabled || !event.isPrimary) return;\n // A right click is a menu, not a move.\n if (event.pointerType === \"mouse\" && event.button !== 0) return;\n if (subject === \"panel\" && isControl(event.target)) return;\n\n // The button when it is closed, the whole box when it is open. Either\n // way the box is what travels; this is only what is being aimed.\n const moving = subject === \"panel\" ? boxRef.current : buttonRef.current;\n const taken = measure();\n if (moving === null || taken === null) return;\n\n // Refused so the press cannot begin a selection, or pick up one already\n // lying around and drag that instead. Focus is handed back by hand,\n // since that was the other thing the default was going to do.\n event.preventDefault();\n // Only the button takes it. An open panel is being moved, not entered,\n // so whatever was half typed in the composer keeps the caret.\n if (subject === \"button\") buttonRef.current?.focus({ preventScroll: true });\n\n const rect = moving.getBoundingClientRect();\n grip.current = {\n subject,\n grabX: event.clientX - rect.left,\n grabY: event.clientY - rect.top,\n startX: event.clientX,\n startY: event.clientY,\n ...taken,\n dragged: subject === \"panel\" ? taken.box : taken.size,\n resting: { x: rect.left, y: rect.top },\n at: { x: rect.left, y: rect.top },\n moved: false,\n };\n\n event.currentTarget.setPointerCapture(event.pointerId);\n },\n [boxRef, buttonRef, enabled, holdPointer, measure, subject],\n );\n\n const handlePointerMove = useCallback(\n (event: ReactPointerEvent<HTMLElement>) => {\n const held = grip.current;\n const box = boxRef.current;\n if (held === null || box === null) return;\n\n if (!held.moved) {\n const travelled = Math.hypot(\n event.clientX - held.startX,\n event.clientY - held.startY,\n );\n if (travelled < DRAG_THRESHOLD) return;\n held.moved = true;\n holdPointer(true);\n setIsMoving(true);\n }\n\n // Kept inside the edges it can settle against. Arithmetic only:\n // everything it needs was measured on the way down.\n held.at = {\n x: clamp(\n event.clientX - held.grabX,\n held.bounds.left + held.margin,\n held.bounds.left +\n held.bounds.width -\n held.margin -\n held.dragged.width,\n ),\n y: clamp(\n event.clientY - held.grabY,\n held.bounds.top + held.margin,\n held.bounds.top +\n held.bounds.height -\n held.margin -\n held.dragged.height,\n ),\n };\n\n box.style.transform = `translate3d(${held.at.x - held.resting.x}px, ${held.at.y - held.resting.y}px, 0)`;\n },\n [boxRef, holdPointer],\n );\n\n /**\n * The end of a drag, wherever the news of it came from.\n *\n * Separate from the handler because a release is not reliably an event on\n * the thing that was grabbed. Pointer capture usually delivers it there, but\n * a capture lost to the window manager, a switch to another window mid\n * press, or an element that went away underneath one all end the gesture\n * with nothing firing on the handle at all. Every one of those used to leave\n * the page holding a grabbing cursor and nothing selectable, with no way\n * back except starting another drag.\n *\n * Nothing to end is not a problem to report: the flight home runs after the\n * grip is let go, and the same release arrives twice on the way out.\n */\n const finishDrag = useCallback(() => {\n const held = grip.current;\n if (held === null) return;\n grip.current = null;\n\n holdPointer(false);\n\n const box = boxRef.current;\n if (!held.moved || box === null) {\n setIsMoving(false);\n return;\n }\n\n justDragged.current = true;\n\n // Where it landed, read back as a spot. Which end of the widget that is\n // asked of is the whole of the difference between the two drags.\n const target =\n held.subject === \"panel\"\n ? spotFromPanel(held.at, held.bounds, held.size, held.box, held.margin)\n : spotFrom(held.at, held.bounds, held.size, held.margin);\n\n const home = metricsFor(\n target,\n held.bounds,\n held.size,\n held.box.height,\n held.margin,\n );\n // Where the thing that travelled belongs once it is there, so the flight\n // below is exactly the gap between that and the finger.\n const from = {\n x:\n held.at.x -\n (target.side === \"left\"\n ? held.bounds.left + held.margin\n : held.bounds.left +\n held.bounds.width -\n held.margin -\n held.dragged.width),\n y: held.at.y - (held.subject === \"panel\" ? home.boxTop : home.buttonY),\n };\n\n // All of this is one frame, and the order is the whole trick.\n //\n // Committing the spot synchronously runs the layout effect above with it,\n // so the box is already positioned where it is going before anything is\n // painted. Clearing the transform then puts it there, which is not where\n // the finger left it, and the animation starts from exactly the gap\n // between the two. The first frame drawn still has whatever was being\n // held under the finger, and the rest of them carry it home.\n flushSync(() => setSpot(target));\n settle();\n\n if (prefersReducedMotion()) {\n setIsMoving(false);\n return;\n }\n\n const flight = box.animate(\n [\n { transform: `translate3d(${from.x}px, ${from.y}px, 0)` },\n { transform: \"none\" },\n ],\n { duration: SNAP_MS, easing: SNAP_EASING },\n );\n // Only once it has landed: the clip must not start transitioning between\n // heights while one of them is still travelling.\n flight.finished.catch(() => {}).finally(() => setIsMoving(false));\n }, [boxRef, holdPointer, settle]);\n\n const handlePointerUp = useCallback(\n (event: ReactPointerEvent<HTMLElement>) => {\n if (event.currentTarget.hasPointerCapture(event.pointerId)) {\n event.currentTarget.releasePointerCapture(event.pointerId);\n }\n finishDrag();\n },\n [finishDrag],\n );\n\n // The safety net, live only while something is actually being dragged. A\n // release anywhere at all ends it, and so does the window losing focus,\n // which is the one that catches a press abandoned to another app.\n useEffect(() => {\n if (!isMoving) return;\n\n const end = () => finishDrag();\n window.addEventListener(\"pointerup\", end);\n window.addEventListener(\"pointercancel\", end);\n window.addEventListener(\"blur\", end);\n return () => {\n window.removeEventListener(\"pointerup\", end);\n window.removeEventListener(\"pointercancel\", end);\n window.removeEventListener(\"blur\", end);\n };\n }, [isMoving, finishDrag]);\n\n const dragHandlers: DragHandlers = {\n onPointerDown: handlePointerDown,\n onPointerMove: handlePointerMove,\n onPointerUp: handlePointerUp,\n onPointerCancel: handlePointerUp,\n };\n\n // Everything above was measured against a page that is no longer that size.\n // Nothing to rescue: it goes back to wherever `place` puts it.\n useEffect(() => {\n function abandon() {\n if (grip.current === null) return;\n grip.current = null;\n holdPointer(false);\n settle();\n setIsMoving(false);\n }\n\n window.addEventListener(\"resize\", abandon);\n return () => {\n window.removeEventListener(\"resize\", abandon);\n // A widget that goes away mid drag must not leave the host's whole page\n // unselectable and wearing a cursor it never asked for. This is their\n // body, not ours.\n holdPointer(false);\n };\n }, [holdPointer, settle]);\n\n return {\n side: spot.side,\n /** Being dragged, or on its way to an edge. Nothing may transition. */\n isMoving,\n /** The side only. Height is measured and written straight to the node. */\n style: { [spot.side]: margin } as { left?: string; right?: string },\n /** True for the click that ended a drag, so it does not also open. */\n swallowClick: () => {\n if (!justDragged.current) return false;\n justDragged.current = false;\n return true;\n },\n /**\n * The same four handlers whichever end is being held. Both ends can wear\n * them at once: only one of the two is ever reachable, since the button is\n * inert while the panel is up and the panel is inert while it is down.\n */\n dragHandlers,\n };\n}\n\nfunction clamp(value: number, low: number, high: number): number {\n return Math.min(Math.max(value, low), Math.max(low, high));\n}\n","\"use client\";\n\nimport { useLayoutEffect, useState, type RefObject } from \"react\";\n\n/**\n * How much room the card needs above the button before it goes there.\n * Roughly its own height plus the gap, with a little to spare.\n */\nconst ROOM_ABOVE_PX = 128;\n\nexport type TeaserPlacement = \"above\" | \"below\";\n\n/**\n * Which side of the button the card hangs off.\n *\n * Above, nearly always: the box the widget lives in hangs upward from the\n * button, so above is where the room is. The exception is a button somebody\n * dragged to the top of the window, where the box is pinned under the top\n * margin and the button sits at its top edge with nothing over it. There\n * the card goes below instead.\n *\n * Measured off the button's own offset inside the box rather than worked\n * out from the spot, since the box is clamped in ways the spot alone does\n * not say. Measured again whenever a drag settles, which is the only time\n * the answer can change short of a resize.\n */\nexport function useTeaserPlacement({\n buttonRef,\n enabled,\n isMoving,\n}: {\n buttonRef: RefObject<HTMLElement | null>;\n /** Whether there is a card to place. Nothing is measured otherwise. */\n enabled: boolean;\n /** A drag is on. The answer waits for it to land. */\n isMoving: boolean;\n}): TeaserPlacement {\n const [placement, setPlacement] = useState<TeaserPlacement>(\"above\");\n\n useLayoutEffect(() => {\n if (!enabled || isMoving) return;\n\n const measure = () => {\n const button = buttonRef.current;\n if (button === null) return;\n setPlacement(button.offsetTop < ROOM_ABOVE_PX ? \"below\" : \"above\");\n };\n\n measure();\n window.addEventListener(\"resize\", measure);\n return () => window.removeEventListener(\"resize\", measure);\n }, [buttonRef, enabled, isMoving]);\n\n return placement;\n}\n","\"use client\";\n\nimport {\n useLayoutEffect,\n useState,\n useSyncExternalStore,\n type RefObject,\n} from \"react\";\nimport { usePageLock } from \"./use-page-lock\";\n\n/**\n * When the panel stops floating and takes the screen.\n *\n * Tailwind's `sm` breakpoint, written out, so this hook and any `max-sm:`\n * styles riding along with it change their minds at the same width.\n */\nconst PHONE = \"(max-width: 40rem)\";\n\n/**\n * How long the panel keeps following the visual viewport after it last heard\n * from it.\n *\n * The keyboard takes about a quarter of a second to come up and reports its\n * progress two or three times on the way, so a panel that moved only when it\n * was told would arrive in two or three jumps. This keeps it measuring every\n * frame for a beat afterwards instead, which is what turns those jumps into\n * the panel riding the keyboard up.\n */\nconst SETTLE_MS = 400;\n\n/**\n * How much of the visible height has to go missing before it is the keyboard\n * and not the browser's own chrome sliding away.\n */\nconst KEYBOARD_PX = 96;\n\nfunction subscribePhone(onChange: () => void) {\n const media = window.matchMedia(PHONE);\n media.addEventListener(\"change\", onChange);\n return () => media.removeEventListener(\"change\", onChange);\n}\n\nconst isPhoneNow = () => window.matchMedia(PHONE).matches;\n\n/** The server's answer. Nothing is open on the server, so nothing is lost. */\nconst isPhoneOnServer = () => false;\n\n/**\n * Whether the open panel should be the whole screen rather than a floating\n * card, and everything that has to be true while it is.\n *\n * A phone has no room for a card floating over a page: the card is nearly the\n * page already, and the margins around it are the only thing the extra rem\n * buys. So under `sm` the panel opens edge to edge instead, the way every\n * sheet on a phone does.\n *\n * Only when the widget is the viewport's, though. The gallery pins its\n * previews inside a frame with `absolute`, and a preview that burst out of\n * its frame to swallow the page would be demonstrating exactly the wrong\n * thing. `offsetParent` is the tell: a `fixed` box answers null, a framed one\n * answers with its frame. An ancestor wearing a transform also answers, and\n * should: a box positioned against somebody's transformed container is not\n * positioned against the screen, so it has no business covering it.\n *\n * While the sheet is up this also does two jobs the floating panel never\n * needed:\n *\n * - The panel is sized to `visualViewport` rather than to `inset-0`, and keeps\n * measuring for a beat after each report so it rides the keyboard up instead\n * of arriving in two jumps. The keyboard shrinks the visual viewport and not\n * the layout one, so a panel pinned to the layout viewport keeps its composer\n * underneath the keyboard, which is the one place the thing being typed into\n * cannot be. Written straight onto the node, the way the drag writes its\n * travel: a render per frame of that would run the whole conversation through\n * React to move an edge.\n *\n * - The host's page is pinned where it stands, by `usePageLock`. A full screen\n * sheet with a live page rumbling underneath it reads as broken, and a page\n * the keyboard is free to scroll takes the sheet's own footing with it.\n */\nexport function useWidgetSheet({\n boxRef,\n panelRef,\n isOpen,\n}: {\n boxRef: RefObject<HTMLDivElement | null>;\n panelRef: RefObject<HTMLDivElement | null>;\n isOpen: boolean;\n}): boolean {\n const isPhone = useSyncExternalStore(\n subscribePhone,\n isPhoneNow,\n isPhoneOnServer,\n );\n\n // Asked once the box is on the page, and asked again if the viewport\n // changed, since that is the only thing here that changes the answer's\n // consequences. The layout effect answers before anything is painted.\n const [isFramed, setIsFramed] = useState(false);\n useLayoutEffect(() => {\n setIsFramed(boxRef.current?.offsetParent != null);\n }, [boxRef, isPhone]);\n\n const isSheet = isPhone && !isFramed;\n const isUp = isSheet && isOpen;\n\n useLayoutEffect(() => {\n if (!isUp) return;\n const panel = panelRef.current;\n const viewport = window.visualViewport;\n if (!panel || !viewport) return;\n\n let frame = 0;\n let until = 0;\n let top = -1;\n let height = -1;\n let keyboard = \"\";\n\n // Exactly what can be seen, top edge included: the keyboard coming up can\n // shove the layout viewport as well as shrink the visible one, and\n // `offsetTop` is the sum of that shove.\n //\n // Whole pixels, and only ever written when they change. Measuring runs\n // every frame while the keyboard moves, and the browser cannot tell a\n // style it has already been given from a new one: it lays the panel out\n // again either way, and a fractional height rewritten sixty times a second\n // is the thread re-wrapping sixty times a second.\n const fit = () => {\n const nextTop = Math.round(viewport.offsetTop);\n const nextHeight = Math.round(viewport.height);\n if (nextTop === top && nextHeight === height) return;\n top = nextTop;\n height = nextHeight;\n\n panel.style.top = `${top}px`;\n panel.style.height = `${height}px`;\n panel.style.bottom = \"auto\";\n\n // The home bar's inset is only worth room while the home bar is what is\n // down there. With the keyboard up the foot of the panel is the top of\n // the keyboard, and the inset is a strip of nothing between the two.\n const next = height < window.innerHeight - KEYBOARD_PX ? \"up\" : \"down\";\n if (next === keyboard) return;\n keyboard = next;\n panel.dataset.keyboard = next;\n };\n\n const follow = () => {\n fit();\n frame = performance.now() < until ? requestAnimationFrame(follow) : 0;\n };\n\n // The viewport reports where it has got to, not where it is going. Each\n // report is taken as a sign it is still moving, and the panel measures for\n // itself until the reports stop coming.\n const track = () => {\n until = performance.now() + SETTLE_MS;\n if (frame === 0) frame = requestAnimationFrame(follow);\n };\n\n fit();\n viewport.addEventListener(\"resize\", track);\n viewport.addEventListener(\"scroll\", track);\n // A tap on the composer is the first anybody knows about a keyboard: the\n // viewport says nothing until it has already started moving.\n panel.addEventListener(\"focusin\", track);\n\n return () => {\n if (frame !== 0) cancelAnimationFrame(frame);\n viewport.removeEventListener(\"resize\", track);\n viewport.removeEventListener(\"scroll\", track);\n panel.removeEventListener(\"focusin\", track);\n // Back to the classes' `inset-0`, for the floating panel these would\n // otherwise misplace.\n panel.style.top = \"\";\n panel.style.height = \"\";\n panel.style.bottom = \"\";\n delete panel.dataset.keyboard;\n };\n }, [panelRef, isUp]);\n\n usePageLock(isUp);\n\n return isSheet;\n}\n","\"use client\";\n\nimport { useEffect } from \"react\";\n\n/**\n * Holds the host's page still underneath a full screen sheet.\n *\n * `overflow: hidden` is not a lock on a phone. Tapping a field inside a fixed\n * panel makes the browser scroll the page to bring the field above the\n * keyboard, and it scrolls a page that only said it had no overflow: the sheet\n * stays where it is, the page slides about behind it, and everything pinned to\n * the visual viewport chases that a frame late. That is the shudder.\n *\n * So the page is pinned instead, fixed at the offset it is already scrolled to.\n * It looks exactly the same and there is nothing left to scroll, which is the\n * only answer the keyboard takes. Letting go puts the offset back first, so the\n * page never flashes at the top on the way out.\n *\n * Width is deliberately not set. A fixed box with both edges pinned already\n * works out to the width the page had, margins and all, and saying `100%` on\n * top of that hangs the host's own margin off the right hand side.\n *\n * Everything it touches is written down first and put back after. This is\n * somebody else's body, and they may have had an opinion about all of it.\n */\nexport function usePageLock(isLocked: boolean) {\n useEffect(() => {\n if (!isLocked) return;\n\n const { style } = document.body;\n const held = {\n position: style.position,\n top: style.top,\n left: style.left,\n right: style.right,\n overflow: style.overflow,\n };\n const scrollY = window.scrollY;\n\n style.position = \"fixed\";\n style.top = `${-scrollY}px`;\n style.left = \"0\";\n style.right = \"0\";\n style.overflow = \"hidden\";\n\n return () => {\n style.position = held.position;\n style.top = held.top;\n style.left = held.left;\n style.right = held.right;\n style.overflow = held.overflow;\n // Instant, or a host who asked for smooth scrolling watches their page\n // sail back down from the top while the sheet is still coming off it.\n window.scrollTo({ top: scrollY, behavior: \"instant\" });\n };\n }, [isLocked]);\n}\n","\"use client\";\n\nimport { useRef, type ReactNode, type RefObject } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/lib/utils\";\nimport { useHydrated } from \"./use-hydrated\";\nimport { useScrollLock } from \"./use-scroll-lock\";\nimport { useWidgetSheet } from \"./use-widget-sheet\";\nimport { WIDGET_CORNER } from \"./widget-corner\";\n\n/** Above the host's own chrome. Their z-indexes are not ours to guess at. */\nconst LAYER = 2_147_483_000;\n\n/**\n * Wide enough to read a conversation the way you read a page, and tall\n * enough for a thread rather than a glance. Not quite twice the corner\n * panel's width, and its height: on the denser scale the panel wears in\n * here, that is more thread than the corner shows expanded, without the\n * modal turning into the whole screen.\n *\n * Neither is a prop. The size of a modal is ours to get right once.\n */\nconst WIDTH = \"50rem\";\nconst HEIGHT = \"40rem\";\n\n/**\n * How the panel arrives and leaves.\n *\n * A modal comes up out of the middle: a little smaller and a little lower,\n * growing into place, on the same spring the corner panel opens with. It\n * overshoots by a hair, which is the difference between a panel that\n * appears and one that pops. Leaving is quicker and has no spring.\n */\n// `scale` and `translate` by name: those are the properties Tailwind's\n// `scale-*` and `translate-y-*` set. Listed as `transform` the panel has\n// neither, and pops open on opacity alone.\nconst MOTION =\n \"transition-[opacity,scale,translate] motion-reduce:transition-none\";\nconst OPEN = \"duration-250 ease-spring\";\nconst SHUT = \"duration-150 ease-out-quint\";\n\n/**\n * The phone's version: a rise into place as a sheet, which is the shape\n * every thumb has already been taught. A pure deceleration, since a\n * spring's overshoot would carry the top edge off the screen and back.\n */\nconst SHEET =\n \"transition-[opacity,translate] duration-300 ease-out-quint motion-reduce:transition-none\";\n\n/**\n * The scrim. Black rather than the host's foreground, because a light\n * theme's foreground is black and a dark theme's is white, and a white\n * scrim over a dark page reads as the page fogging up rather than stepping\n * back. A little slower in than out, like the panel it is behind.\n */\nconst SCRIM =\n \"absolute inset-0 bg-black/40 backdrop-blur-sm transition-opacity ease-out motion-reduce:transition-none\";\n\n/**\n * The support panel in the middle of the screen, over a scrim, rather than\n * in the corner.\n *\n * The same panel: whoever renders this hands it the very element the corner\n * would have held, and only where it is drawn changes. Portalled to the body\n * rather than kept in the launcher's box, because a modal answers to the\n * window and the box answers to wherever the host put the button.\n *\n * On a phone it is the screen, exactly as the corner panel is: same sheet,\n * same ride up over the keyboard, same lock on the page underneath. Bigger\n * screens get a scrim, a centred card, and a page that holds still.\n *\n * Closed, it stays mounted and goes `inert`: the conversation is still live,\n * and nothing behind the fade can be tabbed into.\n */\nexport function SupportModal({\n isOpen,\n onClose,\n panelRef,\n label,\n children,\n}: {\n isOpen: boolean;\n /** A press on the scrim. Escape and the header's cross are the launcher's. */\n onClose: () => void;\n /** The panel, for whoever moves focus into it and hands it back after. */\n panelRef: RefObject<HTMLDivElement | null>;\n /** What a screen reader calls the dialog. */\n label: string;\n /** The panel's contents, told whether they are a sheet right now. */\n children: (isSheet: boolean) => ReactNode;\n}) {\n const layerRef = useRef<HTMLDivElement>(null);\n // The layer is portalled into `document.body`, which does not exist until\n // there is one. Nothing at all until then, on the server and through\n // hydration alike, so both passes agree.\n const isHydrated = useHydrated();\n\n // Never framed, since the layer sits on the body: the only question the\n // hook has left to answer is how wide the screen is.\n const isSheet = useWidgetSheet({ boxRef: layerRef, panelRef, isOpen });\n // The sheet pins the page itself. Up here the page only has to stop.\n useScrollLock(isOpen && !isSheet);\n\n if (!isHydrated) return null;\n\n return createPortal(\n <div\n ref={layerRef}\n inert={!isOpen}\n // The hook the published stylesheet scopes its resets to. On a page\n // without Tailwind there is no preflight, so the package ships one\n // that reaches no further than this layer.\n data-median-widget=\"\"\n style={{ zIndex: LAYER }}\n className={cn(\n // Room to breathe on a screen that has it. A phone gives the sheet\n // every pixel anyway.\n \"fixed inset-0 grid place-items-center p-4 sm:p-8\",\n !isOpen && \"pointer-events-none\",\n )}\n >\n <div\n aria-hidden\n onClick={onClose}\n className={cn(\n SCRIM,\n isOpen ? \"opacity-100 duration-250\" : \"opacity-0 duration-150\",\n )}\n />\n\n <div\n ref={panelRef}\n tabIndex={-1}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={label}\n // What the parts inside read to know when to settle into place.\n // See `widget-settle.ts`.\n data-state={isOpen ? \"open\" : \"closed\"}\n style={isSheet ? undefined : { width: WIDTH, height: HEIGHT }}\n className={cn(\n \"outline-none\",\n isSheet\n ? [\n // The keyboard fit writes its edges straight onto the node,\n // over these.\n \"fixed inset-0\",\n SHEET,\n isOpen ? \"translate-y-0 opacity-100\" : \"translate-y-8 opacity-0\",\n ]\n : [\n // Never past the layer's padding, whatever the size says.\n `relative max-h-full max-w-full ${WIDGET_CORNER} shadow-2xl`,\n MOTION,\n isOpen\n ? [OPEN, \"scale-100 opacity-100 translate-y-0\"]\n : [SHUT, \"scale-[0.96] opacity-0 translate-y-2\"],\n ],\n )}\n >\n {children(isSheet)}\n </div>\n </div>,\n document.body,\n );\n}\n","\"use client\";\n\nimport { useSyncExternalStore } from \"react\";\n\n/**\n * Whether this is running in a browser that has finished hydrating.\n *\n * For the things that cannot exist until there is a document: a median has to\n * be handed a node, and there is no `document.body` on a server. Returning\n * null for those and the real thing once mounted is a server/client branch,\n * which is the first thing React names when it reports a hydration mismatch:\n * the server sends nothing, the client's first pass renders a panel, and the\n * whole tree is thrown away and built again.\n *\n * So it is read as an external store rather than as a mounted flag. React\n * takes the server snapshot both on the server and through hydration, so both\n * passes agree on nothing, and only the commit after it moves. Same trick the\n * session token is read with, for the same reason.\n *\n * The store never changes, so nothing ever subscribes to anything.\n */\nconst subscribe = () => () => {};\nconst onClient = () => true;\nconst onServer = () => false;\n\nexport function useHydrated(): boolean {\n return useSyncExternalStore(subscribe, onClient, onServer);\n}\n","\"use client\";\n\nimport { useEffect } from \"react\";\n\n/**\n * Holds the host's page still under the modal, on a screen big enough to\n * have a scrollbar.\n *\n * The phone's lock is `usePageLock`, which pins the body in place because\n * that is the only answer the keyboard takes. Up here there is no keyboard\n * to argue with, and pinning the body would throw away the page's scroll\n * position for nothing, so this only stops it scrolling.\n *\n * The scrollbar's width goes onto the body as padding as the bar goes, so\n * the page does not lurch sideways by the width of the bar it just lost.\n * Whatever the host had there already is added to rather than replaced,\n * and put back after. This is somebody else's body.\n */\nexport function useScrollLock(isLocked: boolean) {\n useEffect(() => {\n if (!isLocked) return;\n\n const { body } = document;\n const held = {\n overflow: body.style.overflow,\n paddingRight: body.style.paddingRight,\n };\n const gutter = window.innerWidth - document.documentElement.clientWidth;\n\n body.style.overflow = \"hidden\";\n if (gutter > 0) {\n const existing = parseFloat(getComputedStyle(body).paddingRight) || 0;\n body.style.paddingRight = `${existing + gutter}px`;\n }\n\n return () => {\n body.style.overflow = held.overflow;\n body.style.paddingRight = held.paddingRight;\n };\n }, [isLocked]);\n}\n","import { cn } from \"@/lib/utils\";\n\n/**\n * How the panel dresses when it is the whole screen, shared by the corner\n * widget and the modal: both open as a sheet on a phone, and a sheet is a\n * sheet whichever of them it grew out of.\n *\n * A sheet is the screen, so the card's own dress goes: no corners against\n * edges that already have their own, no border where the screen ends, and\n * room for the notch and the home bar on hosts that let content under them.\n *\n * The home bar's room goes back while the keyboard has the bottom of the\n * screen, which is the sheet's own attribute for it: the panel's foot is then\n * the keyboard's top edge, and the inset would hold the composer a thumb's\n * width above the keys for no reason. It transitions rather than snapping,\n * since the keyboard is on its way in while this changes its mind.\n */\nexport const SHEET_PANEL = cn(\n \"rounded-none border-0 pt-[env(safe-area-inset-top)] pb-[env(safe-area-inset-bottom)]\",\n \"transition-[padding] duration-200 ease-out motion-reduce:transition-none\",\n \"[[data-keyboard=up]_&]:pb-0\",\n);\n","\"use client\";\n\nimport { useState } from \"react\";\nimport { X } from \"@/components/icons\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { cn } from \"@/lib/utils\";\nimport type { LauncherSide } from \"./launcher-position\";\nimport { plainTextOf } from \"./plain-text\";\nimport type { TeaserPlacement } from \"./use-teaser-placement\";\nimport { WidgetAvatar } from \"./widget-avatar\";\nimport { WIDGET_CORNER } from \"./widget-corner\";\nimport type { SupportMessage } from \"./types\";\n\n/**\n * The card's own width. Narrower than the panel it stands in for, so it\n * reads as a note left on the button rather than the panel half open. Never\n * wider than the box, which on a phone is the box saying so.\n */\nconst WIDTH = \"min(20rem, 100%)\";\n\n/** The gap between the card and the button it belongs to. */\nconst GAP = \"0.5rem\";\n\n/**\n * A reply, shown on the closed button so somebody sees what came in without\n * opening anything.\n *\n * The count on the button says something arrived; this says what. One card,\n * for the newest unread reply, with the sender's name and face over the\n * first couple of lines. Pressing it opens the panel, and the cross in the\n * corner waves it away without opening anything.\n *\n * Two buttons rather than one with a cross inside it, because a button\n * inside a button is not a thing a browser will let you have. The cross is\n * a sibling, sat over the corner of the card it dismisses.\n *\n * Kept mounted while it leaves rather than unmounted on the spot, so the\n * dismissal is the card fading rather than vanishing. The last message is\n * held for that, since by then the caller has stopped passing one.\n */\nexport function WidgetTeaser({\n message,\n isShown,\n side,\n placement,\n buttonY,\n buttonSize,\n onOpen,\n onDismiss,\n}: {\n /** The reply to show, or null once there is none. */\n message: SupportMessage | null;\n /** Whether it is on screen. False leaves it fading out in place. */\n isShown: boolean;\n /** Which edge the button is parked against. The card lines up with it. */\n side: LauncherSide;\n placement: TeaserPlacement;\n /** The button's top inside the box, as a CSS length. */\n buttonY: string;\n buttonSize: string;\n onOpen: () => void;\n onDismiss: () => void;\n}) {\n // What was last shown, for the fade out after the caller lets go of it.\n // Settled during render rather than in an effect, so the frame the caller\n // drops the message is the frame the card starts leaving.\n const [held, setHeld] = useState(message);\n if (message !== null && message !== held) setHeld(message);\n const shown = message ?? held;\n if (shown === null) return null;\n\n const agent = shown.agent;\n const text = plainTextOf(shown.body);\n\n return (\n <div\n key={shown.id}\n inert={!isShown}\n // Announced as it lands, so a screen reader hears that a reply came\n // in without having to find the corner of the page it landed in.\n role=\"status\"\n aria-live=\"polite\"\n style={{\n width: WIDTH,\n [side]: 0,\n ...(placement === \"above\"\n ? { bottom: `calc(100% - ${buttonY} + ${GAP})` }\n : { top: `calc(${buttonY} + ${buttonSize} + ${GAP})` }),\n }}\n className={cn(\n \"absolute\",\n // Grows out of the corner nearest the button, so it reads as coming\n // from it rather than landing beside it.\n placement === \"above\"\n ? side === \"right\"\n ? \"origin-bottom-right\"\n : \"origin-bottom-left\"\n : side === \"right\"\n ? \"origin-top-right\"\n : \"origin-top-left\",\n \"transition-[opacity,translate,scale] motion-reduce:transition-none\",\n isShown\n ? [\n \"pointer-events-auto opacity-100 duration-300 ease-spring\",\n // The arrival, once. The transition above takes over after.\n \"animate-in fade-in-0 zoom-in-95 duration-300 ease-spring motion-reduce:animate-none\",\n placement === \"above\"\n ? \"slide-in-from-bottom-2\"\n : \"slide-in-from-top-2\",\n ]\n : \"pointer-events-none scale-95 opacity-0 duration-150 ease-out-quint\",\n )}\n >\n <button\n type=\"button\"\n onClick={onOpen}\n aria-label={\n agent ? `Read the reply from ${agent.name}` : \"Read the reply\"\n }\n className={cn(\n // Its own radius scale, like the panel, so the app's token cannot\n // reshape a card that sits on somebody else's site.\n `block w-full ${WIDGET_CORNER} border bg-card p-3.5 pr-10 text-left text-card-foreground shadow-lg [--radius:0.625rem]`,\n \"transition-[background-color,box-shadow] outline-none\",\n \"hover:bg-muted/50 focus-visible:ring-3 focus-visible:ring-ring/50\",\n \"cursor-pointer select-none\",\n )}\n >\n <span className=\"flex items-center gap-2\">\n {agent && (\n <>\n <WidgetAvatar agent={agent} className=\"size-6 rounded-md\" />\n <span className=\"truncate text-sm font-medium\">\n {agent.name}\n </span>\n {agent.kind === \"ai\" && (\n <Badge variant=\"muted\" className=\"shrink-0 rounded-md bg-muted px-1 inset-ring-0\">\n AI\n </Badge>\n )}\n </>\n )}\n <span className=\"ml-auto shrink-0 text-sm text-muted-foreground tabular-nums\">\n {shown.time}\n </span>\n </span>\n\n <span className=\"mt-1.5 line-clamp-2 block text-base leading-snug\">\n {text.length > 0 ? text : \"Sent a file\"}\n </span>\n </button>\n\n <button\n type=\"button\"\n onClick={onDismiss}\n aria-label=\"Dismiss\"\n className={cn(\n \"absolute top-2.5 right-2.5 grid size-7 place-items-center rounded-md\",\n \"text-muted-foreground transition-colors outline-none\",\n \"hover:bg-muted hover:text-foreground focus-visible:ring-3 focus-visible:ring-ring/50\",\n \"cursor-pointer\",\n )}\n >\n <X className=\"size-4\" />\n </button>\n </div>\n );\n}\n","/**\n * A message read aloud rather than rendered: what a reply says, with the\n * markdown taken off it, for the one line a teaser has room for.\n *\n * Light-handed on purpose, and the same reading the inbox's own preview\n * uses. A fence line goes entirely and the code between stays, a link reads\n * as its text, and emphasis marks come off. Anything this misses is shown\n * as typed, which is a fair result for a preview.\n */\nexport function plainTextOf(markdown: string): string {\n return (\n markdown\n .replace(/^\\s*(?:```|~~~).*$/gm, \"\")\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\")\n .replace(/[*`_~]/g, \"\")\n .replace(/^\\s*#{1,6}\\s+/gm, \"\")\n .replace(/^\\s*[-+]\\s+/gm, \"\")\n .replace(/^\\s*>\\s?/gm, \"\")\n .replace(/\\s+/g, \" \")\n .trim()\n );\n}\n","\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\n\n/**\n * How long to keep looking for something that may not be on the page yet.\n * A navigation is usually still resolving when the instruction lands, so the\n * element the tool means is often a route change away.\n */\nconst FIND_TIMEOUT_MS = 4_000;\n\n/** How long the ring stays once it has found something. */\nconst HOLD_MS = 2_600;\n\n/** The fade at the end, which is also how long the node outlives the hold. */\nconst FADE_MS = 300;\n\n/** Above the host's own chrome. Their z-indexes are not ours to guess at. */\nconst LAYER = 2_147_483_000;\n\nfunction prefersReducedMotion(): boolean {\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n}\n\n/**\n * A ring drawn over something on the host's page, because one of their tools\n * asked for it to be pointed at.\n *\n * Nothing on their page is touched. The ring is a box of our own, positioned\n * over the element's rectangle and following it every frame, so a page that\n * scrolls, resizes or animates underneath keeps the ring where the thing is,\n * and there is no style of theirs to put back afterwards.\n *\n * It also waits. The instruction rides the same reply that moves the page, so\n * the element it names is usually a route change away and not in the document\n * yet; this keeps looking for a few seconds and gives up quietly rather than\n * ringing the wrong thing or nothing at all.\n *\n * In `document.body` rather than where the widget sits, since a `fixed` box\n * inside a host container that happens to carry a transform is fixed to that\n * container instead of to the window, and this one has to line up with an\n * element anywhere on their page.\n */\nexport function WidgetSpotlight({ selector }: { selector: string }) {\n const [isShown, setIsShown] = useState(false);\n const [isLeaving, setIsLeaving] = useState(false);\n const ring = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n let target: Element | null = null;\n let frame = 0;\n let startedAt = 0;\n let foundAt = 0;\n let stopped = false;\n let leaveTimer: ReturnType<typeof setTimeout> | undefined;\n\n function find(): Element | null {\n try {\n return document.querySelector(selector);\n } catch {\n // Not a selector at all. Median checks the shape of one before it is\n // stored, but only a browser can say whether it parses.\n console.error(\n `Median: a tool asked to highlight ${selector}, which is not a valid CSS selector.`,\n );\n stopped = true;\n return null;\n }\n }\n\n function tick(now: number) {\n if (stopped) return;\n if (startedAt === 0) startedAt = now;\n\n if (target === null) {\n target = find();\n if (stopped) return;\n\n if (target === null) {\n // Still not there. It may be one route change behind us.\n if (now - startedAt > FIND_TIMEOUT_MS) return;\n frame = requestAnimationFrame(tick);\n return;\n }\n\n foundAt = now;\n target.scrollIntoView({\n behavior: prefersReducedMotion() ? \"auto\" : \"smooth\",\n block: \"center\",\n });\n setIsShown(true);\n }\n\n // Written straight onto the node rather than held in state: this runs\n // every frame for a couple of seconds, and a re-render per frame to\n // move one box is a cost the host's page would pay for nothing.\n const node = ring.current;\n if (node !== null) {\n const rect = target.getBoundingClientRect();\n node.style.transform = `translate(${rect.left}px, ${rect.top}px)`;\n node.style.width = `${rect.width}px`;\n node.style.height = `${rect.height}px`;\n }\n\n if (now - foundAt > HOLD_MS) {\n setIsLeaving(true);\n leaveTimer = setTimeout(() => setIsShown(false), FADE_MS);\n return;\n }\n frame = requestAnimationFrame(tick);\n }\n\n frame = requestAnimationFrame(tick);\n return () => {\n stopped = true;\n cancelAnimationFrame(frame);\n clearTimeout(leaveTimer);\n };\n }, [selector]);\n\n if (!isShown || typeof document === \"undefined\") return null;\n\n return createPortal(\n <div\n ref={ring}\n aria-hidden\n // Portalled to the body, outside the launcher's box, so it carries the\n // published stylesheet's scope hook itself.\n data-median-widget=\"\"\n style={{ zIndex: LAYER, top: 0, left: 0 }}\n className=\"pointer-events-none fixed\"\n >\n {/* Two layers of the same box: one that holds the outline, and one\n that swells out of it and fades, which is the part the eye catches\n on a page somebody is already looking at. */}\n <span\n className={[\n \"absolute -inset-1 rounded-lg outline-2 outline-offset-0 outline-primary\",\n // With the widget's own primary behind the host's, like every\n // other colour it draws.\n \"shadow-[0_0_0_6px_color-mix(in_oklch,var(--primary,oklch(0.205_0_0))_20%,transparent)]\",\n \"transition-opacity duration-300 ease-out motion-reduce:transition-none\",\n isLeaving ? \"opacity-0\" : \"opacity-100\",\n ].join(\" \")}\n />\n <span\n className={[\n \"absolute -inset-1 rounded-lg outline-2 outline-primary\",\n \"animate-ping opacity-60 motion-reduce:animate-none\",\n isLeaving ? \"hidden\" : \"\",\n ].join(\" \")}\n />\n </div>,\n document.body,\n );\n}\n","\"use client\";\n\nimport { useSyncExternalStore } from \"react\";\nimport { hasCustomDataSender, queueCustomData } from \"./custom-data\";\nimport {\n createOpenStore,\n isDevelopment,\n useOpenState,\n useRegister,\n} from \"./open-store\";\nimport {\n reportError,\n type MedianErrorReportOptions,\n type MedianErrorReportOutcome,\n} from \"./report-error\";\nimport { resetSessions } from \"./session\";\nimport { signOutHeldUsers } from \"./signed-out\";\nimport { unreadStore, useUnreadCount } from \"./unread-store\";\nimport { resetVisitorEmails } from \"./visitor-email\";\n\n/**\n * Driving the support panel from anywhere on the page. The open state lives\n * in `open-store.ts`, which the feedback panel keeps its own copy of, and\n * the unread count in `unread-store.ts`; this file is the three things that\n * are the support widget's alone, `attach`, `reportError` and `reset`, and\n * the shape a host reaches for all of it through.\n */\nconst store = createOpenStore(\"MedianSupport\");\n\n/**\n * The widget from anywhere, React or not: the same shape the hook hands\n * back, for a plain `onclick` or a shortcut listener that never sees a\n * component. `isOpen` and `unreadCount` read live but do not re-render\n * anything; the hook is for that.\n */\nexport const medianSupport = {\n /** What the panel is doing right now. */\n get isOpen() {\n return store.readEffective();\n },\n /** Replies the visitor has not read. Zero while nothing is mounted. */\n get unreadCount() {\n return unreadStore.read();\n },\n open() {\n store.request(true);\n store.warnIfNothingIsMounted(\"the widget was asked to open\");\n },\n close() {\n store.request(false);\n },\n toggle() {\n const next = !store.readEffective();\n store.request(next);\n if (next) store.warnIfNothingIsMounted(\"the widget was asked to open\");\n },\n /**\n * Put your own data on the next bug report: a crash trace from your error\n * handler, a request id, the state of a store. Objects are written out as\n * JSON. Entries ride the visitor's next message and land on the report,\n * whether or not diagnostics are on.\n */\n attach(label: string, value: unknown) {\n if (!queueCustomData(label, value)) {\n if (isDevelopment) {\n console.warn(\n \"Median: attach() needs a label and a value, and nothing was queued. Pass a short label naming the data, and the data itself.\",\n );\n }\n return;\n }\n // The support panel is one of three things that send it, so the warning\n // waits on all three rather than on the panel alone.\n if (!isDevelopment || hasCustomDataSender()) return;\n setTimeout(() => {\n if (hasCustomDataSender()) return;\n console.warn(\n \"Median: data was attached, but no <MedianSupport>, <MedianFeedback> or contact form is on the page to send it. It goes with the next message, note, sendMedianContact() or reportError() call.\",\n );\n }, 1000);\n },\n /**\n * File the crash your error screen is showing, with everything the browser\n * was doing when it happened. One press from a \"Report error\" button:\n *\n * const outcome = await medianSupport.reportError(error);\n *\n * It answers rather than throws, so the screen can say whether it landed.\n * It works from a boundary with no widget left under it, which is most of\n * them.\n */\n reportError(error: unknown, options?: MedianErrorReportOptions) {\n return reportError(error, options);\n },\n /**\n * Forget this browser's conversation, so the next one starts fresh and\n * anonymous. Call it when your user signs out: whoever sits down next\n * should not find somebody else's thread waiting.\n *\n * The user the widget holds right now is signed out with it, even if your\n * own state still passes them for a moment, and an identity route is read\n * again.\n */\n reset() {\n signOutHeldUsers();\n resetSessions();\n resetVisitorEmails();\n },\n} satisfies MedianSupportControl;\n\n/** The shape of {@link medianSupport}, and of what the hook returns. */\nexport type MedianSupportControl = {\n /** Whether the panel is open. */\n isOpen: boolean;\n /**\n * Replies the visitor has not read: the same count the closed button\n * wears and the tab's title carries. For a badge on a button of your own,\n * on a page where the launcher is hidden. Zero while the panel is open,\n * once it has been looked at, and whenever no panel is mounted.\n */\n unreadCount: number;\n open: () => void;\n close: () => void;\n toggle: () => void;\n /** Put your own data on the next bug report: a crash trace, an id. */\n attach: (label: string, value: unknown) => void;\n /** File the crash your error screen is showing. For a report button. */\n reportError: (\n error: unknown,\n options?: MedianErrorReportOptions,\n ) => Promise<MedianErrorReportOutcome>;\n /** Forget this browser's conversation. For sign out. */\n reset: () => void;\n};\n\n/**\n * The widget, from anywhere else on the page:\n *\n * const support = useMedianSupport();\n *\n * <button onClick={support.open}>\n * Contact us\n * {support.unreadCount > 0 && <span>{support.unreadCount}</span>}\n * </button>\n *\n * `isOpen` follows the panel however it was opened, so a trigger can render\n * itself as pressed, or step out of the way while the panel is up.\n * `unreadCount` follows the thread, so the trigger can say a reply is\n * waiting where the corner button would have.\n */\nexport function useMedianSupport(): MedianSupportControl {\n const isOpen = useSyncExternalStore(\n store.subscribe,\n store.readEffective,\n store.readServerEffective,\n );\n const unreadCount = useUnreadCount();\n\n return {\n isOpen,\n unreadCount,\n open: medianSupport.open,\n close: medianSupport.close,\n toggle: medianSupport.toggle,\n attach: medianSupport.attach,\n reportError: medianSupport.reportError,\n reset: medianSupport.reset,\n };\n}\n\n/** The panel's own settled open state. See {@link useOpenState}. */\nexport function useMedianOpenState(options: {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n}): [boolean, (next: boolean) => void] {\n return useOpenState(store, options);\n}\n\n/** One widget per page, and a word in the console when there are two. */\nexport function useRegisterMedianSupport(name = \"MedianSupport\") {\n useRegister(store, name);\n}\n","/**\n * Data somebody chose to put on a bug report: a crash trace, a request id,\n * the state of a store when it fell over.\n *\n * The deliberate third of the telemetry. Context is what the browser knows,\n * diagnostics are what the widget caught on its own; this is what the host's\n * code or the agent decided the team should see, word for word. Because it is\n * handed over on purpose, it does not wait behind the diagnostics switch.\n *\n * Pure on purpose, like `diagnostics.ts` next door: the SDK cleans what it\n * queues with this file before it sends, and the backend cleans it again with\n * the same file before it stores, so neither side is trusting the other's\n * arithmetic.\n */\n\nexport type CustomDataEntry = {\n /** What the value is, in a few words. The entry's heading on the report. */\n label: string;\n /** The thing itself, kept exactly as written, up to the cap. */\n value: string;\n};\n\n/** The most entries one report carries. Newest kept, oldest dropped. */\nexport const MAX_CUSTOM_ENTRIES = 8;\n\nexport const CUSTOM_LABEL_MAX_LENGTH = 60;\n\n/** Room for a real stack trace, not just its first line. */\nexport const CUSTOM_VALUE_MAX_LENGTH = 4_000;\n\nfunction cleanLabel(input: unknown): string | undefined {\n if (typeof input !== \"string\") return undefined;\n const label = input.trim().slice(0, CUSTOM_LABEL_MAX_LENGTH);\n return label.length > 0 ? label : undefined;\n}\n\n/**\n * Whatever was handed over, written out as text. Objects become readable\n * JSON, because \"the state of the store\" arrives as an object, and making the\n * caller serialize it first is exactly the chore this exists to remove.\n */\nfunction cleanValue(input: unknown): string | undefined {\n let written: string | undefined;\n if (typeof input === \"string\") {\n written = input.trim();\n } else if (typeof input === \"number\" || typeof input === \"boolean\") {\n written = String(input);\n } else {\n try {\n written = JSON.stringify(input, null, 2);\n } catch {\n written = undefined;\n }\n }\n\n if (written === undefined || written.length === 0) return undefined;\n return written.length > CUSTOM_VALUE_MAX_LENGTH\n ? `${written.slice(0, CUSTOM_VALUE_MAX_LENGTH)}…`\n : written;\n}\n\n/**\n * Whatever arrived, cut down to what may be stored. Undefined when nothing\n * survived, so an empty list never lands on a row and reads later as \"they\n * attached something and it was blank\".\n *\n * One entry per label, the newest one winning: attaching a second crash\n * trace means the first one is no longer the crash.\n */\nexport function cleanCustomData(input: unknown): CustomDataEntry[] | undefined {\n if (!Array.isArray(input)) return undefined;\n\n const byLabel = new Map<string, string>();\n for (const raw of input) {\n if (typeof raw !== \"object\" || raw === null) continue;\n const entry = raw as Record<string, unknown>;\n const label = cleanLabel(entry.label);\n const value = cleanValue(entry.value);\n if (label === undefined || value === undefined) continue;\n // Re-set so a replaced label moves to the end, where the newest live.\n byLabel.delete(label);\n byLabel.set(label, value);\n }\n\n const entries = [...byLabel].map(([label, value]) => ({ label, value }));\n return entries.length > 0 ? entries.slice(-MAX_CUSTOM_ENTRIES) : undefined;\n}\n\n/**\n * Several sources of entries, oldest source first, folded into one list. The\n * same rules as cleaning: one entry per label, later wins, newest kept when\n * the cap says something has to go.\n */\nexport function mergeCustomData(\n ...sources: (CustomDataEntry[] | undefined)[]\n): CustomDataEntry[] | undefined {\n return cleanCustomData(sources.flatMap((entries) => entries ?? []));\n}\n","\"use client\";\n\nimport {\n cleanCustomData,\n mergeCustomData,\n type CustomDataEntry,\n} from \"@median/backend/customData\";\nimport { useEffect } from \"react\";\n\n/**\n * Data the host hands over on purpose, waiting to ride the next message: a\n * crash trace from their error handler, a request id, the state of a store.\n *\n * The deliberate cousin of the diagnostics ring buffer next door, and module\n * level for the same reason: a crash handler runs wherever the crash was,\n * usually nowhere near React, and sometimes before the widget has mounted.\n * Unlike diagnostics it does not wait behind a switch, because calling\n * `attach` is the host offering it.\n *\n * Nothing is sent on its own. Entries ride the next message the visitor\n * sends, land on the conversation, and are copied onto a bug report the\n * moment one is filed.\n */\n\nlet queued: CustomDataEntry[] = [];\n\n/** How many mounted components take the queue with their next send. */\nlet senders = 0;\n\n/**\n * Marks a component that sends the queue: the support panel, the feedback\n * panel, and the contact form. `attach()` warns only when none is mounted.\n */\nexport function useCustomDataSender(): void {\n useEffect(() => {\n senders += 1;\n return () => {\n senders -= 1;\n };\n }, []);\n}\n\n/** Whether anything on the page will send what was attached. */\nexport function hasCustomDataSender(): boolean {\n return senders > 0;\n}\n\n/** True when the entry survived cleaning and is waiting for a send. */\nexport function queueCustomData(label: unknown, value: unknown): boolean {\n const cleaned = cleanCustomData([{ label, value }]);\n if (cleaned === undefined) return false;\n\n queued = mergeCustomData(queued, cleaned) ?? [];\n return true;\n}\n\n/**\n * What a send takes with it: everything attached since the last one, or\n * nothing, so a quiet page costs nothing. Drained rather than copied; a send\n * that fails hands its entries back through {@link restoreCustomData}.\n */\nexport function takeCustomData(): CustomDataEntry[] | undefined {\n if (queued.length === 0) return undefined;\n const taken = queued;\n queued = [];\n return taken;\n}\n\n/** A failed send's entries, back in the queue so the retry still has them. */\nexport function restoreCustomData(entries: CustomDataEntry[]): void {\n // The failed send's entries are the older side: anything attached since\n // the failure wins a collision.\n queued = mergeCustomData(entries, queued) ?? [];\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useRef, useSyncExternalStore } from \"react\";\n\n/**\n * Whether a panel is open, kept outside React.\n *\n * The widget is mounted once, usually in a root layout. The thing that opens\n * it is usually somewhere else entirely: a \"Contact us\" item in a nav, a\n * \"Share feedback\" row in a menu, a keyboard shortcut. Holding this in a\n * module rather than in the panel's own state is what lets those reach it\n * without a host threading state through their whole tree, or wrapping their\n * app in a provider of ours.\n *\n * Two values rather than one, because the host has the last word:\n *\n * - `requested` is what the page last asked for, including the widget's own\n * controls. Null until something asks, so `defaultOpen` still means\n * something on first paint.\n * - `effective` is what the panel is actually doing. A host driving it with\n * the `open` prop can ignore a request, so anything reading the state back\n * reads this one.\n *\n * One store per panel: the support widget and the feedback panel are two\n * things a page can open, and opening one must never open the other.\n */\n\n// The `typeof` guard is for the published bundle, where no framework has\n// promised that `process` exists. A bundler that defines it leaves this line\n// statically true or false; a page without one gets production behavior.\nexport const isDevelopment =\n typeof process !== \"undefined\" && process.env.NODE_ENV === \"development\";\n\nexport type OpenStore = ReturnType<typeof createOpenStore>;\n\nexport function createOpenStore(component: string) {\n let requested: boolean | null = null;\n let effective = false;\n let mounted = 0;\n /** Which components are on the page, by the name the host wrote. */\n const mountedNames: string[] = [];\n const listeners = new Set<() => void>();\n /** Told about every request, wherever it came from. */\n const requestListeners = new Set<(next: boolean) => void>();\n /** Whether a request is being reported right now. */\n let isReporting = false;\n\n function emit() {\n for (const listener of listeners) listener();\n }\n\n return {\n /** The component a warning should name, so it reads like the host's code. */\n component,\n\n subscribe(listener: () => void) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n\n readRequested: () => requested,\n readEffective: () => effective,\n\n /**\n * The server has no page to open a panel on, and neither does the first\n * client paint, which is what keeps hydration quiet.\n */\n readServerRequested: () => null as boolean | null,\n readServerEffective: () => false,\n\n request(next: boolean) {\n requested = next;\n emit();\n // A host whose `onOpenChange` calls `open()` or `close()` back would\n // otherwise go round forever. The request it makes from in there is\n // its own, so it is carried out without being reported back to it.\n if (isReporting) return;\n isReporting = true;\n try {\n for (const listener of requestListeners) listener(next);\n } finally {\n isReporting = false;\n }\n },\n\n /** Hears each request: a trigger, the hook, or the panel's own button. */\n onRequest(listener: (next: boolean) => void) {\n requestListeners.add(listener);\n return () => {\n requestListeners.delete(listener);\n };\n },\n\n /** Told, not asked: the panel saying what it settled on. */\n settle(next: boolean) {\n if (effective === next) return;\n effective = next;\n emit();\n },\n\n countMounted(name: string = component) {\n mounted += 1;\n mountedNames.push(name);\n return mounted;\n },\n\n /** The components on the page, for a warning that names them. */\n mountedNames: () => [...mountedNames],\n\n countUnmounted(name: string = component) {\n mounted -= 1;\n const at = mountedNames.indexOf(name);\n if (at !== -1) mountedNames.splice(at, 1);\n // With nothing on the page, nothing is open, whatever the last one to\n // leave was doing. What was asked for is kept, so a widget that comes\n // back on the next route comes back the way it went.\n if (mounted === 0 && effective) {\n effective = false;\n emit();\n }\n },\n\n /**\n * The likeliest way to spend an afternoon on this: call `open()`, watch\n * nothing happen, and never find out that the component is not on the\n * page. So it says so, in the console, where that afternoon is being\n * spent.\n *\n * Asked a second later rather than now, because a static button wired up\n * before React has hydrated is early, not wrong. The request is kept\n * either way, so a panel that mounts afterwards picks it up.\n */\n warnIfNothingIsMounted(did: string) {\n if (!isDevelopment || mounted > 0) return;\n\n setTimeout(() => {\n if (mounted > 0) return;\n console.warn(\n `Median: ${did}, but no <${component}> is on the page. Mount it once, near the root of your app.`,\n );\n }, 1000);\n },\n };\n}\n\n/**\n * The state a panel actually renders with, and the one place the three ways\n * of driving it are settled:\n *\n * 1. `open`, if the host passed it. Controlled wins, always.\n * 2. Whatever the page last asked for, through a trigger or the hook.\n * 3. `defaultOpen`, until anything at all has happened.\n *\n * A request is reported to `onOpenChange` either way, so a controlled host\n * hears about their own nav item pressing open just as they hear about ours.\n */\nexport function useOpenState(\n store: OpenStore,\n {\n open,\n defaultOpen = false,\n onOpenChange,\n }: {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n },\n): [boolean, (next: boolean) => void] {\n const asked = useSyncExternalStore(\n store.subscribe,\n store.readRequested,\n store.readServerRequested,\n );\n const isOpen = open ?? asked ?? defaultOpen;\n\n // So a trigger reading `isOpen` back is never a frame behind the panel it\n // opened.\n useEffect(() => {\n store.settle(isOpen);\n }, [store, isOpen]);\n\n // Every request reaches `onOpenChange`: the panel's own button, and\n // `open()`, `close()` and `toggle()` from anywhere on the page. Read\n // through a ref so a host passing an inline function does not resubscribe\n // on every render.\n const reportRef = useRef(onOpenChange);\n useEffect(() => {\n reportRef.current = onOpenChange;\n });\n useEffect(\n () => store.onRequest((next) => reportRef.current?.(next)),\n [store],\n );\n\n const setOpen = useCallback(\n (next: boolean) => {\n store.request(next);\n },\n [store],\n );\n\n return [isOpen, setOpen];\n}\n\n/**\n * One of each per page. Two would share the state above and open together,\n * which is never what anyone meant, so it says so where it will be read.\n */\nexport function useRegister(store: OpenStore, name: string = store.component) {\n useEffect(() => {\n if (store.countMounted(name) > 1 && isDevelopment) {\n const names = [...new Set(store.mountedNames())];\n const which =\n names.length === 1\n ? `more than one <${names[0]}> is mounted`\n : `${names.map((one) => `<${one}>`).join(\" and \")} are both mounted`;\n console.warn(\n `Median: ${which}. They share one open state and open together. Mount one, once, near the root of your app.`,\n );\n }\n return () => store.countUnmounted(name);\n }, [store, name]);\n}\n","import { ConvexError } from \"convex/values\";\n\n/**\n * A refusal handed back as a value rather than thrown, by the widget and\n * bridge endpoints whose limits must stick even when the work fails. See\n * `model/trustedMutation.ts`.\n *\n * The key is `auditResult` because widgets already installed on customer\n * sites read it under that name.\n */\nexport type MutationError = {\n code: string;\n message: string;\n retryAfterMs?: number;\n reason?: string;\n};\nexport type MutationResult<T> =\n | { auditResult: \"ok\"; value: T }\n | { auditResult: \"error\"; data: MutationError };\n\n/** Throws a refusal the way a plain mutation would have. */\nexport function unwrapResult<T>(result: MutationResult<T>): T {\n if (result.auditResult === \"error\") throw new ConvexError(result.data);\n return result.value;\n}\n\n/** What a caller may be told about a failure: a refusal's own words, or a generic line. */\nexport function safeError(error: unknown): MutationError {\n if (error instanceof ConvexError) {\n const raw: unknown = error.data;\n if (raw && typeof raw === \"object\") {\n const data = raw as Record<string, unknown>;\n return {\n code:\n typeof data.code === \"string\"\n ? data.code.slice(0, 80)\n : \"invalid_request\",\n message:\n typeof data.message === \"string\"\n ? data.message.slice(0, 1000)\n : \"This action could not be completed.\",\n ...(typeof data.retryAfterMs === \"number\" &&\n Number.isFinite(data.retryAfterMs)\n ? { retryAfterMs: Math.max(0, data.retryAfterMs) }\n : {}),\n ...(data.reason === \"fair_use\" ? { reason: \"fair_use\" } : {}),\n };\n }\n if (typeof raw === \"string\")\n return { code: \"invalid_request\", message: raw.slice(0, 1000) };\n }\n return {\n code: \"internal_error\",\n message: \"This action could not be completed. Please try again.\",\n };\n}\n","import {\n unwrapResult,\n type MutationResult,\n} from \"../convex/shared/mutationResult\";\n\n/** A mutation's value, whether or not it came wrapped in a refusal envelope. */\nexport type MutationValue<T> = T extends { auditResult: \"ok\"; value: infer R }\n ? R\n : T extends { auditResult: \"error\" }\n ? never\n : T;\n\n/**\n * Unwraps what the widget and bridge endpoints answer, which hand a refusal\n * back as a value (see `convex/model/trustedMutation.ts`), and throws it the\n * way a plain mutation would. Any other result passes through untouched.\n */\nexport function unwrapMutationResult<T>(result: T): MutationValue<T> {\n if (\n result &&\n typeof result === \"object\" &&\n \"auditResult\" in result &&\n ((result.auditResult === \"ok\" && \"value\" in result) ||\n (result.auditResult === \"error\" && \"data\" in result))\n ) {\n return unwrapResult(result as MutationResult<unknown>) as MutationValue<T>;\n }\n return result as MutationValue<T>;\n}\n","/* eslint-disable */\n/**\n * Generated `api` utility.\n *\n * THIS CODE IS AUTOMATICALLY GENERATED.\n *\n * To regenerate, run `npx convex dev`.\n * @module\n */\n\nimport { anyApi, componentsGeneric } from \"convex/server\";\n\n/**\n * A utility for referencing Convex functions in your app's API.\n *\n * Usage:\n * ```js\n * const myFunctionReference = api.myModule.myFunction;\n * ```\n */\nexport const api = anyApi;\nexport const internal = anyApi;\nexport const components = componentsGeneric();\n","import { cleanTitle } from \"./signals\";\n\n/**\n * A crash, in the vocabulary both sides share.\n *\n * A page that has fallen over is the one report nobody types: whoever was\n * using it can see that it broke and has nothing to add about why. So the\n * error describes itself, and this file is what it turns into on the Signal\n * tab.\n *\n * Deliberately pure, like `signals.ts` and `feedback.ts` next door. The SDK\n * trims a crash with this file before it sends and the mutation trims it\n * again on arrival, so neither side is trusting the other's arithmetic.\n *\n * Nothing here asks a model. A report somebody pressed a button for has to\n * land, and it has to land under the same title as the last twenty copies of\n * the same crash, which is what makes the list one row rather than twenty.\n */\n\n/** What the boundary saw, as it hands it over. */\nexport type Crash = {\n /** The error's own line, without its stack. */\n message: string;\n /** \"TypeError\", when the thrown thing had a name worth keeping. */\n name?: string;\n stack?: string;\n /** The framework's id for an error that happened on the server. */\n digest?: string;\n /** The component stack, when the boundary was handed one. */\n componentStack?: string;\n /** The route it happened on, as the app's own router writes it. */\n route?: string;\n};\n\n/** One line of error, the same cap a diagnostic message gets. */\nexport const CRASH_MESSAGE_MAX_LENGTH = 300;\n\n/** Room for a real stack, matching what one attachment may hold. */\nexport const CRASH_STACK_MAX_LENGTH = 4_000;\n\nexport const CRASH_NAME_MAX_LENGTH = 60;\nexport const CRASH_ROUTE_MAX_LENGTH = 200;\n\n/** When the thrown thing said nothing for itself. */\nconst UNNAMED = \"Unnamed error\";\n\n/**\n * Names that add nothing to a title. Every thrown thing in a browser is an\n * error, so saying so twice only costs room the message wanted.\n */\nconst GENERIC_NAMES = [\"error\", \"exception\"];\n\nfunction trim(value: unknown, limit: number): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const clean = value.trim().slice(0, limit);\n return clean.length > 0 ? clean : undefined;\n}\n\n/** Whatever arrived, cut down to what may be stored. */\nexport function cleanCrash(input: unknown): Crash {\n const raw =\n typeof input === \"object\" && input !== null\n ? (input as Record<string, unknown>)\n : {};\n\n const name = trim(raw.name, CRASH_NAME_MAX_LENGTH);\n const stack = trim(raw.stack, CRASH_STACK_MAX_LENGTH);\n const digest = trim(raw.digest, CRASH_NAME_MAX_LENGTH);\n const componentStack = trim(raw.componentStack, CRASH_STACK_MAX_LENGTH);\n const route = trim(raw.route, CRASH_ROUTE_MAX_LENGTH);\n\n return {\n message: trim(raw.message, CRASH_MESSAGE_MAX_LENGTH) ?? UNNAMED,\n ...(name !== undefined ? { name } : {}),\n ...(stack !== undefined ? { stack } : {}),\n ...(digest !== undefined ? { digest } : {}),\n ...(componentStack !== undefined ? { componentStack } : {}),\n ...(route !== undefined ? { route } : {}),\n };\n}\n\n/**\n * The error on one line: \"TypeError: user.plan is not a function\".\n *\n * The name is dropped when the message already opens with it, which is what\n * a stack's first line looks like when a host passes one straight through.\n */\nexport function crashLabel(crash: Crash): string {\n const name = crash.name;\n if (name === undefined) return crash.message;\n if (GENERIC_NAMES.includes(name.toLowerCase())) return crash.message;\n if (crash.message.toLowerCase().startsWith(name.toLowerCase())) {\n return crash.message;\n }\n return `${name}: ${crash.message}`;\n}\n\n/**\n * How the row reads in the bug list.\n *\n * The route leads, because it is what makes two crashes the same crash: the\n * error's own line can run long and be cut, and what survives the cut should\n * still be the thing that groups them. The exact words matter more here than\n * anywhere else, since the filing mutation dedupes on them.\n */\nexport function crashTitle(crash: Crash): string {\n const label = crashLabel(crash);\n return cleanTitle(\n crash.route === undefined\n ? `Crash: ${label}`\n : `Crash on ${crash.route}: ${label}`,\n );\n}\n\n/** What the team reads first: what broke, and where. */\nexport function crashBody(crash: Crash): string {\n return [\n `The page stopped working${\n crash.route === undefined ? \"\" : ` on ${crash.route}`\n } and somebody reported it from the error screen.`,\n \"\",\n `**Error**: ${crashLabel(crash)}`,\n ...(crash.digest === undefined ? [] : [`**Digest**: ${crash.digest}`]),\n ].join(\"\\n\");\n}\n\n/**\n * The reporter's own line on the report. They pressed a button rather than\n * writing a sentence, so it says what the button did and what it saw.\n */\nexport function crashQuote(crash: Crash): string {\n return `Reported from the error screen: ${crashLabel(crash)}`;\n}\n\n/**\n * The long parts, as attachments rather than as body text: the stack belongs\n * to this one crash, and the body is shared with every other report of it.\n */\nexport function crashAttachments(crash: Crash): { label: string; value: string }[] {\n return [\n ...(crash.stack === undefined\n ? []\n : [{ label: \"Crash trace\", value: crash.stack }]),\n ...(crash.componentStack === undefined\n ? []\n : [{ label: \"Component stack\", value: crash.componentStack }]),\n ];\n}\n","\"use client\";\n\nimport { unwrapMutationResult } from \"@median/backend/mutationResult\";\n\nimport { api } from \"@median/backend\";\nimport { cleanCrash, type Crash } from \"@median/backend/crashes\";\nimport { mergeCustomData } from \"@median/backend/customData\";\nimport { ConvexHttpClient } from \"convex/browser\";\nimport { useSyncExternalStore } from \"react\";\nimport { uploadAttachments, type SentAttachment } from \"@/lib/upload\";\nimport { collectContext, collectPage } from \"./context\";\nimport { restoreCustomData, takeCustomData } from \"./custom-data\";\nimport { MEDIAN_DEPLOYMENT_URL } from \"./deployment\";\nimport { snapshotNow } from \"./diagnostics\";\nimport { hasInstall, readInstall, subscribeInstall } from \"./install\";\nimport { capturePage } from \"./screenshot\";\nimport { getOrCreateSessionId } from \"./session\";\nimport { splitIdentity, type MedianUser } from \"./use-median-thread\";\n\n/**\n * Filing the crash the visitor is looking at.\n *\n * Every other send in the SDK starts with somebody typing. This one starts\n * with a page that has stopped working, which is the report nobody writes:\n * whoever was using it can see that it broke and has nothing to add about\n * why. So the error describes itself, the browser hands over what it was\n * doing, and one press files it.\n *\n * It brings its own connection and reads its key from the last widget that\n * mounted, because by the time this runs the tree that widget lived in has\n * usually been replaced by the error screen calling it. Nothing here needs a\n * component, a provider, or a panel on the page.\n *\n * It never throws. A crash screen that crashes reporting a crash is a poor\n * apology, so everything that can go wrong comes back as a reason instead.\n */\n\nexport type MedianErrorReportOptions = {\n /**\n * The key, when no widget has mounted on this page to remember it. Both\n * panels write theirs down as they mount, so an app with either one\n * installed can leave this alone.\n */\n apiKey?: string;\n /** Who is in the chair. Defaults to whoever the mounted widget was given. */\n user?: MedianUser;\n /**\n * Where it happened, when your router names the page better than its URL\n * does: \"/orders/[id]\" rather than \"/orders/8912\". It leads the title, so\n * the same crash on the same route is one row on the list however many\n * people hit it. Defaults to the path in the address bar.\n */\n route?: string;\n /** React's component stack, when your boundary was handed one. */\n componentStack?: string;\n /** Facts to put on the report, the same shape as `appState`. */\n state?: Record<string, string | number | boolean>;\n /** Blocks to attach, the same shape and limits as `attach`. */\n attach?: Record<string, unknown>;\n /** Whether the report carries what the browser knows. On by default. */\n telemetry?: boolean;\n /**\n * Whether the report carries a picture of the screen it was filed from. On\n * by default: what a crash screen actually looks like is the one thing the\n * stack does not say. Pass `false` and no picture is taken.\n */\n screenshot?: boolean;\n};\n\n/** Where the report went, or why it did not go. */\nexport type MedianErrorReportOutcome =\n | {\n reported: true;\n /** True when somebody had already reported this one. */\n joined: boolean;\n }\n | { reported: false; reason: string };\n\n/** Why a report did not land, in words the host can act on. */\nconst REASONS = {\n noKey:\n \"Median has no API key on this page, so the crash could not be reported. Mount <MedianSupport> or pass apiKey to reportError.\",\n noDeployment:\n \"The Median widget was built without a deployment URL, so the crash could not be reported.\",\n noBrowser: \"reportError only runs in a browser.\",\n off: \"Error reporting is currently unavailable. Try again later.\",\n failed: \"The report could not be sent. Check the connection and try again.\",\n} as const;\n\nlet client: ConvexHttpClient | null = null;\n\n/**\n * Anything thrown, as the fields a report is made of.\n *\n * Hosts throw all sorts. An Error is the common case, a string is the lazy\n * one, and a framework error is an Error with an id stapled to it. All three\n * arrive here and none of them should cost the report.\n */\nfunction crashFrom(error: unknown, options: MedianErrorReportOptions): Crash {\n const raw = (\n typeof error === \"object\" && error !== null ? error : {}\n ) as Record<string, unknown>;\n\n const message =\n error instanceof Error\n ? error.message\n : typeof error === \"string\"\n ? error\n : typeof raw.message === \"string\"\n ? raw.message\n : String(error);\n\n return cleanCrash({\n message,\n name: error instanceof Error ? error.name : raw.name,\n stack: raw.stack,\n // Next puts one on every error that came from the server, and it is the\n // id in that server's own log line.\n digest: raw.digest,\n componentStack: options.componentStack ?? raw.componentStack,\n route: options.route ?? currentRoute(),\n });\n}\n\n/** The page it happened on, minus anything after the path. */\nfunction currentRoute(): string | undefined {\n if (typeof window === \"undefined\") return undefined;\n return window.location.pathname || undefined;\n}\n\n/**\n * The facts that belong beside the browser's, rather than inside the write-up.\n * Both of these read as a row on the report next to the viewport and the page.\n */\nfunction factsOf(crash: Crash): Record<string, string> {\n return {\n ...(crash.route !== undefined ? { route: crash.route } : {}),\n ...(crash.digest !== undefined ? { digest: crash.digest } : {}),\n };\n}\n\nexport async function reportError(\n error: unknown,\n options: MedianErrorReportOptions = {},\n): Promise<MedianErrorReportOutcome> {\n if (typeof window === \"undefined\") {\n return { reported: false, reason: REASONS.noBrowser };\n }\n\n const apiKey = options.apiKey ?? readInstall()?.apiKey;\n if (!apiKey) {\n console.error(REASONS.noKey);\n return { reported: false, reason: REASONS.noKey };\n }\n if (!MEDIAN_DEPLOYMENT_URL) {\n console.error(REASONS.noDeployment);\n return { reported: false, reason: REASONS.noDeployment };\n }\n\n const sessionId = getOrCreateSessionId(apiKey);\n if (sessionId === null) {\n return { reported: false, reason: REASONS.failed };\n }\n\n const crash = crashFrom(error, options);\n const telemetry = options.telemetry ?? true;\n const user = options.user ?? readInstall()?.user;\n const { facts, claim } = splitIdentity(user ?? null);\n\n // Whatever the host attached since the last send, plus whatever they handed\n // this call, drained the way a message drains it so nothing is sent twice.\n const custom = mergeCustomData(\n takeCustomData(),\n Object.entries(options.attach ?? {}).map(([label, value]) => ({\n label,\n value: typeof value === \"string\" ? value : JSON.stringify(value),\n })),\n );\n\n // Read here rather than on mount, like every other send: all of it is\n // wanted as \"what was true when it broke\".\n const context = telemetry ? collectContext() : undefined;\n const page = telemetry ? collectPage() : undefined;\n const diagnostics = snapshotNow({ ...factsOf(crash), ...options.state });\n\n try {\n client ??= new ConvexHttpClient(MEDIAN_DEPLOYMENT_URL);\n const images =\n options.screenshot === false\n ? []\n : await screenshotOf(client, apiKey, sessionId);\n const outcome = await client\n .mutation(api.feedback.reportErrorV2, {\n apiKey,\n sessionId,\n error: crash,\n ...(images.length > 0 ? { images } : {}),\n ...(user ? { user: facts } : {}),\n ...(claim ? { identity: claim } : {}),\n ...(context ? { context } : {}),\n ...(page ? { page } : {}),\n ...(diagnostics ? { diagnostics } : {}),\n ...(custom ? { customData: custom } : {}),\n })\n .then(unwrapMutationResult);\n\n if (outcome === \"off\") {\n if (custom) restoreCustomData(custom);\n return { reported: false, reason: REASONS.off };\n }\n return { reported: true, joined: outcome === \"joined\" };\n } catch (caught) {\n // Back in the queue, so a second press still carries them.\n if (custom) restoreCustomData(custom);\n console.error(\"Median: sending the crash report failed\", caught);\n return { reported: false, reason: REASONS.failed };\n }\n}\n\n/**\n * The screen they are looking at, uploaded, or nothing.\n *\n * Its own try: a picture is the nicest thing on a crash report and the least\n * important. Whatever goes wrong taking or sending it, the report itself\n * still goes.\n */\nasync function screenshotOf(\n client: ConvexHttpClient,\n apiKey: string,\n sessionId: string,\n): Promise<SentAttachment[]> {\n try {\n const shot = await capturePage();\n if (shot === null) return [];\n return await uploadAttachments(\n () =>\n client\n .mutation(api.widget.generateUploadUrlV2, { apiKey, sessionId })\n .then(unwrapMutationResult),\n [shot],\n );\n } catch (caught) {\n console.warn(\n \"Median: the crash report's screenshot could not be sent\",\n caught,\n );\n return [];\n }\n}\n\n/**\n * Whether a report has anywhere to go: a widget has mounted on this page and\n * left its key, and this build knows which Median to send to.\n *\n * For a \"Report error\" button, which should not be on the screen at all if\n * pressing it could only ever fail. It follows the install rather than\n * reading it once, since an error screen can render before the widget does.\n */\nexport function useCanReportError(): boolean {\n const isInstalled = useSyncExternalStore(\n subscribeInstall,\n hasInstall,\n // The server renders nothing installed: it has no page to have mounted\n // on, and a button that flickered away on hydration is worse than one\n // that arrives a moment late.\n () => false,\n );\n\n return isInstalled && Boolean(MEDIAN_DEPLOYMENT_URL);\n}\n","import type { Id } from \"@median/backend/dataModel\";\n\n/**\n * POSTs one file to a one shot Convex upload URL and hands back its storage\n * id. The URL comes from whichever mutation guards the caller, so the same\n * helper serves the inbox, the widget, and the settings page.\n */\nexport async function uploadFile(\n getUploadUrl: () => Promise<string>,\n file: File,\n): Promise<Id<\"_storage\">> {\n const url = await getUploadUrl();\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": file.type || \"application/octet-stream\" },\n body: file,\n });\n if (!response.ok) {\n throw new Error(`Uploading ${file.name} failed with ${response.status}`);\n }\n\n const { storageId } = (await response.json()) as {\n storageId: Id<\"_storage\">;\n };\n return storageId;\n}\n\n/** The shape a message mutation wants a sent file described in. */\nexport type SentAttachment = {\n storageId: Id<\"_storage\">;\n name: string;\n size: number;\n type: string;\n};\n\n/** Uploads a batch in order and describes each one for the send mutation. */\nexport async function uploadAttachments(\n getUploadUrl: () => Promise<string>,\n files: File[],\n): Promise<SentAttachment[]> {\n const attachments: SentAttachment[] = [];\n for (const file of files) {\n attachments.push({\n storageId: await uploadFile(getUploadUrl, file),\n name: file.name,\n size: file.size,\n type: file.type || \"application/octet-stream\",\n });\n }\n return attachments;\n}\n","import type {\n ConversationPage,\n DeviceKind,\n VisitorContext,\n} from \"@median/backend/telemetry\";\n\n/**\n * The handful of things the browser already knows that a support reply needs.\n *\n * Read at send time rather than watched, because all of it is only ever wanted\n * as \"what was true when they wrote\". Nothing is stored, nothing is polled,\n * and nothing leaves the page unless somebody actually gets in touch.\n *\n * The user agent is parsed here rather than sent up raw. A name and a major\n * version is what the inbox shows and all it can act on; the full string is a\n * fingerprint we would then be keeping on somebody else's customers.\n */\n\n/**\n * Browser names in the order they have to be tested.\n *\n * Every one of these lies about being the others. Chrome's string contains\n * Safari, Edge's contains Chrome and Safari, Opera's contains all three, so\n * the only thing that works is checking the most specific claim first and\n * taking the first hit.\n */\nconst BROWSERS: { name: string; token: RegExp }[] = [\n { name: \"Edge\", token: /Edg(?:e|A|iOS)?\\/(\\d+)/ },\n { name: \"Opera\", token: /OPR\\/(\\d+)/ },\n { name: \"Samsung Internet\", token: /SamsungBrowser\\/(\\d+)/ },\n { name: \"Firefox\", token: /(?:Firefox|FxiOS)\\/(\\d+)/ },\n { name: \"Chrome\", token: /(?:Chrome|CriOS)\\/(\\d+)/ },\n { name: \"Safari\", token: /Version\\/(\\d+).*Safari/ },\n];\n\nconst OPERATING_SYSTEMS: { name: string; token: RegExp }[] = [\n // Before Android, whose string also says Linux.\n { name: \"Android\", token: /Android/ },\n { name: \"iOS\", token: /iPhone|iPad|iPod/ },\n { name: \"macOS\", token: /Mac OS X|Macintosh/ },\n { name: \"Windows\", token: /Windows NT/ },\n { name: \"ChromeOS\", token: /CrOS/ },\n { name: \"Linux\", token: /Linux/ },\n];\n\nfunction browserOf(ua: string): string | undefined {\n for (const { name, token } of BROWSERS) {\n const match = ua.match(token);\n if (match) return match[1] ? `${name} ${match[1]}` : name;\n }\n return undefined;\n}\n\nfunction osOf(ua: string): string | undefined {\n return OPERATING_SYSTEMS.find(({ token }) => token.test(ua))?.name;\n}\n\n/**\n * Desktop, tablet, or phone.\n *\n * An iPad reports itself as a Mac and has done for years, so the touch check\n * is what tells the two apart. Everything else is honest enough about it.\n */\nfunction deviceOf(ua: string): DeviceKind {\n const isTouchMac =\n /Macintosh/.test(ua) && navigator.maxTouchPoints > 1;\n\n if (/iPad|Tablet/.test(ua) || isTouchMac) return \"tablet\";\n if (/Mobi|iPhone|iPod|Android.*Mobile/.test(ua)) return \"mobile\";\n if (/Android/.test(ua)) return \"tablet\";\n return \"desktop\";\n}\n\n/** What the browser will admit to, or nothing if it is not a browser at all. */\nexport function collectContext(): VisitorContext | undefined {\n if (typeof navigator === \"undefined\" || typeof window === \"undefined\") {\n return undefined;\n }\n\n const ua = navigator.userAgent ?? \"\";\n const { width, height } = window.screen ?? { width: 0, height: 0 };\n\n const context: VisitorContext = {\n // Wrapped: a browser with a broken or absent Intl is rare but real, and\n // failing to work out a time zone should not stop a message sending.\n timeZone: safely(() => Intl.DateTimeFormat().resolvedOptions().timeZone),\n locale: navigator.language || undefined,\n browser: browserOf(ua),\n os: osOf(ua),\n device: deviceOf(ua),\n // The multiplication sign rather than an x, since this is being read as a\n // measurement rather than run as one.\n screen: width && height ? `${width} × ${height}` : undefined,\n };\n\n return Object.values(context).some((one) => one !== undefined)\n ? context\n : undefined;\n}\n\n/**\n * The page they are writing from.\n *\n * The query string is dropped on the way out, before it is ever sent, and\n * again on the way in. A support agent needs to know somebody was on the\n * checkout page; the password reset token that got them there is not ours to\n * put in somebody's inbox.\n */\nexport function collectPage(): ConversationPage | undefined {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return undefined;\n }\n\n const url = withoutQuery(window.location.href);\n if (url === undefined) return undefined;\n\n return {\n url,\n title: document.title || undefined,\n referrer: withoutQuery(document.referrer),\n };\n}\n\nfunction withoutQuery(href: string): string | undefined {\n if (!href) return undefined;\n try {\n const url = new URL(href);\n if (url.protocol !== \"https:\" && url.protocol !== \"http:\") return undefined;\n return `${url.origin}${url.pathname}`;\n } catch {\n return undefined;\n }\n}\n\nfunction safely<T>(read: () => T): T | undefined {\n try {\n return read();\n } catch {\n return undefined;\n }\n}\n","/**\n * Which Median the widget talks to.\n *\n * Deliberately not a prop. A host plugs in their API key and that is the whole\n * install; our address is our business, not something they should have to know\n * or keep in sync. It reads from the env while the widget ships inside this\n * app, and the published package bakes it in at build time.\n */\nexport const MEDIAN_DEPLOYMENT_URL = process.env.NEXT_PUBLIC_CONVEX_URL;\n","/**\n * What was going wrong around a bug report, from both ends.\n *\n * The client half is collected by the widget SDK: uncaught errors, rejected\n * promises, failed requests, and whatever the host chose to tell us about\n * their own app. The server half is asked for at report time, from the same\n * endpoint the agent's tools live at, so a team that has already wired up\n * tools gets it for nothing.\n *\n * Pure on purpose. The widget cleans what it collects with this file before\n * it sends, and the backend cleans it again with the same file before it\n * stores, so neither side is trusting the other's arithmetic.\n *\n * The rule everywhere here is the one the rest of the telemetry follows: a\n * fact about a program, never a fact about a person. Nothing reads cookies,\n * nothing reads storage, and every URL loses its query string on the way.\n */\n\n/**\n * Where one entry came from. `error` is an uncaught exception, `rejection`\n * is a promise nobody caught, `console` is a console.error, and `network` is\n * a request that failed or came back unhappy.\n */\nexport const DIAGNOSTIC_KINDS = [\n \"error\",\n \"rejection\",\n \"console\",\n \"network\",\n] as const;\n\nexport type DiagnosticKind = (typeof DIAGNOSTIC_KINDS)[number];\n\nexport type DiagnosticEvent = {\n at: number;\n kind: DiagnosticKind;\n message: string;\n /** The file, or the request's method and path. Never a query string. */\n source?: string;\n /** The first frames, for an exception. */\n stack?: string;\n /** The HTTP status, on a network entry that got one. */\n status?: number;\n};\n\nexport type ClientDiagnostics = {\n /** When the snapshot was taken, by the browser's own clock. */\n at: number;\n /** The page they were on. Query string already dropped. */\n url?: string;\n /** \"1512 × 982\", already formatted, like the rest of the telemetry. */\n viewport?: string;\n /** Whatever the host declared about their own app: version, route, flags. */\n state?: Record<string, string>;\n events: DiagnosticEvent[];\n};\n\n/** One tool endpoint's answer to \"how are you\". */\nexport type ServerDiagnostics = {\n /** Which endpoint answered, so a team with two can tell them apart. */\n url: string;\n ok: boolean;\n /** What it said, as JSON text, or why it could not be asked. */\n detail: string;\n};\n\n/** The most entries one snapshot carries. Newest kept, oldest dropped. */\nexport const MAX_EVENTS = 12;\n\nexport const MESSAGE_MAX_LENGTH = 300;\nexport const SOURCE_MAX_LENGTH = 200;\nexport const STACK_MAX_LENGTH = 800;\n\n/** The host's own state, bounded the same way visitor metadata is. */\nexport const MAX_STATE_ENTRIES = 20;\nexport const STATE_KEY_MAX_LENGTH = 60;\nexport const STATE_VALUE_MAX_LENGTH = 200;\n\n/** What one endpoint may say for itself. Enough to hold a health payload. */\nexport const SERVER_DETAIL_MAX_LENGTH = 2_000;\n\n/** How long the widget waits before deciding an endpoint is not answering. */\nexport const SERVER_TIMEOUT_MS = 8_000;\n\nfunction trim(value: unknown, limit: number): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const clean = value.trim().slice(0, limit);\n return clean.length > 0 ? clean : undefined;\n}\n\n/** A URL with its query string taken off, or nothing if it is not one. */\nexport function withoutQuery(href: unknown): string | undefined {\n if (typeof href !== \"string\" || href.length === 0) return undefined;\n try {\n const url = new URL(href);\n if (url.protocol !== \"https:\" && url.protocol !== \"http:\") return undefined;\n return `${url.origin}${url.pathname}`.slice(0, SOURCE_MAX_LENGTH);\n } catch {\n // Relative paths and the like arrive here. The path is the useful half\n // anyway, so it is kept as written, minus anything after a \"?\".\n return trim(href.split(\"?\")[0], SOURCE_MAX_LENGTH);\n }\n}\n\nfunction cleanEvent(input: unknown): DiagnosticEvent | null {\n if (typeof input !== \"object\" || input === null) return null;\n const raw = input as Record<string, unknown>;\n\n const kind = DIAGNOSTIC_KINDS.find((one) => one === raw.kind);\n const message = trim(raw.message, MESSAGE_MAX_LENGTH);\n if (kind === undefined || message === undefined) return null;\n\n const status =\n typeof raw.status === \"number\" && Number.isFinite(raw.status)\n ? Math.trunc(raw.status)\n : undefined;\n\n return {\n at: typeof raw.at === \"number\" && Number.isFinite(raw.at) ? raw.at : 0,\n kind,\n message,\n ...(trim(raw.source, SOURCE_MAX_LENGTH) !== undefined\n ? { source: trim(raw.source, SOURCE_MAX_LENGTH)! }\n : {}),\n ...(trim(raw.stack, STACK_MAX_LENGTH) !== undefined\n ? { stack: trim(raw.stack, STACK_MAX_LENGTH)! }\n : {}),\n ...(status !== undefined ? { status } : {}),\n };\n}\n\nfunction cleanState(input: unknown): Record<string, string> | undefined {\n if (typeof input !== \"object\" || input === null) return undefined;\n\n const state: Record<string, string> = {};\n for (const [key, value] of Object.entries(input).slice(0, MAX_STATE_ENTRIES)) {\n if (value === null || value === undefined) continue;\n const label = trim(key, STATE_KEY_MAX_LENGTH);\n // Numbers and booleans are the other two things anybody puts in here, so\n // they are written out rather than refused.\n const written =\n typeof value === \"string\"\n ? trim(value, STATE_VALUE_MAX_LENGTH)\n : typeof value === \"number\" || typeof value === \"boolean\"\n ? String(value).slice(0, STATE_VALUE_MAX_LENGTH)\n : undefined;\n if (label === undefined || written === undefined) continue;\n state[label] = written;\n }\n\n return Object.keys(state).length > 0 ? state : undefined;\n}\n\n/**\n * Whatever arrived, cut down to what may be stored. Undefined when there was\n * nothing worth keeping, so an empty snapshot never lands on a row and reads\n * later as \"we looked and the page was fine\".\n */\nexport function cleanClientDiagnostics(\n input: unknown,\n): ClientDiagnostics | undefined {\n if (typeof input !== \"object\" || input === null) return undefined;\n const raw = input as Record<string, unknown>;\n\n const events = Array.isArray(raw.events)\n ? raw.events\n .map(cleanEvent)\n .filter((one): one is DiagnosticEvent => one !== null)\n .slice(-MAX_EVENTS)\n : [];\n\n const url = withoutQuery(raw.url);\n const viewport = trim(raw.viewport, 40);\n const state = cleanState(raw.state);\n\n if (events.length === 0 && state === undefined && url === undefined) {\n return undefined;\n }\n\n return {\n at: typeof raw.at === \"number\" && Number.isFinite(raw.at) ? raw.at : 0,\n ...(url !== undefined ? { url } : {}),\n ...(viewport !== undefined ? { viewport } : {}),\n ...(state !== undefined ? { state } : {}),\n events,\n };\n}\n\n/** Put browser events on the receipt clock while preserving their relative ages. */\nexport function diagnosticEventsAtReceipt(\n snapshot: ClientDiagnostics,\n receivedAt: number,\n): DiagnosticEvent[] {\n if (!Number.isFinite(snapshot.at) || !Number.isFinite(receivedAt)) return [];\n return snapshot.events\n .filter((event) => Number.isFinite(event.at) && event.at <= snapshot.at + 60_000)\n .map((event) => ({\n ...event,\n at: receivedAt - Math.max(0, snapshot.at - event.at),\n }));\n}\n\n/** One endpoint's answer, trimmed to something a card can show. */\nexport function cleanServerDetail(text: string): string {\n return text.length > SERVER_DETAIL_MAX_LENGTH\n ? `${text.slice(0, SERVER_DETAIL_MAX_LENGTH)}…`\n : text;\n}\n","\"use client\";\n\nimport {\n cleanClientDiagnostics,\n MAX_EVENTS,\n MESSAGE_MAX_LENGTH,\n SOURCE_MAX_LENGTH,\n STACK_MAX_LENGTH,\n withoutQuery,\n type ClientDiagnostics,\n type DiagnosticEvent,\n} from \"@median/backend/diagnostics\";\nimport { useCallback, useEffect, useRef } from \"react\";\n\n/**\n * What has been going wrong on the page, kept ready in case somebody asks\n * for help about it.\n *\n * A support agent's first three questions are always the same: what were you\n * doing, what did it say, and what does the console show. The customer can\n * answer the first one. This answers the other two, so a bug report arrives\n * with the error already attached rather than after four messages of asking.\n *\n * Off unless a host switches it on. Everything here reads their program's\n * error stream, which is theirs to offer rather than ours to take.\n *\n * A bounded ring buffer and nothing else. Nothing is sent on its own, nothing\n * is stored, and nothing is polled: the newest snapshot rides the next message\n * the customer sends, and only when there is something new in it.\n */\n\n/** What a host can switch on. `true` is the passive half. */\nexport type MedianDiagnostics =\n | boolean\n | {\n /** Uncaught errors and rejected promises. On whenever diagnostics are. */\n errors?: boolean;\n /** Requests that failed or came back unhappy. Wraps window.fetch. */\n network?: boolean;\n /** console.error calls. Wraps console.error. */\n console?: boolean;\n };\n\ntype Settings = { errors: boolean; network: boolean; console: boolean };\n\nfunction settingsOf(option: MedianDiagnostics | undefined): Settings | null {\n if (option === undefined || option === false) return null;\n if (option === true) return { errors: true, network: false, console: false };\n return {\n errors: option.errors ?? true,\n network: option.network ?? false,\n console: option.console ?? false,\n };\n}\n\n/**\n * The buffer, and how many things have gone into it.\n *\n * Module level rather than per component: a page has one widget, and an\n * error that happened before React mounted it is exactly the kind worth\n * keeping. The count is what lets a sender ask \"anything new since last\n * time\" without comparing arrays.\n */\nconst events: DiagnosticEvent[] = [];\nlet recorded = 0;\n\n/**\n * The host's own state, as the last widget given any was given it. Here\n * rather than only in the hook's ref because a crash report is sent from a\n * tree where that hook no longer exists, and the route a page fell over on\n * is exactly the sort of thing hosts put in `appState`. A panel given none\n * leaves it alone, so the feedback panel cannot blank the widget's.\n */\nlet latestState: Record<string, string | number | boolean> | undefined;\n\nfunction record(event: DiagnosticEvent): void {\n events.push(event);\n if (events.length > MAX_EVENTS) events.shift();\n recorded += 1;\n}\n\nfunction messageOf(value: unknown): string {\n if (value instanceof Error) return value.message.slice(0, MESSAGE_MAX_LENGTH);\n if (typeof value === \"string\") return value.slice(0, MESSAGE_MAX_LENGTH);\n try {\n return JSON.stringify(value).slice(0, MESSAGE_MAX_LENGTH);\n } catch {\n return String(value).slice(0, MESSAGE_MAX_LENGTH);\n }\n}\n\nfunction stackOf(value: unknown): string | undefined {\n return value instanceof Error && typeof value.stack === \"string\"\n ? value.stack.slice(0, STACK_MAX_LENGTH)\n : undefined;\n}\n\n/** Puts one kind of listening in place, and gives back how to take it off. */\ntype Installer = () => () => void;\n\nconst INSTALLERS: Record<keyof Settings, Installer> = {\n errors: () => {\n const onError = (event: ErrorEvent) => {\n // A failed image or script fires here too, with no error on it. Those\n // are the browser's business, not a bug report's.\n if (!event.error && !event.message) return;\n record({\n at: Date.now(),\n kind: \"error\",\n message: messageOf(event.error ?? event.message),\n ...(event.filename\n ? {\n source:\n `${withoutQuery(event.filename) ?? \"\"}:${event.lineno}`.slice(\n 0,\n SOURCE_MAX_LENGTH,\n ),\n }\n : {}),\n ...(stackOf(event.error) !== undefined\n ? { stack: stackOf(event.error)! }\n : {}),\n });\n };\n const onRejection = (event: PromiseRejectionEvent) => {\n record({\n at: Date.now(),\n kind: \"rejection\",\n message: messageOf(event.reason),\n ...(stackOf(event.reason) !== undefined\n ? { stack: stackOf(event.reason)! }\n : {}),\n });\n };\n\n window.addEventListener(\"error\", onError);\n window.addEventListener(\"unhandledrejection\", onRejection);\n return () => {\n window.removeEventListener(\"error\", onError);\n window.removeEventListener(\"unhandledrejection\", onRejection);\n };\n },\n\n network: () => {\n if (typeof window.fetch !== \"function\") return () => {};\n const original = window.fetch;\n window.fetch = async function medianFetch(this: unknown, ...args) {\n const [input, init] = args;\n const method = (init?.method ?? \"GET\").toUpperCase();\n const url =\n typeof input === \"string\"\n ? input\n : input instanceof URL\n ? input.href\n : input.url;\n\n try {\n const response = await original.apply(this as never, args);\n if (!response.ok) {\n record({\n at: Date.now(),\n kind: \"network\",\n message: `${method} ${response.status}`,\n source: `${method} ${withoutQuery(url) ?? \"\"}`.slice(\n 0,\n SOURCE_MAX_LENGTH,\n ),\n status: response.status,\n });\n }\n return response;\n } catch (thrown) {\n record({\n at: Date.now(),\n kind: \"network\",\n message: messageOf(thrown),\n source: `${method} ${withoutQuery(url) ?? \"\"}`.slice(\n 0,\n SOURCE_MAX_LENGTH,\n ),\n });\n // Rethrown untouched. Watching a request is not catching it.\n throw thrown;\n }\n } as typeof window.fetch;\n\n return () => {\n window.fetch = original;\n };\n },\n\n console: () => {\n const original = console.error;\n console.error = function medianConsoleError(...args: unknown[]) {\n record({\n at: Date.now(),\n kind: \"console\",\n message: args.map(messageOf).join(\" \").slice(0, MESSAGE_MAX_LENGTH),\n ...(stackOf(args.find((one) => one instanceof Error)) !== undefined\n ? { stack: stackOf(args.find((one) => one instanceof Error))! }\n : {}),\n });\n original.apply(console, args);\n };\n\n return () => {\n console.error = original;\n };\n },\n};\n\n/**\n * How many mounted components want each kind, and how to take each off.\n * Counted rather than installed per component: a page with the widget and\n * the feedback panel both collecting would otherwise wrap fetch twice and\n * record every event twice.\n */\nconst wanted: Record<keyof Settings, number> = {\n errors: 0,\n network: 0,\n console: 0,\n};\nconst removers: Partial<Record<keyof Settings, () => void>> = {};\n\n/**\n * Installs whatever the host asked for, and gives back the way to take it all\n * off again. Every wrapper calls through to what it replaced first, so a page\n * behaves exactly as it did before the widget arrived.\n */\nfunction collect(settings: Settings): () => void {\n const kinds = (Object.keys(settings) as (keyof Settings)[]).filter(\n (kind) => settings[kind],\n );\n for (const kind of kinds) {\n wanted[kind] += 1;\n if (wanted[kind] === 1) removers[kind] = INSTALLERS[kind]();\n }\n return () => {\n for (const kind of kinds) {\n wanted[kind] -= 1;\n if (wanted[kind] > 0) continue;\n removers[kind]?.();\n delete removers[kind];\n }\n };\n}\n\n/** Everything worth saying about the page right now, or nothing. */\nfunction snapshot(\n state: Record<string, string | number | boolean> | undefined,\n): ClientDiagnostics | undefined {\n if (typeof window === \"undefined\") return undefined;\n\n return cleanClientDiagnostics({\n at: Date.now(),\n url: window.location.href,\n viewport: `${window.innerWidth} × ${window.innerHeight}`,\n state,\n events,\n });\n}\n\n/**\n * Everything the page can say for itself right now, for a send that cannot\n * wait to be asked twice.\n *\n * The hook's `take` answers with nothing when nothing has been recorded since\n * the last send, which is right for a chat message and wrong for a crash\n * report: the viewport and the route are worth having whether or not the ring\n * buffer moved. So this one always answers, and takes anything the caller\n * knows that the host's own state does not.\n */\nexport function snapshotNow(\n extraState?: Record<string, string | number | boolean>,\n): ClientDiagnostics | undefined {\n // The caller's facts first: the report keeps only the first 20 entries,\n // and a host with a full appState would otherwise push the crash's own\n // route and digest off the end. They still win a clash of keys.\n const merged = { ...extraState };\n for (const [key, value] of Object.entries(latestState ?? {})) {\n if (!Object.prototype.hasOwnProperty.call(merged, key)) {\n merged[key] = value;\n }\n }\n return snapshot(merged);\n}\n\n/**\n * The collector, as a component uses it: on while the host wants it, off\n * again when they change their mind or the widget leaves the page.\n *\n * `take` is what a send calls. It answers with a snapshot the first time and\n * whenever something new has been recorded since, and with nothing the rest\n * of the time, so a long conversation on a healthy page sends the state once\n * rather than on every line.\n */\nexport function useDiagnostics(\n option: MedianDiagnostics | undefined,\n appState: Record<string, string | number | boolean> | undefined,\n) {\n const settings = settingsOf(option);\n // Serialised so a host building the object inline does not reinstall the\n // wrappers on every render. The same trick `user` gets in the thread hook.\n const key = settings === null ? null : JSON.stringify(settings);\n\n // Read at send time rather than closed over, since a route or a flag can\n // change between the widget mounting and somebody typing. Written after\n // the render rather than during it: nothing on screen depends on it, and a\n // ref written mid-render is a render that cannot be replayed.\n const stateRef = useRef(appState);\n // What this component last put in `latestState`, so a host that stops\n // passing a state, as on sign out, takes it back rather than leaving the\n // old one for the next crash report. A component that never passed one\n // leaves the others' alone.\n const shared = useRef<typeof appState>(undefined);\n useEffect(() => {\n stateRef.current = appState;\n if (appState !== undefined) {\n latestState = appState;\n } else if (shared.current !== undefined && latestState === shared.current) {\n latestState = undefined;\n }\n shared.current = appState;\n });\n\n const sentAt = useRef<number | null>(null);\n\n useEffect(() => {\n if (key === null) return;\n return collect(JSON.parse(key) as Settings);\n }, [key]);\n\n const take = useCallback((): ClientDiagnostics | undefined => {\n if (key === null) return undefined;\n if (sentAt.current !== null && sentAt.current === recorded)\n return undefined;\n\n sentAt.current = recorded;\n return snapshot(stateRef.current);\n }, [key]);\n\n return take;\n}\n","\"use client\";\n\nimport { useEffect } from \"react\";\nimport type { MedianUser } from \"./use-median-thread\";\n\n/**\n * What the install on this page said, kept where a crash can still reach it.\n *\n * Everything else in the SDK reads its API key and its visitor from props,\n * which is fine while there is a widget on the page to hold them. A crash is\n * the case where there is not: the boundary that catches it has replaced the\n * tree the widget was mounted in, and in the worst of them, the root layout,\n * there is nothing of the app left at all.\n *\n * Module level for the same reason the diagnostics buffer is: it outlives\n * every React tree on the page. A panel writes here as it mounts, and\n * `reportError` reads it long after that panel has been unmounted by the\n * error it is reporting.\n */\n\ntype Install = { apiKey: string; user?: MedianUser };\n\nlet install: Install | null = null;\n\nconst listeners = new Set<() => void>();\n\n/** Called by whichever widget mounted. The newest one wins. */\nexport function rememberInstall(next: Install): void {\n // Only a new key is news. The panels write here after every render, and a\n // report button that re-rendered on each of them would be a waste of a\n // subscription.\n const isNews = install?.apiKey !== next.apiKey;\n install = { apiKey: next.apiKey, ...(next.user ? { user: next.user } : {}) };\n if (!isNews) return;\n for (const listener of listeners) listener();\n}\n\n/** What the last mounted widget was given, or nothing if none ever was. */\nexport function readInstall(): Install | null {\n return install;\n}\n\n/**\n * Whether anything has been installed yet, as an external store.\n *\n * A report button rendered by an error screen can easily be on the page one\n * commit before the widget whose key it needs: a page that failed on the\n * server paints its error boundary and its layout together. Reading this\n * live is what lets that button appear the moment reporting becomes\n * possible, rather than sitting hidden until something else re-renders.\n */\nexport function subscribeInstall(listener: () => void): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\nexport function hasInstall(): boolean {\n return install !== null;\n}\n\n/**\n * Both panels call this as they mount, and again whenever the host changes\n * its mind about who is in the chair.\n *\n * Written after the render rather than during it, and on every one rather\n * than behind a dependency list, because `user` is an object a host builds\n * inline and the write is one assignment.\n */\nexport function useRememberInstall(\n apiKey: string | undefined,\n user?: MedianUser,\n): void {\n useEffect(() => {\n // Nothing to write down yet: a key from an identity route that has not\n // answered is not an install, and a crash before it answers has no key.\n if (apiKey === undefined) return;\n rememberInstall({ apiKey, ...(user ? { user } : {}) });\n });\n}\n","\"use client\";\n\n/**\n * A picture of the page, redrawn from the DOM.\n *\n * Most bug reports are a sentence about something the writer can see and the\n * reader cannot. So the page comes along: what was on the screen, laid out\n * the way it was laid out, at the moment they pressed send.\n *\n * Nothing is recorded and nothing is watched. This runs once per report, and\n * the picture goes with that one report. Hosts who would rather it did not\n * can pass `screenshot={false}` to any of the panels.\n *\n * Redrawn rather than read off the screen, because the other way round means\n * asking the browser for permission to record, and a \"share your screen\"\n * dialog in front of a bug report is a bug report nobody files. The cost is\n * fidelity: a cross-origin image, a canvas or a video can come out blank, and\n * a font that will not load is drawn in whatever the browser falls back to.\n * When the agent asks to see the screen in the chat, the customer is there\n * to say yes, so that path reads the real screen first: see\n * `screen-capture.ts`, which comes back here only when it cannot.\n *\n * It never throws and never blocks for long. A picture that cannot be taken\n * is one the report goes without.\n */\n\n/** The widest picture worth sending. Past this it is a bigger upload, not a clearer page. */\nexport const MAX_WIDTH = 1440;\n\n/**\n * How long a capture may take before the report goes without one. Generous,\n * because a page full of images spends most of it fetching them, and mean\n * enough that nobody is left watching a send that will not finish.\n */\nconst TIMEOUT_MS = 6_000;\n\n/** JPEG rather than PNG: a page is a photograph's worth of pixels, not an icon's. */\nexport const TYPE = \"image/jpeg\";\nexport const QUALITY = 0.85;\n\n/** What the picture is called wherever it lands. */\nexport const SCREENSHOT_NAME = \"screenshot.jpg\";\n\n/**\n * Where a pinned element sits on the screen, written onto it for as long as\n * the capture takes. See `pinFixed`.\n */\nconst PIN_ATTRIBUTE = \"data-median-pin\";\n\n/** An image the drawing does not wait for. See `skipUnloaded`. */\nconst SKIP_ATTRIBUTE = \"data-median-skip\";\n\n/**\n * The page as it looks right now, or null.\n *\n * Null covers every way this can go wrong: no browser, no page, a capture\n * that took too long, a library that would not load, a canvas the browser\n * refused to read. The caller sends the report either way.\n */\nexport async function capturePage(): Promise<File | null> {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return null;\n }\n const page = document.documentElement;\n if (page === null) return null;\n\n try {\n const blob = await Promise.race([\n draw(page),\n new Promise<null>((resolve) => setTimeout(() => resolve(null), TIMEOUT_MS)),\n ]);\n if (blob === null) return null;\n return new File([blob], SCREENSHOT_NAME, { type: TYPE });\n } catch (caught) {\n // A report that arrives without its picture is worth far more than one\n // that fails to send because of it, so this is the end of it.\n console.warn(\"Median: taking a screenshot for this report failed\", caught);\n return null;\n }\n}\n\n/** The viewport, drawn from the page under it. */\nasync function draw(page: HTMLElement): Promise<Blob> {\n // Loaded here rather than at the top of the file so the renderer is\n // fetched by the people who send reports, not by everybody who loads a\n // page with a widget on it.\n const { domToBlob } = await import(\"modern-screenshot\");\n\n const width = page.clientWidth || window.innerWidth;\n const height = page.clientHeight || window.innerHeight;\n // Noted before the drawing starts, and taken off the page after it,\n // however it goes.\n const unpin = pinFixed();\n const unskip = skipUnloaded();\n\n try {\n return await domToBlob(page, {\n width,\n height,\n // Their view, not the whole document: the page is drawn in full and\n // the part they are looking at is what the frame is put around. A page\n // held still by `usePageLock` has already been scrolled to nothing,\n // and is shifted by nothing here.\n style: {\n transform: `translate(${-window.scrollX}px, ${-window.scrollY}px)`,\n transformOrigin: \"top left\",\n },\n scale: Math.min(1, MAX_WIDTH / Math.max(width, 1)),\n type: TYPE,\n quality: QUALITY,\n // JPEG has no transparency, so anything clear would come out black.\n backgroundColor: pageBackground(),\n filter: notOurs,\n onCloneNode: (clone) => {\n placePinned(clone);\n dropSkipped(clone);\n },\n timeout: TIMEOUT_MS,\n features: { copyScrollbar: false },\n });\n } finally {\n unskip();\n unpin();\n }\n}\n\n/**\n * Images the picture has no use for, taken out of the wait.\n *\n * The library waits for every image on the page to finish loading before it\n * draws, for as long as the whole capture is allowed. Two kinds never\n * finish: a lazy image below the fold, which loads once somebody scrolls to\n * it, and the avatars in our own closed panel, which are lazy for the same\n * reason. One of either on the page ran every capture out of time, which on\n * median.sh was every capture.\n *\n * So for as long as the capture takes, those images say they are done. The\n * copies being drawn lose their address in `dropSkipped`, so nothing goes to\n * fetch them either. The page's own elements get their own behaviour back\n * afterwards, however the capture went.\n */\nfunction skipUnloaded(): () => void {\n const skipped: HTMLImageElement[] = [];\n for (const image of Array.from(document.images)) {\n if (image.complete) continue;\n const isOurs = image.closest(\"[data-median-widget]\") !== null;\n if (!isOurs && image.loading !== \"lazy\") continue;\n Object.defineProperty(image, \"complete\", {\n configurable: true,\n get: () => true,\n });\n Object.defineProperty(image, \"decode\", {\n configurable: true,\n value: () => Promise.resolve(),\n });\n image.setAttribute(SKIP_ATTRIBUTE, \"\");\n skipped.push(image);\n }\n return () => {\n for (const image of skipped) {\n Reflect.deleteProperty(image, \"complete\");\n Reflect.deleteProperty(image, \"decode\");\n image.removeAttribute(SKIP_ATTRIBUTE);\n }\n };\n}\n\n/** The copies of those images, emptied before anything is fetched for them. */\nfunction dropSkipped(clone: Node): void {\n if (!(clone instanceof Element)) return;\n for (const image of clone.querySelectorAll(`[${SKIP_ATTRIBUTE}]`)) {\n image.removeAttribute(SKIP_ATTRIBUTE);\n image.removeAttribute(\"srcset\");\n image.removeAttribute(\"src\");\n }\n}\n\n/**\n * The bars that float over the page, noted down before it is drawn.\n *\n * A fixed element is placed against the viewport, and the drawing has no\n * viewport to place it against: it lands wherever it falls in a page that is\n * suddenly its own full height, which for a header at the top of the screen\n * means the top of the document and out of the frame. So each one's place on\n * the screen is written onto it, read back off the copy, and set there.\n *\n * The attribute is on the host's own elements for as long as the capture\n * takes and is taken off again whatever happens.\n */\nfunction pinFixed(): () => void {\n const pinned: Element[] = [];\n for (const element of document.body.querySelectorAll(\"*\")) {\n if (window.getComputedStyle(element).position !== \"fixed\") continue;\n const box = element.getBoundingClientRect();\n if (box.width === 0 || box.height === 0) continue;\n element.setAttribute(\n PIN_ATTRIBUTE,\n [box.left, box.top, box.width, box.height].join(\",\"),\n );\n pinned.push(element);\n }\n return () => {\n for (const element of pinned) element.removeAttribute(PIN_ATTRIBUTE);\n };\n}\n\n/** Each one put back where it was on the screen, in the copy about to be drawn. */\nfunction placePinned(clone: Node): void {\n if (!(clone instanceof Element)) return;\n\n for (const element of clone.querySelectorAll(`[${PIN_ATTRIBUTE}]`)) {\n const [left, top, width, height] = (\n element.getAttribute(PIN_ATTRIBUTE) ?? \"\"\n )\n .split(\",\")\n .map(Number);\n element.removeAttribute(PIN_ATTRIBUTE);\n if (!(element instanceof HTMLElement) || Number.isNaN(top)) continue;\n\n // Still fixed, so it hangs off the page rather than off whichever\n // ancestor happens to be positioned, and moved into the part of the\n // page the frame is around.\n element.style.setProperty(\"left\", `${left + window.scrollX}px`, \"important\");\n element.style.setProperty(\"top\", `${top + window.scrollY}px`, \"important\");\n element.style.setProperty(\"right\", \"auto\", \"important\");\n element.style.setProperty(\"bottom\", \"auto\", \"important\");\n element.style.setProperty(\"width\", `${width}px`, \"important\");\n element.style.setProperty(\"height\", `${height}px`, \"important\");\n element.style.setProperty(\"margin\", \"0\", \"important\");\n }\n}\n\n/**\n * Our own widgets, left out of the picture.\n *\n * The panel they are writing in is usually sitting over the thing they are\n * writing about, and a screenshot of our own panel tells the reader nothing.\n * Every root the SDK renders carries `data-median-widget`, and excluding one\n * excludes everything inside it.\n */\nfunction notOurs(node: Node): boolean {\n return !(\n node instanceof Element && node.hasAttribute(\"data-median-widget\")\n );\n}\n\n/**\n * What the page is painted on. Body first: html is transparent by default,\n * and a host who set a colour usually set it there.\n */\nfunction pageBackground(): string {\n for (const element of [document.body, document.documentElement]) {\n if (element === null) continue;\n const color = window.getComputedStyle(element).backgroundColor;\n if (color && !isClear(color)) return color;\n }\n return \"#ffffff\";\n}\n\n/** Whether a computed colour would paint nothing. */\nfunction isClear(color: string): boolean {\n const clear = color.replace(/\\s/g, \"\");\n return clear === \"transparent\" || clear === \"rgba(0,0,0,0)\";\n}\n","/**\n * Who this browser is, as far as the widget cares: a random token minted on\n * first visit and kept in localStorage, one per API key, so coming back\n * reopens the same conversation without anyone signing in.\n *\n * A small store rather than a bare read, so `resetSessions` can tell a\n * mounted widget its token is gone and the thread re-reads through\n * `useSyncExternalStore`.\n */\n\nconst STORAGE_PREFIX = \"median:session:\";\n\n/**\n * When localStorage is blocked, the token lives here instead: gone on\n * reload, but stable within the page, which is what the reads below need.\n */\nconst inMemorySessions = new Map<string, string>();\n\nconst listeners = new Set<() => void>();\n\n/** How many times `resetSessions` has run on this page. */\nlet resetCount = 0;\n\n/** Read as a store snapshot by anything that re-reads on a reset. */\nexport function getResetCount(): number {\n return resetCount;\n}\n\nexport function subscribeSession(listener: () => void) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\n/**\n * Stable across calls on purpose: this is read as an external-store snapshot,\n * so two calls in a row must agree.\n */\nexport function getOrCreateSessionId(apiKey: string): string | null {\n if (typeof window === \"undefined\") return null;\n\n const storageKey = `${STORAGE_PREFIX}${apiKey}`;\n try {\n const existing = window.localStorage.getItem(storageKey);\n if (existing && existing.length >= 8) return existing;\n\n const created = crypto.randomUUID();\n window.localStorage.setItem(storageKey, created);\n return created;\n } catch {\n // Storage can be blocked entirely. A session that lasts one page load\n // still beats no support at all.\n const remembered = inMemorySessions.get(apiKey) ?? crypto.randomUUID();\n inMemorySessions.set(apiKey, remembered);\n return remembered;\n }\n}\n\n/**\n * Forget every token this browser holds. The next read mints a fresh one, so\n * the next message starts a conversation with nothing behind it.\n *\n * This is for sign out: the conversation belongs to the person who was in the\n * chair, not to the chair. The old thread stays in the team's inbox; it just\n * stops being reachable from this browser.\n */\nexport function resetSessions() {\n resetCount += 1;\n inMemorySessions.clear();\n\n if (typeof window !== \"undefined\") {\n try {\n const stale: string[] = [];\n for (let i = 0; i < window.localStorage.length; i += 1) {\n const key = window.localStorage.key(i);\n if (key !== null && key.startsWith(STORAGE_PREFIX)) stale.push(key);\n }\n for (const key of stale) window.localStorage.removeItem(key);\n } catch {\n // Storage was blocked, so there was nothing durable to forget.\n }\n }\n\n for (const listener of listeners) listener();\n}\n","\"use client\";\n\nimport { api } from \"@median/backend\";\nimport { useMutation, useQuery } from \"convex/react\";\nimport { useWidgetMutation } from \"./use-widget-mutation\";\nimport {\n useCallback,\n useEffect,\n useMemo,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport { useNow } from \"@/hooks/use-now\";\nimport { errorMessage } from \"@/lib/errors\";\nimport { uploadAttachments, type SentAttachment } from \"@/lib/upload\";\nimport type {\n SupportAgent,\n SupportAsk,\n SupportMessage,\n SupportPresence,\n SupportRating,\n SupportRecovery,\n SupportTickets,\n} from \"../types\";\nimport { collectContext, collectPage } from \"./context\";\nimport { restoreCustomData, takeCustomData } from \"./custom-data\";\nimport { useDiagnostics, type MedianDiagnostics } from \"./diagnostics\";\nimport { readSkippedRatings, writeSkippedRating } from \"./rating-skips\";\nimport { isUnreadReply, widgetReadReceipts } from \"./read-state\";\nimport { getOrCreateSessionId, subscribeSession } from \"./session\";\nimport type { Id } from \"@median/backend/dataModel\";\nimport type { ScreenCapture } from \"./screen-capture\";\nimport { toSupportMessage, toSupportMessages } from \"./thread-messages\";\nimport { toSupportTickets } from \"./ticket-list\";\nimport { useScreenshotAsk } from \"./use-screenshot-ask\";\nimport { useTicketView } from \"./use-ticket-view\";\nimport { useVisitorEmail } from \"./visitor-email\";\n\n/** What a host can tell us about who is in the widget. All of it optional. */\nexport type MedianUser = {\n /**\n * Your own id for this person, and that id signed from MEDIAN_KEY.\n *\n * The pair is what makes a conversation follow somebody rather than a\n * browser: sign in on a phone and the thread from the laptop is already\n * there. Both or neither, and the signing belongs on your server, because\n * whoever holds the key can claim to be any of your customers.\n *\n * Without them the widget still works. It is just this browser's thread,\n * which is what it has always been.\n */\n id?: string;\n hash?: string;\n name?: string;\n email?: string;\n /**\n * Their profile picture, so the team sees who they are talking to rather\n * than a letter. Has to be an https URL; anything else is ignored.\n */\n avatarUrl?: string;\n /** Anything else worth showing the team: plan, company, whatever. */\n metadata?: Record<string, string>;\n};\n\n/**\n * Whether the host has told us somebody is in the chair.\n *\n * Anything they say about the person counts, because only they can know it: a\n * host who passes a name and an email for whoever is signed in is as sure as\n * one who passes a signed id. An empty object is not an answer, so a session\n * that is still loading does not open the door on its way past.\n */\nexport function isSignedIn(user: MedianUser | undefined): boolean {\n if (user === undefined) return false;\n\n // Every field checked as it arrives rather than as it is typed: a host\n // spreading a session that is not there yet passes `{ name: undefined }`,\n // and a widget that threw on that would be a page broken by signing out.\n const { metadata, ...facts } = user;\n return (\n Object.values(facts).some(\n (value) => typeof value === \"string\" && value.trim().length > 0,\n ) || Object.keys(metadata ?? {}).length > 0\n );\n}\n\n/**\n * The claim, split from the rest.\n *\n * Two things go up rather than one, and the difference between them is the\n * whole of the security. A name is decoration and is taken at its word; an id\n * decides whose history the widget can reach, so it travels with its signature\n * and is believed only if that checks out. Sending them as one object would\n * blur a line the backend needs to keep sharp.\n */\nexport function splitIdentity(user: MedianUser | null): {\n facts: Omit<MedianUser, \"id\" | \"hash\">;\n claim: { userId: string; userHash: string } | undefined;\n} {\n if (user === null) return { facts: {}, claim: undefined };\n\n const { id, hash, ...facts } = user;\n return {\n facts,\n claim: id && hash ? { userId: id, userHash: hash } : undefined,\n };\n}\n\n/** What the backend made of a claim, and what to say about it. */\nconst IDENTITY_COMPLAINTS: Record<string, string> = {\n unverified:\n \"Median: the user hash passed to <MedianSupport> did not match. Sign the same string you pass as `user.id` with the Median key that produced this widget id. Until it matches, this visitor gets a conversation of their own on every device.\",\n unconfigured:\n \"Median: a user hash was passed to <MedianSupport>, but its widget id and hash came from different Median keys. Derive both from the same key.\",\n};\n\n/**\n * The live conversation behind the widget: a session token for this browser,\n * the visitor's tickets and the one on screen as Convex sees them, and a\n * send that uploads files first. The widget itself stays dumb; this is the\n * seam the SDK promised it.\n */\nexport function useMedianThread({\n apiKey,\n user,\n telemetry = true,\n screenshot = true,\n isLocked = false,\n diagnostics,\n appState,\n requireEmail = true,\n messagePreview = true,\n onSignIn,\n}: {\n apiKey: string;\n user?: MedianUser;\n /** Whether to read the browser's own answers along with a message. */\n telemetry?: boolean;\n /**\n * Whether the agent may take a picture of this page when it asks to see\n * it. Nothing is taken until it asks, and a page that says no answers the\n * ask with nothing, so the agent knows to ask in words instead.\n */\n screenshot?: boolean;\n /**\n * Nobody may write yet, so nothing is read either: a visitor who has to\n * sign in first is not shown a thread they cannot answer, and the widget\n * holds no conversation open behind the invitation to sign in.\n */\n isLocked?: boolean;\n /** What to watch going wrong on the page, if the host wants any of it. */\n diagnostics?: MedianDiagnostics;\n /** The host's own state, read at send time. */\n appState?: Record<string, string | number | boolean>;\n /**\n * Whether this page asks for an email before a visitor's first message.\n * The team answers through `config.requireEmail`, and both have to agree:\n * either side can waive the question.\n */\n requireEmail?: boolean;\n /**\n * Whether this page lets an unread reply be previewed on the closed\n * button. The team answers through `config.messagePreview`, and both have\n * to agree, exactly as the email question is settled: either side can keep\n * the corner quiet.\n */\n messagePreview?: boolean;\n /**\n * Where the host's sign in lives. The history offers it beside the\n * emailed code, for somebody the host has not signed in yet.\n */\n onSignIn?: () => void;\n}) {\n // The session token is external state (localStorage), read as a store: the\n // server snapshot is null, so SSR and the first client paint agree, and the\n // browser's token takes over from there. Subscribed so `reset()` lands\n // here, mints a new token, and the thread starts over.\n const sessionId = useSyncExternalStore(\n subscribeSession,\n useCallback(() => getOrCreateSessionId(apiKey), [apiKey]),\n () => null,\n );\n\n const config = useQuery(api.widget.config, { apiKey });\n\n // The answer to the composer's question, if this browser has given one.\n const [visitorEmail, setVisitorEmail] = useVisitorEmail(apiKey);\n\n /**\n * Whether the composer asks who they are before it takes a message. All\n * three have to say so: the page's own rule, the team's switch, and the\n * fact that nobody here knows an address already. A host who passes\n * `user.email` has answered on their visitor's behalf; the question never\n * comes up. Before the config lands there is no team answer yet, and the\n * panel is still shimmering besides, so the default holds.\n */\n const askForEmail =\n requireEmail &&\n (config?.requireEmail ?? true) &&\n !visitorEmail &&\n !(typeof user?.email === \"string\" && user.email.trim().length > 0);\n\n const ticketList = useQuery(\n api.widget.tickets,\n sessionId === null || isLocked ? \"skip\" : { apiKey, sessionId },\n );\n const view = useTicketView({\n apiKey,\n sessionId,\n isLocked,\n tickets: ticketList,\n });\n const thread = view.thread;\n const send = useWidgetMutation(api.widget.sendV2);\n const sendTyping = useMutation(api.widget.setTyping);\n const sendPresence = useMutation(api.widget.presence);\n const rateConversation = useWidgetMutation(api.widget.rateV2);\n const identify = useWidgetMutation(api.widget.identifyV2);\n const generateUploadUrl = useWidgetMutation(api.widget.generateUploadUrlV2);\n const answerScreenshot = useWidgetMutation(api.widget.answerScreenshotV2);\n const requestHistoryCode = useMutation(api.widget.requestHistoryCode);\n const verifyHistoryCode = useMutation(api.widget.verifyHistoryCode);\n\n /**\n * The picture the agent asked for, sent up, or why there is none. An\n * upload that fails turns into no picture: the agent is waiting, and \"no\n * picture\" is an answer it knows what to do with.\n */\n const askedOn = thread?.conversationId;\n const answerAsk = useCallback(\n async (key: string, capture: ScreenCapture) => {\n if (sessionId === null) return;\n let attachment: SentAttachment | undefined;\n if (capture.kind === \"taken\") {\n try {\n [attachment] = await uploadAttachments(\n () => generateUploadUrl({ apiKey, sessionId }),\n [capture.file],\n );\n } catch (caught) {\n console.warn(\n \"Median: the picture the agent asked for could not be uploaded\",\n caught,\n );\n }\n }\n await answerScreenshot({\n apiKey,\n sessionId,\n key,\n ...(askedOn === undefined ? {} : { conversationId: askedOn }),\n ...(attachment === undefined ? {} : { attachment }),\n ...(capture.kind === \"declined\" ? { declined: true } : {}),\n });\n },\n [apiKey, sessionId, askedOn, generateUploadUrl, answerScreenshot],\n );\n\n // Who they are can change without them saying a word: they sign in on the\n // host's page while the thread a teammate is reading still says nobody.\n //\n // Serialised and parsed back, which looks like a detour and is the point.\n // A host almost always builds this object inline, so it is a new one on\n // every render and a new identity to any effect watching it; the string is\n // the same until the facts in it are not.\n const identityKey = user ? JSON.stringify(user) : null;\n const identity = useMemo(\n () =>\n identityKey === null ? null : (JSON.parse(identityKey) as MedianUser),\n [identityKey],\n );\n\n useEffect(() => {\n if (sessionId === null || identity === null) return;\n const { facts, claim } = splitIdentity(identity);\n\n // Best effort on the facts: they ride along with the next message anyway,\n // which is exactly where they were before this existed. A signature that\n // was offered and refused is worth saying out loud though, because the\n // host is the only one who can fix it and nothing visibly breaks.\n identify({\n apiKey,\n sessionId,\n user: facts,\n ...(claim ? { identity: claim } : {}),\n })\n .then((status) => {\n const complaint = IDENTITY_COMPLAINTS[status];\n if (complaint) console.error(complaint);\n })\n .catch(() => {});\n }, [apiKey, sessionId, identity, identify]);\n\n // A fast clock only while a teammate's dots are on screen, so they expire\n // on time without the widget re-rendering every second forever.\n const hasLiveDots =\n thread?.messages.some(\n (message) => message.pending && message.typingAt !== null,\n ) ?? false;\n const now = useNow(hasLiveDots ? 1000 : 60_000);\n\n const takeDiagnostics = useDiagnostics(diagnostics, appState);\n\n const [isSending, setIsSending] = useState(false);\n const [sendError, setSendError] = useState<string | null>(null);\n\n const agent: SupportAgent | null = useMemo(\n () =>\n config === undefined\n ? null\n : {\n id: \"median-agent\",\n name: config.agent.name,\n kind: \"ai\",\n avatarUrl: config.agent.avatarUrl,\n },\n [config],\n );\n\n const messages: SupportMessage[] = useMemo(\n () =>\n !thread || agent === null\n ? []\n : toSupportMessages(thread.messages, agent, now),\n [thread, agent, now],\n );\n\n // The faces in the corner: the AI, then anyone human who has spoken.\n const presence: SupportPresence | null = useMemo(() => {\n if (config === undefined || agent === null) return null;\n\n const humans = new Map<string, SupportAgent>();\n for (const message of messages) {\n if (message.agent && message.agent.kind === \"human\") {\n humans.set(message.agent.id, message.agent);\n }\n }\n\n return {\n name: config.orgName,\n status: \"Support online\",\n team: [...[...humans.values()].slice(-2), agent],\n };\n }, [config, agent, messages]);\n\n /**\n * Replies they have not looked at, on every ticket. Only the other side's,\n * and only ones that have actually been written: a bubble still holding\n * dots is not news. The ticket on screen counts from its own thread, which\n * moves the moment a reply lands; the rest count from the list.\n */\n const unreadHere = useMemo(\n () => thread?.messages.filter(isUnreadReply).length ?? 0,\n [thread],\n );\n const unreadCount =\n unreadHere +\n (ticketList?.tickets ?? [])\n .filter((ticket) => ticket.conversationId !== view.currentId)\n .reduce((sum, ticket) => sum + ticket.unreadCount, 0);\n\n /**\n * The newest of those, for the card on the closed button, or null when\n * either side has turned the card off. The count on the button is not\n * affected: this decides whether the words are shown, not whether the\n * reply is announced.\n *\n * Before the config lands there is no team answer, and nothing has been\n * read yet either, so the default holds and nothing is shown regardless.\n */\n const newestUnread = ticketList?.latestUnread ?? null;\n\n // The newest unread reply when it landed on a ticket other than the one on\n // screen, whatever the preview settings say: it still deserves the sound.\n const replyElsewhere = useMemo(\n () =>\n newestUnread !== null &&\n newestUnread.conversationId !== view.currentId &&\n agent !== null\n ? toSupportMessage(newestUnread, agent)\n : null,\n [newestUnread, view.currentId, agent],\n );\n const latestUnread = useMemo(\n () =>\n messagePreview &&\n (config?.messagePreview ?? true) &&\n newestUnread !== null &&\n agent !== null\n ? toSupportMessage(newestUnread, agent)\n : null,\n [messagePreview, config, newestUnread, agent],\n );\n\n // Asking how it went, once, on a conversation that is over.\n //\n // Three things decide whether the card is up, and all three are read\n // rather than remembered: the thread is resolved, nobody has answered yet,\n // and they have not waved it away. Pressing a star writes a rating, which\n // would otherwise take the card off screen mid-sentence, so a pick made\n // here keeps it open until they are finished with it.\n const [picked, setPicked] = useState<{\n conversationId: string;\n stars: number;\n } | null>(null);\n // Read from storage rather than started empty: a skip has to outlive the\n // remount that clicking a link on the host's site costs us.\n const [skipped, setSkipped] = useState<ReadonlySet<string>>(() =>\n readSkippedRatings(apiKey),\n );\n\n const conversationId = thread?.conversationId ?? null;\n const savedRating = thread?.rating ?? null;\n const isMine = picked?.conversationId === conversationId;\n\n const rating: SupportRating | null =\n thread?.status === \"resolved\" &&\n conversationId !== null &&\n !skipped.has(conversationId) &&\n (savedRating === null || isMine)\n ? {\n stars: isMine ? picked.stars : (savedRating?.stars ?? null),\n submit: async (stars, feedback) => {\n // Never null by the time a thread has been read, and a quiet\n // return here would read on screen as a rating that landed.\n if (sessionId === null) throw new Error(\"No session yet.\");\n setPicked({ conversationId, stars });\n await rateConversation({\n apiKey,\n sessionId,\n conversationId,\n stars,\n ...(feedback ? { feedback } : {}),\n });\n },\n skip: () => {\n writeSkippedRating(apiKey, conversationId);\n setSkipped((current) => new Set(current).add(conversationId));\n },\n }\n : null;\n\n async function handleSend(body: string, files: File[]) {\n if (sessionId === null || isSending) return;\n\n setSendError(null);\n setIsSending(true);\n view.claim();\n // Whatever the host attached since the last send rides this message.\n // Taken outside the try so a failed upload hands it back too.\n const custom = takeCustomData();\n try {\n const attachments = await uploadAttachments(\n () => generateUploadUrl({ apiKey, sessionId }),\n files,\n );\n\n // Read now rather than on mount: all of it is wanted as \"what was true\n // when they wrote\", and a widget can sit closed on a page for an hour\n // before anybody says anything.\n const context = telemetry ? collectContext() : undefined;\n const page = telemetry ? collectPage() : undefined;\n\n // Nothing the first time a healthy page sends twice in a row: the\n // snapshot only travels when something new has gone wrong since.\n const collected = takeDiagnostics();\n\n // The same split as above, and this is the call that needs it most: a\n // visitor's first message is what creates them, so it is the only chance\n // to put them straight onto the account they are already signed in to.\n const { facts, claim } = splitIdentity(user ?? null);\n\n // The address typed at the composer's question rides along the same way\n // a host's facts do, but never over one the host already knows.\n const withEmail =\n facts.email || !visitorEmail\n ? facts\n : { ...facts, email: visitorEmail };\n\n // The ticket on screen, or a new one from a fresh start. On screen,\n // not the one a pick is still fetching: the box belongs to what they\n // are looking at.\n const target = view.currentId as Id<\"conversations\"> | null;\n const conversationId = await send({\n apiKey,\n sessionId,\n body,\n ...(target === null ? { startNew: true } : { conversationId: target }),\n ...(Object.keys(withEmail).length > 0 ? { user: withEmail } : {}),\n ...(claim ? { identity: claim } : {}),\n ...(context ? { context } : {}),\n ...(page ? { page } : {}),\n ...(attachments.length > 0 ? { attachments } : {}),\n ...(collected ? { diagnostics: collected } : {}),\n ...(custom ? { customData: custom } : {}),\n });\n // A fresh start just became a ticket. The draft stays until its\n // thread is on screen, so the words never vanish before they land.\n if (target === null) await view.landed(conversationId);\n } catch (caught) {\n // Back in the queue, so the retry carries them like it keeps the draft.\n if (custom) restoreCustomData(custom);\n console.error(\"Sending from the support widget failed\", caught);\n setSendError(errorMessage(caught));\n // Rethrown so the composer keeps the draft: a failed send should cost\n // a retry, never a retype.\n throw caught;\n } finally {\n setIsSending(false);\n }\n }\n\n function handleTypingChange(isTyping: boolean) {\n // Nothing to show dots in before a fresh start's first message.\n if (sessionId === null || view.currentId === null) return;\n // Best effort: dots that fail to show are not worth an error.\n sendTyping({\n apiKey,\n sessionId,\n isTyping,\n conversationId: view.currentId as Id<\"conversations\">,\n }).catch(() => {});\n }\n\n // An emailed code, for a browser that lost its history, and the host's\n // sign in beside it. Not for somebody the host signs in: their history\n // follows the sign-in.\n const hasClaim = splitIdentity(user ?? null).claim !== undefined;\n const byCode: SupportRecovery | null = useMemo(\n () =>\n sessionId === null || hasClaim || ticketList?.canRecover !== true\n ? null\n : {\n request: async (email) => {\n await requestHistoryCode({ apiKey, sessionId, email });\n },\n verify: async (email, code) =>\n await verifyHistoryCode({ apiKey, sessionId, email, code }),\n },\n [\n apiKey,\n sessionId,\n hasClaim,\n ticketList?.canRecover,\n requestHistoryCode,\n verifyHistoryCode,\n ],\n );\n // Only while nobody is signed in: a host that names somebody without\n // signing them has signed them in already, as far as the button goes.\n const canSignIn =\n onSignIn !== undefined &&\n ticketList?.canSignIn === true &&\n !isSignedIn(user);\n const recovery: SupportRecovery | null =\n byCode !== null && canSignIn ? { ...byCode, signIn: onSignIn } : byCode;\n\n const tickets: SupportTickets = {\n list: toSupportTickets(ticketList, now),\n currentId: view.currentId,\n viewKey: view.viewKey,\n isOpening: view.isOpening,\n open: view.open,\n startNew: view.startNew,\n recovery,\n };\n\n /**\n * The panel is visible right now. Whatever renders the thread calls this\n * on open and every so often after, and it is what stands between a\n * customer and an email copy of a reply they are already reading.\n */\n // A receipt must not advance past a reply that is still streaming. The\n // callback changes as soon as a completed reply lands, between heartbeats.\n // Keep the callback stable across receipt updates so acknowledging a reply\n // does not itself trigger another heartbeat.\n const readsKey = JSON.stringify(\n widgetReadReceipts(thread?.messages ?? [], now),\n );\n const reads = useMemo(\n () => JSON.parse(readsKey) as ReturnType<typeof widgetReadReceipts>,\n [readsKey],\n );\n const reportPresence = useCallback(() => {\n if (sessionId === null || isLocked) return;\n // The next heartbeat retries a missed receipt while the panel is visible.\n sendPresence({\n apiKey,\n sessionId,\n reads,\n }).catch((caught) => {\n console.warn(\"Median: support read status could not be synced\", caught);\n });\n }, [apiKey, sessionId, isLocked, sendPresence, reads]);\n\n // Nearly always null. Its own value rather than a message in the list,\n // because it belongs to a tool run and not to anything anybody said.\n const ask: SupportAsk | null = thread?.direction ?? null;\n\n // Nearly always null too: the agent asking to see their screen. Offered\n // in the panel as a button to share it and one to say no, and never shown\n // as a message of its own; the picture turns up in the thread as the\n // answer.\n const screenRequest = useScreenshotAsk({\n ask: thread?.screenshotAsk ?? null,\n enabled: screenshot,\n answer: answerAsk,\n });\n\n return {\n presence,\n messages,\n /** Something a tool run asked of this page, with no message to ride on. */\n ask,\n /** The agent asking to see their screen, as an offer to share it. */\n screenRequest,\n /**\n * The live thread, or null before there is one. Read by whatever has to\n * tell \"the next thing in this conversation\" from \"a different\n * conversation altogether\", which is what signing in produces.\n */\n conversationId,\n /**\n * Still finding out whether this browser has a conversation already.\n * Locked, there is only the host's name to wait for, and the invitation\n * to sign in names them.\n */\n isLoading:\n config === undefined ||\n (!isLocked && (sessionId === null || view.isLoading)),\n isSending,\n /**\n * Whether the composer asks for an email before a first message. The\n * page's rule and the team's switch both have to want it, and nobody\n * here may know an address already.\n */\n askForEmail,\n /** What the composer's question does with a good answer. */\n setVisitorEmail,\n /** The agent asked for a person, and nobody has answered yet. */\n awaitingHuman: thread?.awaitingHuman ?? false,\n /** The question a finished conversation asks, while it is worth asking. */\n rating,\n /** Replies that arrived while they were not looking. */\n unreadCount,\n /** The newest of those, for the card on the closed button. */\n latestUnread,\n /** Say the panel is on screen, so replies are not also emailed. */\n reportPresence,\n /** The history, and moving between tickets. */\n tickets,\n /** The ticket holding the newest unread reply, if any. */\n latestUnreadTicketId: newestUnread?.conversationId ?? null,\n /** That reply, when it is on a ticket other than the one on screen. */\n replyElsewhere,\n /** The ticket on screen is answered by email, not from here. */\n continuesByEmail: thread?.channel === \"email\",\n sendError,\n send: handleSend,\n setTyping: handleTypingChange,\n };\n}\n","\"use client\";\n\nimport { unwrapMutationResult } from \"@median/backend/mutationResult\";\nimport { useMutation } from \"convex/react\";\nimport type { FunctionReference, OptionalRestArgs } from \"convex/server\";\nimport { useCallback } from \"react\";\n\n/**\n * A widget endpoint that hands a refusal back as a value, so its rate\n * limits stick even when the work fails. This throws that refusal the way\n * a plain mutation would, so callers catch it like any other error.\n */\nexport function useWidgetMutation<M extends FunctionReference<\"mutation\">>(\n reference: M,\n) {\n const mutation = useMutation(reference);\n return useCallback(\n async (...args: OptionalRestArgs<M>) =>\n unwrapMutationResult(await mutation(...args)),\n [mutation],\n );\n}\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\n/**\n * The current time, refreshed on an interval, so relative timestamps (\"2m\")\n * drift forward without anyone touching the page. One state per caller keeps\n * this dumb; the inbox only needs it once.\n */\nexport function useNow(intervalMs = 30_000): number {\n const [now, setNow] = useState(() => Date.now());\n\n useEffect(() => {\n const timer = setInterval(() => setNow(Date.now()), intervalMs);\n return () => clearInterval(timer);\n }, [intervalMs]);\n\n return now;\n}\n","/**\n * Which finished conversations somebody has already waved the rating card\n * away on.\n *\n * Beside the session token and the read marker, and kept for the same\n * reason: a host's site is several pages, so the widget is remounted every\n * time somebody clicks a link. Held in memory, a skip would last until the\n * next click and the card would be back. Skipping is a no thanks, and asking\n * again is how a no thanks turns into a nuisance.\n *\n * One entry per API key, like the others, and capped: a browser that has\n * skipped a hundred conversations has no reason to carry all hundred ids\n * around, and the oldest of them are threads nobody will see again anyway.\n */\n\nconst STORAGE_PREFIX = \"median:rating-skips:\";\n\n/** How many are worth remembering. Past this, the oldest fall off. */\nconst MAX_REMEMBERED = 20;\n\n/** Where they live when localStorage is blocked. Lasts the page load. */\nconst inMemory = new Map<string, string[]>();\n\nfunction read(apiKey: string): string[] {\n if (typeof window === \"undefined\") return [];\n\n try {\n const stored = window.localStorage.getItem(`${STORAGE_PREFIX}${apiKey}`);\n if (stored === null) return [];\n const parsed: unknown = JSON.parse(stored);\n return Array.isArray(parsed)\n ? parsed.filter((one): one is string => typeof one === \"string\")\n : [];\n } catch {\n // Blocked, or somebody put something else under our key.\n return inMemory.get(apiKey) ?? [];\n }\n}\n\n/** The conversations this browser has already said no thanks to. */\nexport function readSkippedRatings(apiKey: string): ReadonlySet<string> {\n return new Set(read(apiKey));\n}\n\n/** One more no thanks, remembered across the pages of a host's site. */\nexport function writeSkippedRating(\n apiKey: string,\n conversationId: string,\n): void {\n if (typeof window === \"undefined\") return;\n\n const next = [\n ...read(apiKey).filter((one) => one !== conversationId),\n conversationId,\n ].slice(-MAX_REMEMBERED);\n\n inMemory.set(apiKey, next);\n try {\n window.localStorage.setItem(\n `${STORAGE_PREFIX}${apiKey}`,\n JSON.stringify(next),\n );\n } catch {\n // Blocked, so the in-memory copy above is all there is. It still holds\n // for as long as they are on the page they skipped it from.\n }\n}\n","import type { Id } from \"../_generated/dataModel\";\nimport { TYPING_TTL_MS } from \"./support\";\n\ntype ReadMessage = {\n _id: Id<\"conversationMessages\">;\n conversationId: Id<\"conversations\">;\n sender: \"agent\" | \"visitor\";\n pending: boolean;\n isRead: boolean;\n typingAt: number | null;\n body: string;\n attachments: readonly unknown[];\n};\n\nexport function isUnreadReply(message: ReadMessage): boolean {\n return message.sender === \"agent\" && !message.pending && !message.isRead;\n}\n\n/** A separate receipt for each conversation; never read past a streaming reply. */\nexport function widgetReadReceipts(\n messages: readonly ReadMessage[],\n now: number,\n) {\n const receipts = new Map<\n Id<\"conversations\">,\n {\n conversationId: Id<\"conversations\">;\n messageId?: Id<\"conversationMessages\">;\n }\n >();\n const blocked = new Set<Id<\"conversations\">>();\n\n for (const message of messages) {\n const { conversationId } = message;\n if (!receipts.has(conversationId)) {\n receipts.set(conversationId, { conversationId });\n }\n if (blocked.has(conversationId)) continue;\n if (message.pending) {\n if (\n message.typingAt === null ||\n now - message.typingAt <= TYPING_TTL_MS\n ) {\n blocked.add(conversationId);\n }\n continue;\n }\n if (\n message.sender === \"agent\" &&\n (message.body.trim() || message.attachments.length > 0)\n ) {\n receipts.set(conversationId, { conversationId, messageId: message._id });\n }\n }\n\n return [...receipts.values()];\n}\n","/**\n * Timestamps, written the way the inbox and the widget say them. All of it\n * runs on the client with a real clock, so the backend only ever stores\n * numbers.\n */\n\nconst MINUTE = 60_000;\nconst HOUR = 60 * MINUTE;\nconst DAY = 24 * HOUR;\n\n/**\n * A time of day, wherever one is printed: beside a message, and under a\n * visitor's name for the clock where they are.\n *\n * Twelve hour on purpose rather than by the reader's locale. Two clocks can\n * sit on one screen here, one for the message and one for the customer, and\n * they came from different formatters; leaving the shape to the locale meant\n * they agreed only by luck, and a reader in a country that counts to\n * twenty-four saw a screen that could not decide.\n *\n * The rest is still the reader's. Which characters mark the afternoon is a\n * question their locale answers better than we can.\n */\nexport const CLOCK_FORMAT = {\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true,\n} as const satisfies Intl.DateTimeFormatOptions;\n\n/** \"3:58 PM\", for the line beside a message. */\nexport function formatClockTime(timestamp: number): string {\n return new Date(timestamp).toLocaleTimeString([], CLOCK_FORMAT);\n}\n\n/** \"now\", \"2m\", \"3h\", \"2d\", then a date, for a queue row. */\nexport function formatTimeAgo(timestamp: number, now: number): string {\n const elapsed = now - timestamp;\n if (elapsed < MINUTE) return \"now\";\n if (elapsed < HOUR) return `${Math.floor(elapsed / MINUTE)}m`;\n if (elapsed < DAY) return `${Math.floor(elapsed / HOUR)}h`;\n if (elapsed < 7 * DAY) return `${Math.floor(elapsed / DAY)}d`;\n return new Date(timestamp).toLocaleDateString([], {\n month: \"short\",\n day: \"numeric\",\n });\n}\n\n/**\n * \"3h\", \"12m\", \"under a minute\": how long is left before a deadline, in the\n * same shorthand the queue counts back in. Anything already past reads as\n * the least it can be, since a clock a little behind the server's is not\n * news anybody can act on.\n */\nexport function formatTimeLeft(until: number, now: number): string {\n const left = until - now;\n if (left < MINUTE) return \"under a minute\";\n if (left < HOUR) return `${Math.floor(left / MINUTE)}m`;\n if (left < DAY) return `${Math.floor(left / HOUR)}h`;\n return `${Math.floor(left / DAY)}d`;\n}\n\n/**\n * \"just now\", \"2m ago\", \"Mar 3\", for a line about when something last ran.\n *\n * The queue's own shorthand reads as a column of times, which is right beside\n * a conversation and wrong in a sentence: \"synced 2m\" is not a thing anybody\n * says. Same clock, said out loud.\n */\nexport function formatSyncedAgo(timestamp: number, now: number): string {\n const ago = formatTimeAgo(timestamp, now);\n if (ago === \"now\") return \"just now\";\n return /^\\d/.test(ago) ? `${ago} ago` : ago;\n}\n\n/**\n * \"5 minutes ago\", \"yesterday\": a moment said relative to now, in full\n * words, for a detail pane where there is room to say it properly.\n *\n * Never \"in 12 seconds\". A clock that ticks every half minute, or a server\n * a little ahead of this one, hands over moments that are later than `now`,\n * and anything already recorded has happened, so those read as \"now\".\n */\nexport function formatRelative(timestamp: number, now: number): string {\n const elapsed = Math.min(0, timestamp - now);\n const words = new Intl.RelativeTimeFormat([], { numeric: \"auto\" });\n const steps: [number, Intl.RelativeTimeFormatUnit][] = [\n [DAY, \"day\"],\n [HOUR, \"hour\"],\n [MINUTE, \"minute\"],\n ];\n for (const [size, unit] of steps) {\n if (-elapsed >= size) return words.format(Math.round(elapsed / size), unit);\n }\n return words.format(Math.round(elapsed / 1000), \"second\");\n}\n\n/** Minutes since, for sorting rows the way the demo data did. */\nexport function minutesSince(timestamp: number, now: number): number {\n return Math.max(0, Math.floor((now - timestamp) / MINUTE));\n}\n\n/** \"March 2023\", for the customer since row and past conversations. */\nexport function formatMonthYear(timestamp: number): string {\n return new Date(timestamp).toLocaleDateString([], {\n month: \"long\",\n year: \"numeric\",\n });\n}\n\n/** \"Oct 8\", with the year only when it is not this one. */\nexport function formatShortDate(timestamp: number, now = Date.now()): string {\n const date = new Date(timestamp);\n return date.toLocaleDateString([], {\n month: \"short\",\n day: \"numeric\",\n year:\n date.getFullYear() === new Date(now).getFullYear()\n ? undefined\n : \"numeric\",\n });\n}\n\n/** \"Oct 8, 2026\", for a date that is read on its own, like an invoice's. */\nexport function formatFullDate(timestamp: number): string {\n return new Date(timestamp).toLocaleDateString([], {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n });\n}\n\n/**\n * \"Tue, Sep 23, 2026, 2:05:22 PM GMT+8\": the whole moment with its zone,\n * for a hint over a time that a row says more briefly.\n */\nexport function formatExactMoment(timestamp: number): string {\n return new Date(timestamp).toLocaleString([], {\n ...CLOCK_FORMAT,\n second: \"2-digit\",\n weekday: \"short\",\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n timeZoneName: \"short\",\n });\n}\n\nfunction startOfDay(timestamp: number): number {\n const date = new Date(timestamp);\n date.setHours(0, 0, 0, 0);\n return date.getTime();\n}\n\n/** \"Today\", \"Yesterday\", \"Monday\", then a date, for a thread divider. */\nexport function formatDayLabel(timestamp: number, now: number): string {\n const days = Math.round((startOfDay(now) - startOfDay(timestamp)) / DAY);\n if (days <= 0) return \"Today\";\n if (days === 1) return \"Yesterday\";\n if (days < 7) {\n return new Date(timestamp).toLocaleDateString([], { weekday: \"long\" });\n }\n return new Date(timestamp).toLocaleDateString([], {\n month: \"short\",\n day: \"numeric\",\n });\n}\n\n/** How many whole days apart two timestamps land, by the calendar. */\nfunction daysApart(from: number, to: number): number {\n return Math.round((startOfDay(to) - startOfDay(from)) / DAY);\n}\n\n/**\n * \"9:00 AM\", \"tomorrow, 9:00 AM\", \"Mon, 9:00 AM\", \"Mar 4\": when a snooze is\n * up, as short as it can be said without leaving anyone guessing which day.\n */\nexport function formatSnoozeAt(until: number, now: number): string {\n const days = daysApart(now, until);\n if (days >= 7) {\n return new Date(until).toLocaleDateString([], {\n month: \"short\",\n day: \"numeric\",\n });\n }\n\n const time = formatClockTime(until);\n if (days <= 0) return time;\n if (days === 1) return `tomorrow, ${time}`;\n return `${new Date(until).toLocaleDateString([], { weekday: \"short\" })}, ${time}`;\n}\n\n/** Whether two timestamps land on different days, so a thread can divide. */\nexport function isNewDay(previous: number | undefined, next: number): boolean {\n if (previous === undefined) return true;\n return startOfDay(previous) !== startOfDay(next);\n}\n\n/**\n * Rows under the local day they happened on, in the order they came, for a\n * list cut into days. The rows arrive sorted by time, so a day is a run of\n * neighbours and a row never moves out of its place.\n */\nexport function groupByDay<T>(\n rows: readonly T[],\n at: (row: T) => number,\n): { start: number; rows: T[] }[] {\n const days: { start: number; rows: T[] }[] = [];\n for (const row of rows) {\n const start = startOfDay(at(row));\n const last = days.at(-1);\n if (last?.start === start) last.rows.push(row);\n else days.push({ start, rows: [row] });\n }\n return days;\n}\n","import type { api } from \"@median/backend\";\nimport { TYPING_TTL_MS } from \"@median/backend/support\";\nimport type { FunctionReturnType } from \"convex/server\";\nimport { attachmentKindOf, formatFileSize } from \"@/components/attachments\";\nimport { formatClockTime, formatDayLabel, isNewDay } from \"@/lib/time\";\nimport type { SupportAgent, SupportMessage } from \"../types\";\n\n/** A ticket as the backend reads it. */\nexport type ThreadData = NonNullable<FunctionReturnType<typeof api.widget.thread>>;\n\n/** One message as the backend reads it. */\nexport type ThreadMessage = ThreadData[\"messages\"][number];\n\n/**\n * The backend's messages as the panel draws them: times formatted, a day\n * printed where one starts, and a teammate's dots dropped once the browser\n * on the other end has gone quiet.\n */\nexport function toSupportMessages(\n rows: readonly ThreadMessage[],\n agent: SupportAgent,\n now: number,\n): SupportMessage[] {\n const out: SupportMessage[] = [];\n let previousAt: number | undefined;\n\n for (const message of rows) {\n // A typing bubble whose teammate stopped refreshing it: the browser on\n // the other end went away, so the dots go too.\n if (\n message.pending &&\n message.typingAt !== null &&\n now - message.typingAt > TYPING_TTL_MS\n ) {\n continue;\n }\n\n out.push({\n ...toSupportMessage(message, agent),\n dayLabel: isNewDay(previousAt, message.createdAt)\n ? formatDayLabel(message.createdAt, now)\n : undefined,\n });\n previousAt = message.createdAt;\n }\n\n return out;\n}\n\n/** One message, without the day it may start. */\nexport function toSupportMessage(\n message: ThreadMessage,\n agent: SupportAgent,\n): SupportMessage {\n return {\n id: message._id,\n sender: message.sender,\n body: message.body,\n time: formatClockTime(message.createdAt),\n sentAt: message.createdAt,\n agent:\n message.agent === null\n ? undefined\n : message.agent.kind === \"ai\"\n ? agent\n : {\n id: `human-${message.agent.name}`,\n name: message.agent.name,\n kind: \"human\",\n avatarUrl: message.agent.avatarUrl,\n },\n pending: message.pending || undefined,\n attachments:\n message.attachments.length > 0\n ? message.attachments.map((attachment) => {\n const kind = attachmentKindOf(attachment.type);\n return {\n id: attachment.id,\n kind,\n name: attachment.name,\n size: formatFileSize(attachment.size),\n // Only what can actually be shown small. A text file had\n // one of these too, which read as \"there is a thumbnail\n // here\" everywhere it was checked.\n previewUrl:\n kind === \"file\" ? undefined : (attachment.url ?? undefined),\n url: attachment.url ?? undefined,\n };\n })\n : undefined,\n navigateTo: message.navigateTo ?? undefined,\n highlight: message.highlight ?? undefined,\n // Optional in practice: a published widget can meet a backend from\n // before chips, and a missing list is no chips rather than a crash.\n sources: message.sources?.length ? message.sources : undefined,\n };\n}\n","import { formatClockTime, formatShortDate, isNewDay } from \"@/lib/time\";\nimport type { SupportTicket } from \"../types\";\nimport type { TicketList } from \"./use-ticket-view\";\n\n/**\n * The backend's tickets as the history lists them: a clock time for one\n * that moved today, and a date for anything older.\n */\nexport function toSupportTickets(\n list: TicketList | undefined,\n now: number,\n): SupportTicket[] {\n return (list?.tickets ?? []).map((ticket) => ({\n id: ticket.conversationId,\n threadId: ticket.threadId,\n subject: ticket.subject,\n preview: ticket.preview,\n time: isNewDay(ticket.lastMessageAt, now)\n ? formatShortDate(ticket.lastMessageAt, now)\n : formatClockTime(ticket.lastMessageAt),\n status: ticket.status,\n channel: ticket.channel,\n unreadCount: ticket.unreadCount,\n }));\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { SupportScreenRequest, SupportScreenshotAsk } from \"../types\";\nimport { captureScreen, type ScreenCapture } from \"./screen-capture\";\n\n/**\n * Answering the agent when it asks to see the customer's screen.\n *\n * Nothing is taken until the customer says so. The ask becomes an offer in\n * the panel, a button to share and one to say no, and the picture is taken\n * from the press of the first: the browser only lets a page ask to see the\n * screen while a click is fresh, and the customer is the one to decide.\n * Nothing is captured on a first message, and nothing on a schedule.\n *\n * The ask sits on the live thread until it is answered, and goes from it the\n * moment it is, which is what folds the offer away. It is offered on a first\n * look as well as on a change, because an ask is only on the thread while it\n * is open, and one open when this page mounted is one the last page never\n * got to. A second browser on the same account offers it too; the backend\n * takes the first answer and tells the other it was late.\n *\n * A host that passed `screenshot={false}` offers nothing and answers at\n * once, with nothing. That is what lets the agent stop waiting and ask in\n * words, rather than sit on a promise the page will never keep.\n */\nexport function useScreenshotAsk({\n ask,\n enabled,\n answer,\n}: {\n /** The open ask on the thread, or null nearly always. */\n ask: SupportScreenshotAsk | null;\n /** Whether this page allows a picture of itself to be taken. */\n enabled: boolean;\n /** Sends the picture, or why there is none, for the given key. */\n answer: (key: string, capture: ScreenCapture) => Promise<void>;\n}): SupportScreenRequest | null {\n /** Asks this page has answered for a host that allows no pictures. */\n const refused = useRef<Set<string>>(new Set());\n\n /**\n * The ask a press has taken up, while its answer is on the way. The offer\n * stays up, held, until the thread drops the ask, so it folds away once\n * rather than vanishing and coming back.\n */\n const [pressed, setPressed] = useState<string | null>(null);\n\n useEffect(() => {\n if (ask === null || enabled || refused.current.has(ask.key)) return;\n refused.current.add(ask.key);\n answer(ask.key, { kind: \"failed\" }).catch(warn);\n }, [ask, enabled, answer]);\n\n const settle = useCallback(\n (key: string, capture: Promise<ScreenCapture>) => {\n setPressed(key);\n capture\n .then((result) => answer(key, result))\n .catch((caught) => {\n warn(caught);\n // Pressable again. The agent is still waiting, and a second press\n // is how a dropped connection gets another go.\n setPressed((current) => (current === key ? null : current));\n });\n },\n [answer],\n );\n\n const key = ask?.key ?? null;\n const isSharing = key !== null && pressed === key;\n\n const share = useCallback(() => {\n if (key === null || isSharing) return;\n // Called here, inside the press, and not after anything is awaited.\n settle(key, captureScreen());\n }, [key, isSharing, settle]);\n\n const decline = useCallback(() => {\n if (key === null || isSharing) return;\n settle(key, Promise.resolve({ kind: \"declined\" }));\n }, [key, isSharing, settle]);\n\n // One object per state, so the panel holding it through its fold sees the\n // same offer until something about it changes.\n return useMemo(\n () =>\n key === null || !enabled ? null : { key, isSharing, share, decline },\n [key, enabled, isSharing, share, decline],\n );\n}\n\nfunction warn(caught: unknown) {\n // The agent is waiting on this, but a failed answer is bounded on the\n // other side: it stops waiting on its own and asks in words.\n console.warn(\"Median: answering the agent's request to see the screen failed\", caught);\n}\n","\"use client\";\n\nimport { prefersReducedMotion } from \"../motion\";\nimport {\n capturePage,\n MAX_WIDTH,\n QUALITY,\n SCREENSHOT_NAME,\n TYPE,\n} from \"./screenshot\";\n\n/**\n * Their screen, for the agent that asked to see it.\n *\n * Read off the screen itself, with the browser's own screen sharing: the\n * customer presses Share, the browser asks whether this tab may be seen, and\n * one frame is taken and the sharing stopped. That is the real thing, pixel\n * for pixel, canvases and videos and cross-origin frames included, which a\n * page redrawn from the DOM cannot promise.\n *\n * It needs a browser that can share a screen, which phones cannot, and a\n * page allowed to ask. Where either is missing the page is redrawn from the\n * DOM instead (`screenshot.ts`), so the agent still gets a picture. Where the\n * customer says no, it gets no picture, and is told so.\n *\n * Our own panel is faded out for the frame, the way the redrawn page leaves\n * it out: it is usually sitting on top of the thing they are asking about.\n *\n * Call it straight from the press of a button. The browser only lets a page\n * ask to see the screen while a click is fresh, so the asking is the first\n * thing it does, before anything is awaited.\n */\n\nexport type ScreenCapture =\n | { kind: \"taken\"; file: File }\n /** They said no, to our button or to the browser's. */\n | { kind: \"declined\" }\n | { kind: \"failed\" };\n\n/** How long the panel takes to fade out of the way, and back. */\nconst FADE_MS = 150;\n\n/**\n * How long to wait for the tab to send a frame. The first one comes within\n * a frame or two of sharing starting; a new one comes once the panel has\n * gone. A tab that is not changing sends nothing, so this is also how long\n * a still page is waited on before the frame already in hand is used.\n */\nconst FRAME_WAIT_MS = 500;\n\n/** Options newer than the DOM types this package builds against. */\ntype TabShareOptions = DisplayMediaStreamOptions & {\n /** Chrome: offer this tab, on its own. */\n preferCurrentTab?: boolean;\n selfBrowserSurface?: \"include\" | \"exclude\";\n surfaceSwitching?: \"include\" | \"exclude\";\n};\n\nexport async function captureScreen(): Promise<ScreenCapture> {\n const sharing = askToShare();\n if (sharing === null) return await fromPage();\n\n let stream: MediaStream;\n try {\n stream = await sharing;\n } catch (caught) {\n // NotAllowedError is the customer saying no. Anything else is the\n // browser failing to share, which the redrawn page can stand in for.\n if (caught instanceof DOMException && caught.name === \"NotAllowedError\") {\n return { kind: \"declined\" };\n }\n console.warn(\"Median: sharing the screen failed, so the page is redrawn\", caught);\n return await fromPage();\n }\n\n try {\n const file = await frameOf(stream);\n return file === null ? await fromPage() : { kind: \"taken\", file };\n } catch (caught) {\n console.warn(\"Median: reading the shared screen failed\", caught);\n return await fromPage();\n } finally {\n // One frame and done: the browser's \"sharing this tab\" goes away with it.\n for (const track of stream.getTracks()) track.stop();\n }\n}\n\n/**\n * The browser's own prompt to share this tab, or null where there is none to\n * show. Not awaited here: see the note at the top.\n */\nfunction askToShare(): Promise<MediaStream> | null {\n if (typeof navigator === \"undefined\" || typeof document === \"undefined\") {\n return null;\n }\n const media = navigator.mediaDevices;\n if (typeof media?.getDisplayMedia !== \"function\" || !policyAllows()) {\n return null;\n }\n\n const options: TabShareOptions = {\n video: { displaySurface: \"browser\" },\n audio: false,\n preferCurrentTab: true,\n selfBrowserSurface: \"include\",\n surfaceSwitching: \"exclude\",\n };\n try {\n return media.getDisplayMedia(options);\n } catch {\n return null;\n }\n}\n\n/**\n * Whether the host's page may ask at all. A page that forbids it answers\n * with the same error as a customer saying no, so it is asked first, where\n * the browser can say. One that cannot say is assumed to allow it.\n */\nfunction policyAllows(): boolean {\n const policy = (\n document as Document & {\n permissionsPolicy?: { allowsFeature(feature: string): boolean };\n featurePolicy?: { allowsFeature(feature: string): boolean };\n }\n );\n const reader = policy.permissionsPolicy ?? policy.featurePolicy;\n try {\n return reader?.allowsFeature(\"display-capture\") ?? true;\n } catch {\n return true;\n }\n}\n\n/** The page redrawn from the DOM, as the answer to a share that could not happen. */\nasync function fromPage(): Promise<ScreenCapture> {\n const file = await capturePage();\n return file === null ? { kind: \"failed\" } : { kind: \"taken\", file };\n}\n\n/** One frame of the shared tab, with our own panel out of the way. */\nasync function frameOf(stream: MediaStream): Promise<File | null> {\n const video = document.createElement(\"video\");\n video.muted = true;\n video.playsInline = true;\n video.srcObject = stream;\n\n try {\n await video.play();\n if (video.videoWidth === 0) await nextFrame(video);\n\n const show = hideOurs();\n try {\n // The frame taken is one drawn after the panel had gone.\n await delay(FADE_MS);\n await nextFrame(video);\n return await drawn(video);\n } finally {\n show();\n }\n } finally {\n video.pause();\n video.srcObject = null;\n }\n}\n\n/** The video's current frame as the picture that is sent. */\nasync function drawn(video: HTMLVideoElement): Promise<File | null> {\n const { videoWidth: width, videoHeight: height } = video;\n if (width === 0 || height === 0) return null;\n\n const scale = Math.min(1, MAX_WIDTH / width);\n const canvas = document.createElement(\"canvas\");\n canvas.width = Math.round(width * scale);\n canvas.height = Math.round(height * scale);\n const context = canvas.getContext(\"2d\");\n if (context === null) return null;\n context.drawImage(video, 0, 0, canvas.width, canvas.height);\n\n const blob = await new Promise<Blob | null>((resolve) =>\n canvas.toBlob(resolve, TYPE, QUALITY),\n );\n return blob === null ? null : new File([blob], SCREENSHOT_NAME, { type: TYPE });\n}\n\n/**\n * Every root Median draws, faded out, and a way to bring them back.\n *\n * Animated rather than set, so the panel dips out and back like a shutter\n * instead of blinking, and through the Web Animations API rather than the\n * style attribute, which React owns on some of these roots.\n */\nfunction hideOurs(): () => void {\n const duration = prefersReducedMotion() ? 0 : FADE_MS;\n const fades = Array.from(\n document.querySelectorAll<HTMLElement>(\"[data-median-widget]\"),\n ).map((root) =>\n root.animate(\n { opacity: 0 },\n {\n duration,\n easing: \"cubic-bezier(0.22, 1, 0.36, 1)\",\n fill: \"forwards\",\n },\n ),\n );\n\n return () => {\n for (const fade of fades) {\n fade.reverse();\n void fade.finished.then(\n () => fade.cancel(),\n () => undefined,\n );\n }\n };\n}\n\n/** The next frame the tab sends, or the wait running out. */\nfunction nextFrame(video: HTMLVideoElement): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, FRAME_WAIT_MS);\n if (typeof video.requestVideoFrameCallback === \"function\") {\n video.requestVideoFrameCallback(() => {\n clearTimeout(timer);\n resolve();\n });\n }\n });\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","\"use client\";\n\nimport { api } from \"@median/backend\";\nimport type { Id } from \"@median/backend/dataModel\";\nimport type { FunctionReturnType } from \"convex/server\";\nimport { useQuery } from \"convex/react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { ThreadData } from \"./thread-messages\";\n\nexport type TicketList = FunctionReturnType<typeof api.widget.tickets>;\n\n/**\n * Which ticket is asked for. `key` changes when somebody picks a ticket or\n * starts a new one, and only then; `via` says how the ticket got its id,\n * since a first message turning a fresh start into a ticket is the same\n * view carrying on, not a new one.\n */\ntype Selection = {\n sessionId: string;\n key: number;\n id: Id<\"conversations\"> | null;\n via: \"open\" | \"sent\";\n /**\n * The first look, nobody's choice. It may still move to an open ticket\n * that turns up a moment later, as a signed in visitor's do once the\n * widget has said who they are.\n */\n auto: boolean;\n};\n\n/** What is on screen: a ticket and its thread, or a fresh start. */\ntype Shown = {\n sessionId: string;\n key: number;\n id: Id<\"conversations\"> | null;\n thread: ThreadData | null;\n};\n\n/**\n * What `useTicketView` hands back. Spelled out rather than inferred: the\n * thread's own type lives inside the backend, and the published widget's\n * declarations can only name it through `ThreadData`.\n */\nexport type TicketView = {\n /** The ticket on screen, or null for a fresh start. Undefined at first. */\n thread: ThreadData | null | undefined;\n /** Its id, or null for a fresh start. */\n currentId: Id<\"conversations\"> | null;\n viewKey: number;\n /** The first look has not arrived yet. */\n isLoading: boolean;\n /** A picked ticket is on its way. */\n isOpening: boolean;\n open: (id: string) => void;\n startNew: () => void;\n claim: () => void;\n landed: (id: Id<\"conversations\">) => Promise<void>;\n};\n\n/** A fresh start's first message, waiting for its new ticket to arrive. */\nconst LANDING_TIMEOUT_MS = 5_000;\n\n/**\n * The ticket on screen, and moving between them.\n *\n * The panel opens on the newest open chat ticket, or on a fresh start when\n * there is none. After that it only moves when somebody moves it: a reply\n * landing on another ticket marks that one unread and leaves the screen\n * alone.\n *\n * A picked ticket takes over only once it has arrived. Until then the one\n * already on screen stays, so the history can wait over it and give way to\n * the conversation itself rather than to a loading mark.\n */\nexport function useTicketView({\n apiKey,\n sessionId,\n isLocked,\n tickets,\n}: {\n apiKey: string;\n sessionId: string | null;\n isLocked: boolean;\n tickets: TicketList | undefined;\n}): TicketView {\n const [selection, setSelection] = useState<Selection | null>(null);\n\n // A new session is somebody else, or nobody yet: whatever was picked for\n // the last one means nothing to it.\n const current =\n selection !== null && selection.sessionId === sessionId ? selection : null;\n\n const latestOpen =\n tickets?.tickets.find(\n (ticket) => ticket.status === \"open\" && ticket.channel === \"chat\",\n )?.conversationId ?? null;\n\n // The first look, once the list is in. The key it starts with is the one\n // the panel already has, so nothing drawn before it is drawn again.\n if (current === null && sessionId !== null && tickets !== undefined) {\n setSelection({\n sessionId,\n key: selection === null ? 0 : selection.key + 1,\n id: latestOpen,\n via: \"open\",\n auto: true,\n });\n }\n\n // Still the first look, on a fresh start nobody has touched, and an open\n // ticket has turned up: that is where they were.\n if (current?.auto && current.id === null && latestOpen !== null) {\n setSelection({ ...current, key: current.key + 1, id: latestOpen, auto: false });\n }\n\n const wantedId = current?.id ?? null;\n const live = useQuery(\n api.widget.thread,\n sessionId === null || isLocked || wantedId === null\n ? \"skip\"\n : { apiKey, sessionId, conversationId: wantedId },\n );\n\n // A ticket this browser cannot read any more, which is what signing out\n // of the host does to one from before: back to a fresh start.\n if (current !== null && current.id !== null && live === null) {\n setSelection({\n ...current,\n key: current.key + 1,\n id: null,\n via: \"open\",\n auto: false,\n });\n }\n\n // The ticket asked for, once it is here to show.\n const ready: Shown | null =\n current === null\n ? null\n : current.id === null\n ? { sessionId: current.sessionId, key: current.key, id: null, thread: null }\n : live\n ? { sessionId: current.sessionId, key: current.key, id: current.id, thread: live }\n : null;\n const isReady = ready !== null;\n\n // The last thing that was ready, kept current while it stays on screen,\n // so a ticket on its way does not blank the one in front of it.\n const [shown, setShown] = useState<Shown | null>(null);\n // Only ever this session's: after a sign out, nothing of the last\n // person's stays up while the new session's list is on its way.\n const kept = shown !== null && shown.sessionId === sessionId ? shown : null;\n if (\n ready !== null &&\n (shown === null ||\n shown.key !== ready.key ||\n shown.id !== ready.id ||\n shown.thread !== ready.thread)\n ) {\n setShown(ready);\n }\n\n // A first message's ticket, on its way: still the same view, still the\n // greeting, until the thread with the message in it arrives.\n const view: Shown | null =\n ready ??\n (current?.via === \"sent\" && kept !== null && kept.key === current.key\n ? { ...kept, id: current.id }\n : kept);\n\n // Sends from a fresh start wait here until their ticket has arrived, so\n // the composer keeps the draft until the message is on screen to replace\n // it. See `landed`.\n const landings = useRef(new Map<string, () => void>());\n const readyId = ready?.id ?? null;\n useEffect(() => {\n if (readyId === null) return;\n landings.current.get(readyId)?.();\n landings.current.delete(readyId);\n }, [readyId]);\n\n const open = useCallback(\n (id: string) => {\n if (sessionId === null) return;\n setSelection((previous) => ({\n sessionId,\n key: (previous?.key ?? 0) + 1,\n auto: false,\n id: id as Id<\"conversations\">,\n via: \"open\",\n }));\n },\n [sessionId],\n );\n\n const startNew = useCallback(() => {\n if (sessionId === null) return;\n setSelection((previous) => ({\n sessionId,\n key: (previous?.key ?? 0) + 1,\n id: null,\n via: \"open\",\n auto: false,\n }));\n }, [sessionId]);\n\n /**\n * Somebody is writing on what is on screen, so it is theirs now: the first\n * look stops looking for a ticket to move to.\n */\n const claim = useCallback(() => {\n setSelection((previous) =>\n previous !== null && previous.auto ? { ...previous, auto: false } : previous,\n );\n }, []);\n\n /**\n * A fresh start's first message made a ticket. The view carries on as\n * that ticket, and the promise settles once it is on screen, or after a\n * few seconds regardless so a slow connection never holds the box.\n */\n const landed = useCallback(\n (id: Id<\"conversations\">) => {\n if (sessionId === null) return Promise.resolve();\n setSelection((previous) =>\n previous !== null && previous.id === null\n ? { ...previous, id, via: \"sent\", auto: false }\n : previous,\n );\n return new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, LANDING_TIMEOUT_MS);\n landings.current.set(id, () => {\n clearTimeout(timer);\n resolve();\n });\n });\n },\n [sessionId],\n );\n\n return {\n thread: view === null ? undefined : view.thread,\n currentId: view?.id ?? null,\n viewKey: view?.key ?? 0,\n isLoading: view === null,\n isOpening: current !== null && current.via === \"open\" && !isReady && view !== null,\n open,\n startNew,\n claim,\n landed,\n };\n}\n","\"use client\";\n\n/**\n * The address a visitor typed at the composer's question, kept beside the\n * session token and for the same reason: a host's site is usually several\n * pages, so the widget is remounted every time somebody clicks a link, and\n * asking all over again on each one is not a question, it is a wall.\n *\n * It rides the visitor's first message like any other fact the host passes,\n * which is what puts it on their row in the team's inbox.\n */\n\nimport { useCallback, useSyncExternalStore } from \"react\";\n\nconst STORAGE_PREFIX = \"median:email:\";\n\n/** Where it lives when localStorage is blocked. Lasts the page load. */\nconst inMemory = new Map<string, string>();\n\nconst listeners = new Set<() => void>();\n\nexport function subscribeVisitorEmail(listener: () => void) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\n/** The address this browser gave, or null before it has. */\nexport function readVisitorEmail(apiKey: string): string | null {\n if (typeof window === \"undefined\") return null;\n\n try {\n const stored = window.localStorage.getItem(`${STORAGE_PREFIX}${apiKey}`);\n return stored && stored.length > 0 ? stored : null;\n } catch {\n return inMemory.get(apiKey) ?? null;\n }\n}\n\nexport function writeVisitorEmail(apiKey: string, email: string): void {\n inMemory.set(apiKey, email);\n try {\n window.localStorage.setItem(`${STORAGE_PREFIX}${apiKey}`, email);\n } catch {\n // Blocked, so the in-memory copy above is all there is. It still gets\n // them through this page, where the message that carries it goes out.\n }\n\n for (const listener of listeners) listener();\n}\n\n/**\n * Forget every address this browser holds. Part of `medianSupport.reset()`,\n * which has to detach a browser from its conversation completely: an email\n * that survived a sign out would greet the next person at the keyboard with\n * somebody else's name.\n */\nexport function resetVisitorEmails() {\n inMemory.clear();\n\n if (typeof window !== \"undefined\") {\n try {\n const stale: string[] = [];\n for (let i = 0; i < window.localStorage.length; i += 1) {\n const key = window.localStorage.key(i);\n if (key !== null && key.startsWith(STORAGE_PREFIX)) stale.push(key);\n }\n for (const key of stale) window.localStorage.removeItem(key);\n } catch {\n // Storage was blocked, so there was nothing durable to forget.\n }\n }\n\n for (const listener of listeners) listener();\n}\n\n/**\n * The address, live: subscribed, so a reset while the widget is mounted asks\n * again rather than trusting state that outlived its storage. The setter is\n * what the composer's question calls once the answer checks out.\n */\nexport function useVisitorEmail(\n apiKey: string,\n): [string | null, (email: string) => void] {\n const email = useSyncExternalStore(\n subscribeVisitorEmail,\n useCallback(() => readVisitorEmail(apiKey), [apiKey]),\n () => null,\n );\n\n const setEmail = useCallback(\n (next: string) => writeVisitorEmail(apiKey, next),\n [apiKey],\n );\n\n return [email, setEmail];\n}\n","\"use client\";\n\nimport { useEffect, useRef, useState, useSyncExternalStore } from \"react\";\nimport { getResetCount, subscribeSession } from \"./session\";\nimport type { MedianUser } from \"./use-median-thread\";\n\n/**\n * Who the host had signed in when `reset()` ran.\n *\n * A host usually calls `reset()` a beat before its own state lets go of the\n * user, so the old person is still in the props when the fresh session is\n * minted. Sending them again would bind the new session straight back to\n * them. So each person held at reset time is left out until the host passes\n * somebody else, or nobody, and then they can sign back in as normal.\n *\n * A panel can also leave the page still holding them, as when a host only\n * renders the widget while somebody is signed in. The person is let go a\n * moment later, unless a panel has taken them up again by then, so the next\n * time they sign in on this page they are sent.\n */\n\n/** What each mounted panel was told about the person, keyed by panel. */\nconst held = new Map<symbol, string>();\n\n/** People `reset()` signed out whom the host is still passing in. */\nconst signedOut = new Set<string>();\n\n/**\n * How long a person stays signed out after the last panel holding them\n * leaves, so a panel remounting on the same sign out does not bind them.\n */\nconst RELEASE_AFTER_MS = 2_000;\n\n/** Called by `reset()`, before the sessions are forgotten. */\nexport function signOutHeldUsers() {\n for (const person of held.values()) signedOut.add(person);\n}\n\n/** Lets a signed out person go once no panel is holding them any more. */\nfunction releaseLater(person: string) {\n if (!signedOut.has(person)) return;\n setTimeout(() => {\n for (const other of held.values()) if (other === person) return;\n signedOut.delete(person);\n }, RELEASE_AFTER_MS);\n}\n\n/** The signed id when there is one, since other facts can change under it. */\nfunction personKey(user: MedianUser | undefined): string | null {\n if (user === undefined) return null;\n return user.id && user.hash ? `id:${user.id}` : JSON.stringify(user);\n}\n\n/**\n * The user a panel should send, which is the one it was given unless\n * `reset()` signed them out and the host has not let go of them yet.\n */\nexport function useSignedInUser(\n user: MedianUser | undefined,\n): MedianUser | undefined {\n const person = personKey(user);\n // Re-read on every reset, which is the only time `signedOut` grows.\n useSyncExternalStore(subscribeSession, getResetCount, () => 0);\n\n const [token] = useState(() => Symbol(\"median-panel\"));\n const previous = useRef<string | null>(null);\n\n useEffect(() => {\n // The host moved on from somebody who was signed out, so they are free\n // to sign in again later.\n if (previous.current !== null && previous.current !== person) {\n signedOut.delete(previous.current);\n }\n previous.current = person;\n\n if (person === null) held.delete(token);\n else held.set(token, person);\n }, [person, token]);\n\n useEffect(\n () => () => {\n const person = held.get(token);\n held.delete(token);\n if (person !== undefined) releaseLater(person);\n },\n [token],\n );\n\n return person !== null && signedOut.has(person) ? undefined : user;\n}\n","\"use client\";\n\nimport { useSyncExternalStore } from \"react\";\n\n/**\n * How many replies the visitor has not read, kept outside React.\n *\n * The count is worked out inside the panel, from the thread. The thing that\n * wants to show it is usually somewhere else: a button in the host's own\n * nav, on a page where the corner has been taken back. Same reason the open\n * state lives in a module: the host reaches it without threading state\n * through their tree or wrapping their app in a provider of ours.\n *\n * Zero whenever no panel is on the page, since with nothing mounted there\n * is nothing unread to announce.\n */\nlet count = 0;\nconst listeners = new Set<() => void>();\n\nexport const unreadStore = {\n subscribe(listener: () => void) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n read: () => count,\n /** The server has no thread to count, and neither does the first paint. */\n readServer: () => 0,\n set(next: number) {\n if (count === next) return;\n count = next;\n for (const listener of listeners) listener();\n },\n};\n\nexport function useUnreadCount(): number {\n return useSyncExternalStore(\n unreadStore.subscribe,\n unreadStore.read,\n unreadStore.readServer,\n );\n}\n","\"use client\";\n\nimport { ConvexError } from \"convex/values\";\nimport { Component, type ReactNode } from \"react\";\n\n/**\n * The widget runs on somebody else's page, so whatever goes wrong in here must\n * never take the host's site down with it. The widget quietly steps off the\n * page and says why in the console, where the person installing it is already\n * looking.\n */\n\n/**\n * Name the actual problem when we can. \"Check the apiKey\" is the right advice\n * for a rejected key and a wild goose chase for everything else.\n */\nfunction adviceFor(error: unknown, component: string): string {\n if (\n error instanceof ConvexError &&\n typeof error.data === \"object\" &&\n error.data !== null &&\n (error.data as { code?: string }).code === \"invalid_api_key\"\n ) {\n return `Median did not recognize the apiKey passed to <${component}>, so it has been hidden. Derive it from an active MEDIAN_KEY with publicKeyFromMedianKey.`;\n }\n return `Median: <${component}> hit an error and has been hidden. Your page is unaffected.`;\n}\n\nexport class MedianErrorBoundary extends Component<\n {\n children: ReactNode;\n /** The component inside, as the host wrote it, for the console line. */\n component: string;\n },\n { hasFailed: boolean }\n> {\n state = { hasFailed: false };\n\n static getDerivedStateFromError() {\n return { hasFailed: true };\n }\n\n componentDidCatch(error: unknown) {\n console.error(adviceFor(error, this.props.component), error);\n }\n\n render() {\n return this.state.hasFailed ? null : this.props.children;\n }\n}\n","\"use client\";\n\nimport { useEffect, useState, useSyncExternalStore } from \"react\";\nimport { getResetCount, subscribeSession } from \"./session\";\nimport type { MedianUser } from \"./use-median-thread\";\n\n/**\n * Who is signed in, fetched from the host's own route rather than passed down\n * their tree.\n *\n * The widget is a client component and the signature can only be made on a\n * server, so on an app with a server rendered root layout there is nowhere\n * that has both the session and this component. The route in the middle is\n * always the same one, so `medianIdentity` in @mediansh/agent-tools ships it\n * and this reads it.\n *\n * The publishable key comes down the same way. A host who points at a route\n * has one Median value in their app instead of two, and the browser safe half\n * of the key never has to be plumbed into a client environment by hand.\n */\n\n/** What `medianIdentity` answers with. Both fields absent is a valid answer. */\ntype IdentityResponse = {\n apiKey?: string;\n user?: MedianUser;\n};\n\nexport type ResolvedIdentity = {\n apiKey: string | undefined;\n user: MedianUser | undefined;\n /**\n * The answer is still on its way. True only while a route is actually being\n * read: a widget given no route is never waiting for one.\n *\n * The panel holds its loading state on this rather than showing the sign in\n * invitation, because \"we have not asked yet\" and \"they are not signed in\"\n * look identical from here and only one of them is worth telling somebody.\n */\n isLoading: boolean;\n};\n\nconst NOTHING: ResolvedIdentity = {\n apiKey: undefined,\n user: undefined,\n isLoading: false,\n};\n\n/** A route has been named and has not answered yet. */\nconst WAITING: ResolvedIdentity = { ...NOTHING, isLoading: true };\n\n/**\n * The request, while it is in the air, shared by everything waiting on it.\n *\n * A page with the support panel and the feedback panel on it mounts two\n * components that both want the same answer from the same route. One asks and\n * the other joins, rather than the host's server signing the same person\n * twice.\n *\n * Dropped the moment it settles, so nothing here outlives the answer. Signing\n * out and back in re-reads the route rather than being handed the last\n * person's signature out of a cache. Keyed by reset too, so a read that\n * started before `reset()` is not joined after it.\n */\nconst inFlight = new Map<string, Promise<IdentityResponse>>();\n\nfunction read(endpoint: string, resets: number): Promise<IdentityResponse> {\n const cacheKey = `${resets}:${endpoint}`;\n const existing = inFlight.get(cacheKey);\n if (existing !== undefined) return existing;\n\n const request = (async () => {\n const response = await fetch(endpoint, {\n // The session lives in a cookie, and a cross origin route only sees it\n // if the request asks. Same origin routes are unaffected.\n credentials: \"include\",\n headers: { accept: \"application/json\" },\n });\n if (!response.ok) {\n throw new Error(\n `${endpoint} answered ${response.status}. It should return { apiKey, user } from medianIdentity().`,\n );\n }\n return (await response.json()) as IdentityResponse;\n })().finally(() => inFlight.delete(cacheKey));\n\n inFlight.set(cacheKey, request);\n return request;\n}\n\n/**\n * Neither half was passed, so there is nothing to connect to and nothing on\n * the page. Silence here is the worst outcome: the install looks done, the\n * corner is empty, and there is no error anywhere to explain it.\n *\n * Said from the browser rather than checked by the types alone, because a\n * host on plain JavaScript, or one building options up across an `update()`\n * call, never sees the types.\n */\nexport function useMissingCredentials(\n /** As the host wrote it: `<MedianSupport>`, or `useMedianContact()`. */\n caller: string,\n apiKey: string | undefined,\n endpoint: string | undefined,\n /** What that costs them, for the one that still draws something. */\n consequence = \"nothing is on the page\",\n): void {\n useEffect(() => {\n if (apiKey !== undefined || endpoint !== undefined) return;\n console.error(\n `Median: ${caller} was given neither apiKey nor identity, so ${consequence}. Pass the median_pk_ id from publicKeyFromMedianKey(MEDIAN_KEY), or the path of a route built with medianIdentity().`,\n );\n }, [caller, apiKey, endpoint, consequence]);\n}\n\nexport function useMedianIdentity(\n endpoint: string | undefined,\n): ResolvedIdentity {\n // The answer, with the route it is an answer to.\n //\n // Kept as a pair, and read back below only when the two still agree,\n // because that is what makes \"we are waiting\" a fact about the current\n // route rather than a flag something has to remember to set. Nothing here\n // writes state on the way in, and a route that changes is one comparison\n // rather than a reset.\n const [answered, setAnswered] = useState<{\n endpoint: string;\n identity: ResolvedIdentity;\n } | null>(null);\n\n // `reset()` signs the person out, so the route is asked again. The last\n // answer stands meanwhile; the person in it is already signed out.\n const resets = useSyncExternalStore(subscribeSession, getResetCount, () => 0);\n\n useEffect(() => {\n if (endpoint === undefined) return;\n\n // A flag rather than an AbortController: the request can be shared with\n // the other panel on the page, and one of them unmounting must not cancel\n // the answer the other is still waiting for.\n let listening = true;\n\n void (async () => {\n try {\n const body = await read(endpoint, resets);\n if (!listening) return;\n setAnswered({\n endpoint,\n identity: {\n apiKey: body.apiKey,\n // An id without its signature is ignored by the backend anyway,\n // so it is dropped here rather than sent up to be refused.\n user: body.user?.hash ? body.user : undefined,\n isLoading: false,\n },\n });\n } catch (caught) {\n if (!listening) return;\n // Support stays on the page. Without a key from anywhere the widget\n // has nothing to talk to and says so through its own boundary; with\n // one, this is a visitor whose thread belongs to their browser, which\n // is where every anonymous visitor already is.\n console.error(\n \"Median: could not read the identity route.\",\n caught instanceof Error ? caught.message : caught,\n );\n setAnswered({ endpoint, identity: NOTHING });\n }\n })();\n\n return () => {\n listening = false;\n };\n }, [endpoint, resets]);\n\n if (endpoint === undefined) return NOTHING;\n if (answered?.endpoint === endpoint) return answered.identity;\n return WAITING;\n}\n","\"use client\";\n\nimport { api } from \"@median/backend\";\nimport { ConvexHttpClient } from \"convex/browser\";\nimport { useEffect, useState } from \"react\";\nimport { MEDIAN_DEPLOYMENT_URL } from \"./deployment\";\n\n/** A failed/expired entitlement always restores the default visible badge. */\nexport function useMedianBranding(apiKey: string | undefined) {\n const [state, setState] = useState({ key: apiKey, show: true });\n useEffect(() => {\n if (!apiKey || !MEDIAN_DEPLOYMENT_URL) return;\n const client = new ConvexHttpClient(MEDIAN_DEPLOYMENT_URL);\n let active = true;\n let expiry: ReturnType<typeof setTimeout> | undefined;\n async function refresh() {\n try {\n const result = await client.action(api.billingAccess.branding, {\n apiKey: apiKey!,\n });\n if (!active) return;\n clearTimeout(expiry);\n const expired =\n result.expiresAt !== null && result.expiresAt <= Date.now();\n setState({ key: apiKey, show: result.showBranding || expired });\n if (!result.showBranding && result.expiresAt !== null)\n expiry = setTimeout(\n () => setState({ key: apiKey, show: true }),\n Math.min(2_147_483_647, Math.max(0, result.expiresAt - Date.now())),\n );\n } catch {\n if (active) setState({ key: apiKey, show: true });\n }\n }\n void refresh();\n const timer = setInterval(() => void refresh(), 60_000);\n return () => {\n active = false;\n clearInterval(timer);\n clearTimeout(expiry);\n };\n }, [apiKey]);\n return state.key === apiKey ? state.show : true;\n}\n","\"use client\";\n\nimport { ConvexProvider, ConvexReactClient } from \"convex/react\";\nimport { useState, type ReactNode } from \"react\";\nimport { MEDIAN_DEPLOYMENT_URL } from \"./deployment\";\n\n/**\n * A connection of the widget's own, so it is self contained on a host site\n * that has never heard of Convex. One per mounted component, made lazily so\n * a missing URL fails at render, into the boundary above it, rather than at\n * import on a page that was only loading the module.\n */\nexport function MedianConnection({\n what,\n children,\n}: {\n /** Named in the error, so a broken build says which thing it broke. */\n what: string;\n children: ReactNode;\n}) {\n const [client] = useState(() => {\n if (!MEDIAN_DEPLOYMENT_URL) {\n throw new Error(\n `The Median ${what} was built without a deployment URL. Set NEXT_PUBLIC_CONVEX_URL.`,\n );\n }\n return new ConvexReactClient(MEDIAN_DEPLOYMENT_URL);\n });\n\n return <ConvexProvider client={client}>{children}</ConvexProvider>;\n}\n","\"use client\";\n\nimport { API_KEY_PREFIX, hasPrefix } from \"@median/backend/support\";\nimport { useEffect } from \"react\";\n\n/**\n * A key with the wrong shape would only ever fail later, quietly, as a\n * widget that never appears or a form that never sends. Say so now, in the\n * console, while the person who pasted it is still looking at this page.\n *\n * The key is truncated in the message: if what was pasted is a secret from\n * somewhere else, the console should not repeat all of it.\n */\nexport function useApiKeyShapeWarning(\n /** As the host wrote it: `<MedianSupport>`, or `useMedianContact()`. */\n caller: string,\n /** Undefined while an identity route is still answering, which is fine. */\n apiKey: string | undefined,\n) {\n useEffect(() => {\n if (apiKey === undefined || hasPrefix(apiKey, API_KEY_PREFIX)) return;\n\n const shown =\n apiKey === \"\"\n ? \"an empty string\"\n : `\"${apiKey.length > 16 ? `${apiKey.slice(0, 16)}…` : apiKey}\"`;\n console.error(\n `Median: the apiKey passed to ${caller} is ${shown}, which does not look like the browser-safe id from publicKeyFromMedianKey(MEDIAN_KEY). It should start with ${API_KEY_PREFIX}.`,\n );\n }, [caller, apiKey]);\n}\n","/**\n * The sound palette — layer/recipe types plus the seventeen built-in recipes.\n * Each sound has its own distinct shape — a chime, an arpeggio, a pitch\n * glide, a warm pad, a breath — rather than being a volume/EQ tweak on\n * the same click. Add a new one here without touching any audio graph code.\n */\nexport const RECIPES = {\n /** A soft two-note ascending bell, like an iOS/macOS confirmation tink. */\n chime: {\n masterGain: 0.5,\n layers: [\n { kind: \"tone\", waveform: \"sine\", frequency: 1046.5, attack: 0.006, decay: 0.22, peak: 0.09 },\n { kind: \"tone\", waveform: \"sine\", frequency: 1568, offset: 0.09, attack: 0.006, decay: 0.26, peak: 0.08 },\n ],\n shimmer: { delay: 0.12, feedback: 0.25, wet: 0.18, lowpass: 4000 },\n },\n /** A quick ascending twinkle of four notes — bright and playful. */\n sparkle: {\n masterGain: 0.5,\n layers: [\n { kind: \"tone\", waveform: \"sine\", frequency: 1760, offset: 0, attack: 0.003, decay: 0.09, peak: 0.045 },\n { kind: \"tone\", waveform: \"sine\", frequency: 2217, offset: 0.045, attack: 0.003, decay: 0.09, peak: 0.04 },\n { kind: \"tone\", waveform: \"sine\", frequency: 2637, offset: 0.09, attack: 0.003, decay: 0.1, peak: 0.038 },\n { kind: \"tone\", waveform: \"sine\", frequency: 3520, offset: 0.135, attack: 0.003, decay: 0.12, peak: 0.032 },\n ],\n shimmer: { delay: 0.07, feedback: 0.35, wet: 0.22, lowpass: 6000 },\n },\n /** A single note gliding smoothly downward, like a drop of water. */\n droplet: {\n masterGain: 0.55,\n layers: [\n { kind: \"tone\", waveform: \"sine\", frequency: 1200, glideTo: 550, glideTime: 0.14, attack: 0.004, decay: 0.2, peak: 0.075 },\n ],\n shimmer: { delay: 0.09, feedback: 0.2, wet: 0.15, lowpass: 3000 },\n },\n /** A warm, slow-swelling pad from two gently detuned sines. */\n bloom: {\n masterGain: 0.5,\n layers: [\n { kind: \"tone\", waveform: \"sine\", frequency: 528, attack: 0.06, decay: 0.32, peak: 0.06 },\n { kind: \"tone\", waveform: \"sine\", frequency: 528, detune: 12, attack: 0.06, decay: 0.34, peak: 0.05 },\n ],\n shimmer: { delay: 0.15, feedback: 0.2, wet: 0.12, lowpass: 2500 },\n },\n /** A soft hush with a falling tone — for tooltips and low-priority previews. */\n whisper: {\n masterGain: 0.48,\n layers: [\n { kind: \"noise\", filterType: \"lowpass\", filterFrequency: 1600, filterQ: 0.7, attack: 0.025, decay: 0.13, peak: 0.04 },\n { kind: \"tone\", waveform: \"sine\", frequency: 880, glideTo: 660, glideTime: 0.14, offset: 0.01, attack: 0.012, decay: 0.14, peak: 0.025 },\n ],\n },\n /** A focused, bandpass-filtered tick with a bright sine ping on top — crisp and instant. */\n tick: {\n masterGain: 0.4,\n layers: [\n { kind: \"noise\", filterType: \"bandpass\", filterFrequency: 5400, filterQ: 1.8, attack: 0.001, decay: 0.018, peak: 0.14 },\n { kind: \"tone\", waveform: \"sine\", frequency: 2600, attack: 0.001, decay: 0.012, peak: 0.018 },\n ],\n },\n /** A dull, muted knock — the \"down\" half of a press/release pair, like a key bottoming out. */\n press: {\n masterGain: 0.4,\n layers: [\n { kind: \"noise\", filterType: \"bandpass\", filterFrequency: 1700, filterQ: 1.4, attack: 0.001, decay: 0.02, peak: 0.13 },\n ],\n },\n /** A brighter, springier tick — the \"up\" half of a press/release pair, like a key returning. */\n release: {\n masterGain: 0.4,\n layers: [\n { kind: \"noise\", filterType: \"bandpass\", filterFrequency: 4600, filterQ: 1.8, attack: 0.001, decay: 0.016, peak: 0.12 },\n { kind: \"tone\", waveform: \"sine\", frequency: 3200, offset: 0.006, attack: 0.001, decay: 0.05, peak: 0.02 },\n ],\n },\n /** A two-part click-clack, like a mechanical switch flipping between states. */\n toggle: {\n masterGain: 0.4,\n layers: [\n { kind: \"noise\", filterType: \"bandpass\", filterFrequency: 2200, filterQ: 1.6, attack: 0.001, decay: 0.016, peak: 0.12 },\n { kind: \"noise\", filterType: \"bandpass\", filterFrequency: 3800, filterQ: 1.6, offset: 0.024, attack: 0.001, decay: 0.02, peak: 0.1 },\n ],\n },\n /** A short, warm three-note ascending confirmation — \"done\", not a fanfare. */\n success: {\n masterGain: 0.5,\n layers: [\n { kind: \"tone\", waveform: \"sine\", frequency: 880, attack: 0.004, decay: 0.09, peak: 0.06 },\n { kind: \"tone\", waveform: \"sine\", frequency: 1108.73, offset: 0.06, attack: 0.004, decay: 0.1, peak: 0.06 },\n { kind: \"tone\", waveform: \"sine\", frequency: 1318.51, offset: 0.12, attack: 0.004, decay: 0.18, peak: 0.07 },\n ],\n shimmer: { delay: 0.1, feedback: 0.22, wet: 0.16, lowpass: 4500 },\n },\n /** A muted knock followed by two descending tones — a calm, recoverable refusal. */\n error: {\n masterGain: 0.42,\n layers: [\n { kind: \"noise\", filterType: \"bandpass\", filterFrequency: 850, filterQ: 1.1, attack: 0.001, decay: 0.035, peak: 0.13 },\n { kind: \"tone\", waveform: \"triangle\", frequency: 440, offset: 0.025, attack: 0.004, decay: 0.09, peak: 0.045 },\n { kind: \"tone\", waveform: \"triangle\", frequency: 349.23, offset: 0.1, attack: 0.004, decay: 0.14, peak: 0.04 },\n ],\n },\n /** A papery filtered flick with a tiny glass tick — for pages, galleries, and carousels. */\n page: {\n masterGain: 0.38,\n layers: [\n { kind: \"noise\", filterType: \"lowpass\", filterFrequency: 1800, filterQ: 0.7, attack: 0.006, decay: 0.08, peak: 0.11 },\n { kind: \"noise\", filterType: \"bandpass\", filterFrequency: 4200, filterQ: 1.2, offset: 0.04, attack: 0.004, decay: 0.065, peak: 0.08 },\n { kind: \"tone\", waveform: \"sine\", frequency: 2400, offset: 0.075, attack: 0.002, decay: 0.045, peak: 0.02 },\n ],\n },\n /** A brief unresolved lift — signals that user-initiated work has started. */\n loading: {\n masterGain: 0.42,\n layers: [\n { kind: \"noise\", filterType: \"lowpass\", filterFrequency: 1400, filterQ: 0.6, attack: 0.035, decay: 0.14, peak: 0.035 },\n { kind: \"tone\", waveform: \"sine\", frequency: 420, glideTo: 630, glideTime: 0.18, attack: 0.025, decay: 0.18, peak: 0.05 },\n ],\n shimmer: { delay: 0.11, feedback: 0.18, wet: 0.12, lowpass: 2800 },\n },\n /** A quick lock-on sweep resolving to a clear tone — the system is ready. */\n ready: {\n masterGain: 0.48,\n layers: [\n { kind: \"noise\", filterType: \"bandpass\", filterFrequency: 3600, filterQ: 1.8, attack: 0.001, decay: 0.02, peak: 0.11 },\n { kind: \"tone\", waveform: \"triangle\", frequency: 330, glideTo: 660, glideTime: 0.12, offset: 0.012, attack: 0.004, decay: 0.16, peak: 0.055 },\n { kind: \"tone\", waveform: \"sine\", frequency: 990, offset: 0.13, attack: 0.004, decay: 0.22, peak: 0.06 },\n ],\n shimmer: { delay: 0.1, feedback: 0.16, wet: 0.1, lowpass: 4200 },\n },\n /** A compact synthetic chirp — crisp feedback for primary buttons and controls. */\n pulse: {\n masterGain: 0.42,\n layers: [\n { kind: \"noise\", filterType: \"bandpass\", filterFrequency: 2600, filterQ: 2.4, attack: 0.001, decay: 0.022, peak: 0.08 },\n { kind: \"tone\", waveform: \"triangle\", frequency: 620, glideTo: 1240, glideTime: 0.07, attack: 0.002, decay: 0.085, peak: 0.055 },\n ],\n },\n /** A fast three-step locator signal — playful feedback for menus and secondary buttons. */\n scan: {\n masterGain: 0.4,\n layers: [\n { kind: \"tone\", waveform: \"sine\", frequency: 740, attack: 0.002, decay: 0.055, peak: 0.05 },\n { kind: \"tone\", waveform: \"sine\", frequency: 1110, offset: 0.045, attack: 0.002, decay: 0.055, peak: 0.045 },\n { kind: \"tone\", waveform: \"sine\", frequency: 1665, offset: 0.09, attack: 0.002, decay: 0.07, peak: 0.04 },\n ],\n shimmer: { delay: 0.065, feedback: 0.16, wet: 0.1, lowpass: 4200 },\n },\n /** A rising harmonic portal with a soft tail — for client-side page arrivals. */\n arrival: {\n masterGain: 0.44,\n layers: [\n { kind: \"noise\", filterType: \"lowpass\", filterFrequency: 900, filterQ: 0.8, attack: 0.05, decay: 0.24, peak: 0.035 },\n { kind: \"tone\", waveform: \"sine\", frequency: 220, glideTo: 440, glideTime: 0.32, attack: 0.04, decay: 0.34, peak: 0.055 },\n { kind: \"tone\", waveform: \"sine\", frequency: 659.25, offset: 0.12, attack: 0.045, decay: 0.32, peak: 0.04 },\n { kind: \"tone\", waveform: \"sine\", frequency: 987.77, offset: 0.19, attack: 0.045, decay: 0.34, peak: 0.032 },\n ],\n shimmer: { delay: 0.16, feedback: 0.28, wet: 0.18, lowpass: 3200 },\n },\n};\nexport function isSoundName(value) {\n return typeof value === \"string\" && Object.prototype.hasOwnProperty.call(RECIPES, value);\n}\n/** All available sound names, derived from the recipe palette. */\nexport const sounds = Object.keys(RECIPES);\n","/**\n * The audio engine — synthesizes each sound live via the Web Audio API\n * on one shared, lazily created `AudioContext`. No audio files, no\n * dependencies. Every sound carries a gentle envelope (and often a soft\n * shimmer tail) instead of a hard transient, so nothing feels harsh.\n */\nimport { RECIPES, isSoundName, } from \"../sounds/recipes.js\";\nconst SOURCE_STOP_PADDING = 0.05;\nconst CLEANUP_MARGIN = 0.05;\nconst INAUDIBLE_GAIN = 0.001;\nconst OUTPUT_GAIN = 4;\nfunction renderTone(context, destination, layer, startTime) {\n const oscillator = context.createOscillator();\n oscillator.type = layer.waveform;\n oscillator.frequency.setValueAtTime(layer.frequency, startTime);\n if (layer.detune)\n oscillator.detune.value = layer.detune;\n if (layer.glideTo !== undefined) {\n const glideTime = layer.glideTime ?? layer.attack + layer.decay;\n oscillator.frequency.exponentialRampToValueAtTime(layer.glideTo, startTime + glideTime);\n }\n const gain = context.createGain();\n gain.gain.setValueAtTime(0.0001, startTime);\n gain.gain.exponentialRampToValueAtTime(layer.peak, startTime + layer.attack);\n gain.gain.exponentialRampToValueAtTime(0.0001, startTime + layer.attack + layer.decay);\n oscillator.connect(gain).connect(destination);\n oscillator.start(startTime);\n oscillator.stop(startTime + layer.attack + layer.decay + SOURCE_STOP_PADDING);\n}\nfunction renderNoise(context, destination, layer, startTime) {\n const duration = layer.attack + layer.decay + SOURCE_STOP_PADDING;\n const length = Math.max(1, Math.floor(duration * context.sampleRate));\n const buffer = context.createBuffer(1, length, context.sampleRate);\n const data = buffer.getChannelData(0);\n for (let i = 0; i < length; i++)\n data[i] = 2 * Math.random() - 1;\n const source = context.createBufferSource();\n source.buffer = buffer;\n const filter = context.createBiquadFilter();\n filter.type = layer.filterType;\n filter.frequency.value = layer.filterFrequency;\n if (layer.filterQ !== undefined)\n filter.Q.value = layer.filterQ;\n const gain = context.createGain();\n gain.gain.setValueAtTime(0.0001, startTime);\n gain.gain.exponentialRampToValueAtTime(layer.peak, startTime + layer.attack);\n gain.gain.exponentialRampToValueAtTime(0.0001, startTime + layer.attack + layer.decay);\n source.connect(filter).connect(gain).connect(destination);\n source.start(startTime);\n source.stop(startTime + duration);\n}\n/** Wires a soft echo/shimmer send off `source`, feeding back into `destination`. */\nfunction attachShimmer(context, source, destination, shimmer) {\n const delay = context.createDelay(1);\n delay.delayTime.value = shimmer.delay;\n const feedbackFilter = context.createBiquadFilter();\n feedbackFilter.type = \"lowpass\";\n feedbackFilter.frequency.value = shimmer.lowpass;\n const feedbackGain = context.createGain();\n feedbackGain.gain.value = shimmer.feedback;\n const wetGain = context.createGain();\n wetGain.gain.value = shimmer.wet;\n source.connect(delay);\n delay.connect(feedbackFilter);\n feedbackFilter.connect(feedbackGain);\n feedbackGain.connect(delay);\n feedbackFilter.connect(wetGain);\n wetGain.connect(destination);\n return [delay, feedbackFilter, feedbackGain, wetGain];\n}\nfunction sourceEnd(recipe) {\n return Math.max(...recipe.layers.map((layer) => (layer.offset ?? 0) + layer.attack + layer.decay + SOURCE_STOP_PADDING));\n}\nfunction shimmerTail(shimmer) {\n if (!shimmer || shimmer.feedback <= 0)\n return 0;\n if (shimmer.feedback >= 1)\n return shimmer.delay;\n return shimmer.delay * (1 + Math.ceil(Math.log(INAUDIBLE_GAIN) / Math.log(shimmer.feedback)));\n}\nlet sharedOutput = null;\nfunction getOutput(context) {\n if (sharedOutput)\n return sharedOutput;\n const output = context.createGain();\n output.gain.value = OUTPUT_GAIN;\n const limiter = context.createDynamicsCompressor();\n limiter.threshold.value = -8;\n limiter.knee.value = 6;\n limiter.ratio.value = 12;\n limiter.attack.value = 0.002;\n limiter.release.value = 0.08;\n output.connect(limiter).connect(context.destination);\n sharedOutput = output;\n return output;\n}\nfunction renderRecipe(context, recipe, volume) {\n const now = context.currentTime;\n const output = getOutput(context);\n const master = context.createGain();\n master.gain.value = recipe.masterGain * volume;\n master.connect(output);\n const shimmerNodes = recipe.shimmer\n ? attachShimmer(context, master, output, recipe.shimmer)\n : [];\n for (const layer of recipe.layers) {\n const startTime = now + (layer.offset ?? 0);\n if (layer.kind === \"tone\")\n renderTone(context, master, layer, startTime);\n else\n renderNoise(context, master, layer, startTime);\n }\n const cleanupAfterMs = (sourceEnd(recipe) + shimmerTail(recipe.shimmer) + CLEANUP_MARGIN) * 1000;\n setTimeout(() => {\n master.disconnect();\n for (const node of shimmerNodes)\n node.disconnect();\n }, cleanupAfterMs);\n}\nlet sharedContext = null;\nlet enabled = true;\nlet globalVolume = 1;\nfunction normalizeVolume(value, fallback) {\n return typeof value === \"number\" && Number.isFinite(value)\n ? Math.min(1, Math.max(0, value))\n : fallback;\n}\n/** Enables or disables future playback. Preference storage stays with the app. */\nexport function setEnabled(value) {\n if (typeof value === \"boolean\")\n enabled = value;\n}\n/** Sets the volume multiplier for future playback. Preference storage stays with the app. */\nexport function setVolume(value) {\n globalVolume = normalizeVolume(value, globalVolume);\n}\nfunction getAudioContext() {\n if (sharedContext)\n return sharedContext;\n if (typeof window === \"undefined\")\n return null;\n const Ctor = window.AudioContext ??\n window.webkitAudioContext;\n if (!Ctor)\n return null;\n try {\n sharedContext = new Ctor();\n }\n catch {\n return null;\n }\n return sharedContext;\n}\n/**\n * Plays a sound immediately. Safe to call from anywhere — lazily creates\n * the shared `AudioContext` on first use, resumes it if the browser\n * started it suspended (e.g. before any user gesture), and is a no-op\n * when Web Audio is unavailable (SSR, old browsers).\n */\nexport function play(sound = \"chime\", options) {\n if (!enabled || !isSoundName(sound))\n return;\n if (typeof navigator !== \"undefined\" && navigator.userActivation?.hasBeenActive === false)\n return;\n const playVolume = globalVolume * normalizeVolume(options?.volume, 1);\n if (playVolume === 0)\n return;\n const context = getAudioContext();\n if (!context)\n return;\n const recipe = RECIPES[sound];\n if (context.state === \"running\") {\n renderRecipe(context, recipe, playVolume);\n }\n else {\n try {\n void context.resume().then(() => {\n if (enabled && context.state === \"running\")\n renderRecipe(context, recipe, playVolume);\n }, () => { });\n }\n catch {\n // Some browsers throw synchronously when audio is blocked.\n }\n }\n}\n","\"use client\";\n\nimport { play } from \"cuelume\";\nimport { useEffect, useRef } from \"react\";\nimport type { SupportMessage } from \"../types\";\n\n/**\n * Under the default, because this plays on somebody else's site while they are\n * reading it. Loud enough to catch with the panel closed, quiet enough to sit\n * behind whatever else they have on.\n */\nconst ARRIVAL_VOLUME = 0.45;\n\n/**\n * The twinkle a reply lands on, whether the panel is open or not. Closed is\n * where it earns its keep: the count on the launcher is the only other thing\n * saying anything came in, and nobody is looking at the corner.\n *\n * The same rule the ring and the navigation follow, for the same reason: the\n * first snapshot is history, however recent, and history does not ring. Without\n * it, every page of a host's site would open on a sparkle for a conversation\n * from last week. A thread being fetched again is a thread this page has not\n * read either, so whatever lands after that is history too.\n *\n * And a change of thread is a first snapshot of its own. Somebody signing in\n * hands this page an account's whole history at once, which is a great many\n * replies it has never heard and not one of them worth a sound.\n *\n * Only the other side's messages, and only once they are written. A bubble\n * holding dots keeps its id and fills in later, so waiting for the words means\n * the sound arrives with them rather than with the promise of them.\n *\n * `play` neither throws nor asks: it is a no-op without Web Audio, and stays\n * quiet until the visitor has interacted with the page at all. That last one is\n * the browser's rule about autoplay, and it is a rule worth keeping.\n */\nexport function useMessageSound({\n messages,\n conversationId = null,\n isLoading,\n}: {\n messages: SupportMessage[];\n /** The thread being watched. A different one starts the listening over. */\n conversationId?: string | null;\n /** Nothing is history until the first answer has arrived. */\n isLoading: boolean;\n}) {\n /** Every written reply this page has already heard. */\n const heard = useRef<Set<string> | null>(null);\n /** Which thread that set is about. */\n const watching = useRef<string | null>(null);\n\n useEffect(() => {\n if (isLoading) {\n heard.current = null;\n return;\n }\n\n const written = messages.filter(\n (message) => message.sender === \"agent\" && !message.pending,\n );\n\n if (heard.current === null || watching.current !== conversationId) {\n heard.current = new Set(written.map((message) => message.id));\n watching.current = conversationId;\n return;\n }\n\n // One sparkle however many arrived together: a person taking over and\n // answering in the same breath is still one thing to look up at.\n let hasArrived = false;\n for (const message of written) {\n if (heard.current.has(message.id)) continue;\n heard.current.add(message.id);\n hasArrived = true;\n }\n\n if (hasArrived) play(\"sparkle\", { volume: ARRIVAL_VOLUME });\n }, [messages, conversationId, isLoading]);\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport type {\n SupportAsk,\n SupportDestination,\n SupportMessage,\n} from \"../types\";\n\n/**\n * Doing what one of the team's own tools asked to be done to the page:\n * pointing at something on it, or offering to take the reader somewhere.\n *\n * The two are different kinds of thing, so they happen at different times.\n * A ring is ours: drawn over the host's page for a moment and taken away\n * again, with nothing of theirs touched. Moving somebody is not. A page that\n * changes under a reader who touched nothing is the one thing a widget can\n * do that feels like it has been let loose, so a destination is offered as a\n * button and the page changes when it is pressed, or never.\n *\n * That makes most of the timing questions somebody else's. A button is inert\n * until somebody reaches for it, so a message carrying one can be as old as\n * it likes: it is an offer sitting in a thread, the same as a link would be.\n *\n * The ring still has to be timed, and this is where that is done. The thread\n * is a live query and a message stays in it forever, so \"ring what I can see\"\n * would ring every time the query re-ran, and again every time they came back\n * to the page. What counts as new is what arrived after the widget started\n * watching: the first snapshot is history, however recent, and history is not\n * an instruction.\n *\n * A thread can also change under a page that never stopped watching it.\n * Somebody signs in, the widget says who they are, and a query that answered\n * \"no conversation\" a second ago answers with the account's whole history\n * instead. Every word of it is new to this page and none of it is news, so a\n * change of thread starts the first look over, and a ring has to be recent as\n * well as unseen before it is drawn.\n */\n\n// Guarded as in control.ts: the published bundle cannot assume `process`.\nconst isDevelopment =\n typeof process !== \"undefined\" && process.env.NODE_ENV === \"development\";\n\n/**\n * How recently something has to have been asked for to be worth doing.\n *\n * Long enough that a slow network, a reconnect, or a tab coming back from\n * sleep still lands the one that just happened. Short enough that a\n * conversation from last week cannot ring anything, whatever route it takes\n * into the query: the person reading this page did not ask for it, and\n * whoever did is long gone.\n *\n * Generous because the two ends of the comparison are two different clocks.\n * The instruction is stamped by the server and the age is worked out on a\n * machine that may be a minute out either way, so the window is minutes\n * rather than seconds. Nothing about a live exchange needs it tighter.\n */\nconst STALE_AFTER_MS = 5 * 60 * 1000;\n\n/** The ring, once something has asked for one and it is time to draw it. */\nexport type SupportSpotlight = {\n /** New every time, so the same ask pressed twice rings twice. */\n key: string;\n selector: string;\n};\n\n/** Whether something asked for a beat ago rather than a while ago. */\nfunction isFresh(at: number): boolean {\n const age = Date.now() - at;\n // No clock on it at all reads as old. That is canned content, or a\n // deployment answering from before any of this, and the safe reading of\n // \"I cannot tell how old this is\" is to leave the page alone.\n return Number.isFinite(age) && age <= STALE_AFTER_MS;\n}\n\n/**\n * The destination as somewhere on this site, or null.\n *\n * Median checks the path when the tool answers and again before it is stored,\n * and neither of those is on the machine that navigates. This one is. `//host`\n * and `/\\host` resolve to another origin rather than a path, which is the\n * whole reason this asks the URL parser instead of reading the string.\n */\nfunction pathOnThisSite(destination: string): string | null {\n if (typeof window === \"undefined\") return null;\n if (!destination.startsWith(\"/\")) return null;\n\n let url: URL;\n try {\n url = new URL(destination, window.location.origin);\n } catch {\n return null;\n }\n if (url.origin !== window.location.origin) return null;\n\n return `${url.pathname}${url.search}${url.hash}`;\n}\n\n/**\n * Everything asking for a ring right now, oldest first.\n *\n * Only the ones with nowhere to go. A tool that asked for both is pointing at\n * something on the page it offered, and that page is one nobody has opened\n * yet, so its ring waits for the press that takes them there.\n *\n * Two carriers, one list. A message carries what the agent's own turn\n * produced; a standalone ask carries one from a run that never produced a\n * message, which is a teammate running a tool themselves. The loose one goes\n * last because it is the newer of the two whenever both land together: a\n * teammate reaches for a tool in a thread the agent has already answered.\n */\nfunction ringing(messages: SupportMessage[], ask: SupportAsk | null) {\n const asked: { key: string; at: number; selector: string }[] = [];\n\n for (const message of messages) {\n if (message.highlight === undefined) continue;\n if (message.navigateTo !== undefined) continue;\n asked.push({\n key: message.id,\n // Canned content may leave the clock off, and a message with no time on\n // it reads as old here rather than as now.\n at: message.sentAt ?? 0,\n selector: message.highlight,\n });\n }\n\n if (ask !== null && ask.highlight !== null && ask.navigateTo === null) {\n asked.push({ key: ask.key, at: ask.at, selector: ask.highlight });\n }\n return asked;\n}\n\nexport function useSupportNavigation({\n messages,\n ask = null,\n conversationId = null,\n isLoading,\n onNavigate,\n}: {\n messages: SupportMessage[];\n /** The one not riding a message, when a run produced one. */\n ask?: SupportAsk | null;\n /**\n * The thread being watched. When it changes, what is on screen is somebody\n * else's history rather than this page's next line, so the first look\n * starts again.\n */\n conversationId?: string | null;\n /** Nothing is history until the first answer has arrived. */\n isLoading: boolean;\n onNavigate?: (path: string) => void;\n}): {\n /** What to ring, while there is something. */\n spotlight: SupportSpotlight | null;\n /** A page offered by a run with no message to hang a button on. */\n offer: SupportDestination | null;\n /** What a button does. */\n follow: (destination: SupportDestination) => void;\n} {\n /** Every ask this page has already had a look at. */\n const seen = useRef<Set<string> | null>(null);\n /** Which thread that set is about. */\n const watching = useRef<string | null>(null);\n const [spotlight, setSpotlight] = useState<SupportSpotlight | null>(null);\n /** How many rings have been drawn, so a repeat is never the same one. */\n const drawn = useRef(0);\n /** The loose offer they have already taken, which is not one to make twice. */\n const [taken, setTaken] = useState<string | null>(null);\n\n const follow = useCallback(\n (destination: SupportDestination) => {\n if (onNavigate === undefined) return;\n\n const path = pathOnThisSite(destination.path);\n if (path === null) {\n console.error(\n `Median: refused to navigate to ${destination.path}, which is not a path on this site.`,\n );\n return;\n }\n\n onNavigate(path);\n setTaken(destination.key);\n\n // The page is on its way, so whatever was to be pointed out on it is\n // now this page's business. The ring looks for the element for a few\n // seconds, which is roughly how long a route change takes.\n if (destination.highlight !== null) {\n drawn.current += 1;\n setSpotlight({\n key: `${destination.key}:${drawn.current}`,\n selector: destination.highlight,\n });\n }\n },\n [onNavigate],\n );\n\n // A layout effect, as the ring's own arrival is: it and the message that\n // explains it come in one live-query update, and the ring should be up\n // before the browser paints the words about it.\n useLayoutEffect(() => {\n if (isLoading) return;\n\n const asked = ringing(messages, ask);\n\n // The first look is the thread as it already was. Remembered, never\n // acted on: whatever is in it happened before this page existed.\n //\n // A different thread than the one this set was built for is a first look\n // as well. That is signing in: the account's history arrives whole, into\n // a page that has been watching an empty query, and none of it happened\n // here.\n if (seen.current === null || watching.current !== conversationId) {\n seen.current = new Set(asked.map((one) => one.key));\n watching.current = conversationId;\n return;\n }\n\n // Only the newest is worth drawing. Two in one turn is a turn that has\n // already gone wrong, and the later of them is the one the words\n // underneath are about.\n let latest: SupportSpotlight | null = null;\n\n for (const one of asked) {\n if (seen.current.has(one.key)) continue;\n // Marked before it is drawn, so a re-render cannot ring twice.\n seen.current.add(one.key);\n // Unseen and still not news. Pointing at something is a thing being\n // done to a page in front of a reader, so one that has been sitting in\n // a thread since yesterday is not carried out late.\n if (!isFresh(one.at)) continue;\n\n drawn.current += 1;\n latest = { key: `${one.key}:${drawn.current}`, selector: one.selector };\n }\n\n if (latest !== null) setSpotlight(latest);\n }, [messages, ask, conversationId, isLoading]);\n\n // A host who wired a navigating tool but no handler hears about it here\n // rather than by wondering where the button went. Once per page: the\n // message carrying the offer stays in the thread for good, and a warning\n // that repeated with it would be the console's whole contents.\n const warned = useRef(false);\n useEffect(() => {\n if (!isDevelopment || onNavigate !== undefined || warned.current) return;\n\n const offered =\n messages.some((message) => message.navigateTo !== undefined) ||\n ask?.navigateTo != null;\n if (!offered) return;\n\n warned.current = true;\n console.warn(\n \"Median: a tool offered to take this visitor to a page, but <MedianSupport> has no onNavigate, so no button is shown for it. Pass one, such as onNavigate={router.push}.\",\n );\n }, [messages, ask, onNavigate]);\n\n // A loose offer stands while it is fresh. Nothing else expires here, since\n // nothing else is a button standing over the composer of a conversation the\n // reader may have carried on past.\n const [, expire] = useState(0);\n useEffect(() => {\n if (ask === null || ask.navigateTo === null) return;\n\n const left = ask.at + STALE_AFTER_MS - Date.now();\n if (left <= 0) return;\n const timer = setTimeout(() => expire((count) => count + 1), left);\n return () => clearTimeout(timer);\n }, [ask]);\n\n const offer =\n ask !== null &&\n ask.navigateTo !== null &&\n ask.key !== taken &&\n onNavigate !== undefined &&\n isFresh(ask.at)\n ? { key: ask.key, path: ask.navigateTo, highlight: ask.highlight }\n : null;\n\n return { spotlight, offer, follow };\n}\n","\"use client\";\n\nimport { useCallback, useState } from \"react\";\nimport type { SupportMessage } from \"../types\";\nimport { readTeaserDismissedAt, writeTeaserDismissedAt } from \"./teaser-state\";\n\n/**\n * Which reply, if any, gets the card over the closed button.\n *\n * The newest unread reply, unless this browser already waved it away. A\n * reply that lands after a dismissal is news again and gets a card of its\n * own, which is the whole reason the dismissal is a time rather than a flag.\n *\n * Opening the panel is handled elsewhere: it marks everything seen, the\n * unread list empties, and there is nothing left here to show.\n */\nexport function useTeaser(\n apiKey: string,\n /** The newest reply they have not looked at, or null. */\n latestUnread: SupportMessage | null,\n): { teaser: SupportMessage | null; dismiss: () => void } {\n const [dismissedAt, setDismissedAt] = useState(() =>\n readTeaserDismissedAt(apiKey),\n );\n\n const teaser =\n latestUnread !== null && (latestUnread.sentAt ?? 0) > dismissedAt\n ? latestUnread\n : null;\n\n const dismiss = useCallback(() => {\n if (teaser === null) return;\n // Stamped with when the reply was sent rather than with now, so a reply\n // that has been sitting on the server since before the click still\n // counts as dismissed, and one that arrives a second later does not.\n const at = teaser.sentAt ?? Date.now();\n writeTeaserDismissedAt(apiKey, at);\n setDismissedAt(at);\n }, [apiKey, teaser]);\n\n return { teaser, dismiss };\n}\n","/**\n * A timestamp this browser remembers, per widget.\n *\n * Kept beside the session token and for the same reason: a host's site is\n * usually several pages, so the widget is remounted every time somebody\n * clicks a link. Held in memory, anything decided while the panel was\n * closed would be forgotten the moment they wandered to another page.\n *\n * One entry per API key, like the session, so two widgets on one browser\n * cannot read each other's marks. Used for the dismissed teaser; read\n * receipts are stored on the server so they follow the visitor's devices.\n */\n\n/** Where it lives when localStorage is blocked. Lasts the page load. */\nconst inMemory = new Map<string, number>();\n\nfunction keyOf(name: string, apiKey: string): string {\n return `median:${name}:${apiKey}`;\n}\n\n/** The stamp, or 0 when this browser has never written one. */\nexport function readStamp(name: string, apiKey: string): number {\n if (typeof window === \"undefined\") return 0;\n\n const key = keyOf(name, apiKey);\n try {\n const stored = window.localStorage.getItem(key);\n const at = stored === null ? Number.NaN : Number(stored);\n return Number.isFinite(at) ? at : 0;\n } catch {\n return inMemory.get(key) ?? 0;\n }\n}\n\nexport function writeStamp(name: string, apiKey: string, at: number): void {\n if (typeof window === \"undefined\") return;\n\n const key = keyOf(name, apiKey);\n inMemory.set(key, at);\n try {\n window.localStorage.setItem(key, String(at));\n } catch {\n // Blocked, so the in-memory copy above is all there is. It still holds\n // for as long as this page does, which is the part anybody would notice.\n }\n}\n","import { readStamp, writeStamp } from \"./stamps\";\n\n/**\n * The newest reply whose teaser this browser waved away.\n *\n * Its own stamp rather than the read one, because the two mean different\n * things. Dismissing the card says \"not now\", and the count stays on the\n * button until the panel is actually opened; marking a reply read would\n * take the count with it, and a visitor who closed a card has not read the\n * three replies behind it.\n */\n\nconst TEASER = \"teaser\";\n\n/** When the last dismissed reply was sent, or 0 if none ever was. */\nexport function readTeaserDismissedAt(apiKey: string): number {\n return readStamp(TEASER, apiKey);\n}\n\nexport function writeTeaserDismissedAt(apiKey: string, at: number): void {\n writeStamp(TEASER, apiKey, at);\n}\n","\"use client\";\n\nimport { useEffect } from \"react\";\n\n/** The count ahead of the host's title, the way mail has always shown it. */\nfunction badged(count: number, title: string): string {\n return `(${count}) ${title}`.trim();\n}\n\n/**\n * Puts the unread count in the tab's title while there is one, and takes it\n * back out when there is not.\n *\n * A reply that lands while the visitor is on another tab has no way to say\n * so: the launcher's count and the card over it are in a tab nobody is\n * looking at, and the sound is one moment. The title is the one thing of\n * this page that is visible from every other tab, so it carries the count.\n *\n * The host's own title is held and put back, and a host that changes it\n * while the count is up, which is what a router does on every navigation,\n * has the count moved onto the new one. Nothing is put back over a title\n * that is no longer the badged one: if the host wrote something else in\n * the meantime, that is theirs and it stays.\n */\nexport function useUnreadTitle(unreadCount: number) {\n useEffect(() => {\n if (unreadCount === 0 || typeof document === \"undefined\") return;\n\n // Read now rather than on mount, since the host may have renamed the\n // page any number of times since then.\n let plain = document.title;\n let shown = badged(unreadCount, plain);\n document.title = shown;\n\n // The host writing a new title underneath. Our own write lands here\n // too, and is told apart by being exactly what was written.\n const observer = new MutationObserver(() => {\n const current = document.title;\n if (current === shown) return;\n plain = current;\n shown = badged(unreadCount, plain);\n document.title = shown;\n });\n observer.observe(document.head, {\n childList: true,\n subtree: true,\n characterData: true,\n });\n\n return () => {\n observer.disconnect();\n if (document.title === shown) document.title = plain;\n };\n }, [unreadCount]);\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { isDevelopment } from \"./open-store\";\n\n/**\n * What a mounted component hands back: the two things a host without React\n * has no re-render to do for them. Everything else goes through the\n * component's own control, same as anywhere.\n */\nexport type MedianMountInstance<Options> = {\n /**\n * Change any option after mount. Only the keys passed change, so\n * `update({ user })` when somebody signs in leaves the rest alone.\n */\n update: (options: Partial<Options>) => void;\n /** Take it off the page: the component, its container, its connection. */\n unmount: () => void;\n};\n\n/** An instance that is safe to hold when nothing could be mounted. */\nexport const nothingMounted: MedianMountInstance<object> = {\n update: () => {},\n unmount: () => {},\n};\n\n/**\n * The one way every mount renders, so the four of them cannot drift.\n *\n * A container of ours is appended to `into` and rendered into with the\n * package's copy of React. The host's page needs none and never sees it.\n * `into` is the body unless the caller has somewhere better, which the\n * contact form does: it goes where the host's page has room for it.\n *\n * A server rendering the page has nowhere to put anything, so that case\n * says so where the installer is looking and hands back an instance that is\n * safe to hold, because a broken mount must never take the page down with it.\n */\nexport function mountMedian<Options extends object>({\n caller,\n what,\n marker,\n into,\n options,\n render,\n}: {\n /** The function the host called, for the console. */\n caller: string;\n /** What was mounted, in the console's words: \"the widget\", \"the form\". */\n what: string;\n /** The dataset key the container wears, so it can be found in devtools. */\n marker: string;\n /** Where the container goes. Null for the body. */\n into: Element | null;\n options: Options;\n render: (options: Options) => ReactNode;\n}): MedianMountInstance<Options> {\n if (typeof document === \"undefined\") {\n console.error(\n `Median: ${caller}() was called where there is no document, so nothing was mounted. Call it in the browser, after the page exists.`,\n );\n return nothingMounted;\n }\n\n const container = document.createElement(\"div\");\n container.dataset[marker] = \"\";\n (into ?? document.body).appendChild(container);\n\n const root = createRoot(container);\n let current = options;\n let mounted = true;\n\n const paint = () => {\n root.render(render(current));\n };\n paint();\n\n return {\n update(next) {\n if (!mounted) {\n if (isDevelopment) {\n console.warn(\n `Median: update() was called after unmount(), so nothing changed. Mount again with ${caller}() if ${what} should come back.`,\n );\n }\n return;\n }\n current = { ...current, ...next };\n paint();\n },\n unmount() {\n if (!mounted) return;\n mounted = false;\n root.unmount();\n container.remove();\n },\n };\n}\n","\"use client\";\n\nimport { MedianSupport, type MedianSupportProps } from \"./median-support\";\nimport { mountMedian, type MedianMountInstance } from \"./mount-into\";\n\n/**\n * What a mounted widget hands back: the two things a host without React has\n * no re-render to do for them. Everything else goes through `medianSupport`,\n * same as anywhere.\n */\nexport type MedianSupportInstance = MedianMountInstance<MedianSupportProps>;\n\n/**\n * The same widget, for a page that has never heard of React: Vue, Svelte,\n * Rails, a script on plain HTML. One call puts the launcher in the corner,\n * wired to the same panel, thread and inbox as the component:\n *\n * const support = mountMedianSupport({ apiKey: \"median_pk_...\" });\n *\n * It takes the component's props, as one options object, and the rest of the\n * SDK does not know the difference: `medianSupport.open()` opens this widget\n * exactly as it opens the component.\n *\n * What would have been new props on a re-render is `update`:\n *\n * support.update({ user: { name: \"Ada\", email: \"ada@example.com\" } });\n *\n * It renders into a container of its own, appended to `<body>`, with the\n * package's copy of React. The host's page needs none and never sees it.\n */\nexport function mountMedianSupport(\n options: MedianSupportProps,\n): MedianSupportInstance {\n return mountMedian({\n caller: \"mountMedianSupport\",\n what: \"the widget\",\n marker: \"medianSupport\",\n into: null,\n options,\n render: (current) => <MedianSupport {...current} />,\n });\n}\n","\"use client\";\n\nimport { MedianSupportPreview, type MedianSupportProps } from \"./median-support\";\n\n/**\n * Every prop the widget takes, unchanged. The modal is the widget in a\n * different place, not a different widget.\n */\nexport type MedianSupportModalProps = MedianSupportProps;\n\n/**\n * The support widget as a modal: the same button in the corner, and a panel\n * that opens in the middle of the screen over a scrim rather than out of\n * the corner.\n *\n * <MedianSupportModal apiKey=\"median_pk_...\" />\n *\n * Everything else is the widget's. Same props, same thread, same inbox on\n * the other side, same `useMedianSupport()` to open it from your own nav,\n * same sheet on a phone. Pick this for a site where a conversation deserves\n * the room, or where a panel floating over the page would be in the way of\n * the page.\n *\n * One or the other. Mounting both shares one open state, and the console\n * says so.\n */\nexport function MedianSupportModal(props: MedianSupportModalProps) {\n return <MedianSupportPreview {...props} presentation=\"modal\" />;\n}\n","\"use client\";\n\nimport {\n MedianSupportModal,\n type MedianSupportModalProps,\n} from \"./median-support-modal\";\nimport { mountMedian, type MedianMountInstance } from \"./mount-into\";\n\nexport type MedianSupportModalInstance =\n MedianMountInstance<MedianSupportModalProps>;\n\n/**\n * The modal, for a page that has never heard of React: Vue, Svelte, Rails,\n * a script on plain HTML.\n *\n * const support = mountMedianSupportModal({ apiKey: \"median_pk_...\" });\n *\n * It takes the component's props as one options object, and the rest of the\n * SDK does not know the difference: `medianSupport.open()` opens this modal\n * exactly as it opens the component. `update` and `unmount` are the same two\n * `mountMedianSupport` hands back.\n */\nexport function mountMedianSupportModal(\n options: MedianSupportModalProps,\n): MedianSupportModalInstance {\n return mountMedian({\n caller: \"mountMedianSupportModal\",\n what: \"the modal\",\n marker: \"medianSupportModal\",\n into: null,\n options,\n render: (current) => <MedianSupportModal {...current} />,\n });\n}\n","\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport { FeedbackPanel } from \"../feedback/feedback-panel\";\nimport { FeedbackPopover } from \"../feedback/feedback-popover\";\nimport type { MedianDiagnostics } from \"./diagnostics\";\nimport { MedianErrorBoundary } from \"./error-boundary\";\nimport {\n useMedianFeedbackOpenState,\n useRegisterMedianFeedback,\n} from \"./feedback-control\";\nimport { useRememberInstall } from \"./install\";\nimport { MedianConnection } from \"./median-connection\";\nimport { useApiKeyShapeWarning } from \"./use-api-key-warning\";\nimport { useCustomDataSender } from \"./custom-data\";\nimport { useSignedInUser } from \"./signed-out\";\nimport { useMedianIdentity, useMissingCredentials } from \"./use-identity\";\nimport { useFeedbackNote } from \"./use-feedback-note\";\nimport { useMedianBranding } from \"./use-median-branding\";\nimport type { MedianUser } from \"./use-median-thread\";\n\n/** Wide enough for a paragraph, narrow enough to hang off a menu item. */\nconst DEFAULT_WIDTH = \"20rem\";\n\ntype MedianFeedbackOptions = {\n /**\n * Your own trigger. Whatever you pass is rendered where you put it and the\n * panel hangs off it, the way a dropdown hangs off the thing that opens\n * it:\n *\n * <MedianFeedback apiKey=\"median_pk_...\">\n * <button>Share feedback</button>\n * </MedianFeedback>\n *\n * It is wrapped in an inline span that takes the click, so your button\n * keeps its own styles and needs to know nothing about any of this.\n *\n * Without children the panel sits in the bottom right corner instead, for\n * a host opening it from somewhere else entirely with\n * `useMedianFeedback()`.\n */\n children?: ReactNode;\n /**\n * Something already on your page for the panel to hang off, as a CSS\n * selector. For the case children cannot cover: a row inside a dropdown,\n * which closes itself opening this and takes the trigger with it.\n *\n * <MedianFeedback apiKey=\"median_pk_...\" anchor=\"#account-button\" />\n *\n * Looked up each time the panel is placed, so it works for anything that\n * comes and goes. Unlike a trigger it is only a position: pressing it does\n * not open or close the panel, and a press on it dismisses the panel like\n * a press anywhere else. Ignored when `children` are passed.\n */\n anchor?: string;\n /** Who is signed in on your site, if anyone. Shown to your team. */\n user?: MedianUser;\n /**\n * Whether a note carries what the browser already knows: time zone,\n * language, browser, OS, device, screen, and the page it was written on.\n * On by default, because the page is most of what makes a note actionable,\n * which is what the panel says out loud. Pass `false` and none of it is\n * read at all.\n */\n telemetry?: boolean;\n /**\n * Whether a note carries a picture of the page it was written on, drawn\n * from the page itself rather than recorded off the screen, so nobody is\n * asked for permission to share anything. On by default: a bug report with\n * the screen on it is one nobody has to describe. Pass `false` and no\n * picture is taken.\n *\n * The panel itself is never in the picture, and neither is anything else\n * Median renders.\n */\n screenshot?: boolean;\n /**\n * Watch what goes wrong on the page, so a bug filed from a note arrives\n * with the error already attached. The same switch `<MedianSupport>`\n * takes, and the same buffer behind it: mount either one with it on.\n */\n diagnostics?: MedianDiagnostics;\n /**\n * Anything about your own app worth having next to a bug report: a build,\n * a route, a flag. Read when a note is sent.\n */\n appState?: Record<string, string | number | boolean>;\n /** Pass it, with `onOpenChange`, to hold the open state yourself. */\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n /** Any CSS length. The panel never grows past the viewport whatever this says. */\n width?: string;\n /** The heading, for a host whose product calls this something else. */\n title?: string;\n /** The line under it. */\n description?: string;\n placeholder?: string;\n};\n\n/**\n * Two ways in, the same two `<MedianSupport>` takes.\n *\n * `apiKey` is the publishable id on its own. `identity` is the path to a\n * route built with `medianIdentity`, which answers with the key and with\n * whoever is signed in, already signed. Pass both and the panel is usable on\n * the key from the prop while the route is still answering.\n */\nexport type MedianFeedbackProps = MedianFeedbackOptions &\n (\n | { apiKey: string; identity?: string }\n | { apiKey?: string; identity: string }\n );\n\n/** The same, once the gate below has settled which key and which person. */\ntype ConnectedFeedbackProps = MedianFeedbackOptions & { apiKey: string };\n\n/**\n * The same two ways in, for a host without JSX. Spelled out rather than\n * `Omit`ped off the props above, because omitting a key from a union of two\n * shapes flattens it into one and loses the \"either, but not neither\" the\n * union is there to say.\n */\nexport type MedianFeedbackMountOptions = Omit<\n MedianFeedbackOptions,\n \"children\"\n> &\n (\n | { apiKey: string; identity?: string }\n | { apiKey?: string; identity: string }\n );\n\n/**\n * A feedback panel, hung off a trigger of your own.\n *\n * One box and one button. Whoever is on the page types a sentence, and a\n * fast model reads it: a bug goes on the bug list, an idea goes on the\n * suggestions list, and an advert goes nowhere. Nobody has to pick a\n * category, rank anything, or write a title, which is the point, because a\n * form that asks for those is a form nobody fills in.\n *\n * <MedianFeedback apiKey=\"median_pk_...\">\n * <MenuItem>Share feedback</MenuItem>\n * </MedianFeedback>\n *\n * The page they were on rides along on its own, and so does a picture of it.\n * `screenshot={false}` leaves the picture behind.\n *\n * It brings its own connection, so it works on pages that have never heard\n * of Convex, and it is independent of `<MedianSupport>`: mount either, both,\n * or neither. Mounted together they share a visitor, so the same person\n * writing in and leaving a note is one customer with both to their name.\n */\nexport function MedianFeedback(props: MedianFeedbackProps) {\n return (\n <MedianErrorBoundary component=\"MedianFeedback\">\n <FeedbackIdentity {...props} />\n </MedianErrorBoundary>\n );\n}\n\n/**\n * The same gate `<MedianSupport>` has, for the same reason: the signature can\n * only be made on a server and this is a client component. Both panels on one\n * page read the one route between them. See `use-identity.ts`.\n */\nfunction FeedbackIdentity({\n apiKey,\n identity,\n user,\n ...rest\n}: MedianFeedbackOptions & { apiKey?: string; identity?: string }) {\n const resolved = useMedianIdentity(identity);\n useMissingCredentials(\"<MedianFeedback>\", apiKey, identity);\n const key = apiKey ?? resolved.apiKey;\n const person = useSignedInUser(user ?? resolved.user);\n\n if (key === undefined) return null;\n\n return (\n <MedianConnection what=\"feedback panel\">\n <Feedback {...rest} apiKey={key} {...(person ? { user: person } : {})} />\n </MedianConnection>\n );\n}\n\nfunction Feedback({\n apiKey,\n children,\n anchor,\n user,\n telemetry,\n screenshot,\n diagnostics,\n appState,\n open,\n onOpenChange,\n width = DEFAULT_WIDTH,\n title,\n description,\n placeholder,\n}: ConnectedFeedbackProps) {\n useRegisterMedianFeedback();\n useApiKeyShapeWarning(\"<MedianFeedback>\", apiKey);\n useCustomDataSender();\n // The support panel writes the same thing down. Either one is enough for a\n // crash screen to have a key to report with.\n useRememberInstall(apiKey, user);\n\n const triggerRef = useRef<HTMLSpanElement>(null);\n const [isOpen, setOpen] = useMedianFeedbackOpenState({\n ...(open !== undefined ? { open } : {}),\n ...(onOpenChange ? { onOpenChange } : {}),\n });\n\n const note = useFeedbackNote({\n apiKey,\n ...(user ? { user } : {}),\n ...(telemetry !== undefined ? { telemetry } : {}),\n ...(screenshot !== undefined ? { screenshot } : {}),\n ...(diagnostics !== undefined ? { diagnostics } : {}),\n ...(appState ? { appState } : {}),\n });\n const showBranding = useMedianBranding(apiKey);\n\n const close = useCallback(() => setOpen(false), [setOpen]);\n\n // A fresh box for the next note, but not until this one is out of sight:\n // resetting as the panel closes would swap the thank-you back to an empty\n // form halfway through the fade. So the panel is remounted on the way\n // back in, and only when there is something to clear. A draft somebody\n // closed without sending is theirs, and survives.\n const [attempt, setAttempt] = useState(0);\n const hasSent = useRef(false);\n useEffect(() => {\n if (!isOpen || !hasSent.current) return;\n hasSent.current = false;\n setAttempt((count) => count + 1);\n }, [isOpen]);\n\n return (\n <>\n {children !== undefined && (\n // An inline span rather than a clone of whatever was passed: the\n // host's trigger keeps its own props, its own ref and its own\n // styles, and the click is caught on the way past. `display:\n // contents` would lose the rectangle the panel is measured against.\n <span\n ref={triggerRef}\n onClick={() => setOpen(!isOpen)}\n className=\"inline-flex\"\n >\n {children}\n </span>\n )}\n\n <FeedbackPopover\n isOpen={isOpen}\n onClose={close}\n triggerRef={triggerRef}\n {...(anchor !== undefined ? { anchor } : {})}\n width={width}\n >\n <FeedbackPanel\n showBadge={showBranding}\n key={attempt}\n onSubmit={note.submit}\n onClose={close}\n onSent={() => {\n hasSent.current = true;\n close();\n }}\n notice={note.notice}\n {...(title !== undefined ? { title } : {})}\n {...(description !== undefined ? { description } : {})}\n {...(placeholder !== undefined ? { placeholder } : {})}\n />\n </FeedbackPopover>\n </>\n );\n}\n","/**\n * The feedback panel's vocabulary: what a note may be, what it may point at,\n * and the three answers a reader can give about one.\n *\n * Deliberately pure, like `shared/signals.ts`: no Convex imports, nothing\n * that touches a database. The widget reads these limits before it sends and\n * the mutation enforces them on arrival, so neither side can drift.\n */\n\n/**\n * How much one note may be. Longer than a chat message would be worth\n * reading in a bubble and shorter than the write-up it turns into, because\n * this is a box somebody types a paragraph into, not a document.\n */\nexport const NOTE_MAX_LENGTH = 2_000;\n\n/**\n * Where a note goes.\n *\n * The first two are the Signal tab's own kinds, so a note that survives\n * reading lands on the list the team already triages. The third is the one\n * that goes nowhere: a page open to the internet collects adverts, keyboard\n * mashing, and people testing whether the box works, and none of that is\n * worth a row somebody has to close.\n */\nexport const FEEDBACK_VERDICTS = [\"bug\", \"suggestion\", \"spam\"] as const;\n\nexport type FeedbackVerdict = (typeof FEEDBACK_VERDICTS)[number];\n\n/** Trims a note, keeping its line breaks: somebody wrote it in a textarea. */\nexport function cleanNote(input: string): string {\n return input.trim().slice(0, NOTE_MAX_LENGTH);\n}\n\n/** What is wrong with this note, in a sentence, or null if nothing is. */\nexport function noteIssue(note: string): string | null {\n if (note.trim().length === 0) return \"Enter your feedback before sending.\";\n if (note.length > NOTE_MAX_LENGTH) {\n return `Feedback has to be ${NOTE_MAX_LENGTH} characters or fewer.`;\n }\n return null;\n}\n","import { isUnsafeAttachmentType } from \"./support\";\n\n/**\n * The pictures on a bug report: what a customer sent, what the agent found a\n * link to, and what a host handed over on purpose.\n *\n * A screenshot is the fastest thing on a report to read. Everything else on\n * one is a description of what went wrong; a picture is the thing itself. So\n * they are lifted out of wherever they arrived and kept on the report, where\n * a developer meets them without opening the conversation they came from.\n *\n * Two ways a picture can be here, and the difference is who is holding it.\n * `stored` is a file that came through us and lives in our storage, which is\n * what an upload from the widget or the feedback panel becomes. `linked` is\n * an address on somebody else's machine, which is what an agent or an API\n * caller can offer, since neither has a file to hand over.\n *\n * Deliberately pure, like `signals.ts` and `customData.ts` next door: the SDK\n * trims what it sends with this file and the mutation trims it again on\n * arrival, so neither side is trusting the other's arithmetic.\n */\n\n/**\n * The storage id is a plain string here so this file stays free of Convex.\n * The schema's validator is what pins it to `_storage`, and the model layer\n * hands these functions rows that already carry the real id.\n */\nexport type ReportImage<StorageId extends string = string> =\n | {\n kind: \"stored\";\n storageId: StorageId;\n name: string;\n type: string;\n size: number;\n }\n | { kind: \"linked\"; url: string; name: string };\n\n/** What an image link looks like as it arrives, before it is cleaned. */\nexport type ImageLink = { url: string; name?: string };\n\n/**\n * The most one report holds. Past a handful nobody is looking at the sixth\n * screenshot, and a thread that ran long would otherwise push the write-up\n * off the top of the card.\n */\nexport const MAX_REPORT_IMAGES = 6;\n\n/** Enough to say what a picture is without wrapping the row it sits in. */\nexport const IMAGE_NAME_MAX_LENGTH = 80;\n\n/** Long enough for a signed link with its query string still on it. */\nexport const IMAGE_URL_MAX_LENGTH = 2_000;\n\n/** When the address gave nothing worth calling it. */\nconst UNNAMED = \"Screenshot\";\n\n/**\n * Extensions a lone URL has to end in before we will treat it as a picture.\n *\n * Only used for guessing, never for something a caller explicitly called an\n * image. SVG is missing on purpose: a browser runs one, which is the same\n * reason `UNSAFE_ATTACHMENT_TYPES` turns them away as uploads.\n */\nconst IMAGE_EXTENSIONS = [\"png\", \"jpg\", \"jpeg\", \"gif\", \"webp\", \"avif\", \"bmp\"];\n\n/** Whether a content type is a picture we are willing to show. */\nexport function isImageContentType(type: string): boolean {\n const bare = type.split(\";\")[0].trim().toLowerCase();\n return bare.startsWith(\"image/\") && !isUnsafeAttachmentType(bare);\n}\n\n/**\n * An address a browser may be pointed at, or null.\n *\n * Only http and https: a `javascript:` or `data:` value in an `src` is the\n * whole of how a pasted link becomes somebody else's code running on the\n * dashboard, and neither is ever what a screenshot arrives as.\n */\nfunction cleanUrl(input: unknown): string | null {\n if (typeof input !== \"string\") return null;\n const url = input.trim();\n if (url.length === 0 || url.length > IMAGE_URL_MAX_LENGTH) return null;\n return /^https?:\\/\\/\\S+$/i.test(url) ? url : null;\n}\n\n/** The last part of the path, which is usually what the file is called. */\nfunction nameFromUrl(url: string): string {\n // Past the host as well as the query string: an address with nothing after\n // the domain has no name in it, and \"acme.com\" is not what a picture is\n // called.\n const path = url.split(/[?#]/)[0].replace(/^https?:\\/\\/[^/]*/i, \"\");\n const last = path.split(\"/\").filter(Boolean).pop();\n if (last === undefined) return UNNAMED;\n const name = decodeURIComponent(last).slice(0, IMAGE_NAME_MAX_LENGTH);\n return name.length > 0 ? name : UNNAMED;\n}\n\n/** Whatever it was called, trimmed to something a caption can hold. */\nfunction trimName(input: unknown): string | null {\n if (typeof input !== \"string\") return null;\n const name = input.trim().replace(/\\s+/g, \" \").slice(0, IMAGE_NAME_MAX_LENGTH);\n return name.length > 0 ? name : null;\n}\n\n/**\n * Whether an address looks like a picture from the outside.\n *\n * A guess, and only ever used where guessing is the point: a host attaching\n * `{ label: \"Screenshot\", value: \"https://…/broken.png\" }` through the\n * ordinary custom data door meant a picture, and asking them to learn a\n * second API to say so would be a worse product than reading the extension.\n * Anything that does not end in one stays a link, which is what it was.\n */\nexport function looksLikeImageUrl(value: unknown): string | null {\n const url = cleanUrl(value);\n if (url === null) return null;\n\n const path = url.split(/[?#]/)[0].toLowerCase();\n const dot = path.lastIndexOf(\".\");\n if (dot === -1) return null;\n return IMAGE_EXTENSIONS.includes(path.slice(dot + 1)) ? url : null;\n}\n\n/**\n * Whatever arrived, cut down to the links that may be stored. Undefined when\n * nothing survived, so an empty list never lands on a row and reads later as\n * \"they attached something and it was blank\".\n *\n * No extension check here: a caller passing this has said it is a picture,\n * and plenty of real ones are served from an address that ends in an id.\n */\nexport function cleanImageLinks(\n input: unknown,\n): Extract<ReportImage, { kind: \"linked\" }>[] | undefined {\n if (!Array.isArray(input)) return undefined;\n\n const byUrl = new Map<string, string>();\n for (const raw of input) {\n const source =\n typeof raw === \"string\" ? { url: raw } : (raw as ImageLink | null);\n if (typeof source !== \"object\" || source === null) continue;\n\n const url = cleanUrl(source.url);\n if (url === null) continue;\n // Re-set so a repeated address takes the newer name for it.\n byUrl.delete(url);\n byUrl.set(url, trimName(source.name) ?? nameFromUrl(url));\n }\n\n const links = [...byUrl].map(([url, name]) => ({\n kind: \"linked\" as const,\n url,\n name,\n }));\n return links.length > 0 ? links.slice(-MAX_REPORT_IMAGES) : undefined;\n}\n\n/**\n * Whatever arrived describing files already in our storage, cut down the\n * same way. The ids themselves are checked by the validator on the way in;\n * what this decides is whether the thing is a picture worth showing.\n */\nexport function cleanStoredImages<StorageId extends string>(\n input: readonly {\n storageId: StorageId;\n name: string;\n type: string;\n size: number;\n }[],\n): Extract<ReportImage<StorageId>, { kind: \"stored\" }>[] | undefined {\n const kept = input\n .filter((file) => isImageContentType(file.type))\n .map((file) => ({\n kind: \"stored\" as const,\n storageId: file.storageId,\n name: trimName(file.name) ?? UNNAMED,\n type: file.type,\n size: file.size,\n }));\n return kept.length > 0 ? kept.slice(-MAX_REPORT_IMAGES) : undefined;\n}\n\n/** What makes two pictures the same picture: the thing they point at. */\nfunction keyOf(image: ReportImage): string {\n return image.kind === \"stored\"\n ? `stored:${image.storageId}`\n : `linked:${image.url}`;\n}\n\n/**\n * Several sources of pictures, oldest source first, folded into one list.\n * The same rules as cleaning: one entry per picture, later wins, newest kept\n * when the cap says something has to go.\n */\nexport function mergeReportImages<StorageId extends string>(\n ...sources: (readonly ReportImage<StorageId>[] | undefined)[]\n): ReportImage<StorageId>[] | undefined {\n const byKey = new Map<string, ReportImage<StorageId>>();\n for (const image of sources.flatMap((list) => list ?? [])) {\n byKey.delete(keyOf(image));\n byKey.set(keyOf(image), image);\n }\n\n const images = [...byKey.values()];\n return images.length > 0 ? images.slice(-MAX_REPORT_IMAGES) : undefined;\n}\n","\"use client\";\n\nimport { NOTE_MAX_LENGTH } from \"@median/backend/feedback\";\nimport { MAX_REPORT_IMAGES } from \"@median/backend/reportImages\";\nimport {\n useEffect,\n useRef,\n useState,\n type ChangeEvent,\n type KeyboardEvent,\n} from \"react\";\nimport {\n AttachmentDraftChip,\n useAttachmentDrafts,\n useAttachmentTarget,\n} from \"@/components/attachments\";\nimport { ArrowsClockwise, Check, ImageSquare, X } from \"@/components/icons\";\nimport { Collapse } from \"@/components/ui/collapse\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { cn } from \"@/lib/utils\";\nimport { WIDGET_TEXT_FIELD_SHELL as TEXT_FIELD_SHELL } from \"../widget-field\";\nimport { PoweredByMedian } from \"../widget-badge\";\nimport { WidgetButton } from \"../widget-button\";\nimport { WIDGET_CORNER } from \"../widget-corner\";\nimport { AUTOFOCUS_ATTRIBUTE } from \"./feedback-popover\";\n\n/** How long the thank-you sits there before whoever owns the panel closes it. */\nconst THANKS_MS = 1_600;\n\n/**\n * The feedback panel: one box, one button, and no questions in front of them.\n *\n * A visitor with something to say should not first have to decide whether it\n * is a bug or an idea, rank it, or write a title. So the panel asks for a\n * sentence and nothing else, and the reader on the other side does the rest.\n * The page rides along on its own, and so does a picture of it, so a report\n * arrives with the thing being reported already on it.\n *\n * One surface, not two. The box sits straight on the card rather than inside\n * a tray of its own: a panel this small has no room for a container that\n * contains one thing, and the extra edge only made it look like there was\n * more here than there is.\n *\n * Presentational, like `SupportWidget`: a note goes out through `onSubmit`\n * and nothing is held here but the draft.\n */\nexport function FeedbackPanel({\n onSubmit,\n onClose,\n onSent,\n notice,\n showBadge = true,\n title = \"Share feedback\",\n description = \"Bugs, ideas, anything.\",\n placeholder = \"Tell us what could be better.\",\n className,\n}: {\n /**\n * Awaited. A rejection keeps the draft and the pictures, so a send that\n * fails costs a retry rather than a retype.\n */\n onSubmit?: (note: string, images: File[]) => unknown;\n /** Omit to hide the close button, for a panel that is always on the page. */\n onClose?: () => void;\n /** The thank-you has been read. Whoever opened the panel closes it. */\n onSent?: () => void;\n /** One line under the box, for a send that went wrong. */\n notice?: string | null;\n /** The \"Powered by Median\" line. Hosts on a paid plan can turn it off. */\n showBadge?: boolean;\n title?: string;\n description?: string;\n placeholder?: string;\n className?: string;\n}) {\n const [draft, setDraft] = useState(\"\");\n const [isSending, setSending] = useState(false);\n const [isSent, setSent] = useState(false);\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n // Pictures only. Everything else on a report is words, and a note is not a\n // place to hand over a database dump.\n const images = useAttachmentDrafts({\n accept: \"image\",\n max: MAX_REPORT_IMAGES,\n });\n const target = useAttachmentTarget({ onFiles: images.add });\n\n const canSend =\n draft.trim().length > 0 && !images.hasBlocker && !isSending && !isSent;\n\n // The thank-you is read, then the panel is somebody else's problem. In an\n // effect rather than in the handler so a panel unmounted in between never\n // calls back into a host that has moved on.\n useEffect(() => {\n if (!isSent) return;\n const timer = setTimeout(() => onSent?.(), THANKS_MS);\n return () => clearTimeout(timer);\n }, [isSent, onSent]);\n\n async function send() {\n if (!canSend) return;\n setSending(true);\n try {\n await onSubmit?.(draft.trim(), images.files);\n } catch {\n // The notice under the box says why. The draft and its pictures stay put.\n return;\n } finally {\n setSending(false);\n }\n images.clear();\n setSent(true);\n }\n\n function handlePick(event: ChangeEvent<HTMLInputElement>) {\n if (event.target.files?.length) images.add(event.target.files);\n // Cleared so picking the same file twice in a row still fires a change.\n event.target.value = \"\";\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLTextAreaElement>) {\n // Enter is a new line here, not a send: this is a paragraph box rather\n // than a chat composer, and the button is right there. Meta and enter\n // still sends, which is what anybody who tries it expects.\n if (event.key !== \"Enter\") return;\n if (!event.metaKey && !event.ctrlKey) return;\n event.preventDefault();\n void send();\n }\n\n return (\n <section\n aria-label={title}\n {...target.targetProps}\n className={cn(\n // The widget's own radius scale, so the app's token can climb as the\n // chrome gets rounder without this changing shape under its hosts.\n `flex w-full flex-col gap-2 overflow-hidden ${WIDGET_CORNER} border bg-card p-3 text-card-foreground [--radius:0.625rem]`,\n className,\n )}\n >\n <header className=\"flex items-start gap-2\">\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <h2 className=\"truncate text-sm font-semibold tracking-tight\">\n {title}\n </h2>\n <p className=\"text-xs text-muted-foreground\">{description}</p>\n </div>\n\n {onClose && (\n <WidgetButton\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n onClick={onClose}\n aria-label=\"Close feedback\"\n className=\"-mt-0.5 -mr-1 shrink-0 text-muted-foreground\"\n >\n <X />\n </WidgetButton>\n )}\n </header>\n\n {/* Two rows of one grid, one opening as the other closes, so the swap\n is the panel changing height rather than two panels. */}\n <div className=\"grid\">\n <Collapse isOpen={!isSent}>\n <div className=\"flex flex-col gap-1.5\">\n <Textarea\n rows={3}\n value={draft}\n maxLength={NOTE_MAX_LENGTH}\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={target.isDropTarget ? \"Drop to attach\" : placeholder}\n aria-label={title}\n {...{ [AUTOFOCUS_ATTRIBUTE]: \"\" }}\n className={cn(\n TEXT_FIELD_SHELL,\n \"max-h-40 min-h-18 px-2.5 py-2\",\n // 16px on phones, in pixels: any focused field smaller than\n // that makes iOS zoom the host's whole page in.\n \"max-sm:text-[16px]\",\n )}\n />\n\n {images.drafts.length > 0 && (\n <div className=\"flex flex-wrap gap-1.5\">\n {images.drafts.map((draft) => (\n <AttachmentDraftChip\n key={draft.id}\n draft={draft}\n onRemove={() => images.remove(draft.id)}\n />\n ))}\n </div>\n )}\n\n {notice && (\n <p className=\"animate-in px-0.5 text-xs text-destructive fade-in duration-150\">\n {notice}\n </p>\n )}\n </div>\n </Collapse>\n\n <Collapse isOpen={isSent}>\n <div className=\"flex items-center justify-center gap-2 py-4 text-sm font-medium\">\n <span className=\"grid size-5 shrink-0 place-items-center rounded-full bg-primary text-primary-foreground\">\n <Check className=\"size-3\" />\n </span>\n Thanks for your feedback.\n </div>\n </Collapse>\n </div>\n\n {/* Our name and the one action, on the same line: the foot of a panel\n this small is a row rather than two.\n\n The height is held whether or not the button is in it, so the row\n does not shrink under the thank-you while the panel above it is\n already animating. */}\n <div className=\"flex min-h-8 items-center gap-2\">\n {showBadge && <PoweredByMedian className=\"justify-start\" />}\n\n {!isSent && (\n <>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n multiple\n tabIndex={-1}\n onChange={handlePick}\n className=\"sr-only\"\n />\n <WidgetButton\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n disabled={images.isFull}\n onClick={() => fileInputRef.current?.click()}\n aria-label=\"Add a picture\"\n className=\"ml-auto shrink-0 rounded-full text-muted-foreground\"\n >\n <ImageSquare />\n </WidgetButton>\n\n <WidgetButton\n type=\"button\"\n disabled={!canSend}\n aria-busy={isSending}\n onClick={() => void send()}\n className=\"relative shrink-0 rounded-full px-3\"\n >\n {/* The label stays in the box while the spinner is over it, so\n it goes on setting the width and a send never moves the\n button it is happening in. */}\n <span\n className={cn(\n \"transition-opacity duration-150 motion-reduce:transition-none\",\n isSending && \"opacity-0\",\n )}\n >\n Send\n </span>\n\n {isSending && (\n <ArrowsClockwise\n aria-hidden\n className=\"absolute inset-0 m-auto animate-spin motion-reduce:animate-none\"\n />\n )}\n </WidgetButton>\n </>\n )}\n </div>\n </section>\n );\n}\n","\"use client\";\n\nimport { useEffect, useRef, type ReactNode, type RefObject } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/lib/utils\";\nimport { useHydrated } from \"../use-hydrated\";\nimport { useAnchor } from \"./use-anchor\";\n\n/** Above the host's own chrome. Their z-indexes are not ours to guess at. */\nconst LAYER = 2_147_483_000;\n\n/**\n * What inside the panel takes focus when it opens. The panel says which,\n * because which field is the one somebody came to fill in is the panel's\n * business rather than the box it is drawn in.\n */\nexport const AUTOFOCUS_ATTRIBUTE = \"data-median-autofocus\";\n\n/**\n * How the panel arrives and leaves.\n *\n * A dropdown, so it comes out of the thing that opened it: a little smaller\n * and a little towards its trigger, growing into place. The spring is the\n * same one the support panel opens with, and it overshoots by a hair, which\n * is the difference between a panel that appears and one that pops.\n *\n * Leaving is quicker and has no spring. An overshoot on the way out is a\n * panel arguing about being closed.\n */\n// `scale` and `translate` by name: those are the properties Tailwind's\n// `scale-*` and `translate-y-*` set. Listed as `transform` the panel had\n// neither, and popped open on opacity alone.\nconst MOTION =\n \"transition-[opacity,scale,translate] motion-reduce:transition-none\";\nconst OPEN = \"duration-250 ease-spring\";\nconst SHUT = \"duration-150 ease-out-quint\";\n\n/**\n * The panel, in a layer of its own, under whatever opened it.\n *\n * Portalled to the body rather than rendered where the trigger is. A host's\n * trigger lives inside their layout, and a panel rendered there inherits\n * every `overflow: hidden`, stacking context and transform between it and\n * the page. Out here it answers to the window and to nothing else.\n *\n * Closed, it stays mounted and goes `inert`: the draft somebody was halfway\n * through survives a close, and nothing behind the fade can be tabbed into.\n */\nexport function FeedbackPopover({\n isOpen,\n onClose,\n triggerRef,\n anchor,\n width,\n children,\n}: {\n isOpen: boolean;\n onClose: () => void;\n /** The host's own trigger, when they wrapped one. */\n triggerRef: RefObject<HTMLElement | null>;\n /**\n * Something else on their page to hang off, as a selector. Unlike the\n * trigger it is only a position: a press on it closes the panel like a\n * press anywhere else, because it is not the thing doing the opening.\n */\n anchor?: string;\n /** Any CSS length. Never wider than the window, whatever this says. */\n width: string;\n children: ReactNode;\n}) {\n const panelRef = useRef<HTMLDivElement>(null);\n const side = useAnchor({ triggerRef, anchor, panelRef, isOpen });\n // The panel is medianled into `document.body`, which does not exist until\n // there is one. Nothing at all until then, on the server and through\n // hydration alike, so both passes agree.\n const isHydrated = useHydrated();\n\n // Whatever had focus when the panel came up, so closing can hand it back.\n const opener = useRef<HTMLElement | null>(null);\n // Focus only moves once the panel has actually changed state, or a panel\n // that started open would steal focus on page load.\n const wasOpen = useRef(isOpen);\n\n // Escape closes, wherever focus is inside the panel. The element picker\n // takes its own Escape first, in the capture phase, so backing out of\n // pointing at something never closes the whole panel with it.\n useEffect(() => {\n if (!isOpen) return;\n\n function handleKeyDown(event: KeyboardEvent) {\n if (event.key === \"Escape\") onClose();\n }\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [isOpen, onClose]);\n\n // A press anywhere else closes it, the way every dropdown does. On\n // pointerdown rather than click, so it closes as the press lands rather\n // than when it is let go, and the trigger is left out: it does its own\n // toggling, and closing here first would make a second press reopen.\n useEffect(() => {\n if (!isOpen) return;\n\n function handlePointerDown(event: PointerEvent) {\n const target = event.target;\n if (!(target instanceof Node)) return;\n if (panelRef.current?.contains(target)) return;\n if (triggerRef.current?.contains(target)) return;\n onClose();\n }\n document.addEventListener(\"pointerdown\", handlePointerDown);\n return () => document.removeEventListener(\"pointerdown\", handlePointerDown);\n }, [isOpen, onClose, triggerRef]);\n\n useEffect(() => {\n if (wasOpen.current === isOpen) return;\n wasOpen.current = isOpen;\n\n if (isOpen) {\n const active = document.activeElement;\n opener.current = active instanceof HTMLElement ? active : null;\n // Straight into whatever the panel says takes focus, which is the box\n // they are here to type in. Unlike the support widget's launcher this\n // is never opened by accident: somebody chose it from a menu or a\n // button, and landing anywhere but the field would cost them a Tab.\n const field = panelRef.current?.querySelector<HTMLElement>(\n `[${AUTOFOCUS_ATTRIBUTE}]`,\n );\n (field ?? panelRef.current)?.focus();\n return;\n }\n\n const back = triggerRef.current ?? opener.current;\n opener.current = null;\n // Only somewhere focus can actually land, and only if it is still on the\n // page: a menu that closed behind the panel is not there to take it back.\n if (back && back !== document.body && back.isConnected) back.focus();\n }, [isOpen, triggerRef]);\n\n if (!isHydrated) return null;\n\n return createPortal(\n <div\n ref={panelRef}\n tabIndex={-1}\n inert={!isOpen}\n // The hook the published stylesheet scopes its resets to. On a page\n // without Tailwind there is no preflight, so the package ships one\n // that reaches no further than this box.\n data-median-widget=\"\"\n style={{\n zIndex: LAYER,\n width,\n top: 0,\n left: 0,\n // Grows out of the edge nearest its trigger rather than out of its\n // own middle, which is what makes it read as the trigger opening.\n transformOrigin: side === \"top\" ? \"bottom center\" : \"top center\",\n }}\n className={cn(\n \"fixed max-w-[calc(100vw-2rem)] outline-none\",\n // The shadow is on the box rather than inside the panel: a filter on\n // a parent of anything that scrolls makes the browser repaint it on\n // every frame, and there is nothing scrolling in here to protect.\n \"drop-shadow-xl\",\n MOTION,\n isOpen\n ? [OPEN, \"pointer-events-auto scale-100 opacity-100 translate-y-0\"]\n : [\n SHUT,\n \"pointer-events-none scale-[0.97] opacity-0\",\n // Towards its trigger, which is above it or below it.\n side === \"top\" ? \"translate-y-1\" : \"-translate-y-1\",\n ],\n )}\n >\n {children}\n </div>,\n document.body,\n );\n}\n","\"use client\";\n\nimport { useLayoutEffect, useState, type RefObject } from \"react\";\n\n/**\n * Keeping the panel under whatever opened it.\n *\n * The host owns the trigger: it is a nav item, a menu row, a button in a\n * toolbar, and it can be anywhere on their page. So the panel is measured\n * against it rather than pinned to a corner, and it follows it: a page that\n * scrolls or resizes under an open panel keeps the two together.\n *\n * The edges are written straight onto the node rather than held in state,\n * because scrolling fires far more often than anything worth re-rendering\n * for, and the panel's own opening animation is a transform. Position and\n * motion on two different properties is what keeps them from fighting.\n */\n\n/** Between the trigger and the panel. */\nconst GAP = 8;\n\n/** How close to the edge of the window the panel is allowed to sit. */\nconst MARGIN = 16;\n\n/** Which side of the trigger the panel ended up on, for the motion. */\nexport type AnchorSide = \"top\" | \"bottom\";\n\n/**\n * Whatever the selector finds, or nothing.\n *\n * Read every time it is placed rather than once, because the point of taking\n * a selector at all is that the thing it names may not have been on the page\n * when the panel opened. A menu item that opens this closes itself doing it;\n * the button the menu hangs off is still there, and that is what a selector\n * is for.\n */\nfunction findAnchor(selector: string | undefined): HTMLElement | null {\n if (selector === undefined || typeof document === \"undefined\") return null;\n try {\n return document.querySelector<HTMLElement>(selector);\n } catch {\n console.error(\n `Median: the anchor passed to <MedianFeedback> is ${selector}, which is not a valid CSS selector.`,\n );\n return null;\n }\n}\n\nexport function useAnchor({\n triggerRef,\n anchor,\n panelRef,\n isOpen,\n}: {\n /** The host's own trigger, when they wrapped one. */\n triggerRef: RefObject<HTMLElement | null>;\n /** Something on their page to hang off instead. Both absent is the corner. */\n anchor?: string;\n panelRef: RefObject<HTMLDivElement | null>;\n isOpen: boolean;\n}): AnchorSide {\n const [side, setSide] = useState<AnchorSide>(\"bottom\");\n\n useLayoutEffect(() => {\n if (!isOpen) return;\n\n let frame = 0;\n\n function place() {\n const panel = panelRef.current;\n const trigger = triggerRef.current ?? findAnchor(anchor);\n if (panel === null) return;\n\n const width = panel.offsetWidth;\n const height = panel.offsetHeight;\n const room = {\n width: window.innerWidth,\n height: window.innerHeight,\n };\n\n // Nothing to hang off, so it sits in the corner every widget sits in.\n if (trigger === null) {\n panel.style.left = `${Math.max(MARGIN, room.width - width - MARGIN)}px`;\n panel.style.top = `${Math.max(MARGIN, room.height - height - MARGIN)}px`;\n setSide(\"bottom\");\n return;\n }\n\n const rect = trigger.getBoundingClientRect();\n\n // Under the trigger, unless the panel would not fit and there is more\n // room above it. Flipping only when it actually helps: a panel that\n // jumped over its trigger the moment the page got short would move\n // for nothing most of the time.\n const below = room.height - rect.bottom - GAP - MARGIN;\n const above = rect.top - GAP - MARGIN;\n const onTop = height > below && above > below;\n setSide(onTop ? \"top\" : \"bottom\");\n\n const top = onTop ? rect.top - GAP - height : rect.bottom + GAP;\n\n // Aligned to whichever edge of the trigger keeps the panel on screen:\n // its left edge normally, its right edge when the trigger is over\n // towards the right, which is where a nav item usually is.\n const fromStart = rect.left;\n const fromEnd = rect.right - width;\n const left =\n fromStart + width + MARGIN <= room.width ? fromStart : fromEnd;\n\n panel.style.left = `${clamp(left, MARGIN, room.width - width - MARGIN)}px`;\n panel.style.top = `${clamp(top, MARGIN, room.height - height - MARGIN)}px`;\n }\n\n /** One placement per frame at most, however hard the page is scrolling. */\n function schedule() {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(place);\n }\n\n place();\n window.addEventListener(\"scroll\", schedule, true);\n window.addEventListener(\"resize\", schedule);\n\n // The panel changes height as the box grows and as the thank-you takes\n // over, and a panel flipped above its trigger has to move when it does.\n const observer = new ResizeObserver(schedule);\n if (panelRef.current !== null) observer.observe(panelRef.current);\n\n return () => {\n cancelAnimationFrame(frame);\n window.removeEventListener(\"scroll\", schedule, true);\n window.removeEventListener(\"resize\", schedule);\n observer.disconnect();\n };\n }, [isOpen, anchor, panelRef, triggerRef]);\n\n return side;\n}\n\nfunction clamp(value: number, low: number, high: number): number {\n // High can be below low on a window shorter than the panel, and then the\n // top edge wins: the way out is up there.\n return Math.max(low, Math.min(value, Math.max(low, high)));\n}\n","\"use client\";\n\nimport { useSyncExternalStore } from \"react\";\nimport {\n createOpenStore,\n useOpenState,\n useRegister,\n} from \"./open-store\";\n\n/**\n * Driving the feedback panel from anywhere on the page.\n *\n * The panel usually hangs off a trigger the host wrapped it around, and for\n * that this is not needed at all. It is here for the other way of putting\n * feedback on a page: a row in a menu somewhere else entirely, a keyboard\n * shortcut, a link at the foot of an error screen. Same shape as\n * `medianSupport`, and a store of its own, so opening one never opens the\n * other.\n */\nconst store = createOpenStore(\"MedianFeedback\");\n\nexport const medianFeedback = {\n /** What the panel is doing right now. */\n get isOpen() {\n return store.readEffective();\n },\n open() {\n store.request(true);\n store.warnIfNothingIsMounted(\"the feedback panel was asked to open\");\n },\n close() {\n store.request(false);\n },\n toggle() {\n const next = !store.readEffective();\n store.request(next);\n if (next) {\n store.warnIfNothingIsMounted(\"the feedback panel was asked to open\");\n }\n },\n} satisfies MedianFeedbackControl;\n\n/** The shape of {@link medianFeedback}, and of what the hook returns. */\nexport type MedianFeedbackControl = {\n /** Whether the panel is open. */\n isOpen: boolean;\n open: () => void;\n close: () => void;\n toggle: () => void;\n};\n\n/**\n * The feedback panel, from anywhere else on the page:\n *\n * const feedback = useMedianFeedback();\n *\n * <button onClick={feedback.open}>Share feedback</button>\n *\n * `isOpen` follows the panel however it was opened, so a menu row can render\n * itself as pressed while it is up.\n */\nexport function useMedianFeedback(): MedianFeedbackControl {\n const isOpen = useSyncExternalStore(\n store.subscribe,\n store.readEffective,\n store.readServerEffective,\n );\n\n return {\n isOpen,\n open: medianFeedback.open,\n close: medianFeedback.close,\n toggle: medianFeedback.toggle,\n };\n}\n\n/** The panel's own settled open state. See {@link useOpenState}. */\nexport function useMedianFeedbackOpenState(options: {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n}): [boolean, (next: boolean) => void] {\n return useOpenState(store, options);\n}\n\n/** One panel per page, and a word in the console when there are two. */\nexport function useRegisterMedianFeedback() {\n useRegister(store);\n}\n","\"use client\";\n\nimport { useWidgetMutation } from \"./use-widget-mutation\";\nimport { api } from \"@median/backend\";\nimport { MAX_REPORT_IMAGES } from \"@median/backend/reportImages\";\nimport { useCallback, useState, useSyncExternalStore } from \"react\";\nimport { errorMessage } from \"@/lib/errors\";\nimport { uploadAttachments } from \"@/lib/upload\";\nimport { collectContext, collectPage } from \"./context\";\nimport { restoreCustomData, takeCustomData } from \"./custom-data\";\nimport { useDiagnostics, type MedianDiagnostics } from \"./diagnostics\";\nimport { capturePage } from \"./screenshot\";\nimport { getOrCreateSessionId, subscribeSession } from \"./session\";\nimport { splitIdentity, type MedianUser } from \"./use-median-thread\";\n\n/**\n * Sending one note, and everything that rides along with it.\n *\n * The same session token the support widget uses, so a person who has\n * written in and then left feedback is one customer with both to their name\n * rather than two strangers who happen to share a browser. The same\n * telemetry, the same diagnostics ring buffer, and the same attached data\n * too: a bug report filed off a note deserves what a bug report filed off a\n * conversation gets.\n *\n * Nothing is read here. A note is a thing you hand over, not a thread you\n * watch, so there is no subscription and nothing to keep in step.\n */\n\n/** What the panel is told about a note that could not be taken. */\nconst REFUSALS: Record<string, string> = {\n off: \"Feedback is currently unavailable. Try again later.\",\n empty: \"Enter your feedback before sending.\",\n};\n\nexport function useFeedbackNote({\n apiKey,\n user,\n telemetry = true,\n screenshot = true,\n diagnostics,\n appState,\n}: {\n apiKey: string;\n user?: MedianUser;\n telemetry?: boolean;\n /** Whether a note carries a picture of the page it was written on. */\n screenshot?: boolean;\n diagnostics?: MedianDiagnostics;\n appState?: Record<string, string | number | boolean>;\n}) {\n // The session token is external state (localStorage), read as a store: the\n // server snapshot is null, so SSR and the first client paint agree, and\n // `reset()` on sign out lands here like it lands on the thread.\n const sessionId = useSyncExternalStore(\n subscribeSession,\n useCallback(() => getOrCreateSessionId(apiKey), [apiKey]),\n () => null,\n );\n\n const submitNote = useWidgetMutation(api.feedback.submitV2);\n // The widget's own upload door, which the panel may use whether or not the\n // support widget is mounted: it is guarded by the same key and the same\n // toll, and a screenshot is a screenshot either way.\n const getUploadUrl = useWidgetMutation(api.widget.generateUploadUrlV2);\n const takeDiagnostics = useDiagnostics(diagnostics, appState);\n const [notice, setNotice] = useState<string | null>(null);\n\n /**\n * Rejects when the note did not land, which is what keeps the draft in the\n * box. The reason is in `notice` by then, where the panel shows it.\n */\n const submit = useCallback(\n async (body: string, images: File[] = []) => {\n setNotice(null);\n if (sessionId === null) {\n setNotice(\"Still connecting. Try again in a moment.\");\n throw new Error(\"No session yet.\");\n }\n\n // Whatever the host attached since the last send rides this note.\n // Taken outside the try so a failed send hands it back too.\n const custom = takeCustomData();\n // Whether a refusal has already said its own piece. State cannot\n // answer that here: it is set and read inside one tick.\n let explained = false;\n try {\n // Read now rather than on mount: all of it is wanted as \"what was\n // true when they wrote\", and a panel can sit closed on a page for an\n // hour before anybody opens it.\n const context = telemetry ? collectContext() : undefined;\n const page = telemetry ? collectPage() : undefined;\n const collected = takeDiagnostics();\n const { facts, claim } = splitIdentity(user ?? null);\n // The page they were looking at, taken now rather than when the panel\n // opened: a note written over a minute is about the page as it stands\n // when they press send. Only when their own pictures left room for it,\n // since those were chosen and this one was not.\n const shot =\n screenshot && images.length < MAX_REPORT_IMAGES\n ? await capturePage()\n : null;\n // Before the note, so a picture that will not upload stops the send\n // rather than landing as a report with a missing screenshot.\n const attached = await uploadAttachments(\n () => getUploadUrl({ apiKey, sessionId }),\n shot === null ? images : [...images, shot],\n );\n\n const outcome = await submitNote({\n apiKey,\n sessionId,\n body,\n ...(user ? { user: facts } : {}),\n ...(claim ? { identity: claim } : {}),\n ...(context ? { context } : {}),\n ...(page ? { page } : {}),\n ...(collected ? { diagnostics: collected } : {}),\n ...(custom ? { customData: custom } : {}),\n ...(attached.length > 0 ? { images: attached } : {}),\n });\n\n if (outcome !== \"taken\") {\n explained = true;\n setNotice(\n REFUSALS[outcome] ?? \"Could not send your feedback. Try again.\",\n );\n throw new Error(outcome);\n }\n } catch (caught) {\n // Back in the queue, so the retry carries them like it keeps the draft.\n if (custom) restoreCustomData(custom);\n if (!explained) {\n console.error(\n \"Sending feedback from the Median panel failed\",\n caught,\n );\n setNotice(errorMessage(caught));\n }\n // Rethrown so the panel keeps the draft: a failed send should cost a\n // retry, never a retype.\n throw caught;\n }\n },\n [\n apiKey,\n sessionId,\n user,\n telemetry,\n screenshot,\n takeDiagnostics,\n submitNote,\n getUploadUrl,\n ],\n );\n\n return { submit, notice, clearNotice: () => setNotice(null) };\n}\n","\"use client\";\n\nimport {\n MedianFeedback,\n type MedianFeedbackMountOptions,\n type MedianFeedbackProps,\n} from \"./median-feedback\";\nimport { mountMedian, type MedianMountInstance } from \"./mount-into\";\n\n/**\n * What a mounted panel hands back: the two things a host without React has\n * no re-render to do for them. Opening and closing go through\n * `medianFeedback`, same as anywhere.\n */\nexport type MedianFeedbackInstance = MedianMountInstance<MedianFeedbackProps>;\n\n/**\n * The same panel, for a page that has never heard of React: Vue, Svelte,\n * Rails, a script on plain HTML.\n *\n * const feedback = mountMedianFeedback({ apiKey: \"median_pk_...\" });\n *\n * document.querySelector(\"#feedback\").onclick = () => medianFeedback.open();\n *\n * There is no trigger to wrap out here, so the panel opens in the bottom\n * right corner and your own button says when. Everything else is the\n * component's, unchanged.\n */\nexport function mountMedianFeedback(\n options: MedianFeedbackMountOptions,\n): MedianFeedbackInstance {\n return mountMedian<MedianFeedbackProps>({\n caller: \"mountMedianFeedback\",\n what: \"the panel\",\n marker: \"medianFeedback\",\n into: null,\n options,\n render: (current) => <MedianFeedback {...current} />,\n });\n}\n","\"use client\";\n\nimport { useEffect, useMemo } from \"react\";\nimport { ContactForm } from \"../contact/contact-form\";\nimport { fieldComplaints, type MedianContactField } from \"../contact/fields\";\nimport type { MedianDiagnostics } from \"./diagnostics\";\nimport { MedianErrorBoundary } from \"./error-boundary\";\nimport { useMedianContact } from \"./use-median-contact\";\nimport type { MedianUser } from \"./use-median-thread\";\n\ntype MedianContactFormOptions = {\n /**\n * What the form asks, beyond an email and a message, which it always asks:\n *\n * <MedianContactForm\n * apiKey=\"median_pk_...\"\n * fields={[\n * { name: \"name\", label: \"Name\", required: true },\n * { name: \"topic\", type: \"select\", options: [\"Billing\", \"A bug\", \"Something else\"] },\n * ]}\n * />\n *\n * Shown in this order. Email goes first, or right after a `name` field,\n * and the message goes last, unless either is placed here by name. A\n * `name` field lands on the customer record; every other answer goes\n * above the message as a line of its own.\n */\n fields?: MedianContactField[];\n /**\n * Who is signed in on your site, if anyone. Their email and name fill the\n * boxes in, and the rest is shown to your team.\n */\n user?: MedianUser;\n /**\n * Whether the message carries what the browser already knows: time zone,\n * language, browser, OS, device, screen, and the page it was sent from.\n * On by default. Pass `false` and none of it is read at all.\n */\n telemetry?: boolean;\n /**\n * Watch what goes wrong on the page, so a bug report filed from the\n * message arrives with the error already attached. The same switch\n * `<MedianSupport>` takes, and the same buffer behind it.\n */\n diagnostics?: MedianDiagnostics;\n /**\n * Anything about your own app worth having next to a bug report: a build,\n * a route, a flag. Read when the form is sent.\n */\n appState?: Record<string, string | number | boolean>;\n /** The heading, for a page that calls this something else. */\n title?: string;\n /** The line under it. */\n description?: string;\n /**\n * The message landed. For whatever your page does next: a thank-you of its\n * own, a redirect, an event. The form has already said thank you itself.\n */\n onSent?: () => void;\n};\n\n/**\n * Two ways in, the same two `<MedianSupport>` takes.\n *\n * `apiKey` is the publishable id on its own. `identity` is the path to a\n * route built with `medianIdentity`, which answers with the key and with\n * whoever is signed in, already signed. Pass both and the form is up on the\n * key from the prop while the route is still answering.\n */\nexport type MedianContactFormProps = MedianContactFormOptions &\n (\n | { apiKey: string; identity?: string }\n | { apiKey?: string; identity: string }\n );\n\n/**\n * A contact form, for the page where a chat panel is the wrong shape.\n *\n * <MedianContactForm apiKey=\"median_pk_...\" />\n *\n * An email and a message, on a card the shape of the support panel, with\n * whatever else your team wants asked first. What is sent starts a\n * conversation in your inbox exactly as the widget would: the agent answers\n * first, your team picks it up, and the reply goes to the email given.\n *\n * Built on `useMedianContact`, which is also what a form of your own uses.\n * It needs no connection of its own, and it is independent of\n * `<MedianSupport>`: mount either, both, or neither. Mounted together they\n * share a visitor, so somebody who writes here and later opens the widget\n * finds the same thread.\n */\nexport function MedianContactForm(props: MedianContactFormProps) {\n return (\n <MedianErrorBoundary component=\"MedianContactForm\">\n <ConnectedContactForm {...props} />\n </MedianErrorBoundary>\n );\n}\n\n/**\n * A list of fields that will not do says so once, in the console, while the\n * person who wrote it is looking at the page that runs it. Once per list\n * rather than per render, since a host builds the array inline.\n */\nfunction useFieldComplaints(fields: MedianContactField[] | undefined) {\n const key = fields === undefined ? null : JSON.stringify(fields);\n useEffect(() => {\n if (key === null) return;\n for (const complaint of fieldComplaints(JSON.parse(key))) {\n console.error(\n `Median: in the fields passed to <MedianContactForm>, ${complaint}`,\n );\n }\n }, [key]);\n}\n\n/**\n * While an identity route is still answering and no key was passed, the\n * form is on the page and switched off rather than absent. A form that\n * arrives a beat after the page around it shoves that page down, and a page\n * that shoves is a page that reads as broken.\n */\nfunction ConnectedContactForm({\n fields,\n title,\n description,\n onSent,\n ...options\n}: MedianContactFormProps) {\n const contact = useMedianContact(options, \"<MedianContactForm>\");\n useFieldComplaints(fields);\n\n // What is already known about who is typing, for the boxes.\n const knownEmail = contact.user?.email || undefined;\n const knownName = contact.user?.name || undefined;\n const initialValues = useMemo(\n () => ({\n ...(knownEmail ? { email: knownEmail } : {}),\n ...(knownName ? { name: knownName } : {}),\n }),\n [knownEmail, knownName],\n );\n\n return (\n <ContactForm\n showBadge={contact.showBranding}\n fields={fields}\n onSubmit={async (message) => {\n await contact.send(message);\n // The message landed whatever the host's callback does. A throw here\n // must not read as a failed send, or a retry sends it twice.\n try {\n onSent?.();\n } catch (caught) {\n console.error(\n \"Median: onSent threw after the message was sent.\",\n caught,\n );\n }\n }}\n notice={contact.error}\n initialValues={initialValues}\n isDisabled={!contact.isReady}\n {...(title !== undefined ? { title } : {})}\n {...(description !== undefined ? { description } : {})}\n />\n );\n}\n","\"use client\";\n\nimport {\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type FormEvent,\n} from \"react\";\nimport { ArrowsClockwise } from \"@/components/icons\";\nimport { cn } from \"@/lib/utils\";\nimport { PoweredByMedian } from \"../widget-badge\";\nimport { WidgetButton } from \"../widget-button\";\nimport { WIDGET_CORNER } from \"../widget-corner\";\nimport { ContactField } from \"./contact-field\";\nimport { composeMessage, type ContactValues } from \"./contact-body\";\nimport {\n resolveFields,\n type ContactField as ContactFieldShape,\n type MedianContactField,\n} from \"./fields\";\nimport type { MedianContactMessage } from \"./message\";\nimport { SentMark, type SentFrom } from \"./sent-mark\";\n\n/**\n * Loose on purpose, and the same rule the composer's question uses: it\n * decides whether the answer can be sent, not whether the address is real.\n */\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\n/**\n * How the form steps aside and the thank-you takes its place.\n *\n * The two share one cell of a grid, so the card keeps the form's height\n * throughout: nothing on the host's page moves because a message went. The\n * form lifts a touch as it fades, since a message that went is a thing that\n * left, and the send button is gone the instant it does: the mark starts as\n * that button, at its spot, and carries it to the middle of the card. The\n * thank-you layer is there at once for that, and only fades on the way\n * back to the form.\n */\nconst FORM_SWAP =\n \"transition-[opacity,translate] duration-250 ease-out-quint motion-reduce:transition-none\";\nconst THANKS_SWAP =\n \"transition-opacity ease-out motion-reduce:transition-none\";\n\n/** How the words and the button arrive, once the mark has landed. */\nconst ARRIVES =\n \"animate-in duration-500 ease-out fade-in-0 slide-in-from-bottom-2 fill-mode-backwards motion-reduce:animate-none\";\n\n/** Where the button was when the message went, for the mark to start from. */\nfunction placeOf(button: HTMLElement | null): SentFrom | null {\n if (button === null) return null;\n const { left, top, width, height } = button.getBoundingClientRect();\n return { left, top, width, height };\n}\n\n/** What each field says when its answer will not do. */\nfunction complaintFor(field: ContactFieldShape, value: string): string | null {\n const trimmed = value.trim();\n if (field.role === \"email\") {\n if (trimmed.length === 0) return \"Enter your email.\";\n if (!EMAIL_PATTERN.test(trimmed)) return \"Enter a valid email address.\";\n return null;\n }\n if (field.role === \"message\") {\n return trimmed.length === 0 ? \"Write a message.\" : null;\n }\n return field.required && trimmed.length === 0 ? \"Required.\" : null;\n}\n\nfunction emptyValues(fields: readonly ContactFieldShape[]): ContactValues {\n return Object.fromEntries(fields.map((field) => [field.name, \"\"]));\n}\n\n/** Every box empty, apart from the ones something is already known for. */\nfunction startingValues(\n fields: readonly ContactFieldShape[],\n known: Partial<ContactValues> | undefined,\n): ContactValues {\n const values = emptyValues(fields);\n for (const [name, value] of Object.entries(known ?? {})) {\n if (typeof value === \"string\" && name in values) values[name] = value;\n }\n return values;\n}\n\n/**\n * The contact form: an email, a message, and whatever else the host asked\n * for, on a card the shape of the support panel.\n *\n * A form rather than a conversation, for the page where a conversation is\n * the wrong shape: a contact page, a pricing page's \"talk to us\", a footer.\n * What is typed here starts the same thread the widget would, so the team\n * answers it from the same inbox and the reply goes to the email given.\n *\n * Complaints wait for the send button. A form that argues with every\n * keystroke is a form people give up on, and one that says what is missing\n * once, in place, when they ask it to send, is not.\n *\n * Presentational, like `SupportWidget`: the filled in form goes out through\n * `onSubmit` as the same message a host's own form sends, and nothing here\n * holds a thread. A rejection keeps every answer where it was, so a send\n * that fails costs a retry rather than a retype.\n */\nexport function ContactForm({\n fields: hostFields,\n onSubmit,\n notice,\n initialValues,\n title = \"Contact us\",\n description = \"We’ll reply by email.\",\n isDisabled = false,\n showBadge = true,\n className,\n}: {\n /** The host's fields. Email and message are added when not among them. */\n fields?: readonly MedianContactField[];\n /** Awaited. A rejection keeps the answers in place. */\n onSubmit?: (message: MedianContactMessage) => unknown;\n /** One line above the button, for a send that went wrong. */\n notice?: string | null;\n /**\n * Answers already known, by field name: the signed in person's email and\n * name, usually. Filled into an empty box and never over something typed.\n */\n initialValues?: Partial<ContactValues>;\n title?: string;\n description?: string;\n /**\n * Nothing can be sent yet. For the beat before whoever mounted this knows\n * where a message would go, so the form is on the page rather than\n * arriving a moment after it.\n */\n isDisabled?: boolean;\n /** The \"Powered by Median\" line. Hosts on a paid plan can turn it off. */\n showBadge?: boolean;\n className?: string;\n}) {\n const fields = useMemo(() => resolveFields(hostFields), [hostFields]);\n const prefix = useId();\n const formRef = useRef<HTMLFormElement>(null);\n const sendRef = useRef<HTMLButtonElement>(null);\n\n const [values, setValues] = useState<ContactValues>(() =>\n startingValues(fields, initialValues),\n );\n const [errors, setErrors] = useState<Record<string, string>>({});\n const [isSending, setSending] = useState(false);\n // The message that went, while the thank-you is up: the address it names,\n // where the button was when it went, and which send this is, so the\n // thank-you plays from the start each time rather than once for the life\n // of the card.\n const [sent, setSent] = useState<{\n email: string;\n from: SentFrom | null;\n attempt: number;\n } | null>(null);\n const isSent = sent !== null;\n\n // A known answer arriving late, which is what an identity route does: the\n // form is on the page before the route has said who is signed in. Into an\n // empty box only. Somebody halfway through typing their name is not asking\n // to have it replaced. Settled during render rather than in an effect, so\n // the frame the answer lands is the frame the box fills.\n const knownEmail = initialValues?.email;\n const knownName = initialValues?.name;\n const [known, setKnown] = useState({ email: knownEmail, name: knownName });\n if (known.email !== knownEmail || known.name !== knownName) {\n setKnown({ email: knownEmail, name: knownName });\n setValues((current) => {\n const next = { ...current };\n if (knownEmail && \"email\" in next && !next.email) next.email = knownEmail;\n if (knownName && \"name\" in next && !next.name) next.name = knownName;\n return next;\n });\n }\n\n // The first complaint takes the caret, so a keyboard lands on the thing to\n // fix rather than on the button that said so. After the render that draws\n // the complaint, since the box is only marked invalid then.\n const [focusRequest, setFocusRequest] = useState(0);\n useEffect(() => {\n if (focusRequest === 0) return;\n formRef.current\n ?.querySelector<HTMLElement>('[aria-invalid=\"true\"]')\n ?.focus({ preventScroll: false });\n }, [focusRequest]);\n\n function setValue(name: string, value: string) {\n setValues((current) => ({ ...current, [name]: value }));\n // Fixed as it is typed, once it has been complained about. Nothing new is\n // complained about until the next send.\n setErrors((current) => {\n if (!(name in current)) return current;\n return Object.fromEntries(\n Object.entries(current).filter(([key]) => key !== name),\n );\n });\n }\n\n async function send() {\n if (isDisabled || isSending || isSent) return;\n\n const found: Record<string, string> = {};\n for (const field of fields) {\n const complaint = complaintFor(field, values[field.name] ?? \"\");\n if (complaint !== null) found[field.name] = complaint;\n }\n if (Object.keys(found).length > 0) {\n setErrors(found);\n setFocusRequest((count) => count + 1);\n return;\n }\n\n const message = composeMessage(fields, values);\n setSending(true);\n try {\n await onSubmit?.(message);\n } catch {\n // The notice above the button says why. The answers stay where they are.\n return;\n } finally {\n setSending(false);\n }\n // Measured before anything moves: the button is still where they\n // pressed it, and that is where the mark sets off from.\n const from = placeOf(sendRef.current);\n setSent((last) => ({\n email: message.email,\n from,\n attempt: (last?.attempt ?? 0) + 1,\n }));\n // The next message starts clean, apart from who it is from: the address\n // and the name are still theirs.\n setValues(\n startingValues(fields, {\n email: message.email,\n ...(message.name ? { name: message.name } : {}),\n }),\n );\n }\n\n function handleSubmit(event: FormEvent<HTMLFormElement>) {\n event.preventDefault();\n void send();\n }\n\n return (\n <section\n aria-label={title}\n // The hook the published stylesheet scopes its resets to, and the\n // widget's own radius scale, so the app's token cannot reshape a form\n // that sits on somebody else's site.\n data-median-widget=\"\"\n className={cn(\n `flex w-full flex-col overflow-hidden ${WIDGET_CORNER} border bg-card p-5 text-card-foreground [--radius:0.625rem]`,\n className,\n )}\n >\n <header className=\"flex flex-col gap-0.5 pb-4\">\n <h2 className=\"text-lg font-semibold tracking-tight\">{title}</h2>\n <p className=\"text-sm text-muted-foreground\">{description}</p>\n </header>\n\n {/* One cell, two occupants. The form sets the height and keeps it\n whether or not it is the one showing, so the card never changes\n size under the page it sits on. */}\n <div className=\"grid\">\n <form\n ref={formRef}\n noValidate\n inert={isSent}\n onSubmit={handleSubmit}\n className={cn(\n \"col-start-1 row-start-1 flex flex-col gap-4\",\n FORM_SWAP,\n isSent && \"pointer-events-none -translate-y-1.5 opacity-0\",\n )}\n >\n {fields.map((field) => (\n <ContactField\n key={field.name}\n field={field}\n id={`${prefix}-${field.name}`}\n value={values[field.name] ?? \"\"}\n error={errors[field.name] ?? null}\n isDisabled={isDisabled || isSending}\n onChange={(value) => setValue(field.name, value)}\n onSubmit={() => formRef.current?.requestSubmit()}\n />\n ))}\n\n {notice && (\n <p className=\"animate-in px-1 text-sm text-destructive fade-in duration-150\">\n {notice}\n </p>\n )}\n\n {/* Our name and the one action, on the same line. On a card too\n narrow for both, the button drops to a line of its own rather\n than running off the edge. */}\n <div className=\"flex min-h-10 flex-wrap items-center gap-3 pt-1\">\n {showBadge && <PoweredByMedian className=\"justify-start\" />}\n\n <WidgetButton\n ref={sendRef}\n type=\"submit\"\n size=\"xl\"\n disabled={isDisabled || isSending}\n aria-busy={isSending}\n className={cn(\n \"relative ml-auto shrink-0 rounded-full px-5\",\n // Gone at once, not faded with the form: the mark is\n // standing exactly here from the same frame, and a button\n // showing through its edges as the form lifts would read\n // as two of them.\n isSent && \"opacity-0 transition-none\",\n )}\n >\n {/* The label stays in the box while the spinner is over it,\n so it goes on setting the width and a send never moves\n the button it is happening in. */}\n <span\n className={cn(\n \"transition-opacity duration-150 motion-reduce:transition-none\",\n isSending && \"opacity-0\",\n )}\n >\n Send message\n </span>\n\n {isSending && (\n <ArrowsClockwise\n aria-hidden\n className=\"absolute inset-0 m-auto animate-spin motion-reduce:animate-none\"\n />\n )}\n </WidgetButton>\n </div>\n </form>\n\n {/* The thank-you, over the form's footprint. Remounted per send, so\n the mark pops, the tick draws and the words arrive every time\n rather than only the first. Announced, since nothing else on the\n page says the message went. */}\n <div\n inert={!isSent}\n role=\"status\"\n aria-live=\"polite\"\n className={cn(\n \"col-start-1 row-start-1 flex flex-col items-center justify-center gap-5 py-4 text-center\",\n THANKS_SWAP,\n isSent ? \"duration-0\" : \"pointer-events-none opacity-0 duration-200\",\n )}\n >\n {sent !== null && (\n <div key={sent.attempt} className=\"contents\">\n <SentMark from={sent.from} />\n\n <div\n className={cn(\"flex max-w-72 flex-col gap-1\", ARRIVES, \"delay-[400ms]\")}\n >\n <p className=\"text-xl font-semibold tracking-tight\">Message sent</p>\n <p className=\"text-base text-balance text-muted-foreground\">\n Replies go to {sent.email}.\n </p>\n </div>\n\n <div\n className={cn(\n \"flex flex-col items-center gap-4\",\n ARRIVES,\n \"delay-[600ms]\",\n )}\n >\n <WidgetButton\n type=\"button\"\n variant=\"outline\"\n size=\"xl\"\n onClick={() => setSent(null)}\n className=\"rounded-full px-5\"\n >\n Send another\n </WidgetButton>\n\n {showBadge && <PoweredByMedian />}\n </div>\n </div>\n )}\n </div>\n </div>\n </section>\n );\n}\n","\"use client\";\n\nimport { MESSAGE_MAX_LENGTH } from \"@median/backend/support\";\nimport { useState, type KeyboardEvent } from \"react\";\nimport { CaretDown } from \"@/components/icons\";\nimport { Collapse } from \"@/components/ui/collapse\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { cn } from \"@/lib/utils\";\nimport { WIDGET_TEXT_FIELD_SHELL as TEXT_FIELD_SHELL } from \"../widget-field\";\nimport type { ContactField as ContactFieldShape } from \"./fields\";\n\n/** The most a line answer can hold. The message box has the widget's own limit. */\nconst LINE_MAX_LENGTH = 200;\n\n/**\n * The same shell every widget field wears, at the panel's scale: 16px words\n * on the card's own surface. In pixels on phones, since any focused field\n * smaller than that makes iOS zoom the host's whole page in.\n */\nconst SHELL = cn(\n TEXT_FIELD_SHELL,\n \"w-full bg-card text-base max-sm:text-[16px] dark:bg-card\",\n);\n\n/** A line is a pill, the way the composer is. */\nconst LINE = \"h-11 rounded-full px-4\";\n\n/**\n * A paragraph keeps the pill's corner, which is half the line's height, so\n * the two shapes read as one family rather than a pill beside a box.\n */\nconst PARAGRAPH = \"max-h-64 min-h-32 rounded-[1.375rem] px-4 py-2.5\";\n\n/**\n * One labelled field on the contact form: a line, a paragraph, or one of a\n * list, with a complaint underneath when there is one.\n *\n * The complaint opens and closes rather than appearing, and holds its words\n * on the way out, so a field being fixed does not jump the form and does\n * not go blank mid sentence.\n */\nexport function ContactField({\n field,\n id,\n value,\n error,\n isDisabled = false,\n onChange,\n onSubmit,\n}: {\n field: ContactFieldShape;\n /** Unique on the page, since a page may carry two forms. */\n id: string;\n value: string;\n /** What is wrong with the answer, or null. */\n error: string | null;\n isDisabled?: boolean;\n onChange: (value: string) => void;\n /** Meta and enter in the paragraph box, since enter there is a new line. */\n onSubmit?: () => void;\n}) {\n // The last complaint, held for the collapse on the way out.\n const [held, setHeld] = useState(error);\n if (error !== null && error !== held) setHeld(error);\n const shown = error ?? held;\n\n const errorId = `${id}-error`;\n const control = {\n id,\n name: field.name,\n value,\n disabled: isDisabled,\n \"aria-invalid\": error !== null,\n \"aria-describedby\": error !== null ? errorId : undefined,\n };\n\n function handleKeyDown(event: KeyboardEvent<HTMLTextAreaElement>) {\n if (event.key !== \"Enter\") return;\n if (!event.metaKey && !event.ctrlKey) return;\n event.preventDefault();\n onSubmit?.();\n }\n\n return (\n <div className=\"flex flex-col\">\n <label\n htmlFor={id}\n className=\"flex items-baseline gap-2 px-1 pb-1.5 text-sm font-medium\"\n >\n {field.label}\n {!field.required && (\n <span className=\"text-xs font-normal text-muted-foreground\">\n Optional\n </span>\n )}\n </label>\n\n {field.type === \"textarea\" ? (\n <Textarea\n {...control}\n rows={4}\n maxLength={MESSAGE_MAX_LENGTH}\n placeholder={field.placeholder}\n onChange={(event) => onChange(event.target.value)}\n onKeyDown={handleKeyDown}\n className={cn(SHELL, PARAGRAPH)}\n />\n ) : field.type === \"select\" ? (\n <div className=\"relative\">\n <select\n {...control}\n onChange={(event) => onChange(event.target.value)}\n className={cn(\n SHELL,\n LINE,\n \"appearance-none pr-10\",\n // Nothing chosen yet reads as a placeholder, not an answer.\n value === \"\" && \"text-muted-foreground\",\n )}\n >\n <option value=\"\">{field.placeholder ?? \"Choose one\"}</option>\n {field.options.map((option) => (\n <option key={option} value={option}>\n {option}\n </option>\n ))}\n </select>\n <CaretDown\n aria-hidden\n className=\"pointer-events-none absolute top-1/2 right-4 size-4 -translate-y-1/2 text-muted-foreground\"\n />\n </div>\n ) : (\n <input\n {...control}\n type={field.type === \"email\" ? \"email\" : \"text\"}\n maxLength={LINE_MAX_LENGTH}\n placeholder={field.placeholder}\n autoComplete={\n field.role === \"email\"\n ? \"email\"\n : field.role === \"name\"\n ? \"name\"\n : undefined\n }\n spellCheck={field.role === \"custom\" ? undefined : false}\n onChange={(event) => onChange(event.target.value)}\n className={cn(SHELL, LINE)}\n />\n )}\n\n <Collapse isOpen={error !== null}>\n <p id={errorId} className=\"px-1 pt-1.5 text-sm text-destructive\">\n {shown}\n </p>\n </Collapse>\n </div>\n );\n}\n","import type { ContactField } from \"./fields\";\nimport type { MedianContactMessage } from \"./message\";\n\n/** What every field on our own form holds, keyed by its name. */\nexport type ContactValues = Record<string, string>;\n\n/**\n * Our form's answers, as the message every contact form sends.\n *\n * The email and the name go on the customer record, because that is where\n * the team looks for who somebody is. Everything else the host asked for\n * becomes a labelled answer, and `composeBody` puts those above the message.\n */\nexport function composeMessage(\n fields: readonly ContactField[],\n values: ContactValues,\n): MedianContactMessage {\n const name = (values.name ?? \"\").trim();\n const answers: Record<string, string> = {};\n for (const field of fields) {\n if (field.role !== \"custom\") continue;\n answers[field.label] = values[field.name] ?? \"\";\n }\n\n return {\n email: (values.email ?? \"\").trim(),\n message: (values.message ?? \"\").trim(),\n ...(name ? { name } : {}),\n ...(Object.keys(answers).length > 0 ? { fields: answers } : {}),\n };\n}\n\n/**\n * One message to the team out of the words and the extra answers. The\n * answers go first, a line each, so the thread reads the way the form was\n * filled in and the agent reads it too. Empty answers are left off, and a\n * form with none sends the words alone.\n */\nexport function composeBody(message: MedianContactMessage): string {\n const lines = Object.entries(message.fields ?? {})\n .map(([label, value]) => ({ label: label.trim(), value: value.trim() }))\n .filter((line) => line.label.length > 0 && line.value.length > 0)\n .map((line) => `${line.label}: ${line.value}`);\n\n const words = message.message.trim();\n return lines.length > 0 ? `${lines.join(\"\\n\")}\\n\\n${words}` : words;\n}\n","/**\n * What a host can put on the contact form, and what the form always has.\n *\n * Two fields are on every form whatever the host says: an email, because a\n * message nobody can reply to is a message nobody sent, and the message\n * itself. Everything else is the host's to add: a name, a topic, an order\n * number, whatever their team wants to know before they read.\n */\n\n/** A line, a paragraph, or one of a list. */\nexport type MedianContactFieldKind = \"text\" | \"textarea\" | \"select\";\n\n/**\n * One field a host adds to the form.\n *\n * { name: \"topic\", label: \"Topic\", type: \"select\", options: [\"Billing\", \"A bug\"] }\n *\n * Three names are reserved. `email` and `message` are the two the form always\n * has, and naming either here only changes its label and placeholder. `name`\n * lands on the customer record beside the email rather than in the message,\n * so the team sees who wrote rather than a line saying so.\n */\nexport type MedianContactField = {\n /** Its key. Rides the message as the label on its line. */\n name: string;\n /** Shown above the box. Made from `name` when left out. */\n label?: string;\n /** `text` by default. Ignored on `email` and `message`, which know their shape. */\n type?: MedianContactFieldKind;\n /** What a `select` offers. A select with none falls back to a text box. */\n options?: string[];\n placeholder?: string;\n /** Off by default. `email` and `message` are required whatever this says. */\n required?: boolean;\n};\n\n/** What the built-in fields do with a value, and what a custom one does. */\nexport type ContactRole = \"email\" | \"message\" | \"name\" | \"custom\";\n\n/** A field with every choice made, which is what the form renders. */\nexport type ContactField = {\n name: string;\n label: string;\n type: MedianContactFieldKind | \"email\";\n options: string[];\n placeholder: string | undefined;\n required: boolean;\n role: ContactRole;\n};\n\n/** The two the form brings, when the host does not mention them. */\nconst DEFAULT_EMAIL: MedianContactField = {\n name: \"email\",\n label: \"Email\",\n placeholder: \"you@example.com\",\n};\n\nconst DEFAULT_MESSAGE: MedianContactField = {\n name: \"message\",\n label: \"Message\",\n placeholder: \"How can we help?\",\n};\n\nconst RESERVED_ROLES: Record<string, ContactRole> = {\n email: \"email\",\n message: \"message\",\n name: \"name\",\n};\n\n/** \"order_number\" and \"orderNumber\" both read as \"Order number\". */\nfunction labelOf(name: string): string {\n const spaced = name\n .replace(/[_-]+/g, \" \")\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n .trim()\n .toLowerCase();\n return spaced.charAt(0).toUpperCase() + spaced.slice(1);\n}\n\nfunction resolveOne(field: MedianContactField): ContactField {\n const role = RESERVED_ROLES[field.name] ?? \"custom\";\n const options = field.options ?? [];\n const label = field.label?.trim() || labelOf(field.name);\n\n if (role === \"email\") {\n return {\n name: \"email\",\n label,\n type: \"email\",\n options: [],\n placeholder: field.placeholder ?? DEFAULT_EMAIL.placeholder,\n required: true,\n role,\n };\n }\n if (role === \"message\") {\n return {\n name: \"message\",\n label,\n type: \"textarea\",\n options: [],\n placeholder: field.placeholder ?? DEFAULT_MESSAGE.placeholder,\n required: true,\n role,\n };\n }\n\n // A list with nothing in it is a text box that forgot to say so.\n const type =\n field.type === \"select\" && options.length === 0\n ? \"text\"\n : (field.type ?? \"text\");\n\n return {\n name: field.name,\n label,\n type,\n options: type === \"select\" ? options : [],\n placeholder: field.placeholder,\n required: field.required ?? false,\n role,\n };\n}\n\n/**\n * The form, in the order it is shown.\n *\n * The host's fields keep their order. Email goes first when they did not\n * place it, since it is the first thing the team needs, except that a name\n * is let in front of it: that is the order people introduce themselves in.\n * The message goes last when they did not place it, since it is the box\n * everything else is about. A name repeated twice is the first one: a form\n * with two boxes for one answer would send the second and lose the first.\n */\nexport function resolveFields(\n fields: readonly MedianContactField[] = [],\n): ContactField[] {\n const seen = new Set<string>();\n const resolved: ContactField[] = [];\n\n for (const field of fields) {\n const name = field.name?.trim();\n if (!name || seen.has(name)) continue;\n seen.add(name);\n resolved.push(resolveOne({ ...field, name }));\n }\n\n if (!seen.has(\"email\")) {\n const afterName = resolved.findIndex((field) => field.role === \"name\") + 1;\n resolved.splice(afterName, 0, resolveOne(DEFAULT_EMAIL));\n }\n if (!seen.has(\"message\")) resolved.push(resolveOne(DEFAULT_MESSAGE));\n\n return resolved;\n}\n\n/**\n * What is wrong with a list of fields, said the way the console should say\n * it. Empty when nothing is. Checked at mount rather than on send, so the\n * person who wrote the list is still looking at the page that runs it.\n */\nexport function fieldComplaints(\n fields: readonly MedianContactField[] | undefined,\n): string[] {\n if (fields === undefined) return [];\n const complaints: string[] = [];\n const seen = new Set<string>();\n\n for (const field of fields) {\n const name = typeof field.name === \"string\" ? field.name.trim() : \"\";\n if (!name) {\n complaints.push(\n \"a field has no name, so it was left off. Give every field a name, which is the key its answer rides the message under.\",\n );\n continue;\n }\n if (seen.has(name)) {\n complaints.push(\n `two fields are named \"${name}\", so only the first is shown. Give each field its own name.`,\n );\n }\n seen.add(name);\n if (field.type === \"select\" && (field.options?.length ?? 0) === 0) {\n complaints.push(\n `\"${name}\" is a select with no options, so it is shown as a text box. Pass options, or drop the type.`,\n );\n }\n }\n\n return complaints;\n}\n","\"use client\";\n\nimport { useLayoutEffect, useRef } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { prefersReducedMotion } from \"../motion\";\n\n/** Matches --ease-out-quint. JS cannot read a theme token. */\nconst EASE_OUT = \"cubic-bezier(0.22, 1, 0.36, 1)\";\n\n/**\n * How long the button takes to become the mark: across the card and from a\n * pill to a square. A travel rather than a resize, so it leaves at speed\n * and settles, the way everything else in the widget arrives.\n */\nconst TRAVEL_MS = 550;\n\n/** When the tick starts drawing: as the square is settling, not after. */\nconst DRAW_LEAD_MS = 150;\nconst DRAW_MS = 420;\n\n/** The ring leaves as the tick lands, and takes its time fading. */\nconst RING_LEAD_MS = 100;\nconst RING_MS = 800;\n\n/** For a mark that pops in place rather than travelling. */\nconst POP_DRAW_DELAY_MS = 180;\nconst POP_RING_DELAY_MS = 220;\n\n/** Where the button was, in the viewport, when the message went. */\nexport type SentFrom = {\n left: number;\n top: number;\n width: number;\n height: number;\n};\n\n/**\n * The mark a sent message gets: the widget's square with a tick that draws\n * itself across it and one ring that rolls out and fades.\n *\n * Given `from`, the square starts as the send button, at the button's own\n * spot and shape, and travels to its place while it squares off. The button\n * is what was pressed, so it is the thing that should turn into the answer;\n * a mark appearing somewhere else while the button fades is two events\n * where there was one. Without `from` it pops in place on the spring.\n *\n * The tick is drawn rather than faded because a shape arriving stroke by\n * stroke reads as something completing, and a tick that appears reads as\n * something switched on. It runs through the animation API rather than a\n * keyframe, since the stroke's length is measured off the path and a\n * stylesheet cannot ask for it. Under reduced motion the square is simply\n * there with its tick drawn, and nothing travels or rings.\n *\n * Mount it when the message lands. Everything here plays once, from the\n * start, and a fresh key is how the next send gets its own.\n */\nexport function SentMark({\n from = null,\n className,\n}: {\n from?: SentFrom | null;\n className?: string;\n}) {\n const frame = useRef<HTMLSpanElement>(null);\n const square = useRef<HTMLSpanElement>(null);\n const tick = useRef<SVGPathElement>(null);\n const ring = useRef<HTMLSpanElement>(null);\n\n // A layout effect, so the square is already at the button when the first\n // frame paints rather than flashing at rest and jumping back.\n useLayoutEffect(() => {\n const rest = frame.current;\n const box = square.current;\n const path = tick.current;\n const halo = ring.current;\n if (!rest || !box || !path || !halo || prefersReducedMotion()) return;\n\n const animations: Animation[] = [];\n const travels = from !== null;\n\n if (from !== null) {\n const seat = rest.getBoundingClientRect();\n animations.push(\n box.animate(\n [\n {\n left: `${from.left - seat.left}px`,\n top: `${from.top - seat.top}px`,\n width: `${from.width}px`,\n height: `${from.height}px`,\n // The pill's corner is half its height, whatever its class\n // says. Starting from the class's own number would keep the\n // corners round until the last frames and then snap them.\n borderRadius: `${from.height / 2}px`,\n },\n {\n left: \"0px\",\n top: \"0px\",\n width: `${seat.width}px`,\n height: `${seat.height}px`,\n borderRadius: getComputedStyle(box).borderRadius,\n },\n ],\n { duration: TRAVEL_MS, easing: EASE_OUT, fill: \"both\" },\n ),\n );\n }\n\n const drawDelay = travels ? TRAVEL_MS - DRAW_LEAD_MS : POP_DRAW_DELAY_MS;\n const ringDelay = travels ? TRAVEL_MS - RING_LEAD_MS : POP_RING_DELAY_MS;\n\n const length = path.getTotalLength();\n animations.push(\n path.animate(\n [{ strokeDashoffset: length }, { strokeDashoffset: 0 }],\n { duration: DRAW_MS, delay: drawDelay, easing: EASE_OUT, fill: \"both\" },\n ),\n );\n // The ring starts where the mark's edge is and rolls outward, so it\n // reads as leaving the mark rather than arriving from nowhere. Filled\n // forwards only: a backwards fill would hold its first frame through\n // the delay, and that is a hollow square waiting at the seat while the\n // button is still on its way there.\n animations.push(\n halo.animate(\n [\n { transform: \"scale(1)\", opacity: 0.6 },\n { transform: \"scale(1.9)\", opacity: 0 },\n ],\n { duration: RING_MS, delay: ringDelay, easing: EASE_OUT, fill: \"forwards\" },\n ),\n );\n // The dash is set beside the animation rather than in the markup, so a\n // browser with motion turned off never sees an undrawn tick.\n path.style.strokeDasharray = `${length}`;\n\n return () => {\n for (const animation of animations) animation.cancel();\n };\n }, [from]);\n\n return (\n <span\n ref={frame}\n className={cn(\"relative grid size-16 place-items-center\", className)}\n >\n <span\n ref={ring}\n aria-hidden\n className=\"absolute inset-0 rounded-2xl border-2 border-primary opacity-0\"\n />\n {/* Absolute at rest as well as in flight, so the travel is only ever\n four numbers changing and the seat underneath never reflows. */}\n <span\n ref={square}\n className={cn(\n \"absolute inset-0 grid place-items-center rounded-2xl bg-primary text-primary-foreground\",\n from === null &&\n \"animate-in duration-500 ease-spring fade-in-0 zoom-in-50 motion-reduce:animate-none\",\n )}\n >\n <svg\n viewBox=\"0 0 24 24\"\n aria-hidden\n className=\"size-8\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path ref={tick} d=\"M5 12.5 10 17.5 19 7\" />\n </svg>\n </span>\n </span>\n );\n}\n","\"use client\";\n\nimport { useCallback, useMemo, useState, useSyncExternalStore } from \"react\";\nimport type { MedianContactMessage } from \"../contact/message\";\nimport { deliverContact } from \"./contact\";\nimport { useDiagnostics, type MedianDiagnostics } from \"./diagnostics\";\nimport { useRememberInstall } from \"./install\";\nimport { getOrCreateSessionId, subscribeSession } from \"./session\";\nimport { useApiKeyShapeWarning } from \"./use-api-key-warning\";\nimport { useCustomDataSender } from \"./custom-data\";\nimport { useSignedInUser } from \"./signed-out\";\nimport { useMedianIdentity, useMissingCredentials } from \"./use-identity\";\nimport type { MedianUser } from \"./use-median-thread\";\nimport { useVisitorEmail } from \"./visitor-email\";\nimport { useMedianBranding } from \"./use-median-branding\";\n\ntype MedianContactHookOptions = {\n /** Who is signed in on your site, if anyone. */\n user?: MedianUser;\n /** Browser context on the message. On by default. */\n telemetry?: boolean;\n /**\n * Watch what goes wrong on the page, so a bug report filed from the\n * message arrives with the error already attached. The same switch\n * `<MedianSupport>` takes, and the same buffer behind it.\n */\n diagnostics?: MedianDiagnostics;\n /** Anything about your own app worth having next to a bug report. */\n appState?: Record<string, string | number | boolean>;\n};\n\n/**\n * Two ways in, the same two every component takes: the publishable key, or\n * the path of a route built with `medianIdentity`, which answers with the\n * key and whoever is signed in.\n */\nexport type UseMedianContactOptions = MedianContactHookOptions &\n (\n | { apiKey: string; identity?: string }\n | { apiKey?: string; identity: string }\n );\n\n/** What the hook hands back: one call, and the state around it. */\nexport type MedianContactControl = {\n showBranding: boolean;\n /**\n * Send what the form holds. Resolves once the message is in the inbox.\n * Rejects with an `Error` whose `message` is worth showing, and `error`\n * holds the same words until the next send.\n */\n send: (message: MedianContactMessage) => Promise<void>;\n /** A send is in flight. Hold the button. */\n isSending: boolean;\n /** Why the last send failed, or null. Cleared by the next send. */\n error: string | null;\n /**\n * Whether a send has somewhere to go. False on the server, and for the\n * beat before an identity route has answered with the key.\n */\n isReady: boolean;\n /**\n * Who is typing, as far as the page knows: what you passed, what the\n * identity route said, and the email the widget's own question was\n * answered with on this browser. For filling the boxes in.\n */\n user: MedianUser | undefined;\n};\n\n/**\n * A contact form of your own, on Median's inbox:\n *\n * const contact = useMedianContact({ apiKey: \"median_pk_...\" });\n *\n * <form\n * onSubmit={async (event) => {\n * event.preventDefault();\n * await contact.send({ email, message });\n * }}\n * >\n * ...\n * {contact.error && <p>{contact.error}</p>}\n * <button disabled={contact.isSending}>Send</button>\n * </form>\n *\n * Everything about the form is yours. What is sent starts a conversation in\n * your inbox exactly as `<MedianContactForm>` and the widget do, and the\n * reply goes to the email given. No provider, no connection to manage.\n */\nexport function useMedianContact(\n options: UseMedianContactOptions,\n /** Who to name in the console. `<MedianContactForm>` passes itself. */\n caller = \"useMedianContact()\",\n): MedianContactControl {\n const { apiKey, identity, user, telemetry, diagnostics, appState } = options;\n const resolved = useMedianIdentity(identity);\n useMissingCredentials(caller, apiKey, identity, \"it cannot send\");\n const key = apiKey ?? resolved.apiKey;\n const showBranding = useMedianBranding(key);\n useApiKeyShapeWarning(caller, key);\n useCustomDataSender();\n const person = useSignedInUser(user ?? resolved.user);\n // So a crash screen has a key to report with, the same as a mounted panel.\n useRememberInstall(key, person);\n\n // The session token is external state (localStorage), read as a store: the\n // server snapshot is null, so SSR and the first client paint agree, and\n // `reset()` on sign out lands here like it lands on the thread.\n const sessionId = useSyncExternalStore(\n subscribeSession,\n useCallback(\n () => (key === undefined ? null : getOrCreateSessionId(key)),\n [key],\n ),\n () => null,\n );\n const takeDiagnostics = useDiagnostics(diagnostics, appState);\n const [knownEmail] = useVisitorEmail(key ?? \"\");\n\n const [isSending, setSending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const send = useCallback(\n async (message: MedianContactMessage) => {\n setError(null);\n if (key === undefined || sessionId === null) {\n const reason =\n key === undefined\n ? \"This form is not connected yet. Try again in a moment.\"\n : \"Still connecting. Try again in a moment.\";\n setError(reason);\n throw new Error(reason);\n }\n\n setSending(true);\n try {\n const collected = takeDiagnostics();\n await deliverContact(\n {\n apiKey: key,\n sessionId,\n ...(person ? { user: person } : {}),\n ...(telemetry !== undefined ? { telemetry } : {}),\n ...(collected ? { diagnostics: collected } : {}),\n },\n message,\n );\n } catch (caught) {\n setError(caught instanceof Error ? caught.message : String(caught));\n throw caught;\n } finally {\n setSending(false);\n }\n },\n [key, sessionId, person, telemetry, takeDiagnostics],\n );\n\n // The host's word first, then the address this browser already gave.\n const known = useMemo<MedianUser | undefined>(() => {\n const email = person?.email || knownEmail || undefined;\n if (person === undefined && email === undefined) return undefined;\n return { ...person, ...(email ? { email } : {}) };\n }, [person, knownEmail]);\n\n return {\n send,\n showBranding,\n isSending,\n error,\n isReady: key !== undefined && sessionId !== null,\n user: known,\n };\n}\n","\"use client\";\n\nimport { unwrapMutationResult } from \"@median/backend/mutationResult\";\n\nimport { api } from \"@median/backend\";\nimport type { ClientDiagnostics } from \"@median/backend/diagnostics\";\nimport { ConvexHttpClient } from \"convex/browser\";\nimport { errorMessage } from \"@/lib/errors\";\nimport { composeBody } from \"../contact/contact-body\";\nimport type { MedianContactMessage } from \"../contact/message\";\nimport { collectContext, collectPage } from \"./context\";\nimport { restoreCustomData, takeCustomData } from \"./custom-data\";\nimport { MEDIAN_DEPLOYMENT_URL } from \"./deployment\";\nimport { getOrCreateSessionId } from \"./session\";\nimport { splitIdentity, type MedianUser } from \"./use-median-thread\";\nimport { writeVisitorEmail } from \"./visitor-email\";\n\n/**\n * Sending a contact message, for a form of the host's own.\n *\n * The same door the widget's composer uses, on the same session token, so\n * what a host's form sends starts the thread the corner would have started:\n * the team answers it from the inbox, the agent goes first, and with no\n * panel open to watch, the reply goes to the email given. Somebody who\n * writes here and later opens the widget finds their own conversation.\n *\n * A stateless HTTP client rather than the subscribing one, because nothing\n * is read back: a form is a thing you hand over, not a thread you watch.\n */\n\n/** What a send is told about who is sending and from where. */\nexport type MedianContactOptions = {\n /** The browser-safe median_pk_ id. */\n apiKey: string;\n /**\n * Who is signed in on your site, if anyone. What the form says about the\n * email and the name wins over this; the rest is shown to your team.\n */\n user?: MedianUser;\n /**\n * Whether the message carries what the browser already knows: time zone,\n * language, browser, OS, device, screen, and the page it was sent from.\n * On by default. Pass `false` and none of it is read at all.\n */\n telemetry?: boolean;\n};\n\nlet client: ConvexHttpClient | null = null;\n\n/**\n * The delivery itself, shared by the plain function and the hook.\n *\n * Rejects with an `Error` whose message is worth showing, so a form keeps\n * its answers and says why. Anything the host attached through\n * `medianSupport.attach` rides along, and is handed back on a failure so\n * the retry still carries it.\n */\nexport async function deliverContact(\n options: MedianContactOptions & {\n sessionId: string;\n /** What the page has been recording, if the caller collects it. */\n diagnostics?: ClientDiagnostics;\n },\n message: MedianContactMessage,\n): Promise<void> {\n const { apiKey, sessionId, user, telemetry = true } = options;\n const email = message.email.trim();\n const name = message.name?.trim();\n\n const custom = takeCustomData();\n try {\n if (!MEDIAN_DEPLOYMENT_URL) {\n throw new Error(\n \"The Median SDK was built without a deployment URL. Set NEXT_PUBLIC_CONVEX_URL.\",\n );\n }\n client ??= new ConvexHttpClient(MEDIAN_DEPLOYMENT_URL);\n\n // Read now rather than earlier: all of it is wanted as \"what was true\n // when they wrote\", and a form can sit on a page for an hour first.\n const context = telemetry ? collectContext() : undefined;\n const page = telemetry ? collectPage() : undefined;\n const { facts, claim } = splitIdentity(user ?? null);\n\n await client\n .mutation(api.widget.sendV2, {\n apiKey,\n sessionId,\n body: composeBody(message),\n // What was typed wins over what the host passed. They are looking at\n // the box, and the box is what they just filled in.\n user: { ...facts, email, ...(name ? { name } : {}) },\n ...(claim ? { identity: claim } : {}),\n ...(context ? { context } : {}),\n ...(page ? { page } : {}),\n ...(options.diagnostics ? { diagnostics: options.diagnostics } : {}),\n ...(custom ? { customData: custom } : {}),\n // Replies go to the address they typed: nothing on the page shows one.\n fromContactForm: true,\n // A form is a new question every time, never a follow-up to whatever\n // the corner panel had open.\n startNew: true,\n })\n .then(unwrapMutationResult);\n } catch (caught) {\n // Back in the queue, so the retry carries them like it keeps the answers.\n if (custom) restoreCustomData(custom);\n console.error(\"Median: sending a contact message failed\", caught);\n throw new Error(errorMessage(caught), { cause: caught });\n }\n\n // So the widget on the same site never asks for the address again.\n writeVisitorEmail(apiKey, email);\n}\n\n/**\n * Send a contact message from a form of your own, on any page in the\n * browser:\n *\n * await sendMedianContact(\n * { apiKey: \"median_pk_...\" },\n * { email, message, name, fields: { Topic: topic } },\n * );\n *\n * It resolves once the message is in your inbox and rejects with an `Error`\n * whose `message` is worth showing above your button. Nothing about the\n * form is ours: any `<form>`, any framework, any styling.\n *\n * In React, `useMedianContact()` is the same call with `isSending` and\n * `error` kept for you, and an identity route resolved.\n */\nexport async function sendMedianContact(\n options: MedianContactOptions,\n message: MedianContactMessage,\n): Promise<void> {\n const sessionId = getOrCreateSessionId(options.apiKey);\n if (sessionId === null) {\n throw new Error(\n \"sendMedianContact() was called where there is no browser, so nothing was sent. Call it from the page, after it has loaded.\",\n );\n }\n await deliverContact({ ...options, sessionId }, message);\n}\n","\"use client\";\n\nimport {\n MedianContactForm,\n type MedianContactFormProps,\n} from \"./median-contact-form\";\nimport { mountMedian, nothingMounted, type MedianMountInstance } from \"./mount-into\";\n\n/**\n * The component's props, plus where on the page the form goes.\n *\n * Spelled out rather than `Omit`ped off the props, because omitting a key\n * from a union of two shapes flattens it into one and loses the \"either,\n * but not neither\" the union is there to say.\n */\nexport type MedianContactFormMountOptions = MedianContactFormProps & {\n /**\n * The element the form is rendered into, or a selector for it:\n *\n * mountMedianContactForm({ target: \"#contact\", apiKey: \"median_pk_...\" });\n *\n * Unlike the widget, a form has no corner to go to, so this is required.\n */\n target: string | Element;\n};\n\nexport type MedianContactFormInstance = MedianMountInstance<MedianContactFormProps>;\n\n/** What a selector or an element is called in the console. */\nfunction describe(target: string | Element): string {\n return typeof target === \"string\" ? `an element matching \"${target}\"` : \"the element passed as target\";\n}\n\n/**\n * The same form, for a page that has never heard of React: Vue, Svelte,\n * Rails, a script on plain HTML.\n *\n * const form = mountMedianContactForm({\n * target: \"#contact\",\n * apiKey: \"median_pk_...\",\n * fields: [{ name: \"name\", label: \"Name\" }],\n * });\n *\n * It takes the component's props as one options object, plus `target`, and\n * renders into a container of its own inside that element with the\n * package's copy of React. Whatever else the element holds is left alone.\n */\nexport function mountMedianContactForm({\n target,\n ...options\n}: MedianContactFormMountOptions): MedianContactFormInstance {\n let into: Element | null = null;\n\n if (typeof document !== \"undefined\") {\n into = typeof target === \"string\" ? document.querySelector(target) : target;\n if (into === null) {\n console.error(\n `Median: mountMedianContactForm() could not find ${describe(target)}, so nothing was mounted. Pass a selector that matches something on the page, or the element itself, once it exists.`,\n );\n return nothingMounted;\n }\n }\n\n return mountMedian({\n caller: \"mountMedianContactForm\",\n what: \"the form\",\n marker: \"medianContactForm\",\n into,\n options,\n render: (current) => <MedianContactForm {...current} />,\n });\n}\n"],"mappings":"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","names":["useEffect","useMemo","useState","useCallback","useEffect","useRef","useState","at","buttonY","listeners","useState","React","jsx","i","S","l","d","p","f","X","i","S","l","jsx","X","d","p","param","React","i","Slot","__name","Slottable","child","React","jsx","Fragment","jsx","jsxs","useCallback","useLayoutEffect","useRef","prefersReducedMotion","useRef","useCallback","useLayoutEffect","prefersReducedMotion","useEffect","useRef","useState","forwardRef","jsx","Image","jsx","jsx","jsxs","File","useEffect","useRef","useState","useRef","useState","jsx","jsx","useCallback","useEffect","useRef","useLayoutEffect","useRef","d","jsx","jsxs","d","jsx","jsx","jsxs","Fragment","jsx","jsxs","useState","useRef","useEffect","jsx","jsxs","useState","jsx","jsx","useState","shape","jsx","shape","jsx","jsxs","Fragment","jsx","jsxs","useState","useState","EMAIL_PATTERN","useState","jsx","jsx","jsxs","useEffect","useRef","useState","jsx","jsxs","useState","jsx","jsxs","SWAP","useRef","useEffect","jsx","jsxs","jsx","jsxs","useState","jsx","jsx","jsxs","useState","jsx","useLayoutEffect","useRef","jsx","prefersReducedMotion","release","useRef","useLayoutEffect","useState","Fragment","jsx","jsxs","useState","File","jsx","useLayoutEffect","useRef","useState","useSyncExternalStore","jsx","jsxs","node","delay","useRef","play","useState","useLayoutEffect","jsx","useState","jsx","jsxs","useState","jsx","jsxs","jsx","jsxs","delay","Fragment","jsx","jsxs","useState","useEffect","useRef","useState","jsx","jsxs","useState","useRef","useEffect","jsx","jsxs","jsx","jsxs","jsx","jsxs","useState","current","useCallback","useEffect","useLayoutEffect","useRef","useState","useSyncExternalStore","enabled","useSyncExternalStore","useState","useRef","useCallback","useLayoutEffect","useEffect","held","clamp","prefersReducedMotion","useLayoutEffect","useState","enabled","useLayoutEffect","useState","useSyncExternalStore","useEffect","held","useSyncExternalStore","useState","useLayoutEffect","useRef","useSyncExternalStore","subscribe","onServer","useEffect","held","jsx","jsxs","useRef","useState","Fragment","jsx","jsxs","WIDTH","buttonY","held","useState","Fragment","jsx","jsxs","SHEET","useState","useRef","useCallback","useEffect","current","useEffect","useRef","useState","createPortal","jsx","jsxs","LAYER","prefersReducedMotion","useSyncExternalStore","useEffect","useCallback","useEffect","useRef","useSyncExternalStore","listeners","at","store","ConvexError","useSyncExternalStore","read","MESSAGE_MAX_LENGTH","trim","withoutQuery","MESSAGE_MAX_LENGTH","events","useCallback","useEffect","useRef","MESSAGE_MAX_LENGTH","withoutQuery","useEffect","listeners","listeners","i","useMutation","useQuery","useCallback","useCallback","useEffect","useMemo","useState","useSyncExternalStore","useEffect","useState","STORAGE_PREFIX","read","useCallback","useEffect","useMemo","useRef","useState","FADE_MS","prefersReducedMotion","enabled","useRef","useState","useEffect","useCallback","key","current","useMemo","useCallback","useEffect","useRef","useState","current","ready","useCallback","useSyncExternalStore","STORAGE_PREFIX","inMemory","listeners","i","useSyncExternalStore","useCallback","useQuery","useMutation","useMemo","useEffect","useState","current","conversationId","client","useSyncExternalStore","useEffect","useRef","useState","useSyncExternalStore","useSyncExternalStore","useState","useRef","useEffect","person","useSyncExternalStore","listeners","useSyncExternalStore","ConvexError","useEffect","useState","useSyncExternalStore","inFlight","read","useEffect","useState","useSyncExternalStore","ConvexHttpClient","useEffect","useState","useState","useEffect","client","ConvexHttpClient","useState","jsx","client","useState","useEffect","i","delay","useEffect","useRef","useCallback","useEffect","useLayoutEffect","useRef","useState","isDevelopment","at","drawn","count","useCallback","useState","inMemory","at","at","useState","useCallback","at","useEffect","count","current","Fragment","jsx","jsxs","useEffect","useState","useMemo","current","jsx","current","jsx","jsx","current","useCallback","useEffect","useRef","useState","useEffect","useRef","useState","useEffect","useRef","createPortal","useLayoutEffect","useState","GAP","MARGIN","clamp","jsx","LAYER","MOTION","OPEN","SHUT","useRef","useEffect","createPortal","Fragment","jsx","jsxs","THANKS_MS","useState","useRef","useEffect","draft","useSyncExternalStore","store","useSyncExternalStore","useCallback","useState","useSyncExternalStore","useSyncExternalStore","useCallback","useState","Fragment","jsx","jsxs","DEFAULT_WIDTH","useRef","useCallback","useState","useEffect","count","jsx","current","useEffect","useMemo","useEffect","useMemo","useRef","useState","useState","jsx","jsxs","held","useState","useLayoutEffect","useRef","jsx","jsxs","useRef","useLayoutEffect","prefersReducedMotion","jsx","jsxs","EMAIL_PATTERN","ARRIVES","useMemo","useRef","useState","current","useEffect","count","useCallback","useMemo","useState","useSyncExternalStore","ConvexHttpClient","client","ConvexHttpClient","useSyncExternalStore","useCallback","useState","useMemo","jsx","useEffect","useMemo","jsx","current"]}