@alfadocs/ui-kit-debug 1.66.0 → 1.67.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 (235) hide show
  1. package/dist/_chunks/ai-credits-exhausted-notice-DfAgfZDF.js +287 -0
  2. package/dist/_chunks/ai-credits-exhausted-notice-DfAgfZDF.js.map +1 -0
  3. package/dist/_chunks/ai-credits-exhausted-notice.agent-DgTSTgez.js +43 -0
  4. package/dist/_chunks/ai-credits-exhausted-notice.agent-DgTSTgez.js.map +1 -0
  5. package/dist/_chunks/ai-credits-notice-DQ-r3iQU.js +191 -0
  6. package/dist/_chunks/ai-credits-notice-DQ-r3iQU.js.map +1 -0
  7. package/dist/_chunks/{ai-permission-notice-DpRmZ9Q3.js → ai-permission-notice-qsdm4Sx4.js} +2 -2
  8. package/dist/_chunks/{ai-permission-notice-DpRmZ9Q3.js.map → ai-permission-notice-qsdm4Sx4.js.map} +1 -1
  9. package/dist/_chunks/{alert-DdDpOi-P.js → alert-DjsJMGRE.js} +3 -2
  10. package/dist/_chunks/{alert-DdDpOi-P.js.map → alert-DjsJMGRE.js.map} +1 -1
  11. package/dist/_chunks/{anamnesis-summary-rows-BIfw890v.js → anamnesis-summary-rows-BQ_EokKz.js} +2 -2
  12. package/dist/_chunks/{anamnesis-summary-rows-BIfw890v.js.map → anamnesis-summary-rows-BQ_EokKz.js.map} +1 -1
  13. package/dist/_chunks/{audio-recorder-DR-u1qbl.js → audio-recorder-B11RR-rC.js} +2 -2
  14. package/dist/_chunks/{audio-recorder-DR-u1qbl.js.map → audio-recorder-B11RR-rC.js.map} +1 -1
  15. package/dist/_chunks/{auth-form-parts-Cmb41LSQ.js → auth-form-parts-DwUTH6YN.js} +2 -2
  16. package/dist/_chunks/{auth-form-parts-Cmb41LSQ.js.map → auth-form-parts-DwUTH6YN.js.map} +1 -1
  17. package/dist/_chunks/{booking-DkjatiXc.js → booking-tTVxKPQY.js} +2 -2
  18. package/dist/_chunks/{booking-DkjatiXc.js.map → booking-tTVxKPQY.js.map} +1 -1
  19. package/dist/_chunks/{calendar-workspace-Cp8yTZ1g.js → calendar-workspace-MRrzcxAy.js} +6 -6
  20. package/dist/_chunks/{calendar-workspace-Cp8yTZ1g.js.map → calendar-workspace-MRrzcxAy.js.map} +1 -1
  21. package/dist/_chunks/{document-preview-B1dQm292.js → document-preview-VyQOjFVL.js} +2 -2
  22. package/dist/_chunks/{document-preview-B1dQm292.js.map → document-preview-VyQOjFVL.js.map} +1 -1
  23. package/dist/_chunks/{document-scanner-CVPVLK7z.js → document-scanner-CXH_GYh4.js} +2 -2
  24. package/dist/_chunks/{document-scanner-CVPVLK7z.js.map → document-scanner-CXH_GYh4.js.map} +1 -1
  25. package/dist/_chunks/{entity-summary-BQ9_KA8S.js → entity-summary-Bj-Wxq7Y.js} +2 -2
  26. package/dist/_chunks/{entity-summary-BQ9_KA8S.js.map → entity-summary-Bj-Wxq7Y.js.map} +1 -1
  27. package/dist/_chunks/{error-summary-DrUfuKu1.js → error-summary-PUE5Zxp9.js} +2 -2
  28. package/dist/_chunks/{error-summary-DrUfuKu1.js.map → error-summary-PUE5Zxp9.js.map} +1 -1
  29. package/dist/_chunks/{forgot-password-form-CgZkEvZy.js → forgot-password-form-CPOfRE0C.js} +4 -4
  30. package/dist/_chunks/{forgot-password-form-CgZkEvZy.js.map → forgot-password-form-CPOfRE0C.js.map} +1 -1
  31. package/dist/_chunks/{freemium-paywall-DKJHRFpp.js → freemium-paywall-6DO7noHX.js} +2 -2
  32. package/dist/_chunks/{freemium-paywall-DKJHRFpp.js.map → freemium-paywall-6DO7noHX.js.map} +1 -1
  33. package/dist/_chunks/help-me-write-templates-Bw3Q58x-.js +687 -0
  34. package/dist/_chunks/help-me-write-templates-Bw3Q58x-.js.map +1 -0
  35. package/dist/_chunks/hourglass-BEAzW1wE.js +26 -0
  36. package/dist/_chunks/hourglass-BEAzW1wE.js.map +1 -0
  37. package/dist/_chunks/{login-dev-card-PnADNcI9.js → login-dev-card-A-CL3mjt.js} +3 -3
  38. package/dist/_chunks/{login-dev-card-PnADNcI9.js.map → login-dev-card-A-CL3mjt.js.map} +1 -1
  39. package/dist/_chunks/{marketplace-app-shell-C-2e4eCD.js → marketplace-app-shell-DtSX8sYb.js} +2 -2
  40. package/dist/_chunks/{marketplace-app-shell-C-2e4eCD.js.map → marketplace-app-shell-DtSX8sYb.js.map} +1 -1
  41. package/dist/_chunks/{notes-panel-uEGIL7mi.js → notes-panel-IdsmqRFC.js} +3 -3
  42. package/dist/_chunks/{notes-panel-uEGIL7mi.js.map → notes-panel-IdsmqRFC.js.map} +1 -1
  43. package/dist/_chunks/{patient-details-4P1vA9Ls.js → patient-details-C-EWQTms.js} +2 -2
  44. package/dist/_chunks/{patient-details-4P1vA9Ls.js.map → patient-details-C-EWQTms.js.map} +1 -1
  45. package/dist/_chunks/{patient-field-row-CBXhlfGD.js → patient-field-row-lY-Tb5Mo.js} +155 -176
  46. package/dist/_chunks/patient-field-row-lY-Tb5Mo.js.map +1 -0
  47. package/dist/_chunks/{patient-search-DOnDUVsl.js → patient-search-DpAKidZ5.js} +2 -2
  48. package/dist/_chunks/{patient-search-DOnDUVsl.js.map → patient-search-DpAKidZ5.js.map} +1 -1
  49. package/dist/_chunks/{patient-summary-card-CuDLo8MP.js → patient-summary-card-Bpto_VCD.js} +2 -2
  50. package/dist/_chunks/{patient-summary-card-CuDLo8MP.js.map → patient-summary-card-Bpto_VCD.js.map} +1 -1
  51. package/dist/_chunks/{payment-automation-upsell-C8MEu0t6.js → payment-automation-upsell-BQ307qEX.js} +2 -2
  52. package/dist/_chunks/{payment-automation-upsell-C8MEu0t6.js.map → payment-automation-upsell-BQ307qEX.js.map} +1 -1
  53. package/dist/_chunks/{payment-form-C3dGY7v4.js → payment-form-XKtinW8r.js} +2 -2
  54. package/dist/_chunks/{payment-form-C3dGY7v4.js.map → payment-form-XKtinW8r.js.map} +1 -1
  55. package/dist/_chunks/{pdf-viewer-DJ9VN7cu.js → pdf-viewer-BvoVMjH4.js} +2 -2
  56. package/dist/_chunks/{pdf-viewer-DJ9VN7cu.js.map → pdf-viewer-BvoVMjH4.js.map} +1 -1
  57. package/dist/_chunks/{practice-results-8h2Utpt3.js → practice-results-BrUDDMXC.js} +2 -2
  58. package/dist/_chunks/{practice-results-8h2Utpt3.js.map → practice-results-BrUDDMXC.js.map} +1 -1
  59. package/dist/_chunks/{registration-form-BQyT_kBX.js → registration-form-BxwqUZzk.js} +3 -3
  60. package/dist/_chunks/{registration-form-BQyT_kBX.js.map → registration-form-BxwqUZzk.js.map} +1 -1
  61. package/dist/_chunks/{rich-text-editor-M2gT_E7f.js → rich-text-editor-aCIoptbg.js} +1653 -1743
  62. package/dist/_chunks/{rich-text-editor-M2gT_E7f.js.map → rich-text-editor-aCIoptbg.js.map} +1 -1
  63. package/dist/_chunks/{sign-document-CY7xTXbm.js → sign-document-D1P1nUQA.js} +2 -2
  64. package/dist/_chunks/{sign-document-CY7xTXbm.js.map → sign-document-D1P1nUQA.js.map} +1 -1
  65. package/dist/_chunks/{signature-request-BMCHy2ZO.js → signature-request-3u6TvRt2.js} +2 -2
  66. package/dist/_chunks/{signature-request-BMCHy2ZO.js.map → signature-request-3u6TvRt2.js.map} +1 -1
  67. package/dist/_chunks/{task-editor-CifOznfq.js → task-editor-Bb5MMcid.js} +2 -2
  68. package/dist/_chunks/{task-editor-CifOznfq.js.map → task-editor-Bb5MMcid.js.map} +1 -1
  69. package/dist/_chunks/{tasks-panel-8nLwBw6P.js → tasks-panel-BOb57Ysw.js} +2 -2
  70. package/dist/_chunks/{tasks-panel-8nLwBw6P.js.map → tasks-panel-BOb57Ysw.js.map} +1 -1
  71. package/dist/_chunks/{use-appointment-details-read-B-FEWbQM.js → use-appointment-details-read-CVSPoP5j.js} +4 -4
  72. package/dist/_chunks/{use-appointment-details-read-B-FEWbQM.js.map → use-appointment-details-read-CVSPoP5j.js.map} +1 -1
  73. package/dist/_chunks/voice-control-panel-BJIEbDq6.js +1148 -0
  74. package/dist/_chunks/voice-control-panel-BJIEbDq6.js.map +1 -0
  75. package/dist/_chunks/{warning-stack-HYWAytZV.js → warning-stack-BdY-0bd3.js} +2 -2
  76. package/dist/_chunks/{warning-stack-HYWAytZV.js.map → warning-stack-BdY-0bd3.js.map} +1 -1
  77. package/dist/_chunks/{workflow-map-C2B2qIr1.js → workflow-map-CYbRD9ek.js} +2 -2
  78. package/dist/_chunks/{workflow-map-C2B2qIr1.js.map → workflow-map-CYbRD9ek.js.map} +1 -1
  79. package/dist/agent-catalog.json +50 -3
  80. package/dist/agent-i18n/en.json +7 -4
  81. package/dist/components/ai-credits-notice/ai-credits-exhausted-notice.agent.d.ts +4 -0
  82. package/dist/components/ai-credits-notice/ai-credits-exhausted-notice.agent.d.ts.map +1 -0
  83. package/dist/components/ai-credits-notice/ai-credits-exhausted-notice.d.ts +96 -0
  84. package/dist/components/ai-credits-notice/ai-credits-exhausted-notice.d.ts.map +1 -0
  85. package/dist/components/ai-credits-notice/ai-credits-notice.d.ts +30 -4
  86. package/dist/components/ai-credits-notice/ai-credits-notice.d.ts.map +1 -1
  87. package/dist/components/ai-credits-notice/index.d.ts +3 -0
  88. package/dist/components/ai-credits-notice/index.d.ts.map +1 -1
  89. package/dist/components/ai-credits-notice/index.js +10 -4
  90. package/dist/components/ai-credits-notice/index.js.map +1 -1
  91. package/dist/components/ai-permission-notice/index.js +1 -1
  92. package/dist/components/alert/index.js +1 -1
  93. package/dist/components/appointment-details-panel/index.js +1 -1
  94. package/dist/components/audio-recorder/index.js +1 -1
  95. package/dist/components/booking/index.js +1 -1
  96. package/dist/components/document-preview/index.js +1 -1
  97. package/dist/components/document-scanner/index.js +1 -1
  98. package/dist/components/entity-summary/index.js +1 -1
  99. package/dist/components/error-summary/index.js +1 -1
  100. package/dist/components/freemium-paywall/index.js +1 -1
  101. package/dist/components/notes-panel/index.js +1 -1
  102. package/dist/components/patient-details/index.js +1 -1
  103. package/dist/components/patient-search/index.js +1 -1
  104. package/dist/components/patient-summary-card/index.js +1 -1
  105. package/dist/components/payment-form/index.js +1 -1
  106. package/dist/components/pdf-viewer/index.js +1 -1
  107. package/dist/components/practice-results/index.js +1 -1
  108. package/dist/components/rich-text-editor/index.js +1 -1
  109. package/dist/components/rich-text-editor/rich-text-editor.d.ts.map +1 -1
  110. package/dist/components/sign-document/index.js +1 -1
  111. package/dist/components/task-editor/index.js +1 -1
  112. package/dist/components/tasks-panel/index.js +1 -1
  113. package/dist/components/voice-control-panel/index.d.ts +2 -1
  114. package/dist/components/voice-control-panel/index.d.ts.map +1 -1
  115. package/dist/components/voice-control-panel/index.js +1 -1
  116. package/dist/components/voice-control-panel/voice-control-panel.agent.d.ts.map +1 -1
  117. package/dist/components/voice-control-panel/voice-control-panel.d.ts +43 -3
  118. package/dist/components/voice-control-panel/voice-control-panel.d.ts.map +1 -1
  119. package/dist/components/warning-stack/index.js +1 -1
  120. package/dist/components/workflow/index.js +1 -1
  121. package/dist/i18n/locales/ar.d.ts +27 -12
  122. package/dist/i18n/locales/ar.d.ts.map +1 -1
  123. package/dist/i18n/locales/ar.js +27 -12
  124. package/dist/i18n/locales/ar.js.map +1 -1
  125. package/dist/i18n/locales/de.d.ts +27 -12
  126. package/dist/i18n/locales/de.d.ts.map +1 -1
  127. package/dist/i18n/locales/de.js +27 -12
  128. package/dist/i18n/locales/de.js.map +1 -1
  129. package/dist/i18n/locales/el.d.ts +27 -12
  130. package/dist/i18n/locales/el.d.ts.map +1 -1
  131. package/dist/i18n/locales/el.js +27 -12
  132. package/dist/i18n/locales/el.js.map +1 -1
  133. package/dist/i18n/locales/en.d.ts +27 -12
  134. package/dist/i18n/locales/en.d.ts.map +1 -1
  135. package/dist/i18n/locales/en.js +27 -12
  136. package/dist/i18n/locales/en.js.map +1 -1
  137. package/dist/i18n/locales/es.d.ts +27 -12
  138. package/dist/i18n/locales/es.d.ts.map +1 -1
  139. package/dist/i18n/locales/es.js +27 -12
  140. package/dist/i18n/locales/es.js.map +1 -1
  141. package/dist/i18n/locales/fr.d.ts +27 -12
  142. package/dist/i18n/locales/fr.d.ts.map +1 -1
  143. package/dist/i18n/locales/fr.js +27 -12
  144. package/dist/i18n/locales/fr.js.map +1 -1
  145. package/dist/i18n/locales/hi.d.ts +27 -12
  146. package/dist/i18n/locales/hi.d.ts.map +1 -1
  147. package/dist/i18n/locales/hi.js +27 -12
  148. package/dist/i18n/locales/hi.js.map +1 -1
  149. package/dist/i18n/locales/it.d.ts +27 -12
  150. package/dist/i18n/locales/it.d.ts.map +1 -1
  151. package/dist/i18n/locales/it.js +27 -12
  152. package/dist/i18n/locales/it.js.map +1 -1
  153. package/dist/i18n/locales/ja.d.ts +27 -12
  154. package/dist/i18n/locales/ja.d.ts.map +1 -1
  155. package/dist/i18n/locales/ja.js +27 -12
  156. package/dist/i18n/locales/ja.js.map +1 -1
  157. package/dist/i18n/locales/nl.d.ts +27 -12
  158. package/dist/i18n/locales/nl.d.ts.map +1 -1
  159. package/dist/i18n/locales/nl.js +27 -12
  160. package/dist/i18n/locales/nl.js.map +1 -1
  161. package/dist/i18n/locales/pl.d.ts +27 -12
  162. package/dist/i18n/locales/pl.d.ts.map +1 -1
  163. package/dist/i18n/locales/pl.js +27 -12
  164. package/dist/i18n/locales/pl.js.map +1 -1
  165. package/dist/i18n/locales/pt.d.ts +27 -12
  166. package/dist/i18n/locales/pt.d.ts.map +1 -1
  167. package/dist/i18n/locales/pt.js +27 -12
  168. package/dist/i18n/locales/pt.js.map +1 -1
  169. package/dist/i18n/locales/ro.d.ts +27 -12
  170. package/dist/i18n/locales/ro.d.ts.map +1 -1
  171. package/dist/i18n/locales/ro.js +27 -12
  172. package/dist/i18n/locales/ro.js.map +1 -1
  173. package/dist/i18n/locales/ru.d.ts +27 -12
  174. package/dist/i18n/locales/ru.d.ts.map +1 -1
  175. package/dist/i18n/locales/ru.js +27 -12
  176. package/dist/i18n/locales/ru.js.map +1 -1
  177. package/dist/i18n/locales/sq.d.ts +27 -12
  178. package/dist/i18n/locales/sq.d.ts.map +1 -1
  179. package/dist/i18n/locales/sq.js +27 -12
  180. package/dist/i18n/locales/sq.js.map +1 -1
  181. package/dist/i18n/locales/sv.d.ts +27 -12
  182. package/dist/i18n/locales/sv.d.ts.map +1 -1
  183. package/dist/i18n/locales/sv.js +27 -12
  184. package/dist/i18n/locales/sv.js.map +1 -1
  185. package/dist/i18n/locales/tr.d.ts +27 -12
  186. package/dist/i18n/locales/tr.d.ts.map +1 -1
  187. package/dist/i18n/locales/tr.js +27 -12
  188. package/dist/i18n/locales/tr.js.map +1 -1
  189. package/dist/i18n/locales/zh.d.ts +27 -12
  190. package/dist/i18n/locales/zh.d.ts.map +1 -1
  191. package/dist/i18n/locales/zh.js +27 -12
  192. package/dist/i18n/locales/zh.js.map +1 -1
  193. package/dist/index.js +814 -809
  194. package/dist/index.js.map +1 -1
  195. package/dist/locales/ar.json +27 -12
  196. package/dist/locales/de.json +27 -12
  197. package/dist/locales/el.json +27 -12
  198. package/dist/locales/en.json +27 -12
  199. package/dist/locales/es.json +27 -12
  200. package/dist/locales/fr.json +27 -12
  201. package/dist/locales/hi.json +27 -12
  202. package/dist/locales/it.json +27 -12
  203. package/dist/locales/ja.json +27 -12
  204. package/dist/locales/nl.json +27 -12
  205. package/dist/locales/pl.json +27 -12
  206. package/dist/locales/pt.json +27 -12
  207. package/dist/locales/ro.json +27 -12
  208. package/dist/locales/ru.json +27 -12
  209. package/dist/locales/sq.json +27 -12
  210. package/dist/locales/sv.json +27 -12
  211. package/dist/locales/tr.json +27 -12
  212. package/dist/locales/zh.json +27 -12
  213. package/dist/patterns/anamnesis/index.js +1 -1
  214. package/dist/patterns/calendar/index.js +1 -1
  215. package/dist/patterns/forgot-password-form/index.js +1 -1
  216. package/dist/patterns/help-me-write-templates/help-me-write-templates.d.ts +6 -23
  217. package/dist/patterns/help-me-write-templates/help-me-write-templates.d.ts.map +1 -1
  218. package/dist/patterns/help-me-write-templates/index.js +8 -7
  219. package/dist/patterns/help-me-write-templates/index.js.map +1 -1
  220. package/dist/patterns/login-form/index.js +1 -1
  221. package/dist/patterns/marketplace-app-shell/index.js +1 -1
  222. package/dist/patterns/payment-automation-upsell/index.js +1 -1
  223. package/dist/patterns/registration-form/index.js +1 -1
  224. package/dist/patterns/signature-request/index.js +1 -1
  225. package/dist/tokens.css +1 -1
  226. package/package.json +1 -1
  227. package/dist/_chunks/ai-credits-notice-pK9BOmEz.js +0 -90
  228. package/dist/_chunks/ai-credits-notice-pK9BOmEz.js.map +0 -1
  229. package/dist/_chunks/ai-credits-notice.agent-C9L7orrP.js +0 -17
  230. package/dist/_chunks/ai-credits-notice.agent-C9L7orrP.js.map +0 -1
  231. package/dist/_chunks/help-me-write-templates-DFmqnsAa.js +0 -747
  232. package/dist/_chunks/help-me-write-templates-DFmqnsAa.js.map +0 -1
  233. package/dist/_chunks/patient-field-row-CBXhlfGD.js.map +0 -1
  234. package/dist/_chunks/voice-control-panel-C0BfY23Y.js +0 -1009
  235. package/dist/_chunks/voice-control-panel-C0BfY23Y.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help-me-write-templates-Bw3Q58x-.js","sources":["../../src/patterns/help-me-write-templates/help-me-write-templates.tsx"],"sourcesContent":["/**\n * Help me write templates (HTP-6383) — practice-shared AI templates in the\n * RichTextEditor.\n *\n * Runtime pattern: real consuming apps mount this in production (surfaced via\n * `src/patterns/index.ts` and the `./patterns/help-me-write-templates`\n * subpath). Templates are an AI action of the editor itself, next to\n * spelling and auto-format: a Templates segment in the AI pill and in the\n * selection bubble (`aiTemplates`). Picking one rewrites the selection, else\n * the whole note, then this pattern offers Undo. The editor keeps the\n * patient AI-consent gate and resume-after-grant for all of it.\n *\n * Parts, usable on their own or wired together by `useHelpMeWriteTemplates`:\n * - `HelpMeWriteTemplatesBar` — the Help-me-write bar (`renderHelpMeWrite`):\n * a free prompt with an optional mic, the draft and Insert.\n * - `AiTemplateManager` — the dialog to list, create, edit, delete and\n * reorder templates.\n * - `AiCreditsExhaustedNotice`: the out-of-credits state the bar shows when\n * an AI call rejects with `AiCreditsExhaustedError`, from the bar or the\n * pill. It lives in `src/components/ai-credits-notice/` (shared with\n * VoiceControlPanel) and is re-exported here, with `AiCreditsInfo`.\n * - `HelpMeWriteUndoNotice` — the success notice with Undo after a template\n * rewrote the note.\n *\n * The host owns the templates (list + persistence) and the AI call. The kit\n * never talks to a backend.\n */\nimport {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type DragEvent,\n type KeyboardEvent,\n type ReactNode,\n} from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { cva } from 'class-variance-authority';\nimport {\n GripVertical,\n Mic,\n Pencil,\n Plus,\n RotateCcw,\n Send,\n Square,\n Trash2,\n X,\n} from 'lucide-react';\n\nimport { Alert } from '../../components/alert';\nimport {\n AiCreditsExhaustedNotice,\n type AiCreditsInfo,\n} from '../../components/ai-credits-notice';\nimport { Button, IconButton } from '../../components/button';\nimport { Dialog } from '../../components/dialog';\nimport { EditorOverlay } from '../../components/editor-overlay';\nimport { EmptyState } from '../../components/empty-state';\nimport { FormField } from '../../components/form-field';\nimport type {\n HelpMeWriteRenderContext,\n RichTextEditorAiApplied,\n RichTextEditorAiTemplates,\n RichTextEditorProps,\n} from '../../components/rich-text-editor';\nimport { Spinner } from '../../components/spinner';\nimport { TextArea } from '../../components/text-area';\nimport { StateBackedTextInput } from '../../components/_shared/state-backed-text-input';\nimport { SafeHtml } from '../../components/_shared/safe-html';\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\n/** A practice-shared AI template. */\nexport interface AiTemplate {\n id: string;\n /** Up to 100 characters, unique within the practice. */\n name: string;\n /** One line under the name in the Templates menu. */\n description?: string;\n /** The instructions sent to the AI, up to 4000 characters. */\n prompt: string;\n /**\n * @deprecated The list order is the menu order: the first template is the\n * one most people use. Ignored; reorder with `onReorderTemplates`.\n */\n isDefault?: boolean;\n}\n\n/** What the manager hands `onSaveTemplate`: `id` is absent for a new one. */\nexport type AiTemplateDraft = Omit<AiTemplate, 'id'> & { id?: string };\n\n/** One Help-me-write request. `template` is absent for a free prompt. */\nexport interface HelpMeWriteRequest {\n template?: AiTemplate;\n /** The free prompt, or extra instructions for a template. May be ''. */\n prompt: string;\n /**\n * The context, as plain text. For a template, the text it rewrites. For\n * the free prompt, what the draft is about. From the AI pill it is the\n * whole note; from the selection bubble, the selected text.\n */\n sourceText: string;\n /** Where `sourceText` came from. */\n scope?: 'document' | 'selection';\n signal: AbortSignal;\n}\n\n/**\n * Throw (or reject with) this from `onGenerate` when the practice is out of\n * AI credits (the platform endpoint's 402), and the bar shows the\n * out-of-credits notice instead of a generic error.\n */\nexport class AiCreditsExhaustedError extends Error {\n constructor(message = 'AI credits exhausted') {\n super(message);\n this.name = 'AiCreditsExhaustedError';\n }\n}\n\n// The out-of-credits notice and its info type moved to the component layer\n// (`src/components/ai-credits-notice/`) so VoiceControlPanel can share them.\n// Re-exported here so every existing import keeps working.\nexport {\n AiCreditsExhaustedNotice,\n type AiCreditsExhaustedNoticeProps,\n type AiCreditsInfo,\n} from '../../components/ai-credits-notice';\n\nexport type HelpMeWriteApplyMode = 'replace' | 'below' | 'insert';\n\n/** Fired after a draft lands in the editor. */\nexport interface HelpMeWriteApplied {\n mode: HelpMeWriteApplyMode;\n template?: AiTemplate;\n /** The editor's HTML before the change. */\n previousHtml: string;\n /** Puts `previousHtml` back in the editor (what Undo does). */\n undo: () => void;\n}\n\n/* ------------------------------------------------------------------ */\n/* Shared classes */\n/* ------------------------------------------------------------------ */\n\n// Match the composer's input height (--min-target-size), like the kit bar.\nconst ACTION_SIZE = 'ds:!size-[var(--min-target-size)]';\nconst BRAND_ACTION = `${ACTION_SIZE} ds:bg-[image:var(--gradient-brand)] ds:rounded-full`;\n// The recording mic goes solid destructive, as in the kit's own bar.\nconst RECORDING_MIC =\n 'ds:aria-pressed:!bg-[var(--destructive)] ds:aria-pressed:!text-[var(--destructive-foreground)]';\nconst metaClass =\n 'ds:m-0 ds:text-[length:var(--font-size-sm)] ds:text-[color:var(--muted-foreground)]';\nconst eyebrowClass =\n 'type-eyebrow ds:m-0 ds:text-[color:var(--muted-foreground)]';\nconst previewClass = [\n 'ds:rounded-[var(--radius-md)] ds:border ds:border-[color:var(--border)]',\n 'ds:bg-[color-mix(in_srgb,var(--background)_70%,transparent)]',\n 'ds:p-[var(--spacing-sm)] ds:[max-block-size:14rem] ds:overflow-y-auto',\n 'ds:text-[length:var(--font-size-sm)] ds:text-[color:var(--foreground)]',\n 'ds:[&_ul]:list-disc ds:[&_ul]:ps-[var(--spacing-md)]',\n 'ds:[&_h3]:font-medium ds:[&_h3]:[margin-block-start:var(--spacing-xs)]',\n 'ds:[&_p]:[margin-block-end:var(--spacing-xs)]',\n 'ds:[&>:first-child]:[margin-block-start:0]',\n].join(' ');\n\n/* ------------------------------------------------------------------ */\n/* Helpers */\n/* ------------------------------------------------------------------ */\n\n// Join dictated text onto the prompt with a single space.\nfunction joinWith(base: string, extra: string): string {\n if (!extra) return base;\n if (!base) return extra;\n return /\\s$/.test(base) ? `${base}${extra}` : `${base} ${extra}`;\n}\n\nfunction isAbort(err: unknown): boolean {\n return (\n typeof err === 'object' &&\n err !== null &&\n 'name' in err &&\n (err as { name?: string }).name === 'AbortError'\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* HelpMeWriteTemplatesBar */\n/* ------------------------------------------------------------------ */\n\nexport interface HelpMeWriteTemplatesBarProps {\n /** What `RichTextEditor`'s `renderHelpMeWrite` hands its host. */\n context: HelpMeWriteRenderContext;\n /**\n * @deprecated Templates run from the editor's AI pill (`aiTemplates`),\n * not from this bar. Ignored.\n */\n templates?: AiTemplate[];\n /** The AI call. Reject with `AiCreditsExhaustedError` when out of credits. */\n onGenerate: (request: HelpMeWriteRequest) => Promise<string>;\n /** @deprecated The manager opens from the pill's Templates menu. Ignored. */\n onManageTemplates?: (mode: 'list' | 'create') => void;\n /** After a draft lands. */\n onApplied?: (applied: HelpMeWriteApplied) => void;\n /** Shown in the out-of-credits notice. */\n credits?: AiCreditsInfo;\n /** Show the mic in the prompt when the editor has `onDictate`. Default true. */\n enableDictation?: boolean;\n /** Ignore Escape (e.g. while the manager dialog above it is open). */\n suspendClose?: boolean;\n}\n\n/**\n * The Help-me-write bar, docked in the editor's AI slot: a free prompt with\n * an optional mic, then the draft and Insert. It also shows the\n * out-of-credits notice, including when a pill action ran out.\n */\nexport function HelpMeWriteTemplatesBar({\n context,\n onGenerate,\n onApplied,\n credits,\n enableDictation = true,\n suspendClose = false,\n}: HelpMeWriteTemplatesBarProps): React.JSX.Element {\n const { t } = useTranslation('ui');\n const { open } = context;\n const draftId = useId();\n const errorId = useId();\n const [prompt, setPrompt] = useState('');\n const [loading, setLoading] = useState(false);\n const [errored, setErrored] = useState(false);\n const [outOfCredits, setOutOfCredits] = useState(false);\n const [draft, setDraft] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n const onDictate = enableDictation ? context.onDictate : undefined;\n\n /* ---- Mic in the prompt (the editor's own dictation seam) ---- */\n const [isDictating, setIsDictating] = useState(false);\n const [interim, setInterim] = useState('');\n const [announcement, setAnnouncement] = useState('');\n const dictateAbortRef = useRef<AbortController | null>(null);\n const stopDictation = useCallback(() => dictateAbortRef.current?.abort(), []);\n const toggleDictation = useCallback(() => {\n if (isDictating) {\n stopDictation();\n return;\n }\n if (!onDictate) return;\n const controller = new AbortController();\n dictateAbortRef.current = controller;\n setIsDictating(true);\n setAnnouncement(t('helpMeWriteTemplates.bar.listening'));\n onDictate({\n signal: controller.signal,\n onPartialTranscript: (text) => {\n if (!controller.signal.aborted) setInterim(text);\n },\n onFinalTranscript: (text) => {\n if (controller.signal.aborted) return;\n setInterim('');\n const trimmed = text.trim();\n if (trimmed) {\n setPrompt((p) => joinWith(p, trimmed));\n setAnnouncement(trimmed);\n }\n },\n })\n .catch((err: unknown) => {\n // Out of credits shows the notice; other dictation errors are\n // non-fatal (the user can type instead).\n if (err instanceof AiCreditsExhaustedError) setOutOfCredits(true);\n })\n .finally(() => {\n setInterim('');\n if (dictateAbortRef.current === controller) {\n dictateAbortRef.current = null;\n setIsDictating(false);\n setAnnouncement('');\n }\n });\n }, [isDictating, onDictate, stopDictation, t]);\n\n // Each open starts fresh.\n useEffect(() => {\n if (open) {\n setPrompt('');\n setLoading(false);\n setErrored(false);\n // Opened because an AI call ran out of credits: start on the notice.\n setOutOfCredits(context.creditsExhausted);\n setDraft(null);\n } else {\n abortRef.current?.abort();\n dictateAbortRef.current?.abort();\n }\n // Only on open/close; `context` is a fresh object every editor render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open]);\n\n useEffect(\n () => () => {\n abortRef.current?.abort();\n dictateAbortRef.current?.abort();\n },\n [],\n );\n\n const run = useCallback(() => {\n if (loading || !prompt.trim()) return;\n stopDictation();\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n setErrored(false);\n setLoading(true);\n const onSelection = context.scope === 'selection';\n onGenerate({\n prompt: prompt.trim(),\n sourceText: onSelection ? context.selectionText : context.getText(),\n scope: onSelection ? 'selection' : 'document',\n signal: controller.signal,\n })\n .then((result) => {\n if (controller.signal.aborted) return;\n setDraft(result);\n setLoading(false);\n })\n .catch((err: unknown) => {\n if (controller.signal.aborted || isAbort(err)) return;\n if (err instanceof AiCreditsExhaustedError) setOutOfCredits(true);\n else setErrored(true);\n setLoading(false);\n });\n // `context` is a fresh object every editor render; read it at call time.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [loading, prompt, onGenerate, stopDictation]);\n\n const { onClose: contextClose } = context;\n const close = useCallback(() => {\n if (suspendClose) return;\n contextClose();\n }, [suspendClose, contextClose]);\n\n const insert = () => {\n if (draft === null) return;\n const previousHtml = context.getHTML();\n context.insertAtSelection(draft);\n context.onClose();\n const { setContent } = context;\n onApplied?.({\n mode: context.scope === 'selection' ? 'replace' : 'insert',\n previousHtml,\n undo: () => setContent(previousHtml),\n });\n };\n\n let body: ReactNode;\n if (outOfCredits) {\n body = (\n <AiCreditsExhaustedNotice\n credits={credits}\n onBack={() => setOutOfCredits(false)}\n footnote={t('helpMeWriteTemplates.credits.kept')}\n />\n );\n } else if (draft !== null) {\n body = (\n <>\n <section\n aria-labelledby={draftId}\n className=\"ds:flex ds:min-w-0 ds:flex-col ds:gap-[var(--spacing-2xs)]\"\n >\n <h2 id={draftId} className={eyebrowClass}>\n {t('helpMeWriteTemplates.bar.after')}\n </h2>\n <SafeHtml\n html={draft}\n profile=\"rich-text\"\n className={previewClass}\n tabIndex={0}\n />\n </section>\n <div className=\"ds:flex ds:flex-wrap ds:items-center ds:gap-[var(--spacing-sm)]\">\n <Button intent=\"ghost\" startIcon={<X />} onClick={context.onClose}>\n {t('helpMeWriteTemplates.bar.discard')}\n </Button>\n <Button\n intent=\"ghost\"\n startIcon={<RotateCcw />}\n onClick={() => {\n setDraft(null);\n run();\n }}\n >\n {t('helpMeWriteTemplates.bar.retry')}\n </Button>\n <span className=\"ds:flex-1\" aria-hidden=\"true\" />\n <Button intent=\"primary\" onClick={insert}>\n {context.scope === 'selection'\n ? t('helpMeWriteTemplates.bar.replace')\n : t('helpMeWriteTemplates.bar.insert')}\n </Button>\n </div>\n </>\n );\n } else {\n // While dictating the field shows the committed prompt plus live interim\n // text, read-only so the stream is not fought over by typing.\n const fieldValue = isDictating ? joinWith(prompt, interim) : prompt;\n body = (\n <>\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-sm)]\">\n <IconButton\n size=\"md\"\n intent=\"outline\"\n className={ACTION_SIZE}\n icon={<X />}\n aria-label={t('helpMeWriteTemplates.bar.close')}\n onClick={context.onClose}\n />\n <div className=\"ds:min-w-0 ds:flex-1\">\n <TextArea\n size=\"md\"\n value={fieldValue}\n onChange={(event) => setPrompt(event.target.value)}\n placeholder={t('editor.ai.promptPlaceholder')}\n aria-label={t('editor.ai.promptLabel')}\n autoResize\n minRows={1}\n maxRows={3}\n readOnly={loading || isDictating}\n tone={errored ? 'error' : 'default'}\n aria-describedby={errored ? errorId : undefined}\n />\n </div>\n {onDictate ? (\n <IconButton\n size=\"md\"\n intent=\"outline\"\n className={\n isDictating ? `${ACTION_SIZE} ${RECORDING_MIC}` : ACTION_SIZE\n }\n icon={\n isDictating ? <Square className=\"ds:fill-current\" /> : <Mic />\n }\n aria-pressed={isDictating}\n aria-label={\n isDictating\n ? t('helpMeWriteTemplates.bar.dictateStop')\n : t('helpMeWriteTemplates.bar.dictateStart')\n }\n disabled={loading}\n onClick={toggleDictation}\n />\n ) : null}\n <IconButton\n size=\"md\"\n intent=\"primary\"\n className={BRAND_ACTION}\n icon={loading ? <Spinner size=\"sm\" variant=\"pulse\" /> : <Send />}\n disabled={loading || !prompt.trim()}\n aria-busy={loading || undefined}\n aria-label={t('helpMeWriteTemplates.bar.write')}\n onClick={run}\n />\n </div>\n {onDictate ? (\n <p className=\"ds:sr-only\" role=\"status\" aria-live=\"polite\">\n {announcement}\n </p>\n ) : null}\n {errored ? (\n <p id={errorId} role=\"alert\" className={metaClass}>\n {t('helpMeWriteTemplates.bar.error')}\n </p>\n ) : null}\n <p className={metaClass}>\n {context.scope === 'selection'\n ? t('helpMeWriteTemplates.bar.contextSelection')\n : t('helpMeWriteTemplates.bar.contextDocument')}{' '}\n {context.privacyNotice}\n </p>\n </>\n );\n }\n\n return (\n <EditorOverlay\n open={open}\n onClose={close}\n ariaLabel={t('editor.ai.helpMeWrite')}\n >\n <div className=\"ds:contents\" data-component=\"help-me-write-templates-bar\">\n {body}\n </div>\n </EditorOverlay>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* AiTemplateManager */\n/* ------------------------------------------------------------------ */\n\n/* The kit's list-reorder idiom (Sidebar favourites, anamnesis): HTML5 drag\n on the row, a grip handle that also moves with Alt+Arrow / Alt+Home /\n Alt+End, and a polite announcement of every move. Rows are keyed, so the\n moved row's handle keeps focus. */\nconst reorderRowClass = cva('', {\n variants: {\n dragging: { true: 'ds:opacity-60', false: '' },\n dropTarget: {\n true: 'ds:outline-[length:var(--focus-ring-width)] ds:outline-dashed ds:outline-[var(--primary)] ds:outline-offset-[-2px]',\n false: '',\n },\n },\n defaultVariants: { dragging: false, dropTarget: false },\n});\n\nfunction useReorder(\n templates: AiTemplate[],\n onReorder: ((orderedIds: string[]) => void | Promise<void>) | undefined,\n) {\n const { t } = useTranslation('ui');\n const [dragSource, setDragSource] = useState<number | null>(null);\n const [dropTarget, setDropTarget] = useState<number | null>(null);\n const [message, setMessage] = useState('');\n // Toggle an invisible suffix so a repeated move is announced again.\n const toggle = useRef(false);\n\n const move = (from: number, to: number): void => {\n const target = Math.max(0, Math.min(templates.length - 1, to));\n if (!onReorder || target === from) return;\n const ids = templates.map((tpl) => tpl.id);\n const [moved] = ids.splice(from, 1);\n ids.splice(target, 0, moved);\n void onReorder(ids);\n toggle.current = !toggle.current;\n const text = t('helpMeWriteTemplates.manager.reorderAnnounce', {\n label: templates[from].name,\n to: target + 1,\n total: templates.length,\n });\n setMessage(toggle.current ? text : `${text}\\u200b`);\n };\n\n const rowProps = (index: number) =>\n onReorder\n ? {\n draggable: true as const,\n onDragStart: (event: DragEvent<HTMLElement>) => {\n setDragSource(index);\n event.dataTransfer.effectAllowed = 'move';\n // Firefox needs data to start a drag.\n event.dataTransfer.setData('text/plain', String(index));\n },\n onDragEnd: () => {\n setDragSource(null);\n setDropTarget(null);\n },\n onDragOver: (event: DragEvent<HTMLElement>) => {\n if (dragSource === null) return;\n event.preventDefault();\n event.dataTransfer.dropEffect = 'move';\n setDropTarget(index);\n },\n onDragLeave: () => setDropTarget(null),\n onDrop: (event: DragEvent<HTMLElement>) => {\n event.preventDefault();\n const source = dragSource;\n setDragSource(null);\n setDropTarget(null);\n if (source !== null) move(source, index);\n },\n }\n : {};\n\n const rowClass = (index: number): string =>\n reorderRowClass({\n dragging: dragSource === index,\n dropTarget: dropTarget === index && dragSource !== index,\n });\n\n const handle = (index: number, name: string): ReactNode => (\n <IconButton\n size=\"sm\"\n intent=\"ghost\"\n icon={<GripVertical aria-hidden=\"true\" />}\n aria-label={t('helpMeWriteTemplates.manager.reorderHandle', {\n label: name,\n })}\n className=\"ds:cursor-grab ds:active:cursor-grabbing\"\n onKeyDown={(event: KeyboardEvent<HTMLButtonElement>) => {\n if (!event.altKey) return;\n const to =\n event.key === 'ArrowUp'\n ? index - 1\n : event.key === 'ArrowDown'\n ? index + 1\n : event.key === 'Home'\n ? 0\n : event.key === 'End'\n ? templates.length - 1\n : null;\n if (to === null) return;\n event.preventDefault();\n move(index, to);\n }}\n />\n );\n\n const liveRegion = (\n <span role=\"status\" aria-live=\"polite\" className=\"ds:sr-only\">\n {message}\n </span>\n );\n\n return { rowProps, rowClass, handle, liveRegion };\n}\n\nexport interface AiTemplateManagerProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** Open on the list (default) or straight on a new-template form. */\n initialMode?: 'list' | 'create';\n templates: AiTemplate[];\n /** Persist a new (no `id`) or edited template. A rejection keeps the form open. */\n onSaveTemplate: (draft: AiTemplateDraft) => void | Promise<void>;\n /** Omit to hide Delete. */\n onDeleteTemplate?: (id: string) => void | Promise<void>;\n /**\n * Persist a new order (every template id, first to last): the order of\n * the Templates menu. Omit to hide the drag handles.\n */\n onReorderTemplates?: (orderedIds: string[]) => void | Promise<void>;\n}\n\ninterface FormState {\n id?: string;\n name: string;\n description: string;\n prompt: string;\n submitted: boolean;\n}\n\nconst EMPTY_FORM: FormState = {\n name: '',\n description: '',\n prompt: '',\n submitted: false,\n};\n\n/** List, create, edit and delete the practice's templates. */\nexport function AiTemplateManager({\n open,\n onOpenChange,\n initialMode = 'list',\n templates,\n onSaveTemplate,\n onDeleteTemplate,\n onReorderTemplates,\n}: AiTemplateManagerProps): React.JSX.Element {\n const { t } = useTranslation('ui');\n const reorder = useReorder(templates, onReorderTemplates);\n const [form, setForm] = useState<FormState | null>(null);\n const [saving, setSaving] = useState(false);\n const [saveFailed, setSaveFailed] = useState(false);\n const [pendingDelete, setPendingDelete] = useState<AiTemplate | null>(null);\n const [deleting, setDeleting] = useState(false);\n const [deleteFailed, setDeleteFailed] = useState(false);\n\n // Wait for the host: the confirmation stays open (busy) until the delete\n // settles, and shows an error if it fails.\n const confirmDelete = async () => {\n if (!pendingDelete || deleting) return;\n setDeleting(true);\n setDeleteFailed(false);\n try {\n await onDeleteTemplate?.(pendingDelete.id);\n setPendingDelete(null);\n } catch {\n setDeleteFailed(true);\n } finally {\n setDeleting(false);\n }\n };\n\n useEffect(() => {\n if (open) {\n setSaveFailed(false);\n setForm(initialMode === 'create' ? { ...EMPTY_FORM } : null);\n }\n // Only when it opens; `templates` changing mid-edit keeps the form.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open]);\n\n const nameTaken = (name: string, id?: string): boolean =>\n templates.some(\n (tpl) => tpl.id !== id && tpl.name.toLowerCase() === name.toLowerCase(),\n );\n\n const nameError = (() => {\n if (!form?.submitted) return undefined;\n const name = form.name.trim();\n if (!name) return t('helpMeWriteTemplates.manager.nameRequired');\n return nameTaken(name, form.id)\n ? t('helpMeWriteTemplates.manager.nameTaken')\n : undefined;\n })();\n const promptError =\n form?.submitted && !form.prompt.trim()\n ? t('helpMeWriteTemplates.manager.promptRequired')\n : undefined;\n\n const save = async () => {\n if (!form || saving) return;\n const next = { ...form, submitted: true };\n setForm(next);\n const name = next.name.trim();\n const prompt = next.prompt.trim();\n if (!name || !prompt || nameTaken(name, next.id)) return;\n setSaving(true);\n setSaveFailed(false);\n try {\n await onSaveTemplate({\n id: next.id,\n name,\n description: next.description.trim() || undefined,\n prompt,\n });\n setForm(null);\n } catch {\n setSaveFailed(true);\n } finally {\n setSaving(false);\n }\n };\n\n const title = form\n ? form.id\n ? t('helpMeWriteTemplates.manager.formEdit')\n : t('helpMeWriteTemplates.manager.formNew')\n : t('helpMeWriteTemplates.manager.title');\n\n return (\n <>\n <Dialog open={open} onOpenChange={onOpenChange}>\n <Dialog.Content size=\"lg\">\n <div className=\"ds:contents\" data-component=\"ai-template-manager\">\n <Dialog.Header>\n <Dialog.Title>{title}</Dialog.Title>\n <Dialog.Description>\n {t('helpMeWriteTemplates.manager.description')}\n </Dialog.Description>\n </Dialog.Header>\n <Dialog.Body>\n {form ? (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\">\n {saveFailed ? (\n <Alert variant=\"error\">\n {t('helpMeWriteTemplates.manager.saveError')}\n </Alert>\n ) : null}\n <FormField\n label={t('helpMeWriteTemplates.manager.name')}\n error={nameError}\n required\n >\n <StateBackedTextInput\n value={form.name}\n maxLength={100}\n placeholder={t(\n 'helpMeWriteTemplates.manager.namePlaceholder',\n )}\n onChange={(event) =>\n setForm({ ...form, name: event.target.value })\n }\n />\n </FormField>\n <FormField\n label={t('helpMeWriteTemplates.manager.descLabel')}\n description={t('helpMeWriteTemplates.manager.descHint')}\n optional\n >\n <StateBackedTextInput\n value={form.description}\n maxLength={120}\n onChange={(event) =>\n setForm({ ...form, description: event.target.value })\n }\n />\n </FormField>\n <FormField\n label={t('helpMeWriteTemplates.manager.prompt')}\n description={t(\n 'helpMeWriteTemplates.manager.promptDescription',\n )}\n error={promptError}\n required\n >\n <TextArea\n value={form.prompt}\n maxLength={4000}\n autoResize\n minRows={4}\n maxRows={10}\n onChange={(event) =>\n setForm({ ...form, prompt: event.target.value })\n }\n />\n </FormField>\n </div>\n ) : templates.length === 0 ? (\n <EmptyState\n variant=\"first-use\"\n size=\"sm\"\n title={t('helpMeWriteTemplates.manager.emptyTitle')}\n description={t(\n 'helpMeWriteTemplates.manager.emptyDescription',\n )}\n primaryAction={{\n label: t('helpMeWriteTemplates.manager.new'),\n onClick: () => setForm({ ...EMPTY_FORM }),\n }}\n />\n ) : (\n <ul\n aria-label={t('helpMeWriteTemplates.manager.listLabel')}\n className=\"ds:m-0 ds:flex ds:list-none ds:flex-col ds:gap-[var(--spacing-sm)] ds:p-0\"\n >\n {templates.map((tpl, index) => (\n <li\n key={tpl.id}\n {...reorder.rowProps(index)}\n className={`ds:flex ds:items-start ds:gap-[var(--spacing-sm)] ds:rounded-[var(--radius-md)] ds:border ds:border-[color:var(--border)] ds:p-[var(--spacing-sm)] ${reorder.rowClass(index)}`}\n >\n {onReorderTemplates\n ? reorder.handle(index, tpl.name)\n : null}\n <div className=\"ds:flex ds:min-w-0 ds:flex-1 ds:flex-col ds:gap-[var(--spacing-2xs)]\">\n <div className=\"ds:flex ds:flex-wrap ds:items-center ds:gap-[var(--spacing-xs)]\">\n <span className=\"type-title-item ds:text-[color:var(--foreground)]\">\n {tpl.name}\n </span>\n </div>\n <p className={`${metaClass} ds:line-clamp-2`}>\n {tpl.description || tpl.prompt}\n </p>\n </div>\n <IconButton\n size=\"sm\"\n intent=\"ghost\"\n icon={<Pencil />}\n aria-label={t('helpMeWriteTemplates.manager.edit', {\n name: tpl.name,\n })}\n onClick={() =>\n setForm({\n id: tpl.id,\n name: tpl.name,\n description: tpl.description ?? '',\n prompt: tpl.prompt,\n submitted: false,\n })\n }\n />\n {onDeleteTemplate ? (\n <IconButton\n size=\"sm\"\n intent=\"ghost\"\n icon={<Trash2 />}\n aria-label={t('helpMeWriteTemplates.manager.delete', {\n name: tpl.name,\n })}\n onClick={() => setPendingDelete(tpl)}\n />\n ) : null}\n </li>\n ))}\n </ul>\n )}\n {reorder.liveRegion}\n </Dialog.Body>\n <Dialog.Footer>\n {form ? (\n <>\n <Button intent=\"secondary\" onClick={() => setForm(null)}>\n {t('helpMeWriteTemplates.manager.cancel')}\n </Button>\n <Button\n intent=\"primary\"\n loading={saving}\n onClick={() => void save()}\n >\n {t('helpMeWriteTemplates.manager.save')}\n </Button>\n </>\n ) : (\n <>\n {templates.length > 0 ? (\n <Button\n intent=\"outline\"\n startIcon={<Plus />}\n onClick={() => setForm({ ...EMPTY_FORM })}\n >\n {t('helpMeWriteTemplates.manager.new')}\n </Button>\n ) : null}\n <Dialog.Close asChild>\n <Button intent=\"primary\">\n {t('helpMeWriteTemplates.manager.done')}\n </Button>\n </Dialog.Close>\n </>\n )}\n </Dialog.Footer>\n </div>\n </Dialog.Content>\n </Dialog>\n <Dialog\n open={pendingDelete != null}\n onOpenChange={(next) => {\n if (!next && !deleting) {\n setPendingDelete(null);\n setDeleteFailed(false);\n }\n }}\n >\n <Dialog.Content size=\"sm\" variant=\"alert\">\n <Dialog.Header>\n <Dialog.Title>\n {t('helpMeWriteTemplates.manager.deleteTitle', {\n name: pendingDelete?.name ?? '',\n })}\n </Dialog.Title>\n <Dialog.Description>\n {t('helpMeWriteTemplates.manager.deleteDescription')}\n </Dialog.Description>\n </Dialog.Header>\n {deleteFailed ? (\n <Dialog.Body>\n <Alert variant=\"error\">\n {t('helpMeWriteTemplates.manager.deleteError')}\n </Alert>\n </Dialog.Body>\n ) : null}\n <Dialog.Footer>\n <Dialog.Close asChild>\n <Button intent=\"secondary\" disabled={deleting}>\n {t('helpMeWriteTemplates.manager.cancel')}\n </Button>\n </Dialog.Close>\n <Button\n intent=\"destructive\"\n loading={deleting}\n onClick={() => void confirmDelete()}\n >\n {t('helpMeWriteTemplates.manager.deleteConfirm')}\n </Button>\n </Dialog.Footer>\n </Dialog.Content>\n </Dialog>\n </>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* HelpMeWriteUndoNotice */\n/* ------------------------------------------------------------------ */\n\nexport interface HelpMeWriteUndoNoticeProps {\n /** The change to offer Undo for; `null` hides the notice. */\n applied: HelpMeWriteApplied | null;\n onUndo: () => void;\n onDismiss: () => void;\n}\n\n/** \"Note rewritten with …\" plus Undo; takes focus when it appears. */\nexport function HelpMeWriteUndoNotice({\n applied,\n onUndo,\n onDismiss,\n}: HelpMeWriteUndoNoticeProps): React.JSX.Element | null {\n const { t } = useTranslation('ui');\n const undoRef = useRef<HTMLButtonElement>(null);\n useEffect(() => {\n if (applied) undoRef.current?.focus();\n }, [applied]);\n if (!applied?.template) return null;\n const name = applied.template.name;\n return (\n <Alert\n variant=\"success\"\n dismissible\n onOpenChange={(isOpen) => {\n if (!isOpen) onDismiss();\n }}\n data-component=\"help-me-write-undo-notice\"\n >\n <div className=\"ds:flex ds:flex-wrap ds:items-center ds:gap-[var(--spacing-sm)]\">\n <span>\n {applied.mode === 'below'\n ? t('helpMeWriteTemplates.undo.addedBelow', { name })\n : t('helpMeWriteTemplates.undo.replaced', { name })}\n </span>\n <Button ref={undoRef} intent=\"outline\" size=\"sm\" onClick={onUndo}>\n {t('helpMeWriteTemplates.undo.undo')}\n </Button>\n </div>\n </Alert>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* useHelpMeWriteTemplates — the whole thing in one call */\n/* ------------------------------------------------------------------ */\n\nexport interface UseHelpMeWriteTemplatesOptions {\n templates: AiTemplate[];\n /** The AI call, for both templates (from the pill) and the free prompt. */\n onGenerate: HelpMeWriteTemplatesBarProps['onGenerate'];\n /** Enables the manager (create / edit). Omit for read-only template users. */\n onSaveTemplate?: AiTemplateManagerProps['onSaveTemplate'];\n /** Enables Delete in the manager. */\n onDeleteTemplate?: AiTemplateManagerProps['onDeleteTemplate'];\n /** Enables drag-and-drop reordering in the manager (the menu order). */\n onReorderTemplates?: AiTemplateManagerProps['onReorderTemplates'];\n credits?: AiCreditsInfo;\n /** Mic in the bar's prompt when the editor has `onDictate`. Default true. */\n enableDictation?: boolean;\n /** Also told about every applied draft (e.g. for analytics). */\n onApplied?: (applied: HelpMeWriteApplied) => void;\n /**\n * Shows the AI-credits/bundles hover card on every AI trigger the editor\n * has (this bar's own segment included, via `editorProps`). One wiring\n * covers both the bar's existing out-of-credits notice (`credits`, shown\n * only once the practice actually runs out) and this always-available\n * hover preview.\n */\n creditsNotice?: RichTextEditorProps['aiCreditsNotice'];\n}\n\n/** The `RichTextEditor` props the hook drives; spread them on the editor. */\nexport interface HelpMeWriteTemplatesEditorProps {\n renderHelpMeWrite: (context: HelpMeWriteRenderContext) => ReactNode;\n aiTemplates: RichTextEditorAiTemplates;\n onAiApplied: (applied: RichTextEditorAiApplied) => void;\n aiCreditsNotice?: RichTextEditorProps['aiCreditsNotice'];\n}\n\nexport interface UseHelpMeWriteTemplatesResult extends HelpMeWriteTemplatesEditorProps {\n /** All three editor props at once: `<RichTextEditor {...hmw.editorProps} />`. */\n editorProps: HelpMeWriteTemplatesEditorProps;\n /** Mount once, anywhere (a portalled dialog); `null` without `onSaveTemplate`. */\n manager: ReactNode;\n /** Place where the Undo notice should show, e.g. above the editor. */\n undoNotice: ReactNode;\n openManager: (mode?: 'list' | 'create') => void;\n}\n\n/**\n * Wires templates, the Help-me-write bar, the manager and Undo into the\n * editor. Templates run from the editor's AI pill and selection bubble;\n * the bar is the free prompt. Usage:\n *\n * const hmw = useHelpMeWriteTemplates({ templates, onGenerate, onSaveTemplate });\n * {hmw.undoNotice}\n * <RichTextEditor {...hmw.editorProps} … />\n * {hmw.manager}\n */\nexport function useHelpMeWriteTemplates({\n templates,\n onGenerate,\n onSaveTemplate,\n onDeleteTemplate,\n onReorderTemplates,\n credits,\n enableDictation,\n onApplied,\n creditsNotice,\n}: UseHelpMeWriteTemplatesOptions): UseHelpMeWriteTemplatesResult {\n const [manager, setManager] = useState<{\n open: boolean;\n mode: 'list' | 'create';\n }>({ open: false, mode: 'list' });\n const [applied, setApplied] = useState<HelpMeWriteApplied | null>(null);\n const openManager = useCallback(\n (mode: 'list' | 'create' = 'list') => setManager({ open: true, mode }),\n [],\n );\n\n const renderHelpMeWrite = useCallback(\n (context: HelpMeWriteRenderContext): ReactNode => (\n <HelpMeWriteTemplatesBar\n context={context}\n onGenerate={onGenerate}\n credits={credits}\n enableDictation={enableDictation}\n suspendClose={manager.open}\n onApplied={onApplied}\n />\n ),\n [onGenerate, credits, enableDictation, manager.open, onApplied],\n );\n\n // The editor runs templates; the host's full template (with its prompt)\n // goes to `onGenerate`, looked up by id.\n const aiTemplates = useMemo<RichTextEditorAiTemplates>(() => {\n const byId = new Map(templates.map((tpl) => [tpl.id, tpl]));\n return {\n templates,\n onRun: ({ template, text, scope, signal }): Promise<string> => {\n const full = byId.get(template.id);\n if (!full) return Promise.reject(new Error('Unknown template'));\n return onGenerate({\n template: full,\n prompt: '',\n sourceText: text,\n scope,\n signal,\n });\n },\n onManage: onSaveTemplate ? openManager : undefined,\n };\n }, [templates, onGenerate, onSaveTemplate, openManager]);\n\n const onAiApplied = useCallback(\n (event: RichTextEditorAiApplied): void => {\n if (event.action !== 'template' || !event.template) return;\n const template = templates.find((tpl) => tpl.id === event.template?.id);\n const change: HelpMeWriteApplied = {\n mode: 'replace',\n template,\n previousHtml: event.previousHtml,\n undo: event.undo,\n };\n setApplied(template ? change : null);\n onApplied?.(change);\n },\n [templates, onApplied],\n );\n\n const managerNode = useMemo(\n () =>\n onSaveTemplate ? (\n <AiTemplateManager\n open={manager.open}\n initialMode={manager.mode}\n onOpenChange={(isOpen) => setManager((m) => ({ ...m, open: isOpen }))}\n templates={templates}\n onSaveTemplate={onSaveTemplate}\n onDeleteTemplate={onDeleteTemplate}\n onReorderTemplates={onReorderTemplates}\n />\n ) : null,\n [manager, templates, onSaveTemplate, onDeleteTemplate, onReorderTemplates],\n );\n\n const undoNotice = (\n <HelpMeWriteUndoNotice\n applied={applied}\n onUndo={() => {\n applied?.undo();\n setApplied(null);\n }}\n onDismiss={() => setApplied(null)}\n />\n );\n\n const editorProps = useMemo<HelpMeWriteTemplatesEditorProps>(\n () => ({\n renderHelpMeWrite,\n aiTemplates,\n onAiApplied,\n aiCreditsNotice: creditsNotice,\n }),\n [renderHelpMeWrite, aiTemplates, onAiApplied, creditsNotice],\n );\n\n return {\n ...editorProps,\n editorProps,\n manager: managerNode,\n undoNotice,\n openManager,\n };\n}\n"],"names":["AiCreditsExhaustedError","message","ACTION_SIZE","BRAND_ACTION","RECORDING_MIC","metaClass","eyebrowClass","previewClass","joinWith","base","extra","isAbort","err","HelpMeWriteTemplatesBar","context","onGenerate","onApplied","credits","enableDictation","suspendClose","t","useTranslation","open","draftId","useId","errorId","prompt","setPrompt","useState","loading","setLoading","errored","setErrored","outOfCredits","setOutOfCredits","draft","setDraft","abortRef","useRef","onDictate","isDictating","setIsDictating","interim","setInterim","announcement","setAnnouncement","dictateAbortRef","stopDictation","useCallback","_a","toggleDictation","controller","text","trimmed","p","useEffect","_b","run","onSelection","result","contextClose","close","insert","previousHtml","setContent","body","jsx","AiCreditsExhaustedNotice","jsxs","Fragment","SafeHtml","Button","X","RotateCcw","fieldValue","IconButton","TextArea","event","Square","Mic","Spinner","Send","EditorOverlay","reorderRowClass","cva","useReorder","templates","onReorder","dragSource","setDragSource","dropTarget","setDropTarget","setMessage","toggle","move","from","to","target","ids","tpl","moved","index","source","name","GripVertical","EMPTY_FORM","AiTemplateManager","onOpenChange","initialMode","onSaveTemplate","onDeleteTemplate","onReorderTemplates","reorder","form","setForm","saving","setSaving","saveFailed","setSaveFailed","pendingDelete","setPendingDelete","deleting","setDeleting","deleteFailed","setDeleteFailed","confirmDelete","nameTaken","id","nameError","promptError","save","next","title","Dialog","Alert","FormField","StateBackedTextInput","EmptyState","Pencil","Trash2","Plus","HelpMeWriteUndoNotice","applied","onUndo","onDismiss","undoRef","isOpen","useHelpMeWriteTemplates","creditsNotice","manager","setManager","setApplied","openManager","mode","renderHelpMeWrite","aiTemplates","useMemo","byId","template","scope","signal","full","onAiApplied","change","managerNode","m","undoNotice","editorProps"],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { c as a } from "./createLucideIcon-DhHxpK9K.js";
2
+ /**
3
+ * @license lucide-react v1.22.0 - ISC
4
+ *
5
+ * This source code is licensed under the ISC license.
6
+ * See the LICENSE file in the root directory of this source tree.
7
+ */
8
+ const e = [
9
+ ["path", { d: "M5 22h14", key: "ehvnwv" }],
10
+ ["path", { d: "M5 2h14", key: "pdyrp9" }],
11
+ [
12
+ "path",
13
+ {
14
+ d: "M17 22v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22",
15
+ key: "1d314k"
16
+ }
17
+ ],
18
+ [
19
+ "path",
20
+ { d: "M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2", key: "1vvvr6" }
21
+ ]
22
+ ], t = a("hourglass", e);
23
+ export {
24
+ t as H
25
+ };
26
+ //# sourceMappingURL=hourglass-BEAzW1wE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hourglass-BEAzW1wE.js","sources":["../../node_modules/lucide-react/dist/esm/icons/hourglass.mjs"],"sourcesContent":["/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\"path\", { d: \"M5 22h14\", key: \"ehvnwv\" }],\n [\"path\", { d: \"M5 2h14\", key: \"pdyrp9\" }],\n [\n \"path\",\n {\n d: \"M17 22v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22\",\n key: \"1d314k\"\n }\n ],\n [\n \"path\",\n { d: \"M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2\", key: \"1vvvr6\" }\n ]\n];\nconst Hourglass = createLucideIcon(\"hourglass\", __iconNode);\n\nexport { __iconNode, Hourglass as default };\n//# sourceMappingURL=hourglass.mjs.map\n"],"names":["__iconNode","Hourglass","createLucideIcon"],"mappings":";AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASA,MAAMA,IAAa;AAAA,EACjB,CAAC,QAAQ,EAAE,GAAG,YAAY,KAAK,SAAQ,CAAE;AAAA,EACzC,CAAC,QAAQ,EAAE,GAAG,WAAW,KAAK,SAAQ,CAAE;AAAA,EACxC;AAAA,IACE;AAAA,IACA;AAAA,MACE,GAAG;AAAA,MACH,KAAK;AAAA,IACX;AAAA,EACA;AAAA,EACE;AAAA,IACE;AAAA,IACA,EAAE,GAAG,0EAA0E,KAAK,SAAQ;AAAA,EAChG;AACA,GACMC,IAAYC,EAAiB,aAAaF,CAAU;","x_google_ignoreList":[0]}
@@ -5,12 +5,12 @@ import { c as j } from "./index-D2ZczOXr.js";
5
5
  import { i as as, E as Es } from "./email-input-C7THAwh2.js";
6
6
  import { B as ds } from "./button-D-MQxsA_.js";
7
7
  import { C as h } from "./card-BA4Ig_kn.js";
8
- import { E as Ns } from "./error-summary-DrUfuKu1.js";
8
+ import { E as Ns } from "./error-summary-PUE5Zxp9.js";
9
9
  import { F as rs } from "./form-field-Ani72sVY.js";
10
10
  import { P as Is } from "./password-input-BokZ_rnT.js";
11
11
  import { i as is } from "./safe-href-5dbsfbQn.js";
12
12
  import { u as Bs } from "./use-state-backed-input-DhRQo6gM.js";
13
- import { u as Rs, a as Ls, A as As, b as Cs, E as Ms, d as Ss, c as ks } from "./auth-form-parts-Cmb41LSQ.js";
13
+ import { u as Rs, a as Ls, A as As, b as Cs, E as Ms, d as Ss, c as ks } from "./auth-form-parts-DwUTH6YN.js";
14
14
  import { u as qs, b as zs, a as js } from "./use-registration-submit-eWAECgUC.js";
15
15
  import { M as $s } from "./matrix-rain-sDOUUwZp.js";
16
16
  import { a as Ds } from "./error-page-illustration-BdJLuMdV.js";
@@ -306,4 +306,4 @@ export {
306
306
  Gs as a,
307
307
  Hs as b
308
308
  };
309
- //# sourceMappingURL=login-dev-card-PnADNcI9.js.map
309
+ //# sourceMappingURL=login-dev-card-A-CL3mjt.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"login-dev-card-PnADNcI9.js","sources":["../../src/patterns/login-form/login-form.tsx","../../src/patterns/login-form/login-dev-card.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* LoginForm — the sign-in card. */\n/* */\n/* One export for the card: the mark, the heading, email and password, */\n/* the button and the way to a new password. The host keeps the page */\n/* around it and every server rule. The card checks what a browser can */\n/* check, then hands the values to `onSubmit` or posts a real form to */\n/* the host's own field names. */\n/* */\n/* The email must be an email address. A server that also signs people */\n/* in by a username cannot use this card for those accounts. */\n/* -------------------------------------------------------------------- */\n\nimport {\n forwardRef,\n useEffect,\n useId,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { cva } from 'class-variance-authority';\n// The validator EmailInput itself uses, so the two never disagree.\nimport isEmail from 'validator/lib/isEmail';\nimport { Button } from '../../components/button';\nimport { Card } from '../../components/card';\nimport { EmailInput } from '../../components/email-input';\nimport { ErrorSummary } from '../../components/error-summary';\nimport { FormField } from '../../components/form-field';\nimport { PasswordInput } from '../../components/password-input';\nimport { isSafeHref } from '../../components/_shared/safe-href';\nimport { useStateBackedInput } from '../../components/_shared/use-state-backed-input';\nimport {\n AuthCardHeading,\n AuthFormMessage,\n AuthFormStatus,\n AuthTextLink,\n ExtraFields,\n useExtraFields,\n useFormAction,\n} from '../_shared/auth-form-parts';\nimport type { BrandMarkMotion } from '../_shared/brand-mark';\nimport {\n useErrorSummary,\n useFormMessageAnnounce,\n} from '../_shared/use-error-summary';\nimport { useRegistrationSubmit } from '../registration-form/use-registration-submit';\n\n/* -------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------- */\n\n/** The fields of the form, in the order they appear. */\nexport const LOGIN_FORM_FIELDS = ['email', 'password'] as const;\n\nexport type LoginFormField = (typeof LOGIN_FORM_FIELDS)[number];\n\n/** What `onSubmit` receives once both checks have passed. */\nexport interface LoginFormValues {\n email: string;\n password: string;\n}\n\n/** Values to show when the form opens. There is no password here, on\n * purpose: a password is never sent back to the browser. */\nexport type LoginFormInitialValues = Partial<Pick<LoginFormValues, 'email'>>;\n\n/** Messages from the server, already translated: one per field, plus\n * `form` for a message about the whole form, such as \"the email or\n * password is incorrect\". */\nexport type LoginFormErrors = Partial<Record<LoginFormField | 'form', string>>;\n\n/** The host's own form field names, for a native form post. Both are\n * needed: a field without a name is not posted. */\nexport type LoginFormFieldNames = Record<LoginFormField, string>;\n\nexport interface LoginFormProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title' | 'onSubmit'\n> {\n /** Replaces the heading \"Sign in to Alfadocs\". Pass it translated. */\n title?: string;\n /** A line under the heading. Pass it translated. */\n description?: string;\n /**\n * The heading's level. Default 1: a sign-in page usually has no other\n * heading. Pass 2 on a page that has its own `h1`.\n */\n headingLevel?: 1 | 2 | 3;\n /**\n * Shows the Alfadocs mark above the heading, with the heading and its\n * line centred under it. Default true. With `false` the heading sits\n * at the start edge, for a host that shows its own logo.\n */\n showLogo?: boolean;\n /**\n * How the mark arrives when the card opens: its pieces come together\n * (`assemble`, the default), it turns once (`spin`), it beats\n * (`pulse`), or it is still (`none`). It plays once and rests, and\n * holds still under reduced motion and in the accessible themes.\n */\n logoMotion?: BrandMarkMotion;\n /**\n * Host content between the heading and the fields, such as a notice\n * (\"You have been signed out\"). It sits outside the form, so nothing in\n * it is posted. Keep any heading inside it below the card's own level.\n */\n children?: ReactNode;\n /** The email to show when the form opens, such as the one last used.\n * Read once. There is never an initial password. */\n initialValues?: LoginFormInitialValues;\n /**\n * Messages from the server, already translated. A field's message\n * shows on that field until the user changes it; `form` shows above\n * the fields.\n *\n * When messages arrive, the password is emptied and focus moves: to\n * the error summary at the top of the form, which lists each field\n * with a message, or, when only `form` says something, to the\n * password, which is then described by that message. The same happens\n * for messages present when the form opens (a page the server rendered\n * after a failed post).\n *\n * Never say which of the two values was wrong: one message for both.\n * Set `submitting` while you send, so the form also notices an answer\n * that says the same as the last one.\n */\n errors?: LoginFormErrors;\n /**\n * Adds \"Forgot password?\" under the button. A path on the same site,\n * or an address written in full (`https://…`). Anything else shows no\n * link.\n */\n forgotPasswordHref?: string;\n /** Replaces the button's \"Sign in\". Pass it translated. */\n submitLabel?: string;\n /**\n * True while the host sends the values: the button shows a spinner and\n * a second submit is ignored. Set it inside `onSubmit`, before anything\n * is awaited: until it is true, a second press sends a second sign-in.\n */\n submitting?: boolean;\n /** For React hosts: called with the values once both checks have\n * passed. Not called when `fieldNames` is set. */\n onSubmit?: (values: LoginFormValues) => void;\n /**\n * For a native form post: the host's own field names, both of them,\n * e.g. `{ email: 'email', password: 'password' }`. When set, the inputs\n * carry these names and the browser posts the form.\n */\n fieldNames?: LoginFormFieldNames;\n /**\n * Where a native form post goes. Default: the current address. Only a\n * path, or an address on the page's own origin, is accepted: another\n * origin is never sent what the user typed, and the post then goes to\n * the current address. Does nothing without `fieldNames`.\n */\n action?: string;\n /**\n * Extra hidden inputs posted with a native form, by name: a CSRF token,\n * a return address. Not used with `onSubmit`. A name that is also one\n * of `fieldNames` is left out: the form's own field wins. The server\n * must check a return address itself: anyone can change a hidden input.\n */\n hiddenFields?: Readonly<Record<string, string>>;\n /**\n * A second card beside the sign-in, the same height: the development\n * quick sign-in (`LoginDevCard`). Pass it only when the server says\n * this is a development environment: the kit never decides that\n * itself. The two sit side by side where there is room and stack on a\n * phone.\n */\n devCard?: ReactNode;\n}\n\n/* -------------------------------------------------------------------- */\n/* Styles */\n/* -------------------------------------------------------------------- */\n\n// The card is 400px wide at most, written in px: a host whose root font\n// size is not 16px would stretch or squeeze a width in rem.\nconst loginFormVariants = cva(\n 'ds:flex ds:w-full ds:flex-col ds:items-center ds:justify-center ds:gap-[var(--spacing-lg)] ds:text-start ds:text-[var(--foreground)] ds:lg:flex-row ds:lg:items-stretch',\n);\n\nconst columnVariants = cva('ds:w-full ds:max-w-[400px]');\n\n/* -------------------------------------------------------------------- */\n/* Helpers */\n/* -------------------------------------------------------------------- */\n\nconst hasMessage = (errors: LoginFormErrors): boolean =>\n Object.values(errors).some(Boolean);\n\nconst hasFieldMessage = (errors: LoginFormErrors): boolean =>\n LOGIN_FORM_FIELDS.some((field) => Boolean(errors[field]));\n\n/**\n * Where to send the user for a set of server messages: the first field\n * that has one. A message about the whole form sends them to the\n * password, the field they will have to type again.\n */\nconst focusTargetFor = (\n messages: LoginFormErrors,\n): LoginFormField | undefined =>\n LOGIN_FORM_FIELDS.find((field) => messages[field]) ??\n (messages.form ? 'password' : undefined);\n\n/* -------------------------------------------------------------------- */\n/* Component */\n/* -------------------------------------------------------------------- */\n\n/**\n * The sign-in card. Mount it on the login page, inside `SignedOutFrame`\n * or the host's own chrome.\n */\nexport const LoginForm = forwardRef<HTMLDivElement, LoginFormProps>(\n (\n {\n title,\n description,\n headingLevel = 1,\n showLogo = true,\n logoMotion = 'assemble',\n children,\n initialValues,\n errors,\n forgotPasswordHref,\n submitLabel,\n submitting = false,\n onSubmit,\n fieldNames,\n action,\n hiddenFields,\n devCard,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation('ui');\n const titleId = useId();\n const formMessageId = useId();\n const fieldIdBase = useId();\n /** The id of a field's wrapper: where the summary's link leads. */\n const fieldId = (field: LoginFormField) => `${fieldIdBase}-${field}`;\n const native = fieldNames !== undefined;\n\n /* ------------------------------ State ----------------------------- */\n\n // Read once. The password always starts empty.\n const [email, setEmail] = useState(() => initialValues?.email ?? '');\n const [password, setPassword] = useState('');\n // A field the user has filled in and left. An email that arrives\n // filled in counts: a bad one is then flagged at once, with its\n // message. An empty field is not flagged for being passed through: a\n // keyboard user on the way to \"Forgot password?\" has made no mistake.\n // Its message waits for a submit.\n const [touched, setTouched] = useState<\n Partial<Record<LoginFormField, boolean>>\n >(() => ({ email: Boolean(initialValues?.email) }));\n const [submitted, setSubmitted] = useState(false);\n const [serverErrors, setServerErrors] = useState<LoginFormErrors>(\n () => errors ?? {},\n );\n\n const leave = (field: LoginFormField) =>\n setTouched((previous) =>\n previous[field] ? previous : { ...previous, [field]: true },\n );\n // The server's message was about the old value.\n const forget = (field: LoginFormField) =>\n setServerErrors((previous) =>\n previous[field] ? { ...previous, [field]: undefined } : previous,\n );\n\n const emailInput = useRef<HTMLInputElement>(null);\n const passwordInput = useRef<HTMLInputElement>(null);\n // The address stays out of the input's `value` attribute, where a\n // copied outerHTML or a session replay could read it.\n const emailField = useStateBackedInput(email, emailInput);\n\n const emptyPassword = () => {\n setPassword('');\n setTouched((previous) => ({ ...previous, password: false }));\n };\n /**\n * What the two inputs hold now. Read from the inputs, not from state:\n * a browser or a password manager can fill a field without telling\n * React, and the form must not refuse a field that is visibly filled.\n */\n const held = (): LoginFormValues => {\n const values = {\n email: emailInput.current?.value ?? email,\n password: passwordInput.current?.value ?? password,\n };\n if (values.email !== email) setEmail(values.email);\n if (values.password !== password) setPassword(values.password);\n return values;\n };\n\n /* ---------------------------- Validation -------------------------- */\n\n /** What stops a submit, whether or not the message shows yet. The\n * password only has to be there: its rules are for a new password. */\n const invalid: Record<LoginFormField, boolean> = {\n email: !isEmail(email),\n password: password === '',\n };\n // A message shows once the user has filled in and left the field, or\n // tried to submit.\n const due = (field: LoginFormField) => submitted || touched[field] === true;\n const ownErrors: Record<LoginFormField, string | undefined> = {\n email:\n due('email') && invalid.email\n ? email === ''\n ? t('loginForm.emailRequired')\n : t('inputs.email.invalid')\n : undefined,\n password:\n due('password') && invalid.password\n ? t('loginForm.passwordRequired')\n : undefined,\n };\n const errorFor = (field: LoginFormField) =>\n serverErrors[field] ?? ownErrors[field];\n\n /* ------------------------------ Focus ----------------------------- */\n\n // Every field with a message after a refused submit, or after a server\n // answer, is listed at the top of the form, and focus goes there. A\n // page the server rendered after a failed post opens that way too: it\n // announces nothing itself.\n const [openedWithFieldMessage] = useState(() =>\n hasFieldMessage(errors ?? {}),\n );\n const summary = useErrorSummary(\n LOGIN_FORM_FIELDS,\n errorFor,\n openedWithFieldMessage,\n );\n\n // Any other place focus goes: the password, when only the message\n // about the whole form says something. A new object for each request,\n // so asking twice for the same place moves focus twice. The effect runs\n // after the messages are drawn, so a screen reader lands on something\n // that already says what is wrong.\n const [focusRequest, setFocusRequest] = useState<{\n target: LoginFormField;\n } | null>(() => {\n const target = openedWithFieldMessage\n ? undefined\n : focusTargetFor(errors ?? {});\n return target ? { target } : null;\n });\n useEffect(() => {\n if (!focusRequest) return;\n (focusRequest.target === 'email'\n ? emailInput\n : passwordInput\n ).current?.focus();\n }, [focusRequest]);\n\n /* ------------------------------ Submit ---------------------------- */\n\n const submit = useRegistrationSubmit<LoginFormField>({\n errors,\n submitting,\n native,\n captcha: false,\n firstProblem: () => {\n const values = held();\n if (!isEmail(values.email)) return 'email';\n return values.password === '' ? 'password' : undefined;\n },\n onRefused: (field) => {\n // Both messages now show, listed in the summary, which takes focus.\n setSubmitted(true);\n summary.open(() => setFocusRequest({ target: field }));\n },\n // Only a React host gets here: a native post is the browser's own.\n deliver: () => onSubmit?.(held()),\n onAnswer: (answer) => {\n const next: LoginFormErrors = answer;\n setServerErrors(next);\n if (!hasMessage(next)) return;\n // Never keep a password the server did not accept.\n emptyPassword();\n setSubmitted(false);\n // The summary when a field has a message, else the password.\n summary.open(() => {\n const target = focusTargetFor(next);\n if (target) setFocusRequest({ target });\n });\n },\n // The page came back from the browser's cache as it was left: the\n // password must not come back with it.\n onRestore: emptyPassword,\n });\n const { busy } = submit;\n\n /* ------------------------------ Render ---------------------------- */\n\n const formAction = useFormAction('LoginForm', native, action);\n const forgotLink =\n forgotPasswordHref && isSafeHref(forgotPasswordHref)\n ? forgotPasswordHref\n : undefined;\n const extraFields = useExtraFields(\n 'LoginForm',\n native,\n fieldNames,\n hiddenFields,\n );\n const formError = serverErrors.form;\n // Quiet while the summary lists a field: the summary reads it then.\n const formMessageAnnounce = useFormMessageAnnounce(\n formError,\n summary.listed.length > 0,\n );\n\n return (\n <div\n ref={ref}\n {...rest}\n data-component=\"login-form\"\n className={loginFormVariants({ className })}\n >\n <div className={columnVariants()}>\n <Card variant=\"elevated\" className=\"ds:h-full\">\n <Card.Body className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]\">\n <AuthCardHeading\n id={titleId}\n title={title ?? t('loginForm.title')}\n description={description}\n level={headingLevel}\n showLogo={showLogo}\n logoMotion={logoMotion}\n />\n\n {children ? (\n <div\n data-slot=\"host-content\"\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\"\n >\n {children}\n </div>\n ) : null}\n\n {/* Always POST, in both modes: should the script ever fail to\n run, the browser must not put a password in the address. */}\n <form\n noValidate\n method=\"post\"\n action={formAction}\n aria-labelledby={titleId}\n aria-busy={busy || undefined}\n onSubmit={submit.handleSubmit}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\"\n >\n <ErrorSummary\n ref={summary.ref}\n headingLevel={(headingLevel + 1) as 2 | 3 | 4}\n // The message about the whole form is read with it.\n aria-describedby={formError ? formMessageId : undefined}\n errors={summary.listed.map((field) => ({\n field,\n message: errorFor(field) ?? '',\n targetId: fieldId(field),\n }))}\n />\n\n {formError && (\n <AuthFormMessage\n id={formMessageId}\n announce={formMessageAnnounce}\n >\n {formError}\n </AuthFormMessage>\n )}\n\n <FormField\n id={fieldId('email')}\n label={t('loginForm.email')}\n required\n error={errorFor('email')}\n // Quiet while the summary lists it: the summary reads it.\n announceError={!summary.lists('email')}\n data-field=\"email\"\n >\n {/* One check, above, drives the message, the red mark and\n aria-invalid through FormField. EmailInput's own check\n is off so the two cannot disagree. `username` is the\n name browsers and password managers give the field\n that goes with `current-password`. */}\n <EmailInput\n ref={emailField.ref}\n name={native ? fieldNames.email : 'email'}\n autoComplete=\"username\"\n disableBuiltInValidation\n onChange={emailField.quiet((event) => {\n setEmail(event.target.value);\n forget('email');\n })}\n onBlur={(event) => {\n // Whatever a browser filled in without saying so.\n setEmail(event.target.value);\n if (event.target.value !== '') leave('email');\n }}\n />\n </FormField>\n\n <FormField\n id={fieldId('password')}\n label={t('loginForm.password')}\n required\n error={errorFor('password')}\n announceError={!summary.lists('password')}\n data-field=\"password\"\n >\n {/* Described by the message above the fields when there is\n one: focus lands here after a refused sign-in, and a\n screen reader then reads why. */}\n <PasswordInput\n ref={passwordInput}\n name={native ? fieldNames.password : 'password'}\n autoComplete=\"current-password\"\n aria-describedby={formError ? formMessageId : undefined}\n value={password}\n onChange={(event) => {\n setPassword(event.target.value);\n forget('password');\n }}\n onBlur={(event) => {\n setPassword(event.target.value);\n if (event.target.value !== '') leave('password');\n }}\n />\n </FormField>\n\n <ExtraFields fields={extraFields} />\n\n <Button\n type=\"submit\"\n intent=\"primary\"\n size=\"lg\"\n loading={busy}\n className=\"ds:w-full\"\n data-action=\"submit\"\n >\n {submitLabel ?? t('loginForm.submit')}\n </Button>\n\n {forgotLink && (\n <AuthTextLink href={forgotLink} action=\"forgot-password\">\n {t('loginForm.forgot')}\n </AuthTextLink>\n )}\n </form>\n {/* Outside the form: a screen reader may hold back what\n changes inside a busy region. */}\n <AuthFormStatus busy={busy} />\n </Card.Body>\n </Card>\n </div>\n {devCard ? <div className={columnVariants()}>{devCard}</div> : null}\n </div>\n );\n },\n);\nLoginForm.displayName = 'LoginForm';\n","/* -------------------------------------------------------------------- */\n/* LoginDevCard — the development quick sign-in card. */\n/* */\n/* A second card beside the login, for developers: one link per test */\n/* account the host's server can sign in. The kit only draws it. The */\n/* host decides whether it is rendered, supplies every word on it, and */\n/* its server must refuse the routes outside development. */\n/* -------------------------------------------------------------------- */\n\nimport { forwardRef, useId, type HTMLAttributes, type ReactNode } from 'react';\nimport { cva } from 'class-variance-authority';\nimport { Button } from '../../components/button';\nimport { Card } from '../../components/card';\nimport { MatrixRain } from '../../components/matrix-rain';\nimport { isSafeHref } from '../../components/_shared/safe-href';\nimport { ErrorPageIllustration } from '../error-page/error-page-illustration';\n\nexport interface LoginDevOption {\n /** The link's text, e.g. \"Sign in as admin\". The host supplies it, so\n * the kit ships no text for a card that users never see. */\n label: string;\n /** The development-only route that signs the test account in. A path\n * on the same site, or an address written in full. */\n href: string;\n /** A Lucide icon for the link, passed bare: `<ShieldUser />`. */\n icon?: ReactNode;\n}\n\nexport interface LoginDevCardProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title'\n> {\n /** The card's heading, e.g. \"Developer quick login\". */\n title: string;\n /** One link per test account. An option with an unsafe address is\n * left out. */\n options: readonly LoginDevOption[];\n /** The heading's level. Default 2, under the login's own heading. */\n headingLevel?: 2 | 3;\n}\n\n// As tall as the login beside it, so the two cards read as a pair.\nconst loginDevCardVariants = cva('ds:h-full ds:w-full');\n\n/**\n * The development quick sign-in card. Render it only when the server\n * says this is a development environment: it never checks the\n * environment itself. Pass it to `LoginForm` as `devCard`.\n */\nexport const LoginDevCard = forwardRef<HTMLDivElement, LoginDevCardProps>(\n ({ title, options, headingLevel = 2, className, ...rest }, ref) => {\n const titleId = useId();\n const Heading = `h${headingLevel}` as const;\n const links = options.filter((option) => isSafeHref(option.href));\n return (\n <div\n ref={ref}\n {...rest}\n data-component=\"login-dev-card\"\n className={loginDevCardVariants({ className })}\n >\n <Card\n variant=\"elevated\"\n aria-labelledby={titleId}\n className=\"ds:relative ds:isolate ds:flex ds:h-full ds:flex-col ds:overflow-hidden\"\n >\n {/* Code rain round the edges, clear in the middle so it never runs\n behind the links. MatrixRain holds still under reduced motion by\n itself; it does not know the accessible themes, so the rain is\n taken out there, like the kit's other decorative motion. */}\n <div className=\"ds:absolute ds:inset-0 ds:z-0 ds:[display:var(--accessible-off,block)]\">\n <MatrixRain\n glyphs=\"hex\"\n decay=\"fast\"\n mask=\"center\"\n maskRadius=\"xl\"\n className=\"ds:size-full\"\n />\n </div>\n <Card.Header className=\"ds:relative ds:z-[1]\">\n <div className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-sm)] ds:text-center\">\n {/* The melting mark from the error pages, at half its size\n there. */}\n <div className=\"ds:[&>div]:w-24\">\n <ErrorPageIllustration mood=\"melt\" />\n </div>\n <Heading\n id={titleId}\n className=\"type-title-card ds:text-[var(--foreground)]\"\n >\n {title}\n </Heading>\n </div>\n </Card.Header>\n <Card.Body className=\"ds:relative ds:z-[1] ds:flex ds:flex-1 ds:flex-col ds:justify-center\">\n {/* The top margin keeps the heading clear of the links when the\n cards stack and this one is only as tall as its content. */}\n <div className=\"ds:mt-[var(--spacing-md)] ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n {links.map((option, index) => (\n <Button\n // Two options may share an address, so the place in the\n // list is part of the key.\n key={`${index}:${option.href}`}\n asChild\n intent=\"primary\"\n startIcon={option.icon}\n className=\"ds:min-h-[var(--min-target-size)] ds:w-full\"\n >\n <a href={option.href} data-action=\"dev-login\">\n {option.label}\n </a>\n </Button>\n ))}\n </div>\n </Card.Body>\n </Card>\n </div>\n );\n },\n);\nLoginDevCard.displayName = 'LoginDevCard';\n"],"names":["LOGIN_FORM_FIELDS","loginFormVariants","cva","columnVariants","hasMessage","errors","hasFieldMessage","field","focusTargetFor","messages","LoginForm","forwardRef","title","description","headingLevel","showLogo","logoMotion","children","initialValues","forgotPasswordHref","submitLabel","submitting","onSubmit","fieldNames","action","hiddenFields","devCard","className","rest","ref","t","useTranslation","titleId","useId","formMessageId","fieldIdBase","fieldId","native","email","setEmail","useState","password","setPassword","touched","setTouched","submitted","setSubmitted","serverErrors","setServerErrors","leave","previous","forget","emailInput","useRef","passwordInput","emailField","useStateBackedInput","emptyPassword","held","values","_a","_b","invalid","isEmail","due","ownErrors","errorFor","openedWithFieldMessage","summary","useErrorSummary","focusRequest","setFocusRequest","target","useEffect","submit","useRegistrationSubmit","answer","next","busy","formAction","useFormAction","forgotLink","isSafeHref","extraFields","useExtraFields","formError","formMessageAnnounce","useFormMessageAnnounce","jsxs","jsx","Card","AuthCardHeading","ErrorSummary","AuthFormMessage","FormField","EmailInput","event","PasswordInput","ExtraFields","Button","AuthTextLink","AuthFormStatus","loginDevCardVariants","LoginDevCard","options","Heading","links","option","MatrixRain","ErrorPageIllustration","index"],"mappings":"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"}
1
+ {"version":3,"file":"login-dev-card-A-CL3mjt.js","sources":["../../src/patterns/login-form/login-form.tsx","../../src/patterns/login-form/login-dev-card.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* LoginForm — the sign-in card. */\n/* */\n/* One export for the card: the mark, the heading, email and password, */\n/* the button and the way to a new password. The host keeps the page */\n/* around it and every server rule. The card checks what a browser can */\n/* check, then hands the values to `onSubmit` or posts a real form to */\n/* the host's own field names. */\n/* */\n/* The email must be an email address. A server that also signs people */\n/* in by a username cannot use this card for those accounts. */\n/* -------------------------------------------------------------------- */\n\nimport {\n forwardRef,\n useEffect,\n useId,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { cva } from 'class-variance-authority';\n// The validator EmailInput itself uses, so the two never disagree.\nimport isEmail from 'validator/lib/isEmail';\nimport { Button } from '../../components/button';\nimport { Card } from '../../components/card';\nimport { EmailInput } from '../../components/email-input';\nimport { ErrorSummary } from '../../components/error-summary';\nimport { FormField } from '../../components/form-field';\nimport { PasswordInput } from '../../components/password-input';\nimport { isSafeHref } from '../../components/_shared/safe-href';\nimport { useStateBackedInput } from '../../components/_shared/use-state-backed-input';\nimport {\n AuthCardHeading,\n AuthFormMessage,\n AuthFormStatus,\n AuthTextLink,\n ExtraFields,\n useExtraFields,\n useFormAction,\n} from '../_shared/auth-form-parts';\nimport type { BrandMarkMotion } from '../_shared/brand-mark';\nimport {\n useErrorSummary,\n useFormMessageAnnounce,\n} from '../_shared/use-error-summary';\nimport { useRegistrationSubmit } from '../registration-form/use-registration-submit';\n\n/* -------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------- */\n\n/** The fields of the form, in the order they appear. */\nexport const LOGIN_FORM_FIELDS = ['email', 'password'] as const;\n\nexport type LoginFormField = (typeof LOGIN_FORM_FIELDS)[number];\n\n/** What `onSubmit` receives once both checks have passed. */\nexport interface LoginFormValues {\n email: string;\n password: string;\n}\n\n/** Values to show when the form opens. There is no password here, on\n * purpose: a password is never sent back to the browser. */\nexport type LoginFormInitialValues = Partial<Pick<LoginFormValues, 'email'>>;\n\n/** Messages from the server, already translated: one per field, plus\n * `form` for a message about the whole form, such as \"the email or\n * password is incorrect\". */\nexport type LoginFormErrors = Partial<Record<LoginFormField | 'form', string>>;\n\n/** The host's own form field names, for a native form post. Both are\n * needed: a field without a name is not posted. */\nexport type LoginFormFieldNames = Record<LoginFormField, string>;\n\nexport interface LoginFormProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title' | 'onSubmit'\n> {\n /** Replaces the heading \"Sign in to Alfadocs\". Pass it translated. */\n title?: string;\n /** A line under the heading. Pass it translated. */\n description?: string;\n /**\n * The heading's level. Default 1: a sign-in page usually has no other\n * heading. Pass 2 on a page that has its own `h1`.\n */\n headingLevel?: 1 | 2 | 3;\n /**\n * Shows the Alfadocs mark above the heading, with the heading and its\n * line centred under it. Default true. With `false` the heading sits\n * at the start edge, for a host that shows its own logo.\n */\n showLogo?: boolean;\n /**\n * How the mark arrives when the card opens: its pieces come together\n * (`assemble`, the default), it turns once (`spin`), it beats\n * (`pulse`), or it is still (`none`). It plays once and rests, and\n * holds still under reduced motion and in the accessible themes.\n */\n logoMotion?: BrandMarkMotion;\n /**\n * Host content between the heading and the fields, such as a notice\n * (\"You have been signed out\"). It sits outside the form, so nothing in\n * it is posted. Keep any heading inside it below the card's own level.\n */\n children?: ReactNode;\n /** The email to show when the form opens, such as the one last used.\n * Read once. There is never an initial password. */\n initialValues?: LoginFormInitialValues;\n /**\n * Messages from the server, already translated. A field's message\n * shows on that field until the user changes it; `form` shows above\n * the fields.\n *\n * When messages arrive, the password is emptied and focus moves: to\n * the error summary at the top of the form, which lists each field\n * with a message, or, when only `form` says something, to the\n * password, which is then described by that message. The same happens\n * for messages present when the form opens (a page the server rendered\n * after a failed post).\n *\n * Never say which of the two values was wrong: one message for both.\n * Set `submitting` while you send, so the form also notices an answer\n * that says the same as the last one.\n */\n errors?: LoginFormErrors;\n /**\n * Adds \"Forgot password?\" under the button. A path on the same site,\n * or an address written in full (`https://…`). Anything else shows no\n * link.\n */\n forgotPasswordHref?: string;\n /** Replaces the button's \"Sign in\". Pass it translated. */\n submitLabel?: string;\n /**\n * True while the host sends the values: the button shows a spinner and\n * a second submit is ignored. Set it inside `onSubmit`, before anything\n * is awaited: until it is true, a second press sends a second sign-in.\n */\n submitting?: boolean;\n /** For React hosts: called with the values once both checks have\n * passed. Not called when `fieldNames` is set. */\n onSubmit?: (values: LoginFormValues) => void;\n /**\n * For a native form post: the host's own field names, both of them,\n * e.g. `{ email: 'email', password: 'password' }`. When set, the inputs\n * carry these names and the browser posts the form.\n */\n fieldNames?: LoginFormFieldNames;\n /**\n * Where a native form post goes. Default: the current address. Only a\n * path, or an address on the page's own origin, is accepted: another\n * origin is never sent what the user typed, and the post then goes to\n * the current address. Does nothing without `fieldNames`.\n */\n action?: string;\n /**\n * Extra hidden inputs posted with a native form, by name: a CSRF token,\n * a return address. Not used with `onSubmit`. A name that is also one\n * of `fieldNames` is left out: the form's own field wins. The server\n * must check a return address itself: anyone can change a hidden input.\n */\n hiddenFields?: Readonly<Record<string, string>>;\n /**\n * A second card beside the sign-in, the same height: the development\n * quick sign-in (`LoginDevCard`). Pass it only when the server says\n * this is a development environment: the kit never decides that\n * itself. The two sit side by side where there is room and stack on a\n * phone.\n */\n devCard?: ReactNode;\n}\n\n/* -------------------------------------------------------------------- */\n/* Styles */\n/* -------------------------------------------------------------------- */\n\n// The card is 400px wide at most, written in px: a host whose root font\n// size is not 16px would stretch or squeeze a width in rem.\nconst loginFormVariants = cva(\n 'ds:flex ds:w-full ds:flex-col ds:items-center ds:justify-center ds:gap-[var(--spacing-lg)] ds:text-start ds:text-[var(--foreground)] ds:lg:flex-row ds:lg:items-stretch',\n);\n\nconst columnVariants = cva('ds:w-full ds:max-w-[400px]');\n\n/* -------------------------------------------------------------------- */\n/* Helpers */\n/* -------------------------------------------------------------------- */\n\nconst hasMessage = (errors: LoginFormErrors): boolean =>\n Object.values(errors).some(Boolean);\n\nconst hasFieldMessage = (errors: LoginFormErrors): boolean =>\n LOGIN_FORM_FIELDS.some((field) => Boolean(errors[field]));\n\n/**\n * Where to send the user for a set of server messages: the first field\n * that has one. A message about the whole form sends them to the\n * password, the field they will have to type again.\n */\nconst focusTargetFor = (\n messages: LoginFormErrors,\n): LoginFormField | undefined =>\n LOGIN_FORM_FIELDS.find((field) => messages[field]) ??\n (messages.form ? 'password' : undefined);\n\n/* -------------------------------------------------------------------- */\n/* Component */\n/* -------------------------------------------------------------------- */\n\n/**\n * The sign-in card. Mount it on the login page, inside `SignedOutFrame`\n * or the host's own chrome.\n */\nexport const LoginForm = forwardRef<HTMLDivElement, LoginFormProps>(\n (\n {\n title,\n description,\n headingLevel = 1,\n showLogo = true,\n logoMotion = 'assemble',\n children,\n initialValues,\n errors,\n forgotPasswordHref,\n submitLabel,\n submitting = false,\n onSubmit,\n fieldNames,\n action,\n hiddenFields,\n devCard,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation('ui');\n const titleId = useId();\n const formMessageId = useId();\n const fieldIdBase = useId();\n /** The id of a field's wrapper: where the summary's link leads. */\n const fieldId = (field: LoginFormField) => `${fieldIdBase}-${field}`;\n const native = fieldNames !== undefined;\n\n /* ------------------------------ State ----------------------------- */\n\n // Read once. The password always starts empty.\n const [email, setEmail] = useState(() => initialValues?.email ?? '');\n const [password, setPassword] = useState('');\n // A field the user has filled in and left. An email that arrives\n // filled in counts: a bad one is then flagged at once, with its\n // message. An empty field is not flagged for being passed through: a\n // keyboard user on the way to \"Forgot password?\" has made no mistake.\n // Its message waits for a submit.\n const [touched, setTouched] = useState<\n Partial<Record<LoginFormField, boolean>>\n >(() => ({ email: Boolean(initialValues?.email) }));\n const [submitted, setSubmitted] = useState(false);\n const [serverErrors, setServerErrors] = useState<LoginFormErrors>(\n () => errors ?? {},\n );\n\n const leave = (field: LoginFormField) =>\n setTouched((previous) =>\n previous[field] ? previous : { ...previous, [field]: true },\n );\n // The server's message was about the old value.\n const forget = (field: LoginFormField) =>\n setServerErrors((previous) =>\n previous[field] ? { ...previous, [field]: undefined } : previous,\n );\n\n const emailInput = useRef<HTMLInputElement>(null);\n const passwordInput = useRef<HTMLInputElement>(null);\n // The address stays out of the input's `value` attribute, where a\n // copied outerHTML or a session replay could read it.\n const emailField = useStateBackedInput(email, emailInput);\n\n const emptyPassword = () => {\n setPassword('');\n setTouched((previous) => ({ ...previous, password: false }));\n };\n /**\n * What the two inputs hold now. Read from the inputs, not from state:\n * a browser or a password manager can fill a field without telling\n * React, and the form must not refuse a field that is visibly filled.\n */\n const held = (): LoginFormValues => {\n const values = {\n email: emailInput.current?.value ?? email,\n password: passwordInput.current?.value ?? password,\n };\n if (values.email !== email) setEmail(values.email);\n if (values.password !== password) setPassword(values.password);\n return values;\n };\n\n /* ---------------------------- Validation -------------------------- */\n\n /** What stops a submit, whether or not the message shows yet. The\n * password only has to be there: its rules are for a new password. */\n const invalid: Record<LoginFormField, boolean> = {\n email: !isEmail(email),\n password: password === '',\n };\n // A message shows once the user has filled in and left the field, or\n // tried to submit.\n const due = (field: LoginFormField) => submitted || touched[field] === true;\n const ownErrors: Record<LoginFormField, string | undefined> = {\n email:\n due('email') && invalid.email\n ? email === ''\n ? t('loginForm.emailRequired')\n : t('inputs.email.invalid')\n : undefined,\n password:\n due('password') && invalid.password\n ? t('loginForm.passwordRequired')\n : undefined,\n };\n const errorFor = (field: LoginFormField) =>\n serverErrors[field] ?? ownErrors[field];\n\n /* ------------------------------ Focus ----------------------------- */\n\n // Every field with a message after a refused submit, or after a server\n // answer, is listed at the top of the form, and focus goes there. A\n // page the server rendered after a failed post opens that way too: it\n // announces nothing itself.\n const [openedWithFieldMessage] = useState(() =>\n hasFieldMessage(errors ?? {}),\n );\n const summary = useErrorSummary(\n LOGIN_FORM_FIELDS,\n errorFor,\n openedWithFieldMessage,\n );\n\n // Any other place focus goes: the password, when only the message\n // about the whole form says something. A new object for each request,\n // so asking twice for the same place moves focus twice. The effect runs\n // after the messages are drawn, so a screen reader lands on something\n // that already says what is wrong.\n const [focusRequest, setFocusRequest] = useState<{\n target: LoginFormField;\n } | null>(() => {\n const target = openedWithFieldMessage\n ? undefined\n : focusTargetFor(errors ?? {});\n return target ? { target } : null;\n });\n useEffect(() => {\n if (!focusRequest) return;\n (focusRequest.target === 'email'\n ? emailInput\n : passwordInput\n ).current?.focus();\n }, [focusRequest]);\n\n /* ------------------------------ Submit ---------------------------- */\n\n const submit = useRegistrationSubmit<LoginFormField>({\n errors,\n submitting,\n native,\n captcha: false,\n firstProblem: () => {\n const values = held();\n if (!isEmail(values.email)) return 'email';\n return values.password === '' ? 'password' : undefined;\n },\n onRefused: (field) => {\n // Both messages now show, listed in the summary, which takes focus.\n setSubmitted(true);\n summary.open(() => setFocusRequest({ target: field }));\n },\n // Only a React host gets here: a native post is the browser's own.\n deliver: () => onSubmit?.(held()),\n onAnswer: (answer) => {\n const next: LoginFormErrors = answer;\n setServerErrors(next);\n if (!hasMessage(next)) return;\n // Never keep a password the server did not accept.\n emptyPassword();\n setSubmitted(false);\n // The summary when a field has a message, else the password.\n summary.open(() => {\n const target = focusTargetFor(next);\n if (target) setFocusRequest({ target });\n });\n },\n // The page came back from the browser's cache as it was left: the\n // password must not come back with it.\n onRestore: emptyPassword,\n });\n const { busy } = submit;\n\n /* ------------------------------ Render ---------------------------- */\n\n const formAction = useFormAction('LoginForm', native, action);\n const forgotLink =\n forgotPasswordHref && isSafeHref(forgotPasswordHref)\n ? forgotPasswordHref\n : undefined;\n const extraFields = useExtraFields(\n 'LoginForm',\n native,\n fieldNames,\n hiddenFields,\n );\n const formError = serverErrors.form;\n // Quiet while the summary lists a field: the summary reads it then.\n const formMessageAnnounce = useFormMessageAnnounce(\n formError,\n summary.listed.length > 0,\n );\n\n return (\n <div\n ref={ref}\n {...rest}\n data-component=\"login-form\"\n className={loginFormVariants({ className })}\n >\n <div className={columnVariants()}>\n <Card variant=\"elevated\" className=\"ds:h-full\">\n <Card.Body className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]\">\n <AuthCardHeading\n id={titleId}\n title={title ?? t('loginForm.title')}\n description={description}\n level={headingLevel}\n showLogo={showLogo}\n logoMotion={logoMotion}\n />\n\n {children ? (\n <div\n data-slot=\"host-content\"\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\"\n >\n {children}\n </div>\n ) : null}\n\n {/* Always POST, in both modes: should the script ever fail to\n run, the browser must not put a password in the address. */}\n <form\n noValidate\n method=\"post\"\n action={formAction}\n aria-labelledby={titleId}\n aria-busy={busy || undefined}\n onSubmit={submit.handleSubmit}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\"\n >\n <ErrorSummary\n ref={summary.ref}\n headingLevel={(headingLevel + 1) as 2 | 3 | 4}\n // The message about the whole form is read with it.\n aria-describedby={formError ? formMessageId : undefined}\n errors={summary.listed.map((field) => ({\n field,\n message: errorFor(field) ?? '',\n targetId: fieldId(field),\n }))}\n />\n\n {formError && (\n <AuthFormMessage\n id={formMessageId}\n announce={formMessageAnnounce}\n >\n {formError}\n </AuthFormMessage>\n )}\n\n <FormField\n id={fieldId('email')}\n label={t('loginForm.email')}\n required\n error={errorFor('email')}\n // Quiet while the summary lists it: the summary reads it.\n announceError={!summary.lists('email')}\n data-field=\"email\"\n >\n {/* One check, above, drives the message, the red mark and\n aria-invalid through FormField. EmailInput's own check\n is off so the two cannot disagree. `username` is the\n name browsers and password managers give the field\n that goes with `current-password`. */}\n <EmailInput\n ref={emailField.ref}\n name={native ? fieldNames.email : 'email'}\n autoComplete=\"username\"\n disableBuiltInValidation\n onChange={emailField.quiet((event) => {\n setEmail(event.target.value);\n forget('email');\n })}\n onBlur={(event) => {\n // Whatever a browser filled in without saying so.\n setEmail(event.target.value);\n if (event.target.value !== '') leave('email');\n }}\n />\n </FormField>\n\n <FormField\n id={fieldId('password')}\n label={t('loginForm.password')}\n required\n error={errorFor('password')}\n announceError={!summary.lists('password')}\n data-field=\"password\"\n >\n {/* Described by the message above the fields when there is\n one: focus lands here after a refused sign-in, and a\n screen reader then reads why. */}\n <PasswordInput\n ref={passwordInput}\n name={native ? fieldNames.password : 'password'}\n autoComplete=\"current-password\"\n aria-describedby={formError ? formMessageId : undefined}\n value={password}\n onChange={(event) => {\n setPassword(event.target.value);\n forget('password');\n }}\n onBlur={(event) => {\n setPassword(event.target.value);\n if (event.target.value !== '') leave('password');\n }}\n />\n </FormField>\n\n <ExtraFields fields={extraFields} />\n\n <Button\n type=\"submit\"\n intent=\"primary\"\n size=\"lg\"\n loading={busy}\n className=\"ds:w-full\"\n data-action=\"submit\"\n >\n {submitLabel ?? t('loginForm.submit')}\n </Button>\n\n {forgotLink && (\n <AuthTextLink href={forgotLink} action=\"forgot-password\">\n {t('loginForm.forgot')}\n </AuthTextLink>\n )}\n </form>\n {/* Outside the form: a screen reader may hold back what\n changes inside a busy region. */}\n <AuthFormStatus busy={busy} />\n </Card.Body>\n </Card>\n </div>\n {devCard ? <div className={columnVariants()}>{devCard}</div> : null}\n </div>\n );\n },\n);\nLoginForm.displayName = 'LoginForm';\n","/* -------------------------------------------------------------------- */\n/* LoginDevCard — the development quick sign-in card. */\n/* */\n/* A second card beside the login, for developers: one link per test */\n/* account the host's server can sign in. The kit only draws it. The */\n/* host decides whether it is rendered, supplies every word on it, and */\n/* its server must refuse the routes outside development. */\n/* -------------------------------------------------------------------- */\n\nimport { forwardRef, useId, type HTMLAttributes, type ReactNode } from 'react';\nimport { cva } from 'class-variance-authority';\nimport { Button } from '../../components/button';\nimport { Card } from '../../components/card';\nimport { MatrixRain } from '../../components/matrix-rain';\nimport { isSafeHref } from '../../components/_shared/safe-href';\nimport { ErrorPageIllustration } from '../error-page/error-page-illustration';\n\nexport interface LoginDevOption {\n /** The link's text, e.g. \"Sign in as admin\". The host supplies it, so\n * the kit ships no text for a card that users never see. */\n label: string;\n /** The development-only route that signs the test account in. A path\n * on the same site, or an address written in full. */\n href: string;\n /** A Lucide icon for the link, passed bare: `<ShieldUser />`. */\n icon?: ReactNode;\n}\n\nexport interface LoginDevCardProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title'\n> {\n /** The card's heading, e.g. \"Developer quick login\". */\n title: string;\n /** One link per test account. An option with an unsafe address is\n * left out. */\n options: readonly LoginDevOption[];\n /** The heading's level. Default 2, under the login's own heading. */\n headingLevel?: 2 | 3;\n}\n\n// As tall as the login beside it, so the two cards read as a pair.\nconst loginDevCardVariants = cva('ds:h-full ds:w-full');\n\n/**\n * The development quick sign-in card. Render it only when the server\n * says this is a development environment: it never checks the\n * environment itself. Pass it to `LoginForm` as `devCard`.\n */\nexport const LoginDevCard = forwardRef<HTMLDivElement, LoginDevCardProps>(\n ({ title, options, headingLevel = 2, className, ...rest }, ref) => {\n const titleId = useId();\n const Heading = `h${headingLevel}` as const;\n const links = options.filter((option) => isSafeHref(option.href));\n return (\n <div\n ref={ref}\n {...rest}\n data-component=\"login-dev-card\"\n className={loginDevCardVariants({ className })}\n >\n <Card\n variant=\"elevated\"\n aria-labelledby={titleId}\n className=\"ds:relative ds:isolate ds:flex ds:h-full ds:flex-col ds:overflow-hidden\"\n >\n {/* Code rain round the edges, clear in the middle so it never runs\n behind the links. MatrixRain holds still under reduced motion by\n itself; it does not know the accessible themes, so the rain is\n taken out there, like the kit's other decorative motion. */}\n <div className=\"ds:absolute ds:inset-0 ds:z-0 ds:[display:var(--accessible-off,block)]\">\n <MatrixRain\n glyphs=\"hex\"\n decay=\"fast\"\n mask=\"center\"\n maskRadius=\"xl\"\n className=\"ds:size-full\"\n />\n </div>\n <Card.Header className=\"ds:relative ds:z-[1]\">\n <div className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-sm)] ds:text-center\">\n {/* The melting mark from the error pages, at half its size\n there. */}\n <div className=\"ds:[&>div]:w-24\">\n <ErrorPageIllustration mood=\"melt\" />\n </div>\n <Heading\n id={titleId}\n className=\"type-title-card ds:text-[var(--foreground)]\"\n >\n {title}\n </Heading>\n </div>\n </Card.Header>\n <Card.Body className=\"ds:relative ds:z-[1] ds:flex ds:flex-1 ds:flex-col ds:justify-center\">\n {/* The top margin keeps the heading clear of the links when the\n cards stack and this one is only as tall as its content. */}\n <div className=\"ds:mt-[var(--spacing-md)] ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n {links.map((option, index) => (\n <Button\n // Two options may share an address, so the place in the\n // list is part of the key.\n key={`${index}:${option.href}`}\n asChild\n intent=\"primary\"\n startIcon={option.icon}\n className=\"ds:min-h-[var(--min-target-size)] ds:w-full\"\n >\n <a href={option.href} data-action=\"dev-login\">\n {option.label}\n </a>\n </Button>\n ))}\n </div>\n </Card.Body>\n </Card>\n </div>\n );\n },\n);\nLoginDevCard.displayName = 'LoginDevCard';\n"],"names":["LOGIN_FORM_FIELDS","loginFormVariants","cva","columnVariants","hasMessage","errors","hasFieldMessage","field","focusTargetFor","messages","LoginForm","forwardRef","title","description","headingLevel","showLogo","logoMotion","children","initialValues","forgotPasswordHref","submitLabel","submitting","onSubmit","fieldNames","action","hiddenFields","devCard","className","rest","ref","t","useTranslation","titleId","useId","formMessageId","fieldIdBase","fieldId","native","email","setEmail","useState","password","setPassword","touched","setTouched","submitted","setSubmitted","serverErrors","setServerErrors","leave","previous","forget","emailInput","useRef","passwordInput","emailField","useStateBackedInput","emptyPassword","held","values","_a","_b","invalid","isEmail","due","ownErrors","errorFor","openedWithFieldMessage","summary","useErrorSummary","focusRequest","setFocusRequest","target","useEffect","submit","useRegistrationSubmit","answer","next","busy","formAction","useFormAction","forgotLink","isSafeHref","extraFields","useExtraFields","formError","formMessageAnnounce","useFormMessageAnnounce","jsxs","jsx","Card","AuthCardHeading","ErrorSummary","AuthFormMessage","FormField","EmailInput","event","PasswordInput","ExtraFields","Button","AuthTextLink","AuthFormStatus","loginDevCardVariants","LoginDevCard","options","Heading","links","option","MatrixRain","ErrorPageIllustration","index"],"mappings":"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"}
@@ -4,7 +4,7 @@ import { useTranslation as B, Trans as xe } from "react-i18next";
4
4
  import { c as p } from "./index-D2ZczOXr.js";
5
5
  import { A as q } from "./app-frame-DUPEwu4E.js";
6
6
  import { A as F } from "./avatar-WZY3eGE7.js";
7
- import { A as be } from "./alert-DdDpOi-P.js";
7
+ import { A as be } from "./alert-DjsJMGRE.js";
8
8
  import { C as U } from "./card-BA4Ig_kn.js";
9
9
  import { D as u } from "./dropdown-menu-D1we7oAY.js";
10
10
  import { T as ke } from "./tab-bar-BEODTG6k.js";
@@ -656,4 +656,4 @@ export {
656
656
  na as M,
657
657
  y as P
658
658
  };
659
- //# sourceMappingURL=marketplace-app-shell-C-2e4eCD.js.map
659
+ //# sourceMappingURL=marketplace-app-shell-DtSX8sYb.js.map