@alfadocs/ui-kit-debug 1.65.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 (264) 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/chart-CwmyCuWe.js +1340 -0
  22. package/dist/_chunks/chart-CwmyCuWe.js.map +1 -0
  23. package/dist/_chunks/{document-preview-B1dQm292.js → document-preview-VyQOjFVL.js} +2 -2
  24. package/dist/_chunks/{document-preview-B1dQm292.js.map → document-preview-VyQOjFVL.js.map} +1 -1
  25. package/dist/_chunks/{document-scanner-CVPVLK7z.js → document-scanner-CXH_GYh4.js} +2 -2
  26. package/dist/_chunks/{document-scanner-CVPVLK7z.js.map → document-scanner-CXH_GYh4.js.map} +1 -1
  27. package/dist/_chunks/{entity-summary-BQ9_KA8S.js → entity-summary-Bj-Wxq7Y.js} +2 -2
  28. package/dist/_chunks/{entity-summary-BQ9_KA8S.js.map → entity-summary-Bj-Wxq7Y.js.map} +1 -1
  29. package/dist/_chunks/{error-summary-DrUfuKu1.js → error-summary-PUE5Zxp9.js} +2 -2
  30. package/dist/_chunks/{error-summary-DrUfuKu1.js.map → error-summary-PUE5Zxp9.js.map} +1 -1
  31. package/dist/_chunks/{forgot-password-form-CgZkEvZy.js → forgot-password-form-CPOfRE0C.js} +4 -4
  32. package/dist/_chunks/{forgot-password-form-CgZkEvZy.js.map → forgot-password-form-CPOfRE0C.js.map} +1 -1
  33. package/dist/_chunks/{freemium-paywall-DKJHRFpp.js → freemium-paywall-6DO7noHX.js} +2 -2
  34. package/dist/_chunks/{freemium-paywall-DKJHRFpp.js.map → freemium-paywall-6DO7noHX.js.map} +1 -1
  35. package/dist/_chunks/heatmap-C1TeC7wT.js +3650 -0
  36. package/dist/_chunks/heatmap-C1TeC7wT.js.map +1 -0
  37. package/dist/_chunks/help-me-write-templates-Bw3Q58x-.js +687 -0
  38. package/dist/_chunks/help-me-write-templates-Bw3Q58x-.js.map +1 -0
  39. package/dist/_chunks/hourglass-BEAzW1wE.js +26 -0
  40. package/dist/_chunks/hourglass-BEAzW1wE.js.map +1 -0
  41. package/dist/_chunks/{login-dev-card-PnADNcI9.js → login-dev-card-A-CL3mjt.js} +3 -3
  42. package/dist/_chunks/{login-dev-card-PnADNcI9.js.map → login-dev-card-A-CL3mjt.js.map} +1 -1
  43. package/dist/_chunks/{marketplace-app-shell-C-2e4eCD.js → marketplace-app-shell-DtSX8sYb.js} +2 -2
  44. package/dist/_chunks/{marketplace-app-shell-C-2e4eCD.js.map → marketplace-app-shell-DtSX8sYb.js.map} +1 -1
  45. package/dist/_chunks/{notes-panel-uEGIL7mi.js → notes-panel-IdsmqRFC.js} +3 -3
  46. package/dist/_chunks/{notes-panel-uEGIL7mi.js.map → notes-panel-IdsmqRFC.js.map} +1 -1
  47. package/dist/_chunks/{patient-details-4P1vA9Ls.js → patient-details-C-EWQTms.js} +2 -2
  48. package/dist/_chunks/{patient-details-4P1vA9Ls.js.map → patient-details-C-EWQTms.js.map} +1 -1
  49. package/dist/_chunks/{patient-field-row-CBXhlfGD.js → patient-field-row-lY-Tb5Mo.js} +155 -176
  50. package/dist/_chunks/patient-field-row-lY-Tb5Mo.js.map +1 -0
  51. package/dist/_chunks/{patient-search-DOnDUVsl.js → patient-search-DpAKidZ5.js} +2 -2
  52. package/dist/_chunks/{patient-search-DOnDUVsl.js.map → patient-search-DpAKidZ5.js.map} +1 -1
  53. package/dist/_chunks/{patient-summary-card-CuDLo8MP.js → patient-summary-card-Bpto_VCD.js} +2 -2
  54. package/dist/_chunks/{patient-summary-card-CuDLo8MP.js.map → patient-summary-card-Bpto_VCD.js.map} +1 -1
  55. package/dist/_chunks/{payment-automation-upsell-C8MEu0t6.js → payment-automation-upsell-BQ307qEX.js} +2 -2
  56. package/dist/_chunks/{payment-automation-upsell-C8MEu0t6.js.map → payment-automation-upsell-BQ307qEX.js.map} +1 -1
  57. package/dist/_chunks/{payment-form-C3dGY7v4.js → payment-form-XKtinW8r.js} +2 -2
  58. package/dist/_chunks/{payment-form-C3dGY7v4.js.map → payment-form-XKtinW8r.js.map} +1 -1
  59. package/dist/_chunks/{pdf-viewer-DJ9VN7cu.js → pdf-viewer-BvoVMjH4.js} +2 -2
  60. package/dist/_chunks/{pdf-viewer-DJ9VN7cu.js.map → pdf-viewer-BvoVMjH4.js.map} +1 -1
  61. package/dist/_chunks/{practice-results-8h2Utpt3.js → practice-results-BrUDDMXC.js} +2 -2
  62. package/dist/_chunks/{practice-results-8h2Utpt3.js.map → practice-results-BrUDDMXC.js.map} +1 -1
  63. package/dist/_chunks/{registration-form-BQyT_kBX.js → registration-form-BxwqUZzk.js} +3 -3
  64. package/dist/_chunks/{registration-form-BQyT_kBX.js.map → registration-form-BxwqUZzk.js.map} +1 -1
  65. package/dist/_chunks/{rich-text-editor-M2gT_E7f.js → rich-text-editor-aCIoptbg.js} +1653 -1743
  66. package/dist/_chunks/{rich-text-editor-M2gT_E7f.js.map → rich-text-editor-aCIoptbg.js.map} +1 -1
  67. package/dist/_chunks/{sign-document-CY7xTXbm.js → sign-document-D1P1nUQA.js} +2 -2
  68. package/dist/_chunks/{sign-document-CY7xTXbm.js.map → sign-document-D1P1nUQA.js.map} +1 -1
  69. package/dist/_chunks/{signature-request-BMCHy2ZO.js → signature-request-3u6TvRt2.js} +2 -2
  70. package/dist/_chunks/{signature-request-BMCHy2ZO.js.map → signature-request-3u6TvRt2.js.map} +1 -1
  71. package/dist/_chunks/{task-editor-CifOznfq.js → task-editor-Bb5MMcid.js} +2 -2
  72. package/dist/_chunks/{task-editor-CifOznfq.js.map → task-editor-Bb5MMcid.js.map} +1 -1
  73. package/dist/_chunks/{tasks-panel-8nLwBw6P.js → tasks-panel-BOb57Ysw.js} +2 -2
  74. package/dist/_chunks/{tasks-panel-8nLwBw6P.js.map → tasks-panel-BOb57Ysw.js.map} +1 -1
  75. package/dist/_chunks/{use-appointment-details-read-B-FEWbQM.js → use-appointment-details-read-CVSPoP5j.js} +4 -4
  76. package/dist/_chunks/{use-appointment-details-read-B-FEWbQM.js.map → use-appointment-details-read-CVSPoP5j.js.map} +1 -1
  77. package/dist/_chunks/voice-control-panel-BJIEbDq6.js +1148 -0
  78. package/dist/_chunks/voice-control-panel-BJIEbDq6.js.map +1 -0
  79. package/dist/_chunks/{warning-stack-HYWAytZV.js → warning-stack-BdY-0bd3.js} +2 -2
  80. package/dist/_chunks/{warning-stack-HYWAytZV.js.map → warning-stack-BdY-0bd3.js.map} +1 -1
  81. package/dist/_chunks/{workflow-map-C2B2qIr1.js → workflow-map-CYbRD9ek.js} +2 -2
  82. package/dist/_chunks/{workflow-map-C2B2qIr1.js.map → workflow-map-CYbRD9ek.js.map} +1 -1
  83. package/dist/agent-catalog.json +50 -3
  84. package/dist/agent-i18n/en.json +7 -4
  85. package/dist/components/_shared/use-apex-radial-centre.d.ts +7 -4
  86. package/dist/components/_shared/use-apex-radial-centre.d.ts.map +1 -1
  87. package/dist/components/ai-credits-notice/ai-credits-exhausted-notice.agent.d.ts +4 -0
  88. package/dist/components/ai-credits-notice/ai-credits-exhausted-notice.agent.d.ts.map +1 -0
  89. package/dist/components/ai-credits-notice/ai-credits-exhausted-notice.d.ts +96 -0
  90. package/dist/components/ai-credits-notice/ai-credits-exhausted-notice.d.ts.map +1 -0
  91. package/dist/components/ai-credits-notice/ai-credits-notice.d.ts +30 -4
  92. package/dist/components/ai-credits-notice/ai-credits-notice.d.ts.map +1 -1
  93. package/dist/components/ai-credits-notice/index.d.ts +3 -0
  94. package/dist/components/ai-credits-notice/index.d.ts.map +1 -1
  95. package/dist/components/ai-credits-notice/index.js +10 -4
  96. package/dist/components/ai-credits-notice/index.js.map +1 -1
  97. package/dist/components/ai-permission-notice/index.js +1 -1
  98. package/dist/components/alert/index.js +1 -1
  99. package/dist/components/appointment-details-panel/index.js +1 -1
  100. package/dist/components/audio-recorder/index.js +1 -1
  101. package/dist/components/booking/index.js +1 -1
  102. package/dist/components/chart/index.js +1 -1
  103. package/dist/components/document-preview/index.js +1 -1
  104. package/dist/components/document-scanner/index.js +1 -1
  105. package/dist/components/entity-summary/index.js +1 -1
  106. package/dist/components/error-summary/index.js +1 -1
  107. package/dist/components/freemium-paywall/index.js +1 -1
  108. package/dist/components/heatmap/heatmap-context.d.ts +3 -0
  109. package/dist/components/heatmap/heatmap-context.d.ts.map +1 -1
  110. package/dist/components/heatmap/heatmap-disclosure.d.ts +67 -0
  111. package/dist/components/heatmap/heatmap-disclosure.d.ts.map +1 -0
  112. package/dist/components/heatmap/heatmap-format.d.ts.map +1 -1
  113. package/dist/components/heatmap/heatmap-grid.d.ts +1 -1
  114. package/dist/components/heatmap/heatmap-grid.d.ts.map +1 -1
  115. package/dist/components/heatmap/heatmap-legend.d.ts +5 -2
  116. package/dist/components/heatmap/heatmap-legend.d.ts.map +1 -1
  117. package/dist/components/heatmap/heatmap-model.d.ts +107 -15
  118. package/dist/components/heatmap/heatmap-model.d.ts.map +1 -1
  119. package/dist/components/heatmap/heatmap-table-view.d.ts +3 -2
  120. package/dist/components/heatmap/heatmap-table-view.d.ts.map +1 -1
  121. package/dist/components/heatmap/heatmap.d.ts +33 -7
  122. package/dist/components/heatmap/heatmap.d.ts.map +1 -1
  123. package/dist/components/heatmap/index.d.ts +1 -1
  124. package/dist/components/heatmap/index.d.ts.map +1 -1
  125. package/dist/components/heatmap/index.js +1 -1
  126. package/dist/components/notes-panel/index.js +1 -1
  127. package/dist/components/patient-details/index.js +1 -1
  128. package/dist/components/patient-search/index.js +1 -1
  129. package/dist/components/patient-summary-card/index.js +1 -1
  130. package/dist/components/payment-form/index.js +1 -1
  131. package/dist/components/pdf-viewer/index.js +1 -1
  132. package/dist/components/practice-results/index.js +1 -1
  133. package/dist/components/rich-text-editor/index.js +1 -1
  134. package/dist/components/rich-text-editor/rich-text-editor.d.ts.map +1 -1
  135. package/dist/components/sign-document/index.js +1 -1
  136. package/dist/components/task-editor/index.js +1 -1
  137. package/dist/components/tasks-panel/index.js +1 -1
  138. package/dist/components/voice-control-panel/index.d.ts +2 -1
  139. package/dist/components/voice-control-panel/index.d.ts.map +1 -1
  140. package/dist/components/voice-control-panel/index.js +1 -1
  141. package/dist/components/voice-control-panel/voice-control-panel.agent.d.ts.map +1 -1
  142. package/dist/components/voice-control-panel/voice-control-panel.d.ts +43 -3
  143. package/dist/components/voice-control-panel/voice-control-panel.d.ts.map +1 -1
  144. package/dist/components/warning-stack/index.js +1 -1
  145. package/dist/components/workflow/index.js +1 -1
  146. package/dist/i18n/locales/ar.d.ts +29 -12
  147. package/dist/i18n/locales/ar.d.ts.map +1 -1
  148. package/dist/i18n/locales/ar.js +29 -12
  149. package/dist/i18n/locales/ar.js.map +1 -1
  150. package/dist/i18n/locales/de.d.ts +29 -12
  151. package/dist/i18n/locales/de.d.ts.map +1 -1
  152. package/dist/i18n/locales/de.js +29 -12
  153. package/dist/i18n/locales/de.js.map +1 -1
  154. package/dist/i18n/locales/el.d.ts +29 -12
  155. package/dist/i18n/locales/el.d.ts.map +1 -1
  156. package/dist/i18n/locales/el.js +29 -12
  157. package/dist/i18n/locales/el.js.map +1 -1
  158. package/dist/i18n/locales/en.d.ts +29 -12
  159. package/dist/i18n/locales/en.d.ts.map +1 -1
  160. package/dist/i18n/locales/en.js +29 -12
  161. package/dist/i18n/locales/en.js.map +1 -1
  162. package/dist/i18n/locales/es.d.ts +29 -12
  163. package/dist/i18n/locales/es.d.ts.map +1 -1
  164. package/dist/i18n/locales/es.js +29 -12
  165. package/dist/i18n/locales/es.js.map +1 -1
  166. package/dist/i18n/locales/fr.d.ts +29 -12
  167. package/dist/i18n/locales/fr.d.ts.map +1 -1
  168. package/dist/i18n/locales/fr.js +29 -12
  169. package/dist/i18n/locales/fr.js.map +1 -1
  170. package/dist/i18n/locales/hi.d.ts +29 -12
  171. package/dist/i18n/locales/hi.d.ts.map +1 -1
  172. package/dist/i18n/locales/hi.js +29 -12
  173. package/dist/i18n/locales/hi.js.map +1 -1
  174. package/dist/i18n/locales/it.d.ts +29 -12
  175. package/dist/i18n/locales/it.d.ts.map +1 -1
  176. package/dist/i18n/locales/it.js +29 -12
  177. package/dist/i18n/locales/it.js.map +1 -1
  178. package/dist/i18n/locales/ja.d.ts +29 -12
  179. package/dist/i18n/locales/ja.d.ts.map +1 -1
  180. package/dist/i18n/locales/ja.js +29 -12
  181. package/dist/i18n/locales/ja.js.map +1 -1
  182. package/dist/i18n/locales/nl.d.ts +29 -12
  183. package/dist/i18n/locales/nl.d.ts.map +1 -1
  184. package/dist/i18n/locales/nl.js +29 -12
  185. package/dist/i18n/locales/nl.js.map +1 -1
  186. package/dist/i18n/locales/pl.d.ts +29 -12
  187. package/dist/i18n/locales/pl.d.ts.map +1 -1
  188. package/dist/i18n/locales/pl.js +29 -12
  189. package/dist/i18n/locales/pl.js.map +1 -1
  190. package/dist/i18n/locales/pt.d.ts +29 -12
  191. package/dist/i18n/locales/pt.d.ts.map +1 -1
  192. package/dist/i18n/locales/pt.js +29 -12
  193. package/dist/i18n/locales/pt.js.map +1 -1
  194. package/dist/i18n/locales/ro.d.ts +29 -12
  195. package/dist/i18n/locales/ro.d.ts.map +1 -1
  196. package/dist/i18n/locales/ro.js +29 -12
  197. package/dist/i18n/locales/ro.js.map +1 -1
  198. package/dist/i18n/locales/ru.d.ts +29 -12
  199. package/dist/i18n/locales/ru.d.ts.map +1 -1
  200. package/dist/i18n/locales/ru.js +29 -12
  201. package/dist/i18n/locales/ru.js.map +1 -1
  202. package/dist/i18n/locales/sq.d.ts +29 -12
  203. package/dist/i18n/locales/sq.d.ts.map +1 -1
  204. package/dist/i18n/locales/sq.js +29 -12
  205. package/dist/i18n/locales/sq.js.map +1 -1
  206. package/dist/i18n/locales/sv.d.ts +29 -12
  207. package/dist/i18n/locales/sv.d.ts.map +1 -1
  208. package/dist/i18n/locales/sv.js +29 -12
  209. package/dist/i18n/locales/sv.js.map +1 -1
  210. package/dist/i18n/locales/tr.d.ts +29 -12
  211. package/dist/i18n/locales/tr.d.ts.map +1 -1
  212. package/dist/i18n/locales/tr.js +29 -12
  213. package/dist/i18n/locales/tr.js.map +1 -1
  214. package/dist/i18n/locales/zh.d.ts +29 -12
  215. package/dist/i18n/locales/zh.d.ts.map +1 -1
  216. package/dist/i18n/locales/zh.js +29 -12
  217. package/dist/i18n/locales/zh.js.map +1 -1
  218. package/dist/index.js +814 -809
  219. package/dist/index.js.map +1 -1
  220. package/dist/locales/ar.json +29 -12
  221. package/dist/locales/de.json +29 -12
  222. package/dist/locales/el.json +29 -12
  223. package/dist/locales/en.json +29 -12
  224. package/dist/locales/es.json +29 -12
  225. package/dist/locales/fr.json +29 -12
  226. package/dist/locales/hi.json +29 -12
  227. package/dist/locales/it.json +29 -12
  228. package/dist/locales/ja.json +29 -12
  229. package/dist/locales/nl.json +29 -12
  230. package/dist/locales/pl.json +29 -12
  231. package/dist/locales/pt.json +29 -12
  232. package/dist/locales/ro.json +29 -12
  233. package/dist/locales/ru.json +29 -12
  234. package/dist/locales/sq.json +29 -12
  235. package/dist/locales/sv.json +29 -12
  236. package/dist/locales/tr.json +29 -12
  237. package/dist/locales/zh.json +29 -12
  238. package/dist/patterns/anamnesis/index.js +1 -1
  239. package/dist/patterns/calendar/index.js +1 -1
  240. package/dist/patterns/forgot-password-form/index.js +1 -1
  241. package/dist/patterns/help-me-write-templates/help-me-write-templates.d.ts +6 -23
  242. package/dist/patterns/help-me-write-templates/help-me-write-templates.d.ts.map +1 -1
  243. package/dist/patterns/help-me-write-templates/index.js +8 -7
  244. package/dist/patterns/help-me-write-templates/index.js.map +1 -1
  245. package/dist/patterns/login-form/index.js +1 -1
  246. package/dist/patterns/marketplace-app-shell/index.js +1 -1
  247. package/dist/patterns/payment-automation-upsell/index.js +1 -1
  248. package/dist/patterns/registration-form/index.js +1 -1
  249. package/dist/patterns/signature-request/index.js +1 -1
  250. package/dist/tokens.css +1 -1
  251. package/package.json +1 -1
  252. package/dist/_chunks/ai-credits-notice-pK9BOmEz.js +0 -90
  253. package/dist/_chunks/ai-credits-notice-pK9BOmEz.js.map +0 -1
  254. package/dist/_chunks/ai-credits-notice.agent-C9L7orrP.js +0 -17
  255. package/dist/_chunks/ai-credits-notice.agent-C9L7orrP.js.map +0 -1
  256. package/dist/_chunks/chart-CIzXxNeM.js +0 -1322
  257. package/dist/_chunks/chart-CIzXxNeM.js.map +0 -1
  258. package/dist/_chunks/heatmap-BxRHM8Et.js +0 -2865
  259. package/dist/_chunks/heatmap-BxRHM8Et.js.map +0 -1
  260. package/dist/_chunks/help-me-write-templates-DFmqnsAa.js +0 -747
  261. package/dist/_chunks/help-me-write-templates-DFmqnsAa.js.map +0 -1
  262. package/dist/_chunks/patient-field-row-CBXhlfGD.js.map +0 -1
  263. package/dist/_chunks/voice-control-panel-C0BfY23Y.js +0 -1009
  264. package/dist/_chunks/voice-control-panel-C0BfY23Y.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"entity-summary-BQ9_KA8S.js","sources":["../../node_modules/lucide-react/dist/esm/icons/contact-round.mjs","../../src/components/entity-summary/entity-summary.agent.ts","../../src/components/entity-summary/entity-summary.tsx"],"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: \"M16 2v2\", key: \"scm5qe\" }],\n [\"path\", { d: \"M17.915 22a6 6 0 0 0-12 0\", key: \"suqz9p\" }],\n [\"path\", { d: \"M8 2v2\", key: \"pbkmx\" }],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"4\", key: \"4exip2\" }],\n [\"rect\", { x: \"3\", y: \"4\", width: \"18\", height: \"18\", rx: \"2\", key: \"12vinp\" }]\n];\nconst ContactRound = createLucideIcon(\"contact-round\", __iconNode);\n\nexport { __iconNode, ContactRound as default };\n//# sourceMappingURL=contact-round.mjs.map\n","/* -------------------------------------------------------------------- */\n/* Agent adapter — EntitySummary. */\n/* */\n/* The AI narrative brief (\"Sintesi IA\") for an entity. The agent can */\n/* read the brief's STRUCTURE (generated-at + section ids / headings / */\n/* tone / item counts — fixed category labels, never the entity name or */\n/* the bullet items, both PHI) and drive the two affordances: regenerate */\n/* the brief and dismiss the panel. Generation itself is gated upstream */\n/* by AiConsentBanner; this adapter only re-requests an already-consented */\n/* brief. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { EntitySummaryHandle } from './entity-summary';\n\nexport const entitySummaryAgent: AgentAdapter<EntitySummaryHandle> = {\n id: 'entity-summary',\n // 'close' backs close; regenerate is a host-driven refresh with no\n // dedicated capability in the closed vocabulary.\n capabilities: ['close'],\n state: {\n sections: {\n type: 'Array<{ id: string; heading: string; tone: \"default\" | \"attention\"; itemCount: number }>',\n descriptionKey: 'ui.agent.entitySummary.state.sections',\n description:\n 'Structural section list — ids, fixed category headings, tone and item counts. Never the entity name or the bullet items (both PHI).',\n read: (handle) => handle.getState().sections,\n },\n generatedAtIso: {\n type: 'string | undefined',\n descriptionKey: 'ui.agent.entitySummary.state.generatedAtIso',\n description: 'ISO-8601 instant the brief was generated, when known.',\n read: (handle) => handle.getState().generatedAtIso,\n },\n },\n actions: {\n regenerate: {\n // Re-requests a fresh brief from the model — reaches an external system.\n safety: 'write',\n openWorld: true,\n descriptionKey: 'ui.agent.entitySummary.actions.regenerate',\n description:\n 'Request the host to regenerate the AI brief. No-op when the regenerate affordance is not available.',\n invoke: (handle) => {\n if (handle.canRegenerate()) handle.regenerate();\n },\n },\n close: {\n // Ephemeral panel-visibility change — read.\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.entitySummary.actions.close',\n description:\n 'Dismiss the summary panel. No-op when the close affordance is not available (e.g. docked in the rail).',\n invoke: (handle) => {\n if (handle.canClose()) handle.close();\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'entity-summary',\n description: 'Marks the EntitySummary panel region.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description:\n 'Sourced from the id prop. Required to address a specific summary panel from the agent.',\n },\n },\n};\n","import {\n forwardRef,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { ContactRound, Clock, RefreshCw, X } from 'lucide-react';\nimport { Alert } from '../alert';\nimport { Avatar } from '../avatar';\nimport { Button } from '../button';\nimport { Card } from '../card';\nimport { IconButton } from '../button/icon-button';\nimport { Link } from '../link';\nimport { PanelHeader } from '../panel-header';\nimport { Timestamp } from '../timestamp';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { entitySummaryAgent } from './entity-summary.agent';\n\n/* ------------------------------------------------------------------ */\n/* EntitySummary — the AI narrative brief for an entity. */\n/* */\n/* The kit form of platform's \"Sintesi IA\": a generated, sectioned */\n/* brief of a patient / care plan — header (entity · generated-at · */\n/* regenerate · corpus stats), N labelled sections of bullet items */\n/* (with inline entity links), and a BETA disclaimer. The `attention` */\n/* section tone carries the clinically valuable risk flags. */\n/* */\n/* The kit owns the layout, i18n and theming; the consumer passes */\n/* structured `sections` (data-in) so the brief re-themes and */\n/* localises rather than rendering a model HTML blob. Gate generation */\n/* behind `AiConsentBanner` upstream. */\n/* ------------------------------------------------------------------ */\n\n/** A corpus stat shown in the header (e.g. `{ value: 5, label: 'care plans' }`). */\nexport interface EntitySummaryStat {\n value: string | number;\n label: string;\n}\n\n/** One narrative section — a heading over a list of bullet items. */\nexport interface EntitySummarySection {\n id: string;\n heading: string;\n /**\n * Leading glyph for the section. On default sections it's the scannability\n * anchor shown in a `--primary`-tinted medallion beside the heading (e.g. a\n * calendar for \"Scheduled\", a wallet for \"Financial\"); omit it to render no\n * medallion. On `attention` sections it overrides the warning Alert's icon\n * (which defaults to an alert triangle).\n */\n icon?: ReactNode;\n /**\n * `attention` renders the section as the kit's warning `Alert` — the\n * standard risk-flag treatment (border, warning surface, alert glyph,\n * `--warning-foreground` text) — for clinically important callouts.\n */\n tone?: 'default' | 'attention';\n /** Bullet items — `ReactNode` so consumers compose inline entity links. */\n items: ReactNode[];\n}\n\n/**\n * Curated agent handle for EntitySummary. Exposes structural state only —\n * never the entity name or the section items (both PHI). See\n * `entity-summary.agent.ts`.\n */\nexport interface EntitySummaryHandle {\n /** Structural snapshot — generated-at + section ids/headings/tone/count. No PHI. */\n getState: () => {\n generatedAtIso?: string;\n sections: Array<{\n id: string;\n heading: string;\n tone: 'default' | 'attention';\n itemCount: number;\n }>;\n };\n /** Whether the regenerate affordance is available. */\n canRegenerate: () => boolean;\n /** Request the host to regenerate the brief (mirrors onRegenerate). */\n regenerate: () => void;\n /** Whether the close affordance is available. */\n canClose: () => boolean;\n /** Dismiss the panel (mirrors onClose). */\n close: () => void;\n}\n\nexport interface EntitySummaryProps extends Omit<\n HTMLAttributes<HTMLElement>,\n 'title' | 'children'\n> {\n /**\n * Stable instance id. Surfaced on the root as `data-component-id` so an\n * agent / MCP UI bridge can address this specific summary panel.\n */\n id?: string;\n /** Panel title. Defaults to the localised \"AI summary\". */\n title?: string;\n /** Entity name shown beside its avatar in the header (links when `entityHref` is set). */\n entityName?: string;\n /** Entity href for the name link. */\n entityHref?: string;\n /** Avatar image URL for the entity. Falls back to initials derived from `entityName`. */\n entityAvatarSrc?: string;\n /** ISO-8601 instant the brief was generated. */\n generatedAtIso?: string;\n /** Corpus the brief was built from, shown as a compact KPI card. */\n stats?: EntitySummaryStat[];\n /** The narrative sections, in order. */\n sections: EntitySummarySection[];\n /** Regenerate the brief. Omit to hide the affordance. */\n onRegenerate?: () => void;\n /** Regenerate-button label. */\n regenerateLabel?: string;\n /**\n * Dismiss the panel. Renders a close button in the panel header for\n * STANDALONE use (outside the rail). When docked in `ControlRoomRail`, omit\n * this — the rail injects its own close via `PanelHeaderActionsProvider`\n * (passing it while docked would render a duplicate close control).\n */\n onClose?: () => void;\n /** Footer disclaimer. Defaults to the BETA clinical-judgement note. */\n disclaimer?: ReactNode;\n /** BCP-47 locale for the generated-at timestamp. */\n locale?: string;\n /** Accessible name for the panel region (defaults to `title`). */\n 'aria-label'?: string;\n}\n\n/* ------------------------------------------------------------------ */\n/* Section presentation. The `attention` tone renders a different */\n/* element tree (the kit's warning Alert) rather than a class variant, */\n/* so the branch lives in `Section`; these constants carry the shared */\n/* class sets for the default (prose) sections. */\n/* ------------------------------------------------------------------ */\n\n// The default section's leading icon chip — a --primary-tinted medallion\n// that anchors the heading. No tone variants: the attention tone is owned\n// by the Alert primitive, so this stays a plain class set.\nconst MEDALLION_CLASSES =\n 'ds:flex ds:size-7 ds:shrink-0 ds:items-center ds:justify-center ds:rounded-[var(--radius-md)] ds:[&_svg]:size-4 ds:bg-primary/10 ds:text-[color:var(--primary)]';\n\n// The bullet list shared by every section — indented disc items.\nconst ITEMS_CLASSES =\n 'ds:m-0 ds:flex ds:list-disc ds:flex-col ds:gap-[var(--spacing-2xs)] ds:ps-[var(--spacing-md)] type-body-sm';\n\nexport const EntitySummary = forwardRef<HTMLElement, EntitySummaryProps>(\n function EntitySummary(\n {\n id,\n title,\n entityName,\n entityHref,\n entityAvatarSrc,\n generatedAtIso,\n stats,\n sections,\n onRegenerate,\n regenerateLabel,\n onClose,\n disclaimer,\n locale,\n className,\n 'aria-label': ariaLabel,\n ...rest\n },\n ref,\n ) {\n const { t } = useTranslation('ui');\n const headingId = useId();\n const heading =\n title ?? t('entitySummary.title', { defaultValue: 'AI summary' });\n\n /* ----- Agent handle (structural state + regenerate / close) ----- */\n const sectionsRef = useRef(sections);\n sectionsRef.current = sections;\n const generatedAtRef = useRef(generatedAtIso);\n generatedAtRef.current = generatedAtIso;\n const onRegenerateRef = useRef(onRegenerate);\n onRegenerateRef.current = onRegenerate;\n const onCloseRef = useRef(onClose);\n onCloseRef.current = onClose;\n\n const rootRef = useRef<HTMLElement>(null);\n useImperativeHandle(ref, () => rootRef.current as HTMLElement, []);\n\n const handle = useMemo<EntitySummaryHandle>(\n () => ({\n // Structural only — section headings are fixed category labels, never\n // the entity name or the bullet items (both PHI).\n getState: () => ({\n generatedAtIso: generatedAtRef.current,\n sections: sectionsRef.current.map((s) => ({\n id: s.id,\n heading: s.heading,\n tone: s.tone ?? 'default',\n itemCount: s.items.length,\n })),\n }),\n canRegenerate: () => !!onRegenerateRef.current,\n regenerate: () => onRegenerateRef.current?.(),\n canClose: () => !!onCloseRef.current,\n close: () => onCloseRef.current?.(),\n }),\n [],\n );\n useAgentRegistration(entitySummaryAgent, handle, id);\n\n // The generated-at line — the brief's \"subtitle\" under the entity name,\n // or a standalone line when there's no entity. A clock glyph leads it.\n const generatedLine = generatedAtIso ? (\n <span className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)] type-meta ds:text-[color:var(--muted-foreground)]\">\n <Clock aria-hidden=\"true\" className=\"ds:size-3.5 ds:shrink-0\" />\n <span>\n {t('entitySummary.generatedAt', { defaultValue: 'Generated' })}\n </span>\n <Timestamp value={generatedAtIso} format=\"absolute\" locale={locale} />\n </span>\n ) : null;\n\n return (\n <section\n ref={rootRef}\n data-component=\"entity-summary\"\n data-component-id={id}\n aria-labelledby={ariaLabel ? undefined : headingId}\n aria-label={ariaLabel}\n className={['ds:flex ds:flex-col', className].filter(Boolean).join(' ')}\n {...rest}\n >\n {/* Header — the shared rail-panel pattern. Title + Regenerate;\n the close affordance is injected by the rail, not the panel. */}\n <PanelHeader\n title={heading}\n icon={<ContactRound aria-hidden=\"true\" />}\n headingId={ariaLabel ? undefined : headingId}\n sticky\n divider\n actions={\n onRegenerate || onClose ? (\n <>\n {onRegenerate ? (\n <Button\n type=\"button\"\n size=\"sm\"\n intent=\"ghost\"\n startIcon={<RefreshCw aria-hidden=\"true\" />}\n onClick={onRegenerate}\n >\n {regenerateLabel ??\n t('entitySummary.regenerate', {\n defaultValue: 'Regenerate',\n })}\n </Button>\n ) : null}\n {onClose ? (\n <IconButton\n icon={<X aria-hidden=\"true\" />}\n size=\"sm\"\n intent=\"ghost\"\n aria-label={t('common.close', { defaultValue: 'Close' })}\n onClick={onClose}\n />\n ) : null}\n </>\n ) : undefined\n }\n />\n\n {/* Body */}\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)] ds:p-[var(--spacing-md)]\">\n {entityName || generatedAtIso || (stats && stats.length > 0) ? (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\">\n {/* Identity — the entity's avatar + name, generated-at as the\n subtitle beneath. Without a name, the generated line stands\n alone. */}\n {entityName ? (\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-sm)]\">\n <Avatar\n name={entityName}\n src={entityAvatarSrc}\n size=\"sm\"\n className=\"ds:shrink-0\"\n />\n <div className=\"ds:flex ds:min-w-0 ds:flex-col ds:gap-[var(--spacing-2xs)]\">\n {entityHref ? (\n <Link\n href={entityHref}\n className=\"type-body-sm ds:font-semibold\"\n >\n {entityName}\n </Link>\n ) : (\n <span className=\"type-body-sm ds:font-semibold ds:text-[color:var(--foreground)]\">\n {entityName}\n </span>\n )}\n {generatedLine}\n </div>\n </div>\n ) : (\n generatedLine\n )}\n\n {/* Corpus stats — a compact KPI card: value over label, with a\n hairline divider between cells. */}\n {stats && stats.length > 0 ? (\n <Card variant=\"elevated\">\n {/* eslint-disable-next-line jsx-a11y/no-redundant-roles -- role=\"list\" restores list semantics that ds:list-none strips in Safari/VoiceOver */}\n <ul\n role=\"list\"\n className=\"ds:m-0 ds:flex ds:list-none ds:items-stretch\"\n >\n {stats.map((stat, index) => (\n <li\n key={`${stat.label}-${index}`}\n className={[\n 'ds:flex ds:flex-1 ds:flex-col ds:items-center ds:gap-[var(--spacing-2xs)] ds:p-[var(--spacing-sm)] ds:text-center',\n index > 0\n ? 'ds:border-s ds:border-[color:var(--card-border)]'\n : '',\n ]\n .filter(Boolean)\n .join(' ')}\n >\n <span className=\"type-metric ds:[--type-metric-size:var(--font-size-lg)] ds:text-[color:var(--foreground)]\">\n {stat.value}\n </span>\n <span className=\"type-meta ds:text-[color:var(--muted-foreground)]\">\n {stat.label}\n </span>\n </li>\n ))}\n </ul>\n </Card>\n ) : null}\n </div>\n ) : null}\n\n {/* Sections */}\n {sections.map((section) => (\n <Section key={section.id} section={section} />\n ))}\n\n {/* Disclaimer */}\n <p className=\"ds:m-0 type-meta ds:text-[color:var(--muted-foreground)]\">\n {disclaimer ??\n t('entitySummary.disclaimer', {\n defaultValue:\n 'Does not replace clinical judgement · verify accuracy before clinical use · BETA',\n })}\n </p>\n </div>\n </section>\n );\n },\n);\n\nfunction Section({ section }: { section: EntitySummarySection }): ReactNode {\n const headingId = useId();\n\n // The risk-flag section composes the kit's warning `Alert` primitive rather\n // than re-painting the warning tokens by hand — same border, surface, alert\n // glyph, and `--warning-foreground` treatment as every other warning in the\n // app. `live=\"off\"`: the brief is static content, not a just-occurred event,\n // so the items read in normal flow instead of being announced assertively.\n // A per-section `icon` overrides Alert's default alert triangle.\n if (section.tone === 'attention') {\n // The shared `--shadow-card` lifts the warning callout to the same\n // elevation as the surrounding section cards, so the stack reads as one\n // set of raised panels rather than a flat warning band among cards.\n return (\n <Alert\n variant=\"warning\"\n live=\"off\"\n icon={section.icon}\n className=\"ds:shadow-[var(--shadow-card)]\"\n >\n <Alert.Title as=\"h3\">{section.heading}</Alert.Title>\n <ul className={ITEMS_CLASSES}>\n {section.items.map((item, index) => (\n <li key={index}>{item}</li>\n ))}\n </ul>\n </Alert>\n );\n }\n\n // Default sections are raised cards — the scannable \"panel per topic\" stack,\n // matching platform's Sintesi IA. The card owns its own padding (so we drive\n // it from a single content div rather than Card.Header/Body) and is named by\n // its heading for assistive tech.\n return (\n <Card variant=\"elevated\" aria-labelledby={headingId}>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)] ds:p-[var(--spacing-md)]\">\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-xs)]\">\n {section.icon ? (\n <span aria-hidden=\"true\" className={MEDALLION_CLASSES}>\n {section.icon}\n </span>\n ) : null}\n <h3\n id={headingId}\n className=\"ds:m-0 type-eyebrow ds:text-[color:var(--foreground)]\"\n >\n {section.heading}\n </h3>\n </div>\n <ul className={`${ITEMS_CLASSES} ds:text-[color:var(--foreground)]`}>\n {section.items.map((item, index) => (\n <li key={index}>{item}</li>\n ))}\n </ul>\n </div>\n </Card>\n );\n}\n"],"names":["__iconNode","ContactRound","createLucideIcon","entitySummaryAgent","handle","MEDALLION_CLASSES","ITEMS_CLASSES","EntitySummary","forwardRef","id","title","entityName","entityHref","entityAvatarSrc","generatedAtIso","stats","sections","onRegenerate","regenerateLabel","onClose","disclaimer","locale","className","ariaLabel","rest","ref","t","useTranslation","headingId","useId","heading","sectionsRef","useRef","generatedAtRef","onRegenerateRef","onCloseRef","rootRef","useImperativeHandle","useMemo","s","_a","useAgentRegistration","generatedLine","jsxs","jsx","Clock","Timestamp","PanelHeader","Fragment","Button","RefreshCw","IconButton","X","Avatar","Link","Card","stat","index","section","Section","Alert","item"],"mappings":"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","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"entity-summary-Bj-Wxq7Y.js","sources":["../../node_modules/lucide-react/dist/esm/icons/contact-round.mjs","../../src/components/entity-summary/entity-summary.agent.ts","../../src/components/entity-summary/entity-summary.tsx"],"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: \"M16 2v2\", key: \"scm5qe\" }],\n [\"path\", { d: \"M17.915 22a6 6 0 0 0-12 0\", key: \"suqz9p\" }],\n [\"path\", { d: \"M8 2v2\", key: \"pbkmx\" }],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"4\", key: \"4exip2\" }],\n [\"rect\", { x: \"3\", y: \"4\", width: \"18\", height: \"18\", rx: \"2\", key: \"12vinp\" }]\n];\nconst ContactRound = createLucideIcon(\"contact-round\", __iconNode);\n\nexport { __iconNode, ContactRound as default };\n//# sourceMappingURL=contact-round.mjs.map\n","/* -------------------------------------------------------------------- */\n/* Agent adapter — EntitySummary. */\n/* */\n/* The AI narrative brief (\"Sintesi IA\") for an entity. The agent can */\n/* read the brief's STRUCTURE (generated-at + section ids / headings / */\n/* tone / item counts — fixed category labels, never the entity name or */\n/* the bullet items, both PHI) and drive the two affordances: regenerate */\n/* the brief and dismiss the panel. Generation itself is gated upstream */\n/* by AiConsentBanner; this adapter only re-requests an already-consented */\n/* brief. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { EntitySummaryHandle } from './entity-summary';\n\nexport const entitySummaryAgent: AgentAdapter<EntitySummaryHandle> = {\n id: 'entity-summary',\n // 'close' backs close; regenerate is a host-driven refresh with no\n // dedicated capability in the closed vocabulary.\n capabilities: ['close'],\n state: {\n sections: {\n type: 'Array<{ id: string; heading: string; tone: \"default\" | \"attention\"; itemCount: number }>',\n descriptionKey: 'ui.agent.entitySummary.state.sections',\n description:\n 'Structural section list — ids, fixed category headings, tone and item counts. Never the entity name or the bullet items (both PHI).',\n read: (handle) => handle.getState().sections,\n },\n generatedAtIso: {\n type: 'string | undefined',\n descriptionKey: 'ui.agent.entitySummary.state.generatedAtIso',\n description: 'ISO-8601 instant the brief was generated, when known.',\n read: (handle) => handle.getState().generatedAtIso,\n },\n },\n actions: {\n regenerate: {\n // Re-requests a fresh brief from the model — reaches an external system.\n safety: 'write',\n openWorld: true,\n descriptionKey: 'ui.agent.entitySummary.actions.regenerate',\n description:\n 'Request the host to regenerate the AI brief. No-op when the regenerate affordance is not available.',\n invoke: (handle) => {\n if (handle.canRegenerate()) handle.regenerate();\n },\n },\n close: {\n // Ephemeral panel-visibility change — read.\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.entitySummary.actions.close',\n description:\n 'Dismiss the summary panel. No-op when the close affordance is not available (e.g. docked in the rail).',\n invoke: (handle) => {\n if (handle.canClose()) handle.close();\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'entity-summary',\n description: 'Marks the EntitySummary panel region.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description:\n 'Sourced from the id prop. Required to address a specific summary panel from the agent.',\n },\n },\n};\n","import {\n forwardRef,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { ContactRound, Clock, RefreshCw, X } from 'lucide-react';\nimport { Alert } from '../alert';\nimport { Avatar } from '../avatar';\nimport { Button } from '../button';\nimport { Card } from '../card';\nimport { IconButton } from '../button/icon-button';\nimport { Link } from '../link';\nimport { PanelHeader } from '../panel-header';\nimport { Timestamp } from '../timestamp';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { entitySummaryAgent } from './entity-summary.agent';\n\n/* ------------------------------------------------------------------ */\n/* EntitySummary — the AI narrative brief for an entity. */\n/* */\n/* The kit form of platform's \"Sintesi IA\": a generated, sectioned */\n/* brief of a patient / care plan — header (entity · generated-at · */\n/* regenerate · corpus stats), N labelled sections of bullet items */\n/* (with inline entity links), and a BETA disclaimer. The `attention` */\n/* section tone carries the clinically valuable risk flags. */\n/* */\n/* The kit owns the layout, i18n and theming; the consumer passes */\n/* structured `sections` (data-in) so the brief re-themes and */\n/* localises rather than rendering a model HTML blob. Gate generation */\n/* behind `AiConsentBanner` upstream. */\n/* ------------------------------------------------------------------ */\n\n/** A corpus stat shown in the header (e.g. `{ value: 5, label: 'care plans' }`). */\nexport interface EntitySummaryStat {\n value: string | number;\n label: string;\n}\n\n/** One narrative section — a heading over a list of bullet items. */\nexport interface EntitySummarySection {\n id: string;\n heading: string;\n /**\n * Leading glyph for the section. On default sections it's the scannability\n * anchor shown in a `--primary`-tinted medallion beside the heading (e.g. a\n * calendar for \"Scheduled\", a wallet for \"Financial\"); omit it to render no\n * medallion. On `attention` sections it overrides the warning Alert's icon\n * (which defaults to an alert triangle).\n */\n icon?: ReactNode;\n /**\n * `attention` renders the section as the kit's warning `Alert` — the\n * standard risk-flag treatment (border, warning surface, alert glyph,\n * `--warning-foreground` text) — for clinically important callouts.\n */\n tone?: 'default' | 'attention';\n /** Bullet items — `ReactNode` so consumers compose inline entity links. */\n items: ReactNode[];\n}\n\n/**\n * Curated agent handle for EntitySummary. Exposes structural state only —\n * never the entity name or the section items (both PHI). See\n * `entity-summary.agent.ts`.\n */\nexport interface EntitySummaryHandle {\n /** Structural snapshot — generated-at + section ids/headings/tone/count. No PHI. */\n getState: () => {\n generatedAtIso?: string;\n sections: Array<{\n id: string;\n heading: string;\n tone: 'default' | 'attention';\n itemCount: number;\n }>;\n };\n /** Whether the regenerate affordance is available. */\n canRegenerate: () => boolean;\n /** Request the host to regenerate the brief (mirrors onRegenerate). */\n regenerate: () => void;\n /** Whether the close affordance is available. */\n canClose: () => boolean;\n /** Dismiss the panel (mirrors onClose). */\n close: () => void;\n}\n\nexport interface EntitySummaryProps extends Omit<\n HTMLAttributes<HTMLElement>,\n 'title' | 'children'\n> {\n /**\n * Stable instance id. Surfaced on the root as `data-component-id` so an\n * agent / MCP UI bridge can address this specific summary panel.\n */\n id?: string;\n /** Panel title. Defaults to the localised \"AI summary\". */\n title?: string;\n /** Entity name shown beside its avatar in the header (links when `entityHref` is set). */\n entityName?: string;\n /** Entity href for the name link. */\n entityHref?: string;\n /** Avatar image URL for the entity. Falls back to initials derived from `entityName`. */\n entityAvatarSrc?: string;\n /** ISO-8601 instant the brief was generated. */\n generatedAtIso?: string;\n /** Corpus the brief was built from, shown as a compact KPI card. */\n stats?: EntitySummaryStat[];\n /** The narrative sections, in order. */\n sections: EntitySummarySection[];\n /** Regenerate the brief. Omit to hide the affordance. */\n onRegenerate?: () => void;\n /** Regenerate-button label. */\n regenerateLabel?: string;\n /**\n * Dismiss the panel. Renders a close button in the panel header for\n * STANDALONE use (outside the rail). When docked in `ControlRoomRail`, omit\n * this — the rail injects its own close via `PanelHeaderActionsProvider`\n * (passing it while docked would render a duplicate close control).\n */\n onClose?: () => void;\n /** Footer disclaimer. Defaults to the BETA clinical-judgement note. */\n disclaimer?: ReactNode;\n /** BCP-47 locale for the generated-at timestamp. */\n locale?: string;\n /** Accessible name for the panel region (defaults to `title`). */\n 'aria-label'?: string;\n}\n\n/* ------------------------------------------------------------------ */\n/* Section presentation. The `attention` tone renders a different */\n/* element tree (the kit's warning Alert) rather than a class variant, */\n/* so the branch lives in `Section`; these constants carry the shared */\n/* class sets for the default (prose) sections. */\n/* ------------------------------------------------------------------ */\n\n// The default section's leading icon chip — a --primary-tinted medallion\n// that anchors the heading. No tone variants: the attention tone is owned\n// by the Alert primitive, so this stays a plain class set.\nconst MEDALLION_CLASSES =\n 'ds:flex ds:size-7 ds:shrink-0 ds:items-center ds:justify-center ds:rounded-[var(--radius-md)] ds:[&_svg]:size-4 ds:bg-primary/10 ds:text-[color:var(--primary)]';\n\n// The bullet list shared by every section — indented disc items.\nconst ITEMS_CLASSES =\n 'ds:m-0 ds:flex ds:list-disc ds:flex-col ds:gap-[var(--spacing-2xs)] ds:ps-[var(--spacing-md)] type-body-sm';\n\nexport const EntitySummary = forwardRef<HTMLElement, EntitySummaryProps>(\n function EntitySummary(\n {\n id,\n title,\n entityName,\n entityHref,\n entityAvatarSrc,\n generatedAtIso,\n stats,\n sections,\n onRegenerate,\n regenerateLabel,\n onClose,\n disclaimer,\n locale,\n className,\n 'aria-label': ariaLabel,\n ...rest\n },\n ref,\n ) {\n const { t } = useTranslation('ui');\n const headingId = useId();\n const heading =\n title ?? t('entitySummary.title', { defaultValue: 'AI summary' });\n\n /* ----- Agent handle (structural state + regenerate / close) ----- */\n const sectionsRef = useRef(sections);\n sectionsRef.current = sections;\n const generatedAtRef = useRef(generatedAtIso);\n generatedAtRef.current = generatedAtIso;\n const onRegenerateRef = useRef(onRegenerate);\n onRegenerateRef.current = onRegenerate;\n const onCloseRef = useRef(onClose);\n onCloseRef.current = onClose;\n\n const rootRef = useRef<HTMLElement>(null);\n useImperativeHandle(ref, () => rootRef.current as HTMLElement, []);\n\n const handle = useMemo<EntitySummaryHandle>(\n () => ({\n // Structural only — section headings are fixed category labels, never\n // the entity name or the bullet items (both PHI).\n getState: () => ({\n generatedAtIso: generatedAtRef.current,\n sections: sectionsRef.current.map((s) => ({\n id: s.id,\n heading: s.heading,\n tone: s.tone ?? 'default',\n itemCount: s.items.length,\n })),\n }),\n canRegenerate: () => !!onRegenerateRef.current,\n regenerate: () => onRegenerateRef.current?.(),\n canClose: () => !!onCloseRef.current,\n close: () => onCloseRef.current?.(),\n }),\n [],\n );\n useAgentRegistration(entitySummaryAgent, handle, id);\n\n // The generated-at line — the brief's \"subtitle\" under the entity name,\n // or a standalone line when there's no entity. A clock glyph leads it.\n const generatedLine = generatedAtIso ? (\n <span className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)] type-meta ds:text-[color:var(--muted-foreground)]\">\n <Clock aria-hidden=\"true\" className=\"ds:size-3.5 ds:shrink-0\" />\n <span>\n {t('entitySummary.generatedAt', { defaultValue: 'Generated' })}\n </span>\n <Timestamp value={generatedAtIso} format=\"absolute\" locale={locale} />\n </span>\n ) : null;\n\n return (\n <section\n ref={rootRef}\n data-component=\"entity-summary\"\n data-component-id={id}\n aria-labelledby={ariaLabel ? undefined : headingId}\n aria-label={ariaLabel}\n className={['ds:flex ds:flex-col', className].filter(Boolean).join(' ')}\n {...rest}\n >\n {/* Header — the shared rail-panel pattern. Title + Regenerate;\n the close affordance is injected by the rail, not the panel. */}\n <PanelHeader\n title={heading}\n icon={<ContactRound aria-hidden=\"true\" />}\n headingId={ariaLabel ? undefined : headingId}\n sticky\n divider\n actions={\n onRegenerate || onClose ? (\n <>\n {onRegenerate ? (\n <Button\n type=\"button\"\n size=\"sm\"\n intent=\"ghost\"\n startIcon={<RefreshCw aria-hidden=\"true\" />}\n onClick={onRegenerate}\n >\n {regenerateLabel ??\n t('entitySummary.regenerate', {\n defaultValue: 'Regenerate',\n })}\n </Button>\n ) : null}\n {onClose ? (\n <IconButton\n icon={<X aria-hidden=\"true\" />}\n size=\"sm\"\n intent=\"ghost\"\n aria-label={t('common.close', { defaultValue: 'Close' })}\n onClick={onClose}\n />\n ) : null}\n </>\n ) : undefined\n }\n />\n\n {/* Body */}\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)] ds:p-[var(--spacing-md)]\">\n {entityName || generatedAtIso || (stats && stats.length > 0) ? (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\">\n {/* Identity — the entity's avatar + name, generated-at as the\n subtitle beneath. Without a name, the generated line stands\n alone. */}\n {entityName ? (\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-sm)]\">\n <Avatar\n name={entityName}\n src={entityAvatarSrc}\n size=\"sm\"\n className=\"ds:shrink-0\"\n />\n <div className=\"ds:flex ds:min-w-0 ds:flex-col ds:gap-[var(--spacing-2xs)]\">\n {entityHref ? (\n <Link\n href={entityHref}\n className=\"type-body-sm ds:font-semibold\"\n >\n {entityName}\n </Link>\n ) : (\n <span className=\"type-body-sm ds:font-semibold ds:text-[color:var(--foreground)]\">\n {entityName}\n </span>\n )}\n {generatedLine}\n </div>\n </div>\n ) : (\n generatedLine\n )}\n\n {/* Corpus stats — a compact KPI card: value over label, with a\n hairline divider between cells. */}\n {stats && stats.length > 0 ? (\n <Card variant=\"elevated\">\n {/* eslint-disable-next-line jsx-a11y/no-redundant-roles -- role=\"list\" restores list semantics that ds:list-none strips in Safari/VoiceOver */}\n <ul\n role=\"list\"\n className=\"ds:m-0 ds:flex ds:list-none ds:items-stretch\"\n >\n {stats.map((stat, index) => (\n <li\n key={`${stat.label}-${index}`}\n className={[\n 'ds:flex ds:flex-1 ds:flex-col ds:items-center ds:gap-[var(--spacing-2xs)] ds:p-[var(--spacing-sm)] ds:text-center',\n index > 0\n ? 'ds:border-s ds:border-[color:var(--card-border)]'\n : '',\n ]\n .filter(Boolean)\n .join(' ')}\n >\n <span className=\"type-metric ds:[--type-metric-size:var(--font-size-lg)] ds:text-[color:var(--foreground)]\">\n {stat.value}\n </span>\n <span className=\"type-meta ds:text-[color:var(--muted-foreground)]\">\n {stat.label}\n </span>\n </li>\n ))}\n </ul>\n </Card>\n ) : null}\n </div>\n ) : null}\n\n {/* Sections */}\n {sections.map((section) => (\n <Section key={section.id} section={section} />\n ))}\n\n {/* Disclaimer */}\n <p className=\"ds:m-0 type-meta ds:text-[color:var(--muted-foreground)]\">\n {disclaimer ??\n t('entitySummary.disclaimer', {\n defaultValue:\n 'Does not replace clinical judgement · verify accuracy before clinical use · BETA',\n })}\n </p>\n </div>\n </section>\n );\n },\n);\n\nfunction Section({ section }: { section: EntitySummarySection }): ReactNode {\n const headingId = useId();\n\n // The risk-flag section composes the kit's warning `Alert` primitive rather\n // than re-painting the warning tokens by hand — same border, surface, alert\n // glyph, and `--warning-foreground` treatment as every other warning in the\n // app. `live=\"off\"`: the brief is static content, not a just-occurred event,\n // so the items read in normal flow instead of being announced assertively.\n // A per-section `icon` overrides Alert's default alert triangle.\n if (section.tone === 'attention') {\n // The shared `--shadow-card` lifts the warning callout to the same\n // elevation as the surrounding section cards, so the stack reads as one\n // set of raised panels rather than a flat warning band among cards.\n return (\n <Alert\n variant=\"warning\"\n live=\"off\"\n icon={section.icon}\n className=\"ds:shadow-[var(--shadow-card)]\"\n >\n <Alert.Title as=\"h3\">{section.heading}</Alert.Title>\n <ul className={ITEMS_CLASSES}>\n {section.items.map((item, index) => (\n <li key={index}>{item}</li>\n ))}\n </ul>\n </Alert>\n );\n }\n\n // Default sections are raised cards — the scannable \"panel per topic\" stack,\n // matching platform's Sintesi IA. The card owns its own padding (so we drive\n // it from a single content div rather than Card.Header/Body) and is named by\n // its heading for assistive tech.\n return (\n <Card variant=\"elevated\" aria-labelledby={headingId}>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)] ds:p-[var(--spacing-md)]\">\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-xs)]\">\n {section.icon ? (\n <span aria-hidden=\"true\" className={MEDALLION_CLASSES}>\n {section.icon}\n </span>\n ) : null}\n <h3\n id={headingId}\n className=\"ds:m-0 type-eyebrow ds:text-[color:var(--foreground)]\"\n >\n {section.heading}\n </h3>\n </div>\n <ul className={`${ITEMS_CLASSES} ds:text-[color:var(--foreground)]`}>\n {section.items.map((item, index) => (\n <li key={index}>{item}</li>\n ))}\n </ul>\n </div>\n </Card>\n );\n}\n"],"names":["__iconNode","ContactRound","createLucideIcon","entitySummaryAgent","handle","MEDALLION_CLASSES","ITEMS_CLASSES","EntitySummary","forwardRef","id","title","entityName","entityHref","entityAvatarSrc","generatedAtIso","stats","sections","onRegenerate","regenerateLabel","onClose","disclaimer","locale","className","ariaLabel","rest","ref","t","useTranslation","headingId","useId","heading","sectionsRef","useRef","generatedAtRef","onRegenerateRef","onCloseRef","rootRef","useImperativeHandle","useMemo","s","_a","useAgentRegistration","generatedLine","jsxs","jsx","Clock","Timestamp","PanelHeader","Fragment","Button","RefreshCw","IconButton","X","Avatar","Link","Card","stat","index","section","Section","Alert","item"],"mappings":"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","x_google_ignoreList":[0]}
@@ -2,7 +2,7 @@ import { jsx as o, jsxs as E } from "react/jsx-runtime";
2
2
  import { forwardRef as k, useId as l } from "react";
3
3
  import { c } from "./index-D2ZczOXr.js";
4
4
  import { useTranslation as w } from "react-i18next";
5
- import { A as d } from "./alert-DdDpOi-P.js";
5
+ import { A as d } from "./alert-DjsJMGRE.js";
6
6
  const N = c(
7
7
  "ds:block ds:rounded-[var(--radius-md)] ds:text-start ds:focus-visible:outline-solid ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]"
8
8
  ), A = c(
@@ -98,4 +98,4 @@ j.displayName = "ErrorSummary";
98
98
  export {
99
99
  j as E
100
100
  };
101
- //# sourceMappingURL=error-summary-DrUfuKu1.js.map
101
+ //# sourceMappingURL=error-summary-PUE5Zxp9.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"error-summary-DrUfuKu1.js","sources":["../../src/components/error-summary/error-summary.tsx"],"sourcesContent":["import {\n forwardRef,\n useId,\n type HTMLAttributes,\n type MouseEvent,\n type ReactNode,\n} from 'react';\nimport { cva } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { Alert } from '../alert/alert';\n\n/* -------------------------------------------------------------------- */\n/* ErrorSummary: every problem with a form, in one place, at its top. */\n/* */\n/* After a refused submit the form moves focus here. The heading and the */\n/* list are read once, and each problem is a link that moves focus to */\n/* its field. The fields themselves stay quiet meanwhile (FormField's */\n/* `announceError`), so a long form no longer fires one alert per field. */\n/* It is not a live region: focus is what makes it heard. */\n/* -------------------------------------------------------------------- */\n\nconst errorSummaryVariants = cva(\n 'ds:block ds:rounded-[var(--radius-md)] ds:text-start ds:focus-visible:outline-solid ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]',\n);\n\n// The links take the alert's own text colour and an underline: the kit\n// Link's `--primary` is not tested against the error tint. Each is as tall\n// as a touch target, so stacked links never share one.\nconst itemLinkVariants = cva(\n 'type-body-sm ds:inline-flex ds:min-h-[var(--min-target-size)] ds:items-center ds:rounded-[var(--radius-sm)] ds:font-semibold ds:text-current ds:underline ds:underline-offset-4 ds:hover:no-underline ds:focus-visible:outline-solid ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]',\n);\n\nconst itemTextVariants = cva(\n 'type-body-sm ds:inline-flex ds:min-h-[var(--min-target-size)] ds:items-center',\n);\n\n/** One problem in the summary. */\nexport interface ErrorSummaryItem {\n /** What is wrong, already translated: the same words as the field's own\n * message, so the two read as one. */\n message: string;\n /**\n * The field's label, for a message that does not name its field, such\n * as \"This field is required.\": the item then reads \"First name: This\n * field is required.\". Pass it translated. Leave it out when the\n * message already says which field it is about.\n */\n label?: string;\n /**\n * Where the item's link leads: the `id` of the control, or of the\n * element around it, such as the `FormField`. For an element that cannot\n * take focus itself, the link moves focus to the control its label\n * names, else to the first control inside it. Without a `targetId` the\n * message shows as plain text: use that for a problem that belongs to no\n * single field.\n */\n targetId?: string;\n /** The field's name, emitted as `data-error-field` on the item. */\n field?: string;\n}\n\nexport interface ErrorSummaryProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title'\n> {\n /** The problems, in the order their fields appear. Nothing renders while\n * the list is empty. */\n errors: readonly ErrorSummaryItem[];\n /** Replaces the heading \"There is a problem\". Pass it translated. */\n title?: string;\n /** A line between the heading and the list. Pass it translated. */\n description?: ReactNode;\n /** The heading's level: one below the heading of the form it sits in.\n * Default 2. */\n headingLevel?: 2 | 3 | 4 | 5 | 6;\n /**\n * Ids of other text read with the summary when it takes focus, after the\n * description and before the list: a message about the whole form that\n * shows outside the summary, say. Keep that message quiet (no live\n * region) while the summary shows, or the two are read over each other.\n */\n 'aria-describedby'?: string;\n}\n\nconst FOCUSABLE =\n 'input:not([type=\"hidden\"]), select, textarea, button, a[href], [tabindex]';\n\n/** The control a link names: the element itself when it takes focus, else\n * the control its label names, else the first control inside it. */\nfunction controlIn(element: HTMLElement): HTMLElement {\n if (element.matches(FOCUSABLE)) return element;\n const named = element.querySelector('label')?.control;\n if (named instanceof HTMLElement) return named;\n return element.querySelector<HTMLElement>(FOCUSABLE) ?? element;\n}\n\n/** What to bring into view with a control: its whole field, so the label\n * shows with it, else its own label, else the control. */\nfunction viewAnchorFor(control: HTMLElement): Element {\n const field = control.closest('[data-component=\"form-field\"]');\n if (field) return field;\n const labels = (control as HTMLInputElement).labels;\n return labels && labels.length > 0 ? labels[0] : control;\n}\n\n/**\n * Lists every problem with a form after a refused submit, at the top of\n * the form. Move focus to it (its ref) each time a submit is refused.\n */\nexport const ErrorSummary = forwardRef<HTMLDivElement, ErrorSummaryProps>(\n (\n {\n errors,\n title,\n description,\n headingLevel = 2,\n className,\n 'aria-describedby': describedByProp,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation('ui');\n const titleId = useId();\n const descriptionId = useId();\n const listId = useId();\n\n if (errors.length === 0) return null;\n\n const Heading = `h${headingLevel}` as const;\n\n /** Moves focus to the field itself, without changing the address. */\n const moveTo = (event: MouseEvent<HTMLAnchorElement>, targetId: string) => {\n const root = event.currentTarget.getRootNode();\n const target =\n root instanceof Document || root instanceof ShadowRoot\n ? root.getElementById(targetId)\n : null;\n // Nothing to focus here: let the browser follow the address.\n if (!(target instanceof HTMLElement)) return;\n event.preventDefault();\n const control = controlIn(target);\n // No smooth scroll: the jump follows the user's own click, and a\n // reduced-motion user is never made to watch the page slide.\n viewAnchorFor(control).scrollIntoView({ block: 'center' });\n control.focus({ preventScroll: true });\n };\n\n return (\n <div\n ref={ref}\n {...rest}\n data-component=\"error-summary\"\n // Takes focus from script after a refused submit, never by Tab.\n tabIndex={-1}\n role=\"group\"\n aria-labelledby={titleId}\n // Focus reads the heading, then every problem, once.\n aria-describedby={[\n description ? descriptionId : undefined,\n describedByProp,\n listId,\n ]\n .filter(Boolean)\n .join(' ')}\n className={errorSummaryVariants({ className })}\n >\n {/* Not a live region: focus announces it. A live region as well\n would read it twice. */}\n <Alert variant=\"error\" announce=\"off\">\n <Alert.Title as={Heading} id={titleId}>\n {title ?? t('feedback.errorSummary.title')}\n </Alert.Title>\n {description ? (\n <Alert.Description id={descriptionId}>\n {description}\n </Alert.Description>\n ) : null}\n <ul\n id={listId}\n className=\"ds:m-0 ds:mt-[var(--spacing-xs)] ds:flex ds:list-none ds:flex-col ds:p-0\"\n >\n {errors.map((item, index) => {\n const text = item.label\n ? t('feedback.errorSummary.item', {\n label: item.label,\n message: item.message,\n })\n : item.message;\n return (\n <li\n key={item.field ?? item.targetId ?? index}\n data-error-field={item.field}\n >\n {item.targetId ? (\n <a\n href={`#${item.targetId}`}\n data-action=\"go-to-field\"\n onClick={(event) =>\n moveTo(event, item.targetId as string)\n }\n className={itemLinkVariants()}\n >\n {text}\n </a>\n ) : (\n <span className={itemTextVariants()}>{text}</span>\n )}\n </li>\n );\n })}\n </ul>\n </Alert>\n </div>\n );\n },\n);\n\nErrorSummary.displayName = 'ErrorSummary';\n"],"names":["errorSummaryVariants","cva","itemLinkVariants","itemTextVariants","FOCUSABLE","controlIn","element","named","_a","viewAnchorFor","control","field","labels","ErrorSummary","forwardRef","errors","title","description","headingLevel","className","describedByProp","rest","ref","t","useTranslation","titleId","useId","descriptionId","listId","Heading","moveTo","event","targetId","root","target","jsx","jsxs","Alert","item","index","text"],"mappings":"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"}
1
+ {"version":3,"file":"error-summary-PUE5Zxp9.js","sources":["../../src/components/error-summary/error-summary.tsx"],"sourcesContent":["import {\n forwardRef,\n useId,\n type HTMLAttributes,\n type MouseEvent,\n type ReactNode,\n} from 'react';\nimport { cva } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { Alert } from '../alert/alert';\n\n/* -------------------------------------------------------------------- */\n/* ErrorSummary: every problem with a form, in one place, at its top. */\n/* */\n/* After a refused submit the form moves focus here. The heading and the */\n/* list are read once, and each problem is a link that moves focus to */\n/* its field. The fields themselves stay quiet meanwhile (FormField's */\n/* `announceError`), so a long form no longer fires one alert per field. */\n/* It is not a live region: focus is what makes it heard. */\n/* -------------------------------------------------------------------- */\n\nconst errorSummaryVariants = cva(\n 'ds:block ds:rounded-[var(--radius-md)] ds:text-start ds:focus-visible:outline-solid ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]',\n);\n\n// The links take the alert's own text colour and an underline: the kit\n// Link's `--primary` is not tested against the error tint. Each is as tall\n// as a touch target, so stacked links never share one.\nconst itemLinkVariants = cva(\n 'type-body-sm ds:inline-flex ds:min-h-[var(--min-target-size)] ds:items-center ds:rounded-[var(--radius-sm)] ds:font-semibold ds:text-current ds:underline ds:underline-offset-4 ds:hover:no-underline ds:focus-visible:outline-solid ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]',\n);\n\nconst itemTextVariants = cva(\n 'type-body-sm ds:inline-flex ds:min-h-[var(--min-target-size)] ds:items-center',\n);\n\n/** One problem in the summary. */\nexport interface ErrorSummaryItem {\n /** What is wrong, already translated: the same words as the field's own\n * message, so the two read as one. */\n message: string;\n /**\n * The field's label, for a message that does not name its field, such\n * as \"This field is required.\": the item then reads \"First name: This\n * field is required.\". Pass it translated. Leave it out when the\n * message already says which field it is about.\n */\n label?: string;\n /**\n * Where the item's link leads: the `id` of the control, or of the\n * element around it, such as the `FormField`. For an element that cannot\n * take focus itself, the link moves focus to the control its label\n * names, else to the first control inside it. Without a `targetId` the\n * message shows as plain text: use that for a problem that belongs to no\n * single field.\n */\n targetId?: string;\n /** The field's name, emitted as `data-error-field` on the item. */\n field?: string;\n}\n\nexport interface ErrorSummaryProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title'\n> {\n /** The problems, in the order their fields appear. Nothing renders while\n * the list is empty. */\n errors: readonly ErrorSummaryItem[];\n /** Replaces the heading \"There is a problem\". Pass it translated. */\n title?: string;\n /** A line between the heading and the list. Pass it translated. */\n description?: ReactNode;\n /** The heading's level: one below the heading of the form it sits in.\n * Default 2. */\n headingLevel?: 2 | 3 | 4 | 5 | 6;\n /**\n * Ids of other text read with the summary when it takes focus, after the\n * description and before the list: a message about the whole form that\n * shows outside the summary, say. Keep that message quiet (no live\n * region) while the summary shows, or the two are read over each other.\n */\n 'aria-describedby'?: string;\n}\n\nconst FOCUSABLE =\n 'input:not([type=\"hidden\"]), select, textarea, button, a[href], [tabindex]';\n\n/** The control a link names: the element itself when it takes focus, else\n * the control its label names, else the first control inside it. */\nfunction controlIn(element: HTMLElement): HTMLElement {\n if (element.matches(FOCUSABLE)) return element;\n const named = element.querySelector('label')?.control;\n if (named instanceof HTMLElement) return named;\n return element.querySelector<HTMLElement>(FOCUSABLE) ?? element;\n}\n\n/** What to bring into view with a control: its whole field, so the label\n * shows with it, else its own label, else the control. */\nfunction viewAnchorFor(control: HTMLElement): Element {\n const field = control.closest('[data-component=\"form-field\"]');\n if (field) return field;\n const labels = (control as HTMLInputElement).labels;\n return labels && labels.length > 0 ? labels[0] : control;\n}\n\n/**\n * Lists every problem with a form after a refused submit, at the top of\n * the form. Move focus to it (its ref) each time a submit is refused.\n */\nexport const ErrorSummary = forwardRef<HTMLDivElement, ErrorSummaryProps>(\n (\n {\n errors,\n title,\n description,\n headingLevel = 2,\n className,\n 'aria-describedby': describedByProp,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation('ui');\n const titleId = useId();\n const descriptionId = useId();\n const listId = useId();\n\n if (errors.length === 0) return null;\n\n const Heading = `h${headingLevel}` as const;\n\n /** Moves focus to the field itself, without changing the address. */\n const moveTo = (event: MouseEvent<HTMLAnchorElement>, targetId: string) => {\n const root = event.currentTarget.getRootNode();\n const target =\n root instanceof Document || root instanceof ShadowRoot\n ? root.getElementById(targetId)\n : null;\n // Nothing to focus here: let the browser follow the address.\n if (!(target instanceof HTMLElement)) return;\n event.preventDefault();\n const control = controlIn(target);\n // No smooth scroll: the jump follows the user's own click, and a\n // reduced-motion user is never made to watch the page slide.\n viewAnchorFor(control).scrollIntoView({ block: 'center' });\n control.focus({ preventScroll: true });\n };\n\n return (\n <div\n ref={ref}\n {...rest}\n data-component=\"error-summary\"\n // Takes focus from script after a refused submit, never by Tab.\n tabIndex={-1}\n role=\"group\"\n aria-labelledby={titleId}\n // Focus reads the heading, then every problem, once.\n aria-describedby={[\n description ? descriptionId : undefined,\n describedByProp,\n listId,\n ]\n .filter(Boolean)\n .join(' ')}\n className={errorSummaryVariants({ className })}\n >\n {/* Not a live region: focus announces it. A live region as well\n would read it twice. */}\n <Alert variant=\"error\" announce=\"off\">\n <Alert.Title as={Heading} id={titleId}>\n {title ?? t('feedback.errorSummary.title')}\n </Alert.Title>\n {description ? (\n <Alert.Description id={descriptionId}>\n {description}\n </Alert.Description>\n ) : null}\n <ul\n id={listId}\n className=\"ds:m-0 ds:mt-[var(--spacing-xs)] ds:flex ds:list-none ds:flex-col ds:p-0\"\n >\n {errors.map((item, index) => {\n const text = item.label\n ? t('feedback.errorSummary.item', {\n label: item.label,\n message: item.message,\n })\n : item.message;\n return (\n <li\n key={item.field ?? item.targetId ?? index}\n data-error-field={item.field}\n >\n {item.targetId ? (\n <a\n href={`#${item.targetId}`}\n data-action=\"go-to-field\"\n onClick={(event) =>\n moveTo(event, item.targetId as string)\n }\n className={itemLinkVariants()}\n >\n {text}\n </a>\n ) : (\n <span className={itemTextVariants()}>{text}</span>\n )}\n </li>\n );\n })}\n </ul>\n </Alert>\n </div>\n );\n },\n);\n\nErrorSummary.displayName = 'ErrorSummary';\n"],"names":["errorSummaryVariants","cva","itemLinkVariants","itemTextVariants","FOCUSABLE","controlIn","element","named","_a","viewAnchorFor","control","field","labels","ErrorSummary","forwardRef","errors","title","description","headingLevel","className","describedByProp","rest","ref","t","useTranslation","titleId","useId","descriptionId","listId","Heading","moveTo","event","targetId","root","target","jsx","jsxs","Alert","item","index","text"],"mappings":"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"}
@@ -3,14 +3,14 @@ import { forwardRef as po, useId as f, useState as d, useRef as p, useEffect as
3
3
  import { useTranslation as vo } from "react-i18next";
4
4
  import { c as U } from "./index-D2ZczOXr.js";
5
5
  import { i as J, E as Fo } from "./email-input-C7THAwh2.js";
6
- import { A as P } from "./alert-DdDpOi-P.js";
6
+ import { A as P } from "./alert-DjsJMGRE.js";
7
7
  import { B as ho } from "./button-D-MQxsA_.js";
8
8
  import { C as K } from "./card-BA4Ig_kn.js";
9
- import { E as bo } from "./error-summary-DrUfuKu1.js";
9
+ import { E as bo } from "./error-summary-PUE5Zxp9.js";
10
10
  import { F as wo } from "./form-field-Ani72sVY.js";
11
11
  import { i as xo } from "./safe-href-5dbsfbQn.js";
12
12
  import { u as Eo } from "./use-state-backed-input-DhRQo6gM.js";
13
- import { u as Io, a as Ao, A as Po, b as yo, E as So, c as Ro, d as To } from "./auth-form-parts-Cmb41LSQ.js";
13
+ import { u as Io, a as Ao, A as Po, b as yo, E as So, c as Ro, d as To } from "./auth-form-parts-DwUTH6YN.js";
14
14
  import { u as Bo, a as Mo, b as No } from "./use-registration-submit-eWAECgUC.js";
15
15
  const Vo = ["email"], Do = U(
16
16
  "ds:mx-auto ds:w-full ds:max-w-[400px] ds:text-start ds:text-[var(--foreground)]"
@@ -231,4 +231,4 @@ export {
231
231
  Vo as F,
232
232
  _o as a
233
233
  };
234
- //# sourceMappingURL=forgot-password-form-CgZkEvZy.js.map
234
+ //# sourceMappingURL=forgot-password-form-CPOfRE0C.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"forgot-password-form-CgZkEvZy.js","sources":["../../src/patterns/forgot-password-form/forgot-password-form.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* ForgotPasswordForm — the card that asks for a password reset link. */\n/* */\n/* One email field and a button, then a confirmation. The confirmation */\n/* is vague on purpose: it never says whether an account exists for the */\n/* address, and never repeats the address. The host sends the email and */\n/* owns every server rule; the card checks what a browser can check, */\n/* then hands the email to `onSubmit` or posts a real form. */\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 { Alert } from '../../components/alert';\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 { 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 form's one field. */\nexport const FORGOT_PASSWORD_FORM_FIELDS = ['email'] as const;\n\nexport type ForgotPasswordFormField =\n (typeof FORGOT_PASSWORD_FORM_FIELDS)[number];\n\n/** What `onSubmit` receives once the email has passed its check. */\nexport interface ForgotPasswordFormValues {\n email: string;\n}\n\n/** The email to show when the form opens. */\nexport type ForgotPasswordFormInitialValues = Partial<ForgotPasswordFormValues>;\n\n/** Messages from the server, already translated: one for the email,\n * plus `form` for a message about the whole form, such as \"too many\n * requests, try again later\". */\nexport type ForgotPasswordFormErrors = Partial<\n Record<ForgotPasswordFormField | 'form', string>\n>;\n\n/** The host's own form field name, for a native form post. It is needed:\n * a field without a name is not posted. */\nexport type ForgotPasswordFormFieldNames = Record<\n ForgotPasswordFormField,\n string\n>;\n\nexport interface ForgotPasswordFormProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title' | 'onSubmit'\n> {\n /** Replaces the heading \"Reset your password\". Pass it translated. */\n title?: string;\n /**\n * Replaces the line under the heading (\"Enter the email linked to your\n * account and we will send a reset link.\"). Pass it translated.\n */\n description?: string;\n /**\n * The heading's level. Default 1: this 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.\n */\n showLogo?: boolean;\n /** How the mark arrives when the card opens. Default `assemble`. It\n * plays once and rests. */\n logoMotion?: BrandMarkMotion;\n /**\n * Host content between the heading and the field, such as a notice. It\n * sits outside the form, so nothing in it is posted.\n */\n children?: ReactNode;\n /** The email to show when the form opens. Read once. */\n initialValues?: ForgotPasswordFormInitialValues;\n /**\n * Messages from the server, already translated. The email's message\n * shows on the field until the user changes it; `form` shows above the\n * field. When messages arrive, focus moves to the error summary at the\n * top of the form when the email has a message, or to the message\n * above the field when only that says something.\n *\n * Do not use a message to say that no account has the address: that\n * tells a stranger who has an account. Show `sent` for every address.\n */\n errors?: ForgotPasswordFormErrors;\n /**\n * Shows the confirmation in place of the form: \"Check your inbox\". Set\n * it once the request has been accepted, whether or not an account\n * exists for the address. The confirmation says \"if an account exists\"\n * and never shows the address.\n */\n sent?: boolean;\n /**\n * Adds \"Back to sign in\" under the card's content. A path on the same\n * site, or an address written in full. Anything else shows no link.\n */\n loginHref?: string;\n /** Replaces the button's \"Send reset link\". Pass it translated. */\n submitLabel?: string;\n /**\n * True while the host sends the email: 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 request.\n */\n submitting?: boolean;\n /** For React hosts: called with the email once it has passed its\n * check. Not called when `fieldNames` is set. */\n onSubmit?: (values: ForgotPasswordFormValues) => void;\n /**\n * For a native form post: the host's own field name, e.g.\n * `{ email: 'email' }`. When set, the input carries that name and the\n * browser posts the form.\n */\n fieldNames?: ForgotPasswordFormFieldNames;\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\n * token. Not used with `onSubmit`. A name that is also the one in\n * `fieldNames` is left out: the form's own field wins.\n */\n hiddenFields?: Readonly<Record<string, string>>;\n}\n\n/* -------------------------------------------------------------------- */\n/* Styles */\n/* -------------------------------------------------------------------- */\n\n// 400px at most, in px: a host whose root font size is not 16px would\n// stretch or squeeze a width in rem.\nconst forgotPasswordFormVariants = cva(\n 'ds:mx-auto ds:w-full ds:max-w-[400px] ds:text-start ds:text-[var(--foreground)]',\n);\n\n// The confirmation takes focus when it appears, so it shows a ring.\nconst confirmationVariants = cva(\n 'ds:focus-visible:outline-solid ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]',\n);\n\n/* -------------------------------------------------------------------- */\n/* Helpers */\n/* -------------------------------------------------------------------- */\n\ntype FocusTarget = 'email' | 'form';\n\n/** The form's one field, as the error summary lists it. */\nconst FIELDS = ['email'] as const;\n\n/** The confirmation's heading, by the level of the card's own. */\nconst CONFIRMATION_HEADING = { 1: 'h2', 2: 'h3', 3: 'h4' } as const;\n\nconst hasMessage = (errors: ForgotPasswordFormErrors): boolean =>\n Object.values(errors).some(Boolean);\n\nconst focusTargetFor = (\n messages: ForgotPasswordFormErrors,\n): FocusTarget | undefined =>\n messages.email ? 'email' : messages.form ? 'form' : undefined;\n\n/* -------------------------------------------------------------------- */\n/* Component */\n/* -------------------------------------------------------------------- */\n\n/**\n * The card that asks for a password reset link. Mount it on the\n * forgot-password page, inside `SignedOutFrame` or the host's own chrome.\n */\nexport const ForgotPasswordForm = forwardRef<\n HTMLDivElement,\n ForgotPasswordFormProps\n>(\n (\n {\n title,\n description,\n headingLevel = 1,\n showLogo = true,\n logoMotion = 'assemble',\n children,\n initialValues,\n errors,\n sent = false,\n loginHref,\n submitLabel,\n submitting = false,\n onSubmit,\n fieldNames,\n action,\n hiddenFields,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation('ui');\n const titleId = useId();\n const sentTitleId = useId();\n const sentDescriptionId = useId();\n const emailFieldId = useId();\n const formMessageId = useId();\n const native = fieldNames !== undefined;\n\n /* ------------------------------ State ----------------------------- */\n\n const [email, setEmail] = useState(() => initialValues?.email ?? '');\n // True once the user has filled in the field and left it. An email\n // that arrives filled in counts: a bad one is then flagged at once,\n // with its message. An empty field is not flagged for being passed\n // through; its message waits for a submit.\n const [touched, setTouched] = useState(() => Boolean(initialValues?.email));\n const [submitted, setSubmitted] = useState(false);\n const [serverErrors, setServerErrors] = useState<ForgotPasswordFormErrors>(\n () => errors ?? {},\n );\n\n const emailInput = 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 * What the input holds now. Read from the input, not from state: a\n * browser can fill the field without telling React, and the form must\n * not refuse a field that is visibly filled.\n */\n const held = (): string => {\n const value = emailInput.current?.value ?? email;\n if (value !== email) setEmail(value);\n return value;\n };\n\n /* ---------------------------- Validation -------------------------- */\n\n const invalid = !isEmail(email);\n // The message shows once the user has left the field or tried to\n // submit.\n const ownError =\n (submitted || touched) && invalid\n ? email === ''\n ? t('forgotPasswordForm.emailRequired')\n : t('inputs.email.invalid')\n : undefined;\n const emailError = serverErrors.email ?? ownError;\n\n /* ------------------------------ Focus ----------------------------- */\n\n const formMessage = useRef<HTMLDivElement>(null);\n const confirmation = useRef<HTMLDivElement>(null);\n\n // The email's 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 [openedWithEmailMessage] = useState(() => Boolean(errors?.email));\n const summary = useErrorSummary(\n FIELDS,\n () => emailError,\n openedWithEmailMessage,\n );\n // The message about the whole form: quiet while the summary lists the\n // email, because the summary reads it then.\n const formMessageAnnounce = useFormMessageAnnounce(\n serverErrors.form,\n summary.listed.length > 0,\n );\n\n // Any other place focus goes: the message above the field, when only\n // it says something. A new object for each request, so asking twice\n // for the same place moves focus twice.\n const [focusRequest, setFocusRequest] = useState<{\n target: FocusTarget;\n } | null>(() => {\n const target = openedWithEmailMessage\n ? undefined\n : focusTargetFor(errors ?? {});\n return target ? { target } : null;\n });\n useEffect(() => {\n if (!focusRequest) return;\n (focusRequest.target === 'email'\n ? emailInput\n : formMessage\n ).current?.focus();\n }, [focusRequest]);\n\n // The form, and the button that had focus, are gone once the\n // confirmation shows. Move focus to it, so a keyboard or screen\n // reader user lands on what happened. Not on a page that opens\n // already sent: that one is read from the top like any other.\n const wasSent = useRef(sent);\n useEffect(() => {\n if (sent && !wasSent.current) confirmation.current?.focus();\n wasSent.current = sent;\n }, [sent]);\n\n /* ------------------------------ Submit ---------------------------- */\n\n const submit = useRegistrationSubmit<'email'>({\n errors,\n submitting,\n native,\n captcha: false,\n firstProblem: () => (isEmail(held()) ? undefined : 'email'),\n onRefused: () => {\n setSubmitted(true);\n summary.open(() => setFocusRequest({ target: 'email' }));\n },\n // Only a React host gets here: a native post is the browser's own.\n deliver: () => onSubmit?.({ email: held() }),\n onAnswer: (answer) => {\n const next: ForgotPasswordFormErrors = answer;\n setServerErrors(next);\n if (!hasMessage(next)) return;\n setSubmitted(false);\n // The summary when the email has a message, else the message above.\n summary.open(() => {\n const target = focusTargetFor(next);\n if (target) setFocusRequest({ target });\n });\n },\n });\n const { busy } = submit;\n\n /* ------------------------------ Render ---------------------------- */\n\n const formAction = useFormAction('ForgotPasswordForm', native, action);\n const loginLink =\n loginHref && isSafeHref(loginHref) ? loginHref : undefined;\n const extraFields = useExtraFields(\n 'ForgotPasswordForm',\n native,\n fieldNames,\n hiddenFields,\n );\n\n return (\n <div\n ref={ref}\n {...rest}\n data-component=\"forgot-password-form\"\n data-state={sent ? 'sent' : 'asking'}\n className={forgotPasswordFormVariants({ className })}\n >\n <Card variant=\"elevated\">\n <Card.Body className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]\">\n <AuthCardHeading\n id={titleId}\n title={title ?? t('forgotPasswordForm.title')}\n // The instruction belongs to the form: once it is sent there\n // is nothing left to enter.\n description={\n sent\n ? undefined\n : (description ?? t('forgotPasswordForm.description'))\n }\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 {sent ? (\n // Named and described, so a screen reader that lands here\n // reads the whole confirmation, not only \"status\".\n <Alert\n ref={confirmation}\n variant=\"success\"\n tabIndex={-1}\n aria-labelledby={sentTitleId}\n aria-describedby={sentDescriptionId}\n data-slot=\"confirmation\"\n className={confirmationVariants()}\n >\n {/* One level under the card's own heading. */}\n <Alert.Title\n id={sentTitleId}\n as={CONFIRMATION_HEADING[headingLevel]}\n >\n {t('forgotPasswordForm.sentTitle')}\n </Alert.Title>\n <Alert.Description id={sentDescriptionId}>\n {t('forgotPasswordForm.sentDescription')}\n </Alert.Description>\n </Alert>\n ) : (\n <>\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={\n serverErrors.form ? formMessageId : undefined\n }\n errors={summary.listed.map((field) => ({\n field,\n message: emailError ?? '',\n targetId: emailFieldId,\n }))}\n />\n\n {serverErrors.form && (\n <AuthFormMessage\n ref={formMessage}\n id={formMessageId}\n announce={formMessageAnnounce}\n >\n {serverErrors.form}\n </AuthFormMessage>\n )}\n\n <FormField\n id={emailFieldId}\n label={t('forgotPasswordForm.email')}\n required\n error={emailError}\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. */}\n <EmailInput\n ref={emailField.ref}\n name={native ? fieldNames.email : 'email'}\n disableBuiltInValidation\n onChange={emailField.quiet((event) => {\n setEmail(event.target.value);\n // The server's message was about the old value.\n setServerErrors((previous) =>\n previous.email\n ? { ...previous, email: undefined }\n : previous,\n );\n })}\n onBlur={(event) => {\n // Whatever a browser filled in without saying so.\n setEmail(event.target.value);\n if (event.target.value !== '') setTouched(true);\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('forgotPasswordForm.submit')}\n </Button>\n </form>\n {/* Outside the form: a screen reader may hold back what\n changes inside a busy region. */}\n <AuthFormStatus busy={busy} />\n </>\n )}\n\n {loginLink && (\n <AuthTextLink href={loginLink} action=\"login\">\n {t('forgotPasswordForm.back')}\n </AuthTextLink>\n )}\n </Card.Body>\n </Card>\n </div>\n );\n },\n);\nForgotPasswordForm.displayName = 'ForgotPasswordForm';\n"],"names":["FORGOT_PASSWORD_FORM_FIELDS","forgotPasswordFormVariants","cva","confirmationVariants","FIELDS","CONFIRMATION_HEADING","hasMessage","errors","focusTargetFor","messages","ForgotPasswordForm","forwardRef","title","description","headingLevel","showLogo","logoMotion","children","initialValues","sent","loginHref","submitLabel","submitting","onSubmit","fieldNames","action","hiddenFields","className","rest","ref","useTranslation","titleId","useId","sentTitleId","sentDescriptionId","emailFieldId","formMessageId","native","email","setEmail","useState","touched","setTouched","submitted","setSubmitted","serverErrors","setServerErrors","emailInput","useRef","emailField","useStateBackedInput","held","value","_a","invalid","isEmail","ownError","emailError","formMessage","confirmation","openedWithEmailMessage","summary","useErrorSummary","formMessageAnnounce","useFormMessageAnnounce","focusRequest","setFocusRequest","target","useEffect","wasSent","submit","useRegistrationSubmit","answer","next","busy","formAction","useFormAction","loginLink","isSafeHref","extraFields","useExtraFields","jsx","Card","AuthCardHeading","jsxs","Alert","Fragment","ErrorSummary","field","AuthFormMessage","FormField","EmailInput","event","previous","ExtraFields","Button","AuthFormStatus","AuthTextLink"],"mappings":"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"}
1
+ {"version":3,"file":"forgot-password-form-CPOfRE0C.js","sources":["../../src/patterns/forgot-password-form/forgot-password-form.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* ForgotPasswordForm — the card that asks for a password reset link. */\n/* */\n/* One email field and a button, then a confirmation. The confirmation */\n/* is vague on purpose: it never says whether an account exists for the */\n/* address, and never repeats the address. The host sends the email and */\n/* owns every server rule; the card checks what a browser can check, */\n/* then hands the email to `onSubmit` or posts a real form. */\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 { Alert } from '../../components/alert';\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 { 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 form's one field. */\nexport const FORGOT_PASSWORD_FORM_FIELDS = ['email'] as const;\n\nexport type ForgotPasswordFormField =\n (typeof FORGOT_PASSWORD_FORM_FIELDS)[number];\n\n/** What `onSubmit` receives once the email has passed its check. */\nexport interface ForgotPasswordFormValues {\n email: string;\n}\n\n/** The email to show when the form opens. */\nexport type ForgotPasswordFormInitialValues = Partial<ForgotPasswordFormValues>;\n\n/** Messages from the server, already translated: one for the email,\n * plus `form` for a message about the whole form, such as \"too many\n * requests, try again later\". */\nexport type ForgotPasswordFormErrors = Partial<\n Record<ForgotPasswordFormField | 'form', string>\n>;\n\n/** The host's own form field name, for a native form post. It is needed:\n * a field without a name is not posted. */\nexport type ForgotPasswordFormFieldNames = Record<\n ForgotPasswordFormField,\n string\n>;\n\nexport interface ForgotPasswordFormProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title' | 'onSubmit'\n> {\n /** Replaces the heading \"Reset your password\". Pass it translated. */\n title?: string;\n /**\n * Replaces the line under the heading (\"Enter the email linked to your\n * account and we will send a reset link.\"). Pass it translated.\n */\n description?: string;\n /**\n * The heading's level. Default 1: this 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.\n */\n showLogo?: boolean;\n /** How the mark arrives when the card opens. Default `assemble`. It\n * plays once and rests. */\n logoMotion?: BrandMarkMotion;\n /**\n * Host content between the heading and the field, such as a notice. It\n * sits outside the form, so nothing in it is posted.\n */\n children?: ReactNode;\n /** The email to show when the form opens. Read once. */\n initialValues?: ForgotPasswordFormInitialValues;\n /**\n * Messages from the server, already translated. The email's message\n * shows on the field until the user changes it; `form` shows above the\n * field. When messages arrive, focus moves to the error summary at the\n * top of the form when the email has a message, or to the message\n * above the field when only that says something.\n *\n * Do not use a message to say that no account has the address: that\n * tells a stranger who has an account. Show `sent` for every address.\n */\n errors?: ForgotPasswordFormErrors;\n /**\n * Shows the confirmation in place of the form: \"Check your inbox\". Set\n * it once the request has been accepted, whether or not an account\n * exists for the address. The confirmation says \"if an account exists\"\n * and never shows the address.\n */\n sent?: boolean;\n /**\n * Adds \"Back to sign in\" under the card's content. A path on the same\n * site, or an address written in full. Anything else shows no link.\n */\n loginHref?: string;\n /** Replaces the button's \"Send reset link\". Pass it translated. */\n submitLabel?: string;\n /**\n * True while the host sends the email: 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 request.\n */\n submitting?: boolean;\n /** For React hosts: called with the email once it has passed its\n * check. Not called when `fieldNames` is set. */\n onSubmit?: (values: ForgotPasswordFormValues) => void;\n /**\n * For a native form post: the host's own field name, e.g.\n * `{ email: 'email' }`. When set, the input carries that name and the\n * browser posts the form.\n */\n fieldNames?: ForgotPasswordFormFieldNames;\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\n * token. Not used with `onSubmit`. A name that is also the one in\n * `fieldNames` is left out: the form's own field wins.\n */\n hiddenFields?: Readonly<Record<string, string>>;\n}\n\n/* -------------------------------------------------------------------- */\n/* Styles */\n/* -------------------------------------------------------------------- */\n\n// 400px at most, in px: a host whose root font size is not 16px would\n// stretch or squeeze a width in rem.\nconst forgotPasswordFormVariants = cva(\n 'ds:mx-auto ds:w-full ds:max-w-[400px] ds:text-start ds:text-[var(--foreground)]',\n);\n\n// The confirmation takes focus when it appears, so it shows a ring.\nconst confirmationVariants = cva(\n 'ds:focus-visible:outline-solid ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]',\n);\n\n/* -------------------------------------------------------------------- */\n/* Helpers */\n/* -------------------------------------------------------------------- */\n\ntype FocusTarget = 'email' | 'form';\n\n/** The form's one field, as the error summary lists it. */\nconst FIELDS = ['email'] as const;\n\n/** The confirmation's heading, by the level of the card's own. */\nconst CONFIRMATION_HEADING = { 1: 'h2', 2: 'h3', 3: 'h4' } as const;\n\nconst hasMessage = (errors: ForgotPasswordFormErrors): boolean =>\n Object.values(errors).some(Boolean);\n\nconst focusTargetFor = (\n messages: ForgotPasswordFormErrors,\n): FocusTarget | undefined =>\n messages.email ? 'email' : messages.form ? 'form' : undefined;\n\n/* -------------------------------------------------------------------- */\n/* Component */\n/* -------------------------------------------------------------------- */\n\n/**\n * The card that asks for a password reset link. Mount it on the\n * forgot-password page, inside `SignedOutFrame` or the host's own chrome.\n */\nexport const ForgotPasswordForm = forwardRef<\n HTMLDivElement,\n ForgotPasswordFormProps\n>(\n (\n {\n title,\n description,\n headingLevel = 1,\n showLogo = true,\n logoMotion = 'assemble',\n children,\n initialValues,\n errors,\n sent = false,\n loginHref,\n submitLabel,\n submitting = false,\n onSubmit,\n fieldNames,\n action,\n hiddenFields,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation('ui');\n const titleId = useId();\n const sentTitleId = useId();\n const sentDescriptionId = useId();\n const emailFieldId = useId();\n const formMessageId = useId();\n const native = fieldNames !== undefined;\n\n /* ------------------------------ State ----------------------------- */\n\n const [email, setEmail] = useState(() => initialValues?.email ?? '');\n // True once the user has filled in the field and left it. An email\n // that arrives filled in counts: a bad one is then flagged at once,\n // with its message. An empty field is not flagged for being passed\n // through; its message waits for a submit.\n const [touched, setTouched] = useState(() => Boolean(initialValues?.email));\n const [submitted, setSubmitted] = useState(false);\n const [serverErrors, setServerErrors] = useState<ForgotPasswordFormErrors>(\n () => errors ?? {},\n );\n\n const emailInput = 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 * What the input holds now. Read from the input, not from state: a\n * browser can fill the field without telling React, and the form must\n * not refuse a field that is visibly filled.\n */\n const held = (): string => {\n const value = emailInput.current?.value ?? email;\n if (value !== email) setEmail(value);\n return value;\n };\n\n /* ---------------------------- Validation -------------------------- */\n\n const invalid = !isEmail(email);\n // The message shows once the user has left the field or tried to\n // submit.\n const ownError =\n (submitted || touched) && invalid\n ? email === ''\n ? t('forgotPasswordForm.emailRequired')\n : t('inputs.email.invalid')\n : undefined;\n const emailError = serverErrors.email ?? ownError;\n\n /* ------------------------------ Focus ----------------------------- */\n\n const formMessage = useRef<HTMLDivElement>(null);\n const confirmation = useRef<HTMLDivElement>(null);\n\n // The email's 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 [openedWithEmailMessage] = useState(() => Boolean(errors?.email));\n const summary = useErrorSummary(\n FIELDS,\n () => emailError,\n openedWithEmailMessage,\n );\n // The message about the whole form: quiet while the summary lists the\n // email, because the summary reads it then.\n const formMessageAnnounce = useFormMessageAnnounce(\n serverErrors.form,\n summary.listed.length > 0,\n );\n\n // Any other place focus goes: the message above the field, when only\n // it says something. A new object for each request, so asking twice\n // for the same place moves focus twice.\n const [focusRequest, setFocusRequest] = useState<{\n target: FocusTarget;\n } | null>(() => {\n const target = openedWithEmailMessage\n ? undefined\n : focusTargetFor(errors ?? {});\n return target ? { target } : null;\n });\n useEffect(() => {\n if (!focusRequest) return;\n (focusRequest.target === 'email'\n ? emailInput\n : formMessage\n ).current?.focus();\n }, [focusRequest]);\n\n // The form, and the button that had focus, are gone once the\n // confirmation shows. Move focus to it, so a keyboard or screen\n // reader user lands on what happened. Not on a page that opens\n // already sent: that one is read from the top like any other.\n const wasSent = useRef(sent);\n useEffect(() => {\n if (sent && !wasSent.current) confirmation.current?.focus();\n wasSent.current = sent;\n }, [sent]);\n\n /* ------------------------------ Submit ---------------------------- */\n\n const submit = useRegistrationSubmit<'email'>({\n errors,\n submitting,\n native,\n captcha: false,\n firstProblem: () => (isEmail(held()) ? undefined : 'email'),\n onRefused: () => {\n setSubmitted(true);\n summary.open(() => setFocusRequest({ target: 'email' }));\n },\n // Only a React host gets here: a native post is the browser's own.\n deliver: () => onSubmit?.({ email: held() }),\n onAnswer: (answer) => {\n const next: ForgotPasswordFormErrors = answer;\n setServerErrors(next);\n if (!hasMessage(next)) return;\n setSubmitted(false);\n // The summary when the email has a message, else the message above.\n summary.open(() => {\n const target = focusTargetFor(next);\n if (target) setFocusRequest({ target });\n });\n },\n });\n const { busy } = submit;\n\n /* ------------------------------ Render ---------------------------- */\n\n const formAction = useFormAction('ForgotPasswordForm', native, action);\n const loginLink =\n loginHref && isSafeHref(loginHref) ? loginHref : undefined;\n const extraFields = useExtraFields(\n 'ForgotPasswordForm',\n native,\n fieldNames,\n hiddenFields,\n );\n\n return (\n <div\n ref={ref}\n {...rest}\n data-component=\"forgot-password-form\"\n data-state={sent ? 'sent' : 'asking'}\n className={forgotPasswordFormVariants({ className })}\n >\n <Card variant=\"elevated\">\n <Card.Body className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]\">\n <AuthCardHeading\n id={titleId}\n title={title ?? t('forgotPasswordForm.title')}\n // The instruction belongs to the form: once it is sent there\n // is nothing left to enter.\n description={\n sent\n ? undefined\n : (description ?? t('forgotPasswordForm.description'))\n }\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 {sent ? (\n // Named and described, so a screen reader that lands here\n // reads the whole confirmation, not only \"status\".\n <Alert\n ref={confirmation}\n variant=\"success\"\n tabIndex={-1}\n aria-labelledby={sentTitleId}\n aria-describedby={sentDescriptionId}\n data-slot=\"confirmation\"\n className={confirmationVariants()}\n >\n {/* One level under the card's own heading. */}\n <Alert.Title\n id={sentTitleId}\n as={CONFIRMATION_HEADING[headingLevel]}\n >\n {t('forgotPasswordForm.sentTitle')}\n </Alert.Title>\n <Alert.Description id={sentDescriptionId}>\n {t('forgotPasswordForm.sentDescription')}\n </Alert.Description>\n </Alert>\n ) : (\n <>\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={\n serverErrors.form ? formMessageId : undefined\n }\n errors={summary.listed.map((field) => ({\n field,\n message: emailError ?? '',\n targetId: emailFieldId,\n }))}\n />\n\n {serverErrors.form && (\n <AuthFormMessage\n ref={formMessage}\n id={formMessageId}\n announce={formMessageAnnounce}\n >\n {serverErrors.form}\n </AuthFormMessage>\n )}\n\n <FormField\n id={emailFieldId}\n label={t('forgotPasswordForm.email')}\n required\n error={emailError}\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. */}\n <EmailInput\n ref={emailField.ref}\n name={native ? fieldNames.email : 'email'}\n disableBuiltInValidation\n onChange={emailField.quiet((event) => {\n setEmail(event.target.value);\n // The server's message was about the old value.\n setServerErrors((previous) =>\n previous.email\n ? { ...previous, email: undefined }\n : previous,\n );\n })}\n onBlur={(event) => {\n // Whatever a browser filled in without saying so.\n setEmail(event.target.value);\n if (event.target.value !== '') setTouched(true);\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('forgotPasswordForm.submit')}\n </Button>\n </form>\n {/* Outside the form: a screen reader may hold back what\n changes inside a busy region. */}\n <AuthFormStatus busy={busy} />\n </>\n )}\n\n {loginLink && (\n <AuthTextLink href={loginLink} action=\"login\">\n {t('forgotPasswordForm.back')}\n </AuthTextLink>\n )}\n </Card.Body>\n </Card>\n </div>\n );\n },\n);\nForgotPasswordForm.displayName = 'ForgotPasswordForm';\n"],"names":["FORGOT_PASSWORD_FORM_FIELDS","forgotPasswordFormVariants","cva","confirmationVariants","FIELDS","CONFIRMATION_HEADING","hasMessage","errors","focusTargetFor","messages","ForgotPasswordForm","forwardRef","title","description","headingLevel","showLogo","logoMotion","children","initialValues","sent","loginHref","submitLabel","submitting","onSubmit","fieldNames","action","hiddenFields","className","rest","ref","useTranslation","titleId","useId","sentTitleId","sentDescriptionId","emailFieldId","formMessageId","native","email","setEmail","useState","touched","setTouched","submitted","setSubmitted","serverErrors","setServerErrors","emailInput","useRef","emailField","useStateBackedInput","held","value","_a","invalid","isEmail","ownError","emailError","formMessage","confirmation","openedWithEmailMessage","summary","useErrorSummary","formMessageAnnounce","useFormMessageAnnounce","focusRequest","setFocusRequest","target","useEffect","wasSent","submit","useRegistrationSubmit","answer","next","busy","formAction","useFormAction","loginLink","isSafeHref","extraFields","useExtraFields","jsx","Card","AuthCardHeading","jsxs","Alert","Fragment","ErrorSummary","field","AuthFormMessage","FormField","EmailInput","event","previous","ExtraFields","Button","AuthFormStatus","AuthTextLink"],"mappings":"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"}
@@ -9,7 +9,7 @@ import { u as $ } from "./use-select-on-arrow-Cr4iBuW5.js";
9
9
  import { u as ee } from "./registry-DvAUVLHh.js";
10
10
  import { B as R } from "./button-D-MQxsA_.js";
11
11
  import { P as ae } from "./progress-D_g_Sl2g.js";
12
- import { f as se } from "./payment-form-C3dGY7v4.js";
12
+ import { f as se } from "./payment-form-XKtinW8r.js";
13
13
  import { T as re } from "./triangle-alert-CBnO6nq_.js";
14
14
  import { C as te } from "./check-DrFSN__t.js";
15
15
  const de = {
@@ -282,4 +282,4 @@ export {
282
282
  le as F,
283
283
  de as f
284
284
  };
285
- //# sourceMappingURL=freemium-paywall-DKJHRFpp.js.map
285
+ //# sourceMappingURL=freemium-paywall-6DO7noHX.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"freemium-paywall-DKJHRFpp.js","sources":["../../src/components/freemium-paywall/freemium-paywall.agent.ts","../../src/components/freemium-paywall/freemium-paywall.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — FreemiumPaywall. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { FreemiumPaywallHandle } from './freemium-paywall';\n\nexport const freemiumPaywallAgent: AgentAdapter<FreemiumPaywallHandle> = {\n id: 'freemium-paywall',\n capabilities: ['submit', 'dismiss'],\n state: {\n isOpen: {\n type: 'boolean',\n descriptionKey: 'ui.agent.freemiumPaywall.state.isOpen',\n description: 'True when the paywall dialog is open.',\n read: (handle) => handle.isOpen(),\n },\n selectedPlanId: {\n type: 'string | null',\n descriptionKey: 'ui.agent.freemiumPaywall.state.selectedPlanId',\n description: 'Opaque id of the currently-selected plan.',\n read: (handle) => handle.getSelectedPlanId(),\n },\n },\n actions: {\n upgrade: {\n safety: 'write',\n // Backs the `submit` capability: commits the current plan selection\n // to the host's upgrade flow (checkout / billing).\n openWorld: true,\n descriptionKey: 'ui.agent.freemiumPaywall.actions.upgrade',\n description: 'Fire onUpgrade with the currently-selected plan id.',\n errors: ['No plan selected — upgrade is a no-op until a plan is chosen.'],\n invoke: (handle) => {\n handle.upgrade();\n },\n },\n dismiss: {\n safety: 'destructive',\n // Backs the `dismiss` capability.\n descriptionKey: 'ui.agent.freemiumPaywall.actions.dismiss',\n description:\n 'Dismiss the paywall via the Not Now action. Irreversible from the same UI.',\n invoke: (handle) => {\n handle.dismiss();\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'freemium-paywall',\n description: 'Marks the FreemiumPaywall dialog content.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n },\n};\n","import {\n forwardRef,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n type ReactNode,\n} from 'react';\nimport * as RadixAlertDialog from '@radix-ui/react-alert-dialog';\nimport * as RadixRadioGroup from '@radix-ui/react-radio-group';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { useControllableState } from '../../hooks/use-controllable-state';\nimport { useSelectOnArrow } from '../_shared/use-select-on-arrow';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { freemiumPaywallAgent } from './freemium-paywall.agent';\nimport { TriangleAlert, Check } from 'lucide-react';\nimport { Button } from '../button';\nimport { Progress } from '../progress';\nimport { formatPaymentAmount } from '../payment-form';\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\nexport interface FreemiumPlan {\n id: string;\n /** Translation key for the plan name (e.g. `app.paywall.plans.pro`). */\n nameKey: string;\n /** Amount in minor units (cents, centesimi). */\n priceAmount: number;\n /** ISO 4217 currency code. */\n currency: string;\n /** Translation key for the cadence suffix (e.g. `app.paywall.period.month`). */\n perKey?: string;\n /** Per-line feature translation keys. */\n featuresKeys?: string[];\n}\n\nconst contentVariants = cva(\n [\n 'ds:fixed ds:z-[var(--z-modal)] ds:overflow-auto',\n 'ds:bg-[color:var(--popover)] ds:text-[color:var(--popover-foreground)]',\n 'ds:border ds:border-border ds:shadow-[var(--shadow-lg)]',\n // forced-colors: keep the dialog boundary visible under Windows High\n // Contrast, where `var(--border)` may collapse to system colours.\n 'ds:forced-colors:border-[CanvasText]',\n 'ds:focus:outline-none',\n ].join(' '),\n {\n variants: {\n layout: {\n centered:\n 'ds:start-1/2 ds:top-1/2 ds:-translate-x-1/2 ds:-translate-y-1/2 ds:rtl:translate-x-1/2 ds:rounded-[var(--radius-md)] ds:max-w-[var(--dialog-width-xl)] ds:w-[calc(100vw-2rem)] ds:max-h-[90vh]',\n sheet:\n // `inset-x-0` compiles to logical `inset-inline`; the\n // `inset-inline-0` spelling compiled to nothing.\n 'ds:inset-x-0 ds:bottom-0 ds:w-full ds:max-h-[90vh] ds:rounded-t-[var(--radius-md)]',\n },\n density: {\n compact:\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:pt-[var(--spacing-md)] ds:pb-[var(--spacing-md)]',\n full: 'ds:ps-[var(--spacing-lg)] ds:pe-[var(--spacing-lg)] ds:pt-[var(--spacing-lg)] ds:pb-[var(--spacing-lg)]',\n },\n },\n defaultVariants: { layout: 'centered', density: 'full' },\n },\n);\n\n/** Curated imperative handle for agent / external automation. */\nexport interface FreemiumPaywallHandle {\n isOpen: () => boolean;\n getSelectedPlanId: () => string | null;\n upgrade: () => void;\n dismiss: () => void;\n}\n\nexport interface FreemiumPaywallProps extends VariantProps<\n typeof contentVariants\n> {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n plans: FreemiumPlan[];\n /** Currently-selected plan id (controlled) or default. */\n selectedPlanId?: string;\n defaultSelectedPlanId?: string;\n onSelectedPlanChange?: (id: string) => void;\n /** Usage indicator values — omit the object to hide the bar. */\n usage?: { used: number; limit: number };\n /** Fires on upgrade. */\n onUpgrade: (planId: string) => void;\n /** Fires when the user dismisses via Not Now. */\n onCancel?: () => void;\n /** Title override; defaults to t('paywall.title'). */\n title?: ReactNode;\n /** Description override; defaults to t('paywall.description'). */\n description?: ReactNode;\n /**\n * Stable instance id. Emitted on the dialog content as\n * `data-component-id` and used as the agent-registry instance key so\n * a host can address this specific paywall.\n */\n id?: string;\n className?: string;\n}\n\nexport const FreemiumPaywall = forwardRef<HTMLDivElement, FreemiumPaywallProps>(\n (\n {\n open,\n onOpenChange,\n plans,\n selectedPlanId,\n defaultSelectedPlanId,\n onSelectedPlanChange,\n usage,\n onUpgrade,\n onCancel,\n title,\n description,\n id,\n layout = 'centered',\n density = 'full',\n className,\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation('ui');\n const titleId = useId();\n const descId = useId();\n\n const [currentSelectedRaw, handleSelectedChange] =\n useControllableState<string>({\n value: selectedPlanId,\n defaultValue: defaultSelectedPlanId ?? plans[0]?.id ?? '',\n onChange: onSelectedPlanChange,\n });\n const currentSelected = currentSelectedRaw ?? '';\n const selectOnArrow = useSelectOnArrow();\n\n const handleCancel = () => {\n onCancel?.();\n onOpenChange(false);\n };\n\n const handleUpgrade = () => {\n if (!currentSelected) return;\n onUpgrade(currentSelected);\n };\n\n const openRef = useRef(open);\n openRef.current = open;\n const selectedRef = useRef<string>(currentSelected);\n selectedRef.current = currentSelected;\n\n const contentRef = useRef<HTMLDivElement>(null);\n useImperativeHandle(ref, () => contentRef.current as HTMLDivElement, []);\n\n const agentHandle = useMemo<FreemiumPaywallHandle>(\n () => ({\n isOpen: () => openRef.current,\n getSelectedPlanId: () => selectedRef.current || null,\n upgrade: () => handleUpgrade(),\n dismiss: () => handleCancel(),\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [],\n );\n useAgentRegistration(freemiumPaywallAgent, agentHandle, id);\n\n const usagePercent = usage\n ? Math.min(\n 100,\n Math.max(0, (usage.used / Math.max(1, usage.limit)) * 100),\n )\n : 0;\n const usageFull = usage ? usage.used >= usage.limit : false;\n\n return (\n <RadixAlertDialog.Root open={open} onOpenChange={onOpenChange}>\n <RadixAlertDialog.Portal>\n <RadixAlertDialog.Overlay\n className={[\n 'ds:fixed ds:inset-0 ds:z-[var(--z-modal-backdrop)]',\n 'ds:bg-[color:var(--background)]/70',\n 'ds:motion-safe:data-[state=open]:animate-in ds:motion-safe:data-[state=closed]:animate-out',\n // --animation-duration is 0ms in the accessible themes, as on\n // Dialog, so the overlay does not animate there.\n 'ds:duration-[var(--animation-duration)]',\n ].join(' ')}\n />\n <RadixAlertDialog.Content\n ref={contentRef}\n aria-labelledby={titleId}\n aria-describedby={descId}\n className={contentVariants({ layout, density, className })}\n data-component=\"freemium-paywall\"\n data-component-id={id}\n >\n <RadixAlertDialog.Title id={titleId} asChild>\n <h2 className=\"type-title-card\">{title ?? t('paywall.title')}</h2>\n </RadixAlertDialog.Title>\n <RadixAlertDialog.Description id={descId} asChild>\n <p className=\"ds:mt-[var(--spacing-xs)] ds:text-[color:var(--muted-foreground)]\">\n {description ?? t('paywall.description')}\n </p>\n </RadixAlertDialog.Description>\n\n {usage ? (\n <div className=\"ds:mt-[var(--spacing-md)]\">\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-xs)] ds:mb-[var(--spacing-xs)]\">\n {usageFull ? (\n <TriangleAlert\n aria-hidden=\"true\"\n className=\"ds:size-4 ds:text-[color:var(--destructive)]\"\n />\n ) : null}\n <span\n className={[\n 'type-body-sm',\n usageFull\n ? 'ds:text-[color:var(--destructive)]'\n : 'ds:text-[color:var(--muted-foreground)]',\n ].join(' ')}\n >\n {usageFull\n ? t('paywall.usageFull')\n : t('paywall.usageLabel', {\n used: usage.used,\n limit: usage.limit,\n })}\n </span>\n </div>\n <Progress\n value={usagePercent}\n max={100}\n ariaLabel={t('paywall.usageLabel', {\n used: usage.used,\n limit: usage.limit,\n })}\n className={\n usageFull\n ? 'ds:[&_[role=progressbar]>*]:bg-[color:var(--destructive)]'\n : ''\n }\n />\n </div>\n ) : null}\n\n <RadixRadioGroup.Root\n value={currentSelected}\n onValueChange={(next) => {\n selectOnArrow.noteSelection();\n handleSelectedChange(next);\n }}\n onKeyDown={selectOnArrow.onKeyDown}\n aria-label={t('paywall.title')}\n className=\"ds:mt-[var(--spacing-md)] ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\"\n >\n {plans.map((plan) => {\n // Reuse the payment-form helper — it knows about\n // zero-decimal currencies (JPY, KRW, VND …) and applies\n // the right divisor. Raw `/ 100` would 100× the price in\n // those currencies.\n const formattedPrice = formatPaymentAmount(\n plan.priceAmount,\n plan.currency,\n i18n.language,\n );\n const period = plan.perKey ? t(plan.perKey) : undefined;\n const priceLine = period\n ? t('paywall.pricePer', { price: formattedPrice, period })\n : formattedPrice;\n const planName = t(plan.nameKey);\n return (\n <RadixRadioGroup.Item\n key={plan.id}\n value={plan.id}\n className={[\n 'ds:group ds:relative ds:flex ds:flex-col ds:items-stretch',\n 'ds:rounded-[var(--radius-md)] ds:[:where(&)]:border-[length:var(--surface-border-width,var(--border-width-sm))] ds:border-[color:var(--card-border)]',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-sm)]',\n 'ds:text-start ds:bg-[color:var(--card)] ds:text-[color:var(--card-foreground)]',\n 'ds:data-[state=checked]:border-[color:var(--primary)]',\n 'ds:data-[state=checked]:bg-[color:var(--primary)]/5',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n ].join(' ')}\n >\n <span className=\"ds:flex ds:items-center ds:justify-between ds:gap-[var(--spacing-sm)]\">\n <span className=\"ds:font-semibold\">{planName}</span>\n <span className=\"ds:tabular-nums\">{priceLine}</span>\n </span>\n {plan.featuresKeys && plan.featuresKeys.length > 0 ? (\n <ul\n aria-label={t('paywall.features')}\n className=\"ds:mt-[var(--spacing-xs)] ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\"\n >\n {plan.featuresKeys.map((key, i) => (\n <li\n key={i}\n className=\"ds:flex ds:items-start ds:gap-[var(--spacing-xs)] type-body-sm\"\n >\n <Check\n aria-hidden=\"true\"\n className=\"ds:size-4 ds:shrink-0 ds:text-[color:var(--primary)] ds:mt-[1px]\"\n />\n <span>{t(key)}</span>\n </li>\n ))}\n </ul>\n ) : null}\n </RadixRadioGroup.Item>\n );\n })}\n </RadixRadioGroup.Root>\n\n {/* Stacks full-width in DOM order below 384px, as Dialog.Footer does. */}\n <div className=\"ds:@container/paywall-actions ds:mt-[var(--spacing-lg)] ds:flex ds:flex-wrap ds:items-center ds:gap-[var(--spacing-sm)] ds:justify-end ds:@max-[384px]/paywall-actions:[&>*]:w-full\">\n <RadixAlertDialog.Cancel asChild>\n <Button intent=\"outline\" onClick={handleCancel}>\n {t('paywall.notNow')}\n </Button>\n </RadixAlertDialog.Cancel>\n <RadixAlertDialog.Action asChild>\n <Button intent=\"primary\" onClick={handleUpgrade}>\n {t('paywall.upgrade')}\n </Button>\n </RadixAlertDialog.Action>\n </div>\n </RadixAlertDialog.Content>\n </RadixAlertDialog.Portal>\n </RadixAlertDialog.Root>\n );\n },\n);\n\nFreemiumPaywall.displayName = 'FreemiumPaywall';\n"],"names":["freemiumPaywallAgent","handle","contentVariants","cva","FreemiumPaywall","forwardRef","open","onOpenChange","plans","selectedPlanId","defaultSelectedPlanId","onSelectedPlanChange","usage","onUpgrade","onCancel","title","description","id","layout","density","className","ref","t","i18n","useTranslation","titleId","useId","descId","currentSelectedRaw","handleSelectedChange","useControllableState","_a","currentSelected","selectOnArrow","useSelectOnArrow","handleCancel","handleUpgrade","openRef","useRef","selectedRef","contentRef","useImperativeHandle","agentHandle","useMemo","useAgentRegistration","usagePercent","usageFull","jsx","RadixAlertDialog","jsxs","TriangleAlert","Progress","RadixRadioGroup","next","plan","formattedPrice","formatPaymentAmount","period","priceLine","planName","key","i","Check","Button"],"mappings":"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"}
1
+ {"version":3,"file":"freemium-paywall-6DO7noHX.js","sources":["../../src/components/freemium-paywall/freemium-paywall.agent.ts","../../src/components/freemium-paywall/freemium-paywall.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — FreemiumPaywall. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { FreemiumPaywallHandle } from './freemium-paywall';\n\nexport const freemiumPaywallAgent: AgentAdapter<FreemiumPaywallHandle> = {\n id: 'freemium-paywall',\n capabilities: ['submit', 'dismiss'],\n state: {\n isOpen: {\n type: 'boolean',\n descriptionKey: 'ui.agent.freemiumPaywall.state.isOpen',\n description: 'True when the paywall dialog is open.',\n read: (handle) => handle.isOpen(),\n },\n selectedPlanId: {\n type: 'string | null',\n descriptionKey: 'ui.agent.freemiumPaywall.state.selectedPlanId',\n description: 'Opaque id of the currently-selected plan.',\n read: (handle) => handle.getSelectedPlanId(),\n },\n },\n actions: {\n upgrade: {\n safety: 'write',\n // Backs the `submit` capability: commits the current plan selection\n // to the host's upgrade flow (checkout / billing).\n openWorld: true,\n descriptionKey: 'ui.agent.freemiumPaywall.actions.upgrade',\n description: 'Fire onUpgrade with the currently-selected plan id.',\n errors: ['No plan selected — upgrade is a no-op until a plan is chosen.'],\n invoke: (handle) => {\n handle.upgrade();\n },\n },\n dismiss: {\n safety: 'destructive',\n // Backs the `dismiss` capability.\n descriptionKey: 'ui.agent.freemiumPaywall.actions.dismiss',\n description:\n 'Dismiss the paywall via the Not Now action. Irreversible from the same UI.',\n invoke: (handle) => {\n handle.dismiss();\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'freemium-paywall',\n description: 'Marks the FreemiumPaywall dialog content.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n },\n};\n","import {\n forwardRef,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n type ReactNode,\n} from 'react';\nimport * as RadixAlertDialog from '@radix-ui/react-alert-dialog';\nimport * as RadixRadioGroup from '@radix-ui/react-radio-group';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { useControllableState } from '../../hooks/use-controllable-state';\nimport { useSelectOnArrow } from '../_shared/use-select-on-arrow';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { freemiumPaywallAgent } from './freemium-paywall.agent';\nimport { TriangleAlert, Check } from 'lucide-react';\nimport { Button } from '../button';\nimport { Progress } from '../progress';\nimport { formatPaymentAmount } from '../payment-form';\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\nexport interface FreemiumPlan {\n id: string;\n /** Translation key for the plan name (e.g. `app.paywall.plans.pro`). */\n nameKey: string;\n /** Amount in minor units (cents, centesimi). */\n priceAmount: number;\n /** ISO 4217 currency code. */\n currency: string;\n /** Translation key for the cadence suffix (e.g. `app.paywall.period.month`). */\n perKey?: string;\n /** Per-line feature translation keys. */\n featuresKeys?: string[];\n}\n\nconst contentVariants = cva(\n [\n 'ds:fixed ds:z-[var(--z-modal)] ds:overflow-auto',\n 'ds:bg-[color:var(--popover)] ds:text-[color:var(--popover-foreground)]',\n 'ds:border ds:border-border ds:shadow-[var(--shadow-lg)]',\n // forced-colors: keep the dialog boundary visible under Windows High\n // Contrast, where `var(--border)` may collapse to system colours.\n 'ds:forced-colors:border-[CanvasText]',\n 'ds:focus:outline-none',\n ].join(' '),\n {\n variants: {\n layout: {\n centered:\n 'ds:start-1/2 ds:top-1/2 ds:-translate-x-1/2 ds:-translate-y-1/2 ds:rtl:translate-x-1/2 ds:rounded-[var(--radius-md)] ds:max-w-[var(--dialog-width-xl)] ds:w-[calc(100vw-2rem)] ds:max-h-[90vh]',\n sheet:\n // `inset-x-0` compiles to logical `inset-inline`; the\n // `inset-inline-0` spelling compiled to nothing.\n 'ds:inset-x-0 ds:bottom-0 ds:w-full ds:max-h-[90vh] ds:rounded-t-[var(--radius-md)]',\n },\n density: {\n compact:\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:pt-[var(--spacing-md)] ds:pb-[var(--spacing-md)]',\n full: 'ds:ps-[var(--spacing-lg)] ds:pe-[var(--spacing-lg)] ds:pt-[var(--spacing-lg)] ds:pb-[var(--spacing-lg)]',\n },\n },\n defaultVariants: { layout: 'centered', density: 'full' },\n },\n);\n\n/** Curated imperative handle for agent / external automation. */\nexport interface FreemiumPaywallHandle {\n isOpen: () => boolean;\n getSelectedPlanId: () => string | null;\n upgrade: () => void;\n dismiss: () => void;\n}\n\nexport interface FreemiumPaywallProps extends VariantProps<\n typeof contentVariants\n> {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n plans: FreemiumPlan[];\n /** Currently-selected plan id (controlled) or default. */\n selectedPlanId?: string;\n defaultSelectedPlanId?: string;\n onSelectedPlanChange?: (id: string) => void;\n /** Usage indicator values — omit the object to hide the bar. */\n usage?: { used: number; limit: number };\n /** Fires on upgrade. */\n onUpgrade: (planId: string) => void;\n /** Fires when the user dismisses via Not Now. */\n onCancel?: () => void;\n /** Title override; defaults to t('paywall.title'). */\n title?: ReactNode;\n /** Description override; defaults to t('paywall.description'). */\n description?: ReactNode;\n /**\n * Stable instance id. Emitted on the dialog content as\n * `data-component-id` and used as the agent-registry instance key so\n * a host can address this specific paywall.\n */\n id?: string;\n className?: string;\n}\n\nexport const FreemiumPaywall = forwardRef<HTMLDivElement, FreemiumPaywallProps>(\n (\n {\n open,\n onOpenChange,\n plans,\n selectedPlanId,\n defaultSelectedPlanId,\n onSelectedPlanChange,\n usage,\n onUpgrade,\n onCancel,\n title,\n description,\n id,\n layout = 'centered',\n density = 'full',\n className,\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation('ui');\n const titleId = useId();\n const descId = useId();\n\n const [currentSelectedRaw, handleSelectedChange] =\n useControllableState<string>({\n value: selectedPlanId,\n defaultValue: defaultSelectedPlanId ?? plans[0]?.id ?? '',\n onChange: onSelectedPlanChange,\n });\n const currentSelected = currentSelectedRaw ?? '';\n const selectOnArrow = useSelectOnArrow();\n\n const handleCancel = () => {\n onCancel?.();\n onOpenChange(false);\n };\n\n const handleUpgrade = () => {\n if (!currentSelected) return;\n onUpgrade(currentSelected);\n };\n\n const openRef = useRef(open);\n openRef.current = open;\n const selectedRef = useRef<string>(currentSelected);\n selectedRef.current = currentSelected;\n\n const contentRef = useRef<HTMLDivElement>(null);\n useImperativeHandle(ref, () => contentRef.current as HTMLDivElement, []);\n\n const agentHandle = useMemo<FreemiumPaywallHandle>(\n () => ({\n isOpen: () => openRef.current,\n getSelectedPlanId: () => selectedRef.current || null,\n upgrade: () => handleUpgrade(),\n dismiss: () => handleCancel(),\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [],\n );\n useAgentRegistration(freemiumPaywallAgent, agentHandle, id);\n\n const usagePercent = usage\n ? Math.min(\n 100,\n Math.max(0, (usage.used / Math.max(1, usage.limit)) * 100),\n )\n : 0;\n const usageFull = usage ? usage.used >= usage.limit : false;\n\n return (\n <RadixAlertDialog.Root open={open} onOpenChange={onOpenChange}>\n <RadixAlertDialog.Portal>\n <RadixAlertDialog.Overlay\n className={[\n 'ds:fixed ds:inset-0 ds:z-[var(--z-modal-backdrop)]',\n 'ds:bg-[color:var(--background)]/70',\n 'ds:motion-safe:data-[state=open]:animate-in ds:motion-safe:data-[state=closed]:animate-out',\n // --animation-duration is 0ms in the accessible themes, as on\n // Dialog, so the overlay does not animate there.\n 'ds:duration-[var(--animation-duration)]',\n ].join(' ')}\n />\n <RadixAlertDialog.Content\n ref={contentRef}\n aria-labelledby={titleId}\n aria-describedby={descId}\n className={contentVariants({ layout, density, className })}\n data-component=\"freemium-paywall\"\n data-component-id={id}\n >\n <RadixAlertDialog.Title id={titleId} asChild>\n <h2 className=\"type-title-card\">{title ?? t('paywall.title')}</h2>\n </RadixAlertDialog.Title>\n <RadixAlertDialog.Description id={descId} asChild>\n <p className=\"ds:mt-[var(--spacing-xs)] ds:text-[color:var(--muted-foreground)]\">\n {description ?? t('paywall.description')}\n </p>\n </RadixAlertDialog.Description>\n\n {usage ? (\n <div className=\"ds:mt-[var(--spacing-md)]\">\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-xs)] ds:mb-[var(--spacing-xs)]\">\n {usageFull ? (\n <TriangleAlert\n aria-hidden=\"true\"\n className=\"ds:size-4 ds:text-[color:var(--destructive)]\"\n />\n ) : null}\n <span\n className={[\n 'type-body-sm',\n usageFull\n ? 'ds:text-[color:var(--destructive)]'\n : 'ds:text-[color:var(--muted-foreground)]',\n ].join(' ')}\n >\n {usageFull\n ? t('paywall.usageFull')\n : t('paywall.usageLabel', {\n used: usage.used,\n limit: usage.limit,\n })}\n </span>\n </div>\n <Progress\n value={usagePercent}\n max={100}\n ariaLabel={t('paywall.usageLabel', {\n used: usage.used,\n limit: usage.limit,\n })}\n className={\n usageFull\n ? 'ds:[&_[role=progressbar]>*]:bg-[color:var(--destructive)]'\n : ''\n }\n />\n </div>\n ) : null}\n\n <RadixRadioGroup.Root\n value={currentSelected}\n onValueChange={(next) => {\n selectOnArrow.noteSelection();\n handleSelectedChange(next);\n }}\n onKeyDown={selectOnArrow.onKeyDown}\n aria-label={t('paywall.title')}\n className=\"ds:mt-[var(--spacing-md)] ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\"\n >\n {plans.map((plan) => {\n // Reuse the payment-form helper — it knows about\n // zero-decimal currencies (JPY, KRW, VND …) and applies\n // the right divisor. Raw `/ 100` would 100× the price in\n // those currencies.\n const formattedPrice = formatPaymentAmount(\n plan.priceAmount,\n plan.currency,\n i18n.language,\n );\n const period = plan.perKey ? t(plan.perKey) : undefined;\n const priceLine = period\n ? t('paywall.pricePer', { price: formattedPrice, period })\n : formattedPrice;\n const planName = t(plan.nameKey);\n return (\n <RadixRadioGroup.Item\n key={plan.id}\n value={plan.id}\n className={[\n 'ds:group ds:relative ds:flex ds:flex-col ds:items-stretch',\n 'ds:rounded-[var(--radius-md)] ds:[:where(&)]:border-[length:var(--surface-border-width,var(--border-width-sm))] ds:border-[color:var(--card-border)]',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-sm)]',\n 'ds:text-start ds:bg-[color:var(--card)] ds:text-[color:var(--card-foreground)]',\n 'ds:data-[state=checked]:border-[color:var(--primary)]',\n 'ds:data-[state=checked]:bg-[color:var(--primary)]/5',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n ].join(' ')}\n >\n <span className=\"ds:flex ds:items-center ds:justify-between ds:gap-[var(--spacing-sm)]\">\n <span className=\"ds:font-semibold\">{planName}</span>\n <span className=\"ds:tabular-nums\">{priceLine}</span>\n </span>\n {plan.featuresKeys && plan.featuresKeys.length > 0 ? (\n <ul\n aria-label={t('paywall.features')}\n className=\"ds:mt-[var(--spacing-xs)] ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\"\n >\n {plan.featuresKeys.map((key, i) => (\n <li\n key={i}\n className=\"ds:flex ds:items-start ds:gap-[var(--spacing-xs)] type-body-sm\"\n >\n <Check\n aria-hidden=\"true\"\n className=\"ds:size-4 ds:shrink-0 ds:text-[color:var(--primary)] ds:mt-[1px]\"\n />\n <span>{t(key)}</span>\n </li>\n ))}\n </ul>\n ) : null}\n </RadixRadioGroup.Item>\n );\n })}\n </RadixRadioGroup.Root>\n\n {/* Stacks full-width in DOM order below 384px, as Dialog.Footer does. */}\n <div className=\"ds:@container/paywall-actions ds:mt-[var(--spacing-lg)] ds:flex ds:flex-wrap ds:items-center ds:gap-[var(--spacing-sm)] ds:justify-end ds:@max-[384px]/paywall-actions:[&>*]:w-full\">\n <RadixAlertDialog.Cancel asChild>\n <Button intent=\"outline\" onClick={handleCancel}>\n {t('paywall.notNow')}\n </Button>\n </RadixAlertDialog.Cancel>\n <RadixAlertDialog.Action asChild>\n <Button intent=\"primary\" onClick={handleUpgrade}>\n {t('paywall.upgrade')}\n </Button>\n </RadixAlertDialog.Action>\n </div>\n </RadixAlertDialog.Content>\n </RadixAlertDialog.Portal>\n </RadixAlertDialog.Root>\n );\n },\n);\n\nFreemiumPaywall.displayName = 'FreemiumPaywall';\n"],"names":["freemiumPaywallAgent","handle","contentVariants","cva","FreemiumPaywall","forwardRef","open","onOpenChange","plans","selectedPlanId","defaultSelectedPlanId","onSelectedPlanChange","usage","onUpgrade","onCancel","title","description","id","layout","density","className","ref","t","i18n","useTranslation","titleId","useId","descId","currentSelectedRaw","handleSelectedChange","useControllableState","_a","currentSelected","selectOnArrow","useSelectOnArrow","handleCancel","handleUpgrade","openRef","useRef","selectedRef","contentRef","useImperativeHandle","agentHandle","useMemo","useAgentRegistration","usagePercent","usageFull","jsx","RadixAlertDialog","jsxs","TriangleAlert","Progress","RadixRadioGroup","next","plan","formattedPrice","formatPaymentAmount","period","priceLine","planName","key","i","Check","Button"],"mappings":"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"}