@alfadocs/ui-kit 0.0.19 → 0.1.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 (552) hide show
  1. package/dist/_chunks/accordion-BJD1aM67.js +117 -0
  2. package/dist/_chunks/accordion-BJD1aM67.js.map +1 -0
  3. package/dist/_chunks/agenda-card-Bld47Eul.js +130 -0
  4. package/dist/_chunks/agenda-card-Bld47Eul.js.map +1 -0
  5. package/dist/_chunks/agenda-tray-D86cNIJ0.js +119 -0
  6. package/dist/_chunks/agenda-tray-D86cNIJ0.js.map +1 -0
  7. package/dist/_chunks/ai-prompt-input-CdYwt2VP.js +379 -0
  8. package/dist/_chunks/ai-prompt-input-CdYwt2VP.js.map +1 -0
  9. package/dist/_chunks/{alert-C5ud6CfE.js → alert-ETrF7Q8J.js} +51 -51
  10. package/dist/_chunks/alert-ETrF7Q8J.js.map +1 -0
  11. package/dist/_chunks/apexcharts-theme-BkSShpEy.js +48 -0
  12. package/dist/_chunks/apexcharts-theme-BkSShpEy.js.map +1 -0
  13. package/dist/_chunks/{app-frame-CmIUp9BD.js → app-frame-uq2Gy0vs.js} +37 -37
  14. package/dist/_chunks/app-frame-uq2Gy0vs.js.map +1 -0
  15. package/dist/_chunks/aspect-ratio-BqU4itGW.js +56 -0
  16. package/dist/_chunks/aspect-ratio-BqU4itGW.js.map +1 -0
  17. package/dist/_chunks/{audio-recorder-GkGNtW2X.js → audio-recorder-D2UEBF9B.js} +98 -98
  18. package/dist/_chunks/audio-recorder-D2UEBF9B.js.map +1 -0
  19. package/dist/_chunks/{audio-visualiser-CIO3yy23.js → audio-visualiser-B4u4goV5.js} +46 -46
  20. package/dist/_chunks/audio-visualiser-B4u4goV5.js.map +1 -0
  21. package/dist/_chunks/{autocomplete.agent-kb0nmr6F.js → autocomplete.agent-Bi6CiRKa.js} +111 -111
  22. package/dist/_chunks/autocomplete.agent-Bi6CiRKa.js.map +1 -0
  23. package/dist/_chunks/{avatar-BsV7vRA6.js → avatar-BAhxbDEu.js} +38 -38
  24. package/dist/_chunks/avatar-BAhxbDEu.js.map +1 -0
  25. package/dist/_chunks/badge-zDghajh8.js +104 -0
  26. package/dist/_chunks/badge-zDghajh8.js.map +1 -0
  27. package/dist/_chunks/{balance-cell-renderer-BQwN51rT.js → balance-cell-renderer-BGyvZWjB.js} +123 -124
  28. package/dist/_chunks/balance-cell-renderer-BGyvZWjB.js.map +1 -0
  29. package/dist/_chunks/{breadcrumb-DnO6eHSS.js → breadcrumb-pdUacgm1.js} +40 -40
  30. package/dist/_chunks/breadcrumb-pdUacgm1.js.map +1 -0
  31. package/dist/_chunks/button-DmiGFnNA.js +119 -0
  32. package/dist/_chunks/button-DmiGFnNA.js.map +1 -0
  33. package/dist/_chunks/{button-group-CI7LFxt3.js → button-group-BtTHSyU0.js} +24 -24
  34. package/dist/_chunks/button-group-BtTHSyU0.js.map +1 -0
  35. package/dist/_chunks/{calendar-BIXVzhkL.js → calendar-BkDeDTaX.js} +333 -290
  36. package/dist/_chunks/calendar-BkDeDTaX.js.map +1 -0
  37. package/dist/_chunks/card-DeItIBcV.js +158 -0
  38. package/dist/_chunks/card-DeItIBcV.js.map +1 -0
  39. package/dist/_chunks/{chart.agent-DkmVXNsI.js → chart.agent-BdS-_8MO.js} +66 -64
  40. package/dist/_chunks/chart.agent-BdS-_8MO.js.map +1 -0
  41. package/dist/_chunks/chat-container-Co8HpB64.js +174 -0
  42. package/dist/_chunks/chat-container-Co8HpB64.js.map +1 -0
  43. package/dist/_chunks/{chat-input-aphxDjZL.js → chat-input-3rstZhHR.js} +57 -57
  44. package/dist/_chunks/chat-input-3rstZhHR.js.map +1 -0
  45. package/dist/_chunks/{chat-message-CdJSD18D.js → chat-message-dDMVSYBs.js} +66 -66
  46. package/dist/_chunks/chat-message-dDMVSYBs.js.map +1 -0
  47. package/dist/_chunks/checkbox-DMzgtnqw.js +116 -0
  48. package/dist/_chunks/checkbox-DMzgtnqw.js.map +1 -0
  49. package/dist/_chunks/{checkbox-group-D1aqVI0-.js → checkbox-group-DBnIBRT_.js} +34 -34
  50. package/dist/_chunks/checkbox-group-DBnIBRT_.js.map +1 -0
  51. package/dist/_chunks/{collapsible-jBFm7Ah8.js → collapsible-DPGQnHZh.js} +16 -16
  52. package/dist/_chunks/collapsible-DPGQnHZh.js.map +1 -0
  53. package/dist/_chunks/{color-picker-CiotzQJF.js → color-picker-OKKF3Dww.js} +158 -158
  54. package/dist/_chunks/color-picker-OKKF3Dww.js.map +1 -0
  55. package/dist/_chunks/{combobox.agent-B6RwDSct.js → combobox.agent-CfeB-IZ1.js} +107 -107
  56. package/dist/_chunks/combobox.agent-CfeB-IZ1.js.map +1 -0
  57. package/dist/_chunks/{command-palette.agent-B87_OkmC.js → command-palette.agent-XLfSGHCL.js} +27 -27
  58. package/dist/_chunks/command-palette.agent-XLfSGHCL.js.map +1 -0
  59. package/dist/_chunks/{date-picker-CdPuI3dK.js → date-picker-DXx8oSJb.js} +5 -5
  60. package/dist/_chunks/{date-picker-CdPuI3dK.js.map → date-picker-DXx8oSJb.js.map} +1 -1
  61. package/dist/_chunks/{date-range-picker-DfK4cjln.js → date-range-picker-C3CbY__H.js} +29 -29
  62. package/dist/_chunks/{date-range-picker-DfK4cjln.js.map → date-range-picker-C3CbY__H.js.map} +1 -1
  63. package/dist/_chunks/{date-time-picker-CX_FMsLG.js → date-time-picker-Bn3FPeAc.js} +58 -58
  64. package/dist/_chunks/date-time-picker-Bn3FPeAc.js.map +1 -0
  65. package/dist/_chunks/description-list-B1CL3RTG.js +187 -0
  66. package/dist/_chunks/description-list-B1CL3RTG.js.map +1 -0
  67. package/dist/_chunks/{dialog.agent-B276rAQF.js → dialog.agent-D9WeIWi2.js} +39 -39
  68. package/dist/_chunks/dialog.agent-D9WeIWi2.js.map +1 -0
  69. package/dist/_chunks/dropdown-menu-BnVUeVG3.js +210 -0
  70. package/dist/_chunks/dropdown-menu-BnVUeVG3.js.map +1 -0
  71. package/dist/_chunks/{empty-state-DiG7vYBM.js → empty-state-DV96gCnp.js} +37 -37
  72. package/dist/_chunks/empty-state-DV96gCnp.js.map +1 -0
  73. package/dist/_chunks/{file-upload.agent-BMow5yDY.js → file-upload.agent-DYFnqdxw.js} +185 -185
  74. package/dist/_chunks/file-upload.agent-DYFnqdxw.js.map +1 -0
  75. package/dist/_chunks/{flag-ChP784EM.js → flag-C3BUEwHH.js} +14 -14
  76. package/dist/_chunks/{flag-ChP784EM.js.map → flag-C3BUEwHH.js.map} +1 -1
  77. package/dist/_chunks/floating-action-button-RigP2E7o.js +95 -0
  78. package/dist/_chunks/floating-action-button-RigP2E7o.js.map +1 -0
  79. package/dist/_chunks/form-field-DI5LY5aG.js +121 -0
  80. package/dist/_chunks/form-field-DI5LY5aG.js.map +1 -0
  81. package/dist/_chunks/freemium-paywall-D0GiUFOe.js +197 -0
  82. package/dist/_chunks/freemium-paywall-D0GiUFOe.js.map +1 -0
  83. package/dist/_chunks/{header-BpKaSL_v.js → header-CW2oRd5H.js} +111 -111
  84. package/dist/_chunks/header-CW2oRd5H.js.map +1 -0
  85. package/dist/_chunks/icon-button-C482ii4y.js +45 -0
  86. package/dist/_chunks/icon-button-C482ii4y.js.map +1 -0
  87. package/dist/_chunks/{icon-button-group-tERESY-n.js → icon-button-group-BnhkUWUg.js} +56 -56
  88. package/dist/_chunks/icon-button-group-BnhkUWUg.js.map +1 -0
  89. package/dist/_chunks/{kbd-0iPlQjgC.js → kbd-DTcIjYA7.js} +52 -52
  90. package/dist/_chunks/kbd-DTcIjYA7.js.map +1 -0
  91. package/dist/_chunks/{key-value-pair--Je59tAF.js → key-value-pair-DDhSYdDL.js} +26 -26
  92. package/dist/_chunks/key-value-pair-DDhSYdDL.js.map +1 -0
  93. package/dist/_chunks/leo-sidebar-gXXcGPKk.js +755 -0
  94. package/dist/_chunks/leo-sidebar-gXXcGPKk.js.map +1 -0
  95. package/dist/_chunks/{list-n8vI8Yvn.js → list-Cwe8mcmh.js} +96 -96
  96. package/dist/_chunks/list-Cwe8mcmh.js.map +1 -0
  97. package/dist/_chunks/live-region-COggO6x6.js +57 -0
  98. package/dist/_chunks/live-region-COggO6x6.js.map +1 -0
  99. package/dist/_chunks/{logo-UNWYb9p7.js → logo-3wrZGpwg.js} +29 -29
  100. package/dist/_chunks/logo-3wrZGpwg.js.map +1 -0
  101. package/dist/_chunks/{matrix-rain-Q7xTEpKu.js → matrix-rain-gsHqSvW7.js} +75 -75
  102. package/dist/_chunks/matrix-rain-gsHqSvW7.js.map +1 -0
  103. package/dist/_chunks/message-card-DID3cXUW.js +275 -0
  104. package/dist/_chunks/message-card-DID3cXUW.js.map +1 -0
  105. package/dist/_chunks/message-tray-CVMLBnVp.js +302 -0
  106. package/dist/_chunks/message-tray-CVMLBnVp.js.map +1 -0
  107. package/dist/_chunks/{multi-select.agent-Cf4pU636.js → multi-select.agent-BUKYZJfp.js} +126 -126
  108. package/dist/_chunks/multi-select.agent-BUKYZJfp.js.map +1 -0
  109. package/dist/_chunks/{navigation-menu-DBDsAmXc.js → navigation-menu-NjwxyshT.js} +68 -97
  110. package/dist/_chunks/navigation-menu-NjwxyshT.js.map +1 -0
  111. package/dist/_chunks/notification-card-BZ33fq8H.js +255 -0
  112. package/dist/_chunks/notification-card-BZ33fq8H.js.map +1 -0
  113. package/dist/_chunks/{notification-tray-CNWPuPZp.js → notification-tray-CnEd7B2q.js} +96 -96
  114. package/dist/_chunks/notification-tray-CnEd7B2q.js.map +1 -0
  115. package/dist/_chunks/{number-input-B1Th0DdC.js → number-input-D7rSa_ef.js} +73 -73
  116. package/dist/_chunks/number-input-D7rSa_ef.js.map +1 -0
  117. package/dist/_chunks/{otp-input-CmoBuZ4K.js → otp-input-C9R9sC74.js} +42 -42
  118. package/dist/_chunks/otp-input-C9R9sC74.js.map +1 -0
  119. package/dist/_chunks/{pagination.agent-B5KLDCMN.js → pagination.agent-D75FB6XP.js} +106 -106
  120. package/dist/_chunks/pagination.agent-D75FB6XP.js.map +1 -0
  121. package/dist/_chunks/{password-input-DnjNh-hQ.js → password-input-C6PvKyQV.js} +59 -59
  122. package/dist/_chunks/password-input-C6PvKyQV.js.map +1 -0
  123. package/dist/_chunks/{patient-shell-D0RaWDMR.js → patient-shell-CGsmI5LJ.js} +9 -9
  124. package/dist/_chunks/{patient-shell-D0RaWDMR.js.map → patient-shell-CGsmI5LJ.js.map} +1 -1
  125. package/dist/_chunks/{payment-form-Cf7U6r5L.js → payment-form-l3j-gA-t.js} +108 -108
  126. package/dist/_chunks/payment-form-l3j-gA-t.js.map +1 -0
  127. package/dist/_chunks/{pdf-viewer.agent-DXjgDItg.js → pdf-viewer.agent-DuGfSoep.js} +234 -233
  128. package/dist/_chunks/pdf-viewer.agent-DuGfSoep.js.map +1 -0
  129. package/dist/_chunks/{phone-input-C2dtIuxW.js → phone-input-ZWa_FU4R.js} +112 -112
  130. package/dist/_chunks/phone-input-ZWa_FU4R.js.map +1 -0
  131. package/dist/_chunks/{popover-D1woU9mP.js → popover-CMr1pTPO.js} +58 -58
  132. package/dist/_chunks/popover-CMr1pTPO.js.map +1 -0
  133. package/dist/_chunks/{privacy-lock-C1X42Sit.js → privacy-lock-Br5k7Rul.js} +36 -36
  134. package/dist/_chunks/privacy-lock-Br5k7Rul.js.map +1 -0
  135. package/dist/_chunks/{progress-DL3-izuO.js → progress-D4ELgHG3.js} +43 -43
  136. package/dist/_chunks/progress-D4ELgHG3.js.map +1 -0
  137. package/dist/_chunks/radio-B_gvGU29.js +106 -0
  138. package/dist/_chunks/radio-B_gvGU29.js.map +1 -0
  139. package/dist/_chunks/radio-group-Bn8Wt0yc.js +88 -0
  140. package/dist/_chunks/radio-group-Bn8Wt0yc.js.map +1 -0
  141. package/dist/_chunks/{react-day-picker-DQKDiJ37.js → react-day-picker-d0MHsyCj.js} +174 -174
  142. package/dist/_chunks/{react-day-picker-DQKDiJ37.js.map → react-day-picker-d0MHsyCj.js.map} +1 -1
  143. package/dist/_chunks/{resizable.agent-DVk_jJur.js → resizable.agent-DBpPGNdy.js} +122 -122
  144. package/dist/_chunks/resizable.agent-DBpPGNdy.js.map +1 -0
  145. package/dist/_chunks/{rich-text-editor.agent-BOjF4Xao.js → rich-text-editor.agent-C1_E7_7t.js} +225 -225
  146. package/dist/_chunks/rich-text-editor.agent-C1_E7_7t.js.map +1 -0
  147. package/dist/_chunks/scroll-area-Ba99pJ_R.js +144 -0
  148. package/dist/_chunks/scroll-area-Ba99pJ_R.js.map +1 -0
  149. package/dist/_chunks/{search-bar-DgSOAajC.js → search-bar-VoTqJhRp.js} +131 -131
  150. package/dist/_chunks/search-bar-VoTqJhRp.js.map +1 -0
  151. package/dist/_chunks/search-input-D6rarD0_.js +197 -0
  152. package/dist/_chunks/search-input-D6rarD0_.js.map +1 -0
  153. package/dist/_chunks/select-DbxWF3O_.js +320 -0
  154. package/dist/_chunks/select-DbxWF3O_.js.map +1 -0
  155. package/dist/_chunks/{separator-DExI4amU.js → separator-BRQHi8s0.js} +17 -17
  156. package/dist/_chunks/separator-BRQHi8s0.js.map +1 -0
  157. package/dist/_chunks/sheet-DyWqluiS.js +188 -0
  158. package/dist/_chunks/sheet-DyWqluiS.js.map +1 -0
  159. package/dist/_chunks/sidebar-B52iGGNV.js +1076 -0
  160. package/dist/_chunks/sidebar-B52iGGNV.js.map +1 -0
  161. package/dist/_chunks/sign-in-with-alfadocs-button-BU7MP5Hg.js +44 -0
  162. package/dist/_chunks/sign-in-with-alfadocs-button-BU7MP5Hg.js.map +1 -0
  163. package/dist/_chunks/{signature-capture.agent-L_1_-zVR.js → signature-capture.agent-4htVctJ2.js} +163 -163
  164. package/dist/_chunks/signature-capture.agent-4htVctJ2.js.map +1 -0
  165. package/dist/_chunks/skeleton-ClO1v5GE.js +95 -0
  166. package/dist/_chunks/skeleton-ClO1v5GE.js.map +1 -0
  167. package/dist/_chunks/skip-link-CASJkBOe.js +68 -0
  168. package/dist/_chunks/skip-link-CASJkBOe.js.map +1 -0
  169. package/dist/_chunks/slider-n8JWpJvT.js +273 -0
  170. package/dist/_chunks/slider-n8JWpJvT.js.map +1 -0
  171. package/dist/_chunks/slot-grid-BRAkqChA.js +491 -0
  172. package/dist/_chunks/slot-grid-BRAkqChA.js.map +1 -0
  173. package/dist/_chunks/{sparkline.agent-BDTXzTy9.js → sparkline.agent-BLY1IMyW.js} +45 -45
  174. package/dist/_chunks/sparkline.agent-BLY1IMyW.js.map +1 -0
  175. package/dist/_chunks/{spinner-77xUGpuX.js → spinner-CoAOGcDa.js} +40 -40
  176. package/dist/_chunks/spinner-CoAOGcDa.js.map +1 -0
  177. package/dist/_chunks/stat-D76MNHzK.js +208 -0
  178. package/dist/_chunks/stat-D76MNHzK.js.map +1 -0
  179. package/dist/_chunks/{stepper-accordion-BkvqQWPI.js → stepper-accordion-DHQ80A4v.js} +33 -33
  180. package/dist/_chunks/stepper-accordion-DHQ80A4v.js.map +1 -0
  181. package/dist/_chunks/{stepper-calendar-BQMjrwzo.js → stepper-calendar-vtWwa2bY.js} +159 -159
  182. package/dist/_chunks/stepper-calendar-vtWwa2bY.js.map +1 -0
  183. package/dist/_chunks/stepper-progress-DMZ5w5VR.js +128 -0
  184. package/dist/_chunks/stepper-progress-DMZ5w5VR.js.map +1 -0
  185. package/dist/_chunks/streaming-text-D0cW8pwq.js +104 -0
  186. package/dist/_chunks/streaming-text-D0cW8pwq.js.map +1 -0
  187. package/dist/_chunks/suggestion-chip-BgNFpPEE.js +157 -0
  188. package/dist/_chunks/suggestion-chip-BgNFpPEE.js.map +1 -0
  189. package/dist/_chunks/switch-DtLPKO0p.js +137 -0
  190. package/dist/_chunks/switch-DtLPKO0p.js.map +1 -0
  191. package/dist/_chunks/{tabs.agent-FsGU3sZL.js → tabs.agent-BDUlyPbJ.js} +101 -101
  192. package/dist/_chunks/tabs.agent-BDUlyPbJ.js.map +1 -0
  193. package/dist/_chunks/tag-CfSZZN2f.js +194 -0
  194. package/dist/_chunks/tag-CfSZZN2f.js.map +1 -0
  195. package/dist/_chunks/task-card-B5xfiFg5.js +112 -0
  196. package/dist/_chunks/task-card-B5xfiFg5.js.map +1 -0
  197. package/dist/_chunks/task-tray-BnpiodZ4.js +99 -0
  198. package/dist/_chunks/task-tray-BnpiodZ4.js.map +1 -0
  199. package/dist/_chunks/text-area-BqbruBWx.js +152 -0
  200. package/dist/_chunks/text-area-BqbruBWx.js.map +1 -0
  201. package/dist/_chunks/text-input-lh6kRXZS.js +187 -0
  202. package/dist/_chunks/text-input-lh6kRXZS.js.map +1 -0
  203. package/dist/_chunks/theme-toggle-BHKMiORD.js +326 -0
  204. package/dist/_chunks/theme-toggle-BHKMiORD.js.map +1 -0
  205. package/dist/_chunks/{time-picker-BapZq0OY.js → time-picker-DbpAmPux.js} +102 -102
  206. package/dist/_chunks/time-picker-DbpAmPux.js.map +1 -0
  207. package/dist/_chunks/timeline-vjsUeuq1.js +219 -0
  208. package/dist/_chunks/timeline-vjsUeuq1.js.map +1 -0
  209. package/dist/_chunks/{timestamp-BLM-jjdw.js → timestamp-DmSt92P1.js} +30 -30
  210. package/dist/_chunks/timestamp-DmSt92P1.js.map +1 -0
  211. package/dist/_chunks/{toast-DSCqnDRl.js → toast-DllSITLf.js} +151 -151
  212. package/dist/_chunks/{toast-DSCqnDRl.js.map → toast-DllSITLf.js.map} +1 -1
  213. package/dist/_chunks/{tooltip-DF6WjD1r.js → tooltip-Dp3u8jGz.js} +30 -30
  214. package/dist/_chunks/tooltip-Dp3u8jGz.js.map +1 -0
  215. package/dist/_chunks/{tooth-scheme.agent-CFKl3iQr.js → tooth-scheme.agent-BRqxWa1D.js} +75 -75
  216. package/dist/_chunks/tooth-scheme.agent-BRqxWa1D.js.map +1 -0
  217. package/dist/_chunks/transcript-panel-Bg1BTMSr.js +265 -0
  218. package/dist/_chunks/transcript-panel-Bg1BTMSr.js.map +1 -0
  219. package/dist/_chunks/typing-indicator-BRg22Rqr.js +81 -0
  220. package/dist/_chunks/typing-indicator-BRg22Rqr.js.map +1 -0
  221. package/dist/_chunks/use-locale-BuXR_Zl9.js +32 -0
  222. package/dist/_chunks/use-locale-BuXR_Zl9.js.map +1 -0
  223. package/dist/_chunks/visually-hidden-Y3jcdCv-.js +21 -0
  224. package/dist/_chunks/visually-hidden-Y3jcdCv-.js.map +1 -0
  225. package/dist/_chunks/{warning-stack-Cyf_81SL.js → warning-stack-B9N9yWet.js} +38 -38
  226. package/dist/_chunks/warning-stack-B9N9yWet.js.map +1 -0
  227. package/dist/_chunks/{workflow-map-Uf-hHAO3.js → workflow-map-gBhL_Wrs.js} +385 -385
  228. package/dist/_chunks/workflow-map-gBhL_Wrs.js.map +1 -0
  229. package/dist/agent-catalog.json +1 -1
  230. package/dist/components/_shared/index.d.ts +2 -0
  231. package/dist/components/_shared/index.d.ts.map +1 -1
  232. package/dist/components/_shared/use-focus-trap.d.ts +32 -0
  233. package/dist/components/_shared/use-focus-trap.d.ts.map +1 -0
  234. package/dist/components/_shared/use-neutralize-hidden-focus.d.ts +3 -0
  235. package/dist/components/_shared/use-neutralize-hidden-focus.d.ts.map +1 -0
  236. package/dist/components/accordion/index.js +1 -1
  237. package/dist/components/agenda-card/index.js +1 -1
  238. package/dist/components/agenda-tray/index.js +1 -1
  239. package/dist/components/ai-prompt-input/index.js +1 -1
  240. package/dist/components/alert/index.js +1 -1
  241. package/dist/components/app-frame/index.js +1 -1
  242. package/dist/components/aspect-ratio/index.js +1 -1
  243. package/dist/components/audio-recorder/index.js +1 -1
  244. package/dist/components/audio-visualiser/index.js +1 -1
  245. package/dist/components/autocomplete/index.js +1 -1
  246. package/dist/components/avatar/index.js +1 -1
  247. package/dist/components/badge/index.js +1 -1
  248. package/dist/components/breadcrumb/index.js +1 -1
  249. package/dist/components/button/index.js +2 -2
  250. package/dist/components/button-group/index.js +1 -1
  251. package/dist/components/calendar/calendar.d.ts.map +1 -1
  252. package/dist/components/calendar/index.js +1 -1
  253. package/dist/components/card/index.js +1 -1
  254. package/dist/components/chart/chart.d.ts.map +1 -1
  255. package/dist/components/chart/index.js +1 -1
  256. package/dist/components/chat-container/index.js +1 -1
  257. package/dist/components/chat-input/index.js +1 -1
  258. package/dist/components/chat-message/index.js +1 -1
  259. package/dist/components/checkbox/index.js +1 -1
  260. package/dist/components/checkbox-group/index.js +1 -1
  261. package/dist/components/collapsible/index.js +1 -1
  262. package/dist/components/color-picker/index.js +1 -1
  263. package/dist/components/combobox/index.js +1 -1
  264. package/dist/components/command-palette/index.js +1 -1
  265. package/dist/components/data-table/index.js +1 -1
  266. package/dist/components/data-table/toolbar.d.ts.map +1 -1
  267. package/dist/components/date-picker/index.js +1 -1
  268. package/dist/components/date-range-picker/index.js +1 -1
  269. package/dist/components/date-time-picker/index.js +1 -1
  270. package/dist/components/description-list/index.js +1 -1
  271. package/dist/components/dialog/index.js +1 -1
  272. package/dist/components/dropdown-menu/index.js +1 -1
  273. package/dist/components/empty-state/index.js +1 -1
  274. package/dist/components/file-upload/index.js +1 -1
  275. package/dist/components/flag/index.js +1 -1
  276. package/dist/components/floating-action-button/index.js +1 -1
  277. package/dist/components/form-field/index.js +1 -1
  278. package/dist/components/freemium-paywall/index.js +1 -1
  279. package/dist/components/header/index.js +1 -1
  280. package/dist/components/icon-button/index.d.ts +3 -0
  281. package/dist/components/icon-button/index.d.ts.map +1 -0
  282. package/dist/components/icon-button/index.js +5 -0
  283. package/dist/components/icon-button/index.js.map +1 -0
  284. package/dist/components/icon-button-group/index.js +1 -1
  285. package/dist/components/index.d.ts +8 -2
  286. package/dist/components/index.d.ts.map +1 -1
  287. package/dist/components/kbd/index.js +1 -1
  288. package/dist/components/key-value-pair/index.js +1 -1
  289. package/dist/components/list/index.js +1 -1
  290. package/dist/components/live-region/index.d.ts +3 -0
  291. package/dist/components/live-region/index.d.ts.map +1 -0
  292. package/dist/components/live-region/index.js +5 -0
  293. package/dist/components/live-region/index.js.map +1 -0
  294. package/dist/components/live-region/live-region.d.ts +49 -0
  295. package/dist/components/live-region/live-region.d.ts.map +1 -0
  296. package/dist/components/logo/index.js +1 -1
  297. package/dist/components/matrix-rain/index.js +1 -1
  298. package/dist/components/message-card/index.js +1 -1
  299. package/dist/components/message-tray/index.js +1 -1
  300. package/dist/components/multi-select/index.js +1 -1
  301. package/dist/components/navigation-menu/index.d.ts +4 -2
  302. package/dist/components/navigation-menu/index.d.ts.map +1 -1
  303. package/dist/components/navigation-menu/index.js +3 -2
  304. package/dist/components/navigation-menu/index.js.map +1 -1
  305. package/dist/components/navigation-menu/navigation-menu.d.ts +2 -6
  306. package/dist/components/navigation-menu/navigation-menu.d.ts.map +1 -1
  307. package/dist/components/notification-card/index.js +1 -1
  308. package/dist/components/notification-tray/index.js +1 -1
  309. package/dist/components/number-input/index.js +1 -1
  310. package/dist/components/otp-input/index.js +1 -1
  311. package/dist/components/pagination/index.js +1 -1
  312. package/dist/components/password-input/index.js +1 -1
  313. package/dist/components/payment-form/index.js +1 -1
  314. package/dist/components/pdf-viewer/index.js +1 -1
  315. package/dist/components/pdf-viewer/pdf-viewer.d.ts.map +1 -1
  316. package/dist/components/phone-input/index.js +1 -1
  317. package/dist/components/popover/index.js +1 -1
  318. package/dist/components/privacy-lock/index.js +1 -1
  319. package/dist/components/progress/index.js +1 -1
  320. package/dist/components/radio/index.d.ts +3 -0
  321. package/dist/components/radio/index.d.ts.map +1 -0
  322. package/dist/components/radio/index.js +5 -0
  323. package/dist/components/radio/index.js.map +1 -0
  324. package/dist/components/radio-group/index.js +4 -3
  325. package/dist/components/radio-group/index.js.map +1 -1
  326. package/dist/components/resizable/index.js +1 -1
  327. package/dist/components/rich-text-editor/index.js +1 -1
  328. package/dist/components/scroll-area/index.js +1 -1
  329. package/dist/components/search-bar/index.js +1 -1
  330. package/dist/components/search-input/index.js +1 -1
  331. package/dist/components/select/index.js +1 -1
  332. package/dist/components/separator/index.js +1 -1
  333. package/dist/components/sheet/index.js +1 -1
  334. package/dist/components/sidebar/index.js +1 -1
  335. package/dist/components/sign-in-with-alfadocs-button/index.js +1 -1
  336. package/dist/components/sign-in-with-alfadocs-button/sign-in-with-alfadocs-button.d.ts +20 -0
  337. package/dist/components/sign-in-with-alfadocs-button/sign-in-with-alfadocs-button.d.ts.map +1 -1
  338. package/dist/components/signature-capture/index.js +1 -1
  339. package/dist/components/skeleton/index.js +1 -1
  340. package/dist/components/skip-link/index.d.ts +2 -0
  341. package/dist/components/skip-link/index.d.ts.map +1 -0
  342. package/dist/components/skip-link/index.js +5 -0
  343. package/dist/components/skip-link/index.js.map +1 -0
  344. package/dist/components/skip-link/skip-link.d.ts +34 -0
  345. package/dist/components/skip-link/skip-link.d.ts.map +1 -0
  346. package/dist/components/slider/index.js +1 -1
  347. package/dist/components/slot-grid/index.js +1 -1
  348. package/dist/components/sparkline/index.js +1 -1
  349. package/dist/components/sparkline/sparkline.d.ts.map +1 -1
  350. package/dist/components/spinner/index.js +1 -1
  351. package/dist/components/stat/index.js +1 -1
  352. package/dist/components/stepper-accordion/index.js +1 -1
  353. package/dist/components/stepper-calendar/index.js +1 -1
  354. package/dist/components/stepper-progress/index.js +1 -1
  355. package/dist/components/streaming-text/index.js +1 -1
  356. package/dist/components/suggestion-chip/index.js +1 -1
  357. package/dist/components/switch/index.js +1 -1
  358. package/dist/components/tabs/index.js +1 -1
  359. package/dist/components/tag/index.js +1 -1
  360. package/dist/components/task-card/index.js +1 -1
  361. package/dist/components/task-tray/index.js +1 -1
  362. package/dist/components/text-area/index.js +1 -1
  363. package/dist/components/text-input/index.js +1 -1
  364. package/dist/components/theme-toggle/index.d.ts +3 -0
  365. package/dist/components/theme-toggle/index.d.ts.map +1 -0
  366. package/dist/components/theme-toggle/index.js +5 -0
  367. package/dist/components/theme-toggle/index.js.map +1 -0
  368. package/dist/components/theme-toggle/theme-toggle.d.ts +34 -0
  369. package/dist/components/theme-toggle/theme-toggle.d.ts.map +1 -0
  370. package/dist/components/time-picker/index.js +1 -1
  371. package/dist/components/timeline/index.js +1 -1
  372. package/dist/components/timestamp/index.js +1 -1
  373. package/dist/components/toast/index.js +1 -1
  374. package/dist/components/tooltip/index.js +1 -1
  375. package/dist/components/tooth-scheme/index.js +1 -1
  376. package/dist/components/transcript-panel/index.js +1 -1
  377. package/dist/components/typing-indicator/index.js +1 -1
  378. package/dist/components/visually-hidden/index.d.ts +2 -0
  379. package/dist/components/visually-hidden/index.d.ts.map +1 -0
  380. package/dist/components/visually-hidden/index.js +5 -0
  381. package/dist/components/visually-hidden/index.js.map +1 -0
  382. package/dist/components/visually-hidden/visually-hidden.d.ts +11 -0
  383. package/dist/components/visually-hidden/visually-hidden.d.ts.map +1 -0
  384. package/dist/components/warning-stack/index.js +1 -1
  385. package/dist/components/workflow/index.js +1 -1
  386. package/dist/hooks/index.d.ts +3 -0
  387. package/dist/hooks/index.d.ts.map +1 -1
  388. package/dist/hooks/index.js +10 -7
  389. package/dist/hooks/index.js.map +1 -1
  390. package/dist/hooks/use-locale.d.ts +27 -0
  391. package/dist/hooks/use-locale.d.ts.map +1 -0
  392. package/dist/hooks/use-media-query.d.ts +17 -0
  393. package/dist/hooks/use-media-query.d.ts.map +1 -0
  394. package/dist/i18n/config.js +59 -2
  395. package/dist/i18n/config.js.map +1 -1
  396. package/dist/i18n/resources.d.ts +57 -0
  397. package/dist/i18n/resources.d.ts.map +1 -1
  398. package/dist/index.js +442 -431
  399. package/dist/index.js.map +1 -1
  400. package/dist/locales/de.json +19 -0
  401. package/dist/locales/en.json +19 -0
  402. package/dist/locales/it.json +19 -0
  403. package/dist/patterns/leo-assistant/index.js +1 -1
  404. package/dist/patterns/patient-shell/index.js +1 -1
  405. package/dist/tokens.css +2 -2
  406. package/package.json +4 -2
  407. package/dist/_chunks/accordion-Dgcd38U_.js +0 -117
  408. package/dist/_chunks/accordion-Dgcd38U_.js.map +0 -1
  409. package/dist/_chunks/agenda-card-B2gq-gTq.js +0 -130
  410. package/dist/_chunks/agenda-card-B2gq-gTq.js.map +0 -1
  411. package/dist/_chunks/agenda-tray-twa8g_87.js +0 -119
  412. package/dist/_chunks/agenda-tray-twa8g_87.js.map +0 -1
  413. package/dist/_chunks/ai-prompt-input-C6IY_Eds.js +0 -379
  414. package/dist/_chunks/ai-prompt-input-C6IY_Eds.js.map +0 -1
  415. package/dist/_chunks/alert-C5ud6CfE.js.map +0 -1
  416. package/dist/_chunks/apexcharts-theme-cpBYmCcr.js +0 -17
  417. package/dist/_chunks/apexcharts-theme-cpBYmCcr.js.map +0 -1
  418. package/dist/_chunks/app-frame-CmIUp9BD.js.map +0 -1
  419. package/dist/_chunks/aspect-ratio-Bo3cZXmR.js +0 -56
  420. package/dist/_chunks/aspect-ratio-Bo3cZXmR.js.map +0 -1
  421. package/dist/_chunks/audio-recorder-GkGNtW2X.js.map +0 -1
  422. package/dist/_chunks/audio-visualiser-CIO3yy23.js.map +0 -1
  423. package/dist/_chunks/autocomplete.agent-kb0nmr6F.js.map +0 -1
  424. package/dist/_chunks/avatar-BsV7vRA6.js.map +0 -1
  425. package/dist/_chunks/badge--CmSr7ok.js +0 -104
  426. package/dist/_chunks/badge--CmSr7ok.js.map +0 -1
  427. package/dist/_chunks/balance-cell-renderer-BQwN51rT.js.map +0 -1
  428. package/dist/_chunks/breadcrumb-DnO6eHSS.js.map +0 -1
  429. package/dist/_chunks/button-CXL8bA8G.js +0 -119
  430. package/dist/_chunks/button-CXL8bA8G.js.map +0 -1
  431. package/dist/_chunks/button-group-CI7LFxt3.js.map +0 -1
  432. package/dist/_chunks/calendar-BIXVzhkL.js.map +0 -1
  433. package/dist/_chunks/card-n97MNPP2.js +0 -158
  434. package/dist/_chunks/card-n97MNPP2.js.map +0 -1
  435. package/dist/_chunks/chart.agent-DkmVXNsI.js.map +0 -1
  436. package/dist/_chunks/chat-container-SR3UWxbL.js +0 -174
  437. package/dist/_chunks/chat-container-SR3UWxbL.js.map +0 -1
  438. package/dist/_chunks/chat-input-aphxDjZL.js.map +0 -1
  439. package/dist/_chunks/chat-message-CdJSD18D.js.map +0 -1
  440. package/dist/_chunks/checkbox-CfiZ0FZc.js +0 -116
  441. package/dist/_chunks/checkbox-CfiZ0FZc.js.map +0 -1
  442. package/dist/_chunks/checkbox-group-D1aqVI0-.js.map +0 -1
  443. package/dist/_chunks/collapsible-jBFm7Ah8.js.map +0 -1
  444. package/dist/_chunks/color-picker-CiotzQJF.js.map +0 -1
  445. package/dist/_chunks/combobox.agent-B6RwDSct.js.map +0 -1
  446. package/dist/_chunks/command-palette.agent-B87_OkmC.js.map +0 -1
  447. package/dist/_chunks/date-time-picker-CX_FMsLG.js.map +0 -1
  448. package/dist/_chunks/description-list-C5UPZgX6.js +0 -187
  449. package/dist/_chunks/description-list-C5UPZgX6.js.map +0 -1
  450. package/dist/_chunks/dialog.agent-B276rAQF.js.map +0 -1
  451. package/dist/_chunks/dropdown-menu-CJKvK7VG.js +0 -210
  452. package/dist/_chunks/dropdown-menu-CJKvK7VG.js.map +0 -1
  453. package/dist/_chunks/empty-state-DiG7vYBM.js.map +0 -1
  454. package/dist/_chunks/file-upload.agent-BMow5yDY.js.map +0 -1
  455. package/dist/_chunks/floating-action-button-BM9ib-Wf.js +0 -95
  456. package/dist/_chunks/floating-action-button-BM9ib-Wf.js.map +0 -1
  457. package/dist/_chunks/form-field-B4bwrccN.js +0 -121
  458. package/dist/_chunks/form-field-B4bwrccN.js.map +0 -1
  459. package/dist/_chunks/freemium-paywall-DVyNsQHS.js +0 -197
  460. package/dist/_chunks/freemium-paywall-DVyNsQHS.js.map +0 -1
  461. package/dist/_chunks/header-BpKaSL_v.js.map +0 -1
  462. package/dist/_chunks/icon-button-C1vpylg3.js +0 -45
  463. package/dist/_chunks/icon-button-C1vpylg3.js.map +0 -1
  464. package/dist/_chunks/icon-button-group-tERESY-n.js.map +0 -1
  465. package/dist/_chunks/kbd-0iPlQjgC.js.map +0 -1
  466. package/dist/_chunks/key-value-pair--Je59tAF.js.map +0 -1
  467. package/dist/_chunks/leo-sidebar-9A9AWBxX.js +0 -755
  468. package/dist/_chunks/leo-sidebar-9A9AWBxX.js.map +0 -1
  469. package/dist/_chunks/list-n8vI8Yvn.js.map +0 -1
  470. package/dist/_chunks/logo-UNWYb9p7.js.map +0 -1
  471. package/dist/_chunks/matrix-rain-Q7xTEpKu.js.map +0 -1
  472. package/dist/_chunks/message-card-g5VS5Q80.js +0 -275
  473. package/dist/_chunks/message-card-g5VS5Q80.js.map +0 -1
  474. package/dist/_chunks/message-tray-CAk-iibU.js +0 -302
  475. package/dist/_chunks/message-tray-CAk-iibU.js.map +0 -1
  476. package/dist/_chunks/multi-select.agent-Cf4pU636.js.map +0 -1
  477. package/dist/_chunks/navigation-menu-DBDsAmXc.js.map +0 -1
  478. package/dist/_chunks/notification-card-CQxJporb.js +0 -255
  479. package/dist/_chunks/notification-card-CQxJporb.js.map +0 -1
  480. package/dist/_chunks/notification-tray-CNWPuPZp.js.map +0 -1
  481. package/dist/_chunks/number-input-B1Th0DdC.js.map +0 -1
  482. package/dist/_chunks/otp-input-CmoBuZ4K.js.map +0 -1
  483. package/dist/_chunks/pagination.agent-B5KLDCMN.js.map +0 -1
  484. package/dist/_chunks/password-input-DnjNh-hQ.js.map +0 -1
  485. package/dist/_chunks/payment-form-Cf7U6r5L.js.map +0 -1
  486. package/dist/_chunks/pdf-viewer.agent-DXjgDItg.js.map +0 -1
  487. package/dist/_chunks/phone-input-C2dtIuxW.js.map +0 -1
  488. package/dist/_chunks/popover-D1woU9mP.js.map +0 -1
  489. package/dist/_chunks/privacy-lock-C1X42Sit.js.map +0 -1
  490. package/dist/_chunks/progress-DL3-izuO.js.map +0 -1
  491. package/dist/_chunks/radio-group-CXN9qNPe.js +0 -185
  492. package/dist/_chunks/radio-group-CXN9qNPe.js.map +0 -1
  493. package/dist/_chunks/resizable.agent-DVk_jJur.js.map +0 -1
  494. package/dist/_chunks/rich-text-editor.agent-BOjF4Xao.js.map +0 -1
  495. package/dist/_chunks/scroll-area-DXn-YWHY.js +0 -144
  496. package/dist/_chunks/scroll-area-DXn-YWHY.js.map +0 -1
  497. package/dist/_chunks/search-bar-DgSOAajC.js.map +0 -1
  498. package/dist/_chunks/search-input-D4tC_Prn.js +0 -197
  499. package/dist/_chunks/search-input-D4tC_Prn.js.map +0 -1
  500. package/dist/_chunks/select-B4m4upMg.js +0 -320
  501. package/dist/_chunks/select-B4m4upMg.js.map +0 -1
  502. package/dist/_chunks/separator-DExI4amU.js.map +0 -1
  503. package/dist/_chunks/sheet-D6XOx0fK.js +0 -188
  504. package/dist/_chunks/sheet-D6XOx0fK.js.map +0 -1
  505. package/dist/_chunks/sidebar-r645O-k9.js +0 -1076
  506. package/dist/_chunks/sidebar-r645O-k9.js.map +0 -1
  507. package/dist/_chunks/sign-in-with-alfadocs-button-y3gy-W-D.js +0 -38
  508. package/dist/_chunks/sign-in-with-alfadocs-button-y3gy-W-D.js.map +0 -1
  509. package/dist/_chunks/signature-capture.agent-L_1_-zVR.js.map +0 -1
  510. package/dist/_chunks/skeleton-vbTWUntB.js +0 -95
  511. package/dist/_chunks/skeleton-vbTWUntB.js.map +0 -1
  512. package/dist/_chunks/slider-B3DBwmcm.js +0 -273
  513. package/dist/_chunks/slider-B3DBwmcm.js.map +0 -1
  514. package/dist/_chunks/slot-grid-DL_Tuj0p.js +0 -491
  515. package/dist/_chunks/slot-grid-DL_Tuj0p.js.map +0 -1
  516. package/dist/_chunks/sparkline.agent-BDTXzTy9.js.map +0 -1
  517. package/dist/_chunks/spinner-77xUGpuX.js.map +0 -1
  518. package/dist/_chunks/stat-CrumvZWf.js +0 -208
  519. package/dist/_chunks/stat-CrumvZWf.js.map +0 -1
  520. package/dist/_chunks/stepper-accordion-BkvqQWPI.js.map +0 -1
  521. package/dist/_chunks/stepper-calendar-BQMjrwzo.js.map +0 -1
  522. package/dist/_chunks/stepper-progress-Bwfl-0rV.js +0 -128
  523. package/dist/_chunks/stepper-progress-Bwfl-0rV.js.map +0 -1
  524. package/dist/_chunks/streaming-text-Cu9YQe_1.js +0 -104
  525. package/dist/_chunks/streaming-text-Cu9YQe_1.js.map +0 -1
  526. package/dist/_chunks/suggestion-chip-XkxDJiPW.js +0 -157
  527. package/dist/_chunks/suggestion-chip-XkxDJiPW.js.map +0 -1
  528. package/dist/_chunks/switch-DyC0ThNT.js +0 -137
  529. package/dist/_chunks/switch-DyC0ThNT.js.map +0 -1
  530. package/dist/_chunks/tabs.agent-FsGU3sZL.js.map +0 -1
  531. package/dist/_chunks/tag-BucAvfTX.js +0 -194
  532. package/dist/_chunks/tag-BucAvfTX.js.map +0 -1
  533. package/dist/_chunks/task-card-uxjApfcg.js +0 -112
  534. package/dist/_chunks/task-card-uxjApfcg.js.map +0 -1
  535. package/dist/_chunks/task-tray-BdfM3p8N.js +0 -99
  536. package/dist/_chunks/task-tray-BdfM3p8N.js.map +0 -1
  537. package/dist/_chunks/text-area-CKxZ7cS3.js +0 -152
  538. package/dist/_chunks/text-area-CKxZ7cS3.js.map +0 -1
  539. package/dist/_chunks/text-input-D16CcMlL.js +0 -187
  540. package/dist/_chunks/text-input-D16CcMlL.js.map +0 -1
  541. package/dist/_chunks/time-picker-BapZq0OY.js.map +0 -1
  542. package/dist/_chunks/timeline-D3VOf4RT.js +0 -219
  543. package/dist/_chunks/timeline-D3VOf4RT.js.map +0 -1
  544. package/dist/_chunks/timestamp-BLM-jjdw.js.map +0 -1
  545. package/dist/_chunks/tooltip-DF6WjD1r.js.map +0 -1
  546. package/dist/_chunks/tooth-scheme.agent-CFKl3iQr.js.map +0 -1
  547. package/dist/_chunks/transcript-panel-aI14fHYA.js +0 -265
  548. package/dist/_chunks/transcript-panel-aI14fHYA.js.map +0 -1
  549. package/dist/_chunks/typing-indicator-BTVDEpnd.js +0 -81
  550. package/dist/_chunks/typing-indicator-BTVDEpnd.js.map +0 -1
  551. package/dist/_chunks/warning-stack-Cyf_81SL.js.map +0 -1
  552. package/dist/_chunks/workflow-map-Uf-hHAO3.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tooth-scheme.agent-BRqxWa1D.js","sources":["../../src/components/tooth-scheme/tooth-data.ts","../../src/components/tooth-scheme/tooth-scheme.tsx","../../src/components/tooth-scheme/tooth-scheme.agent.ts"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* ToothScheme — data model + lookup tables. */\n/* */\n/* FDI ISO 3950 is the canonical internal id. This module exposes */\n/* conversion tables to Universal (ADA) and a simplified Palmer-style */\n/* quadrant+position string so consuming UIs can toggle numbering. */\n/* */\n/* Why simplified Palmer? True Palmer notation uses quadrant bracket */\n/* glyphs (\"⌐\", \"¬\", \"L\", \"J\") that do not render reliably in every */\n/* browser/font combination. We emit a readable `UR3` / `LL6` form */\n/* instead and document the deviation in the component MDX. */\n/* ------------------------------------------------------------------ */\n\n/* ------------------------------------------------------------------ */\n/* Core types */\n/* ------------------------------------------------------------------ */\n\nexport type FdiId = string; // '11'–'48' permanent, '51'–'85' primary\n\nexport type Dentition = 'permanent' | 'primary' | 'mixed';\nexport type Numbering = 'fdi' | 'universal' | 'palmer';\nexport type ToothMode = 'interactive' | 'display';\nexport type Surface = 'mesial' | 'distal' | 'occlusal' | 'buccal' | 'lingual';\nexport type ToothCondition =\n | 'caries'\n | 'filled'\n | 'crowned'\n | 'missing'\n | 'implant'\n | 'rootCanal';\n\nexport interface ToothState {\n conditions: ToothCondition[];\n surfaces: Surface[];\n notes?: string;\n}\n\nexport type ToothChart = Record<FdiId, ToothState>;\n\nexport type Anatomy = 'incisor' | 'canine' | 'premolar' | 'molar';\nexport type Arch = 'upper' | 'lower';\nexport type Side = 'right' | 'left'; // patient's side (NOT viewer's)\nexport type Quadrant = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;\n\nexport interface ToothMeta {\n fdi: FdiId;\n /** '1'–'32' for permanent, 'A'–'T' for primary. */\n universal: string;\n /** Simplified Palmer quadrant-and-position string, e.g. 'UR3', 'LL6'. */\n palmer: string;\n quadrant: Quadrant;\n /** 1–8 (permanent) or 1–5 (primary), counted from the midline outward. */\n positionInQuadrant: number;\n anatomy: Anatomy;\n arch: Arch;\n side: Side;\n dentition: 'permanent' | 'primary';\n}\n\n/* ------------------------------------------------------------------ */\n/* Layout orders */\n/* */\n/* Layout reads from the viewer's perspective, top-left to bottom- */\n/* right. The chart is FIXED in clinical convention: the patient's */\n/* right side sits on the viewer's left, regardless of document `dir`. */\n/* ------------------------------------------------------------------ */\n\n/**\n * 32 permanent teeth in visual layout order:\n * upper row: 18..11 (patient upper-right, outermost → midline), then 21..28 (upper-left, midline → outermost)\n * lower row: 48..41 (patient lower-right, outermost → midline), then 31..38 (lower-left, midline → outermost)\n */\nexport const PERMANENT_TEETH: FdiId[] = [\n // upper\n '18', '17', '16', '15', '14', '13', '12', '11',\n '21', '22', '23', '24', '25', '26', '27', '28',\n // lower\n '48', '47', '46', '45', '44', '43', '42', '41',\n '31', '32', '33', '34', '35', '36', '37', '38',\n];\n\n/**\n * 20 primary teeth in visual layout order:\n * upper row: 55..51 (upper-right outermost → midline), then 61..65\n * lower row: 85..81, then 71..75\n */\nexport const PRIMARY_TEETH: FdiId[] = [\n // upper\n '55', '54', '53', '52', '51',\n '61', '62', '63', '64', '65',\n // lower\n '85', '84', '83', '82', '81',\n '71', '72', '73', '74', '75',\n];\n\n/* ------------------------------------------------------------------ */\n/* Anatomy by position */\n/* ------------------------------------------------------------------ */\n\nfunction permanentAnatomy(positionInQuadrant: number): Anatomy {\n if (positionInQuadrant <= 2) return 'incisor';\n if (positionInQuadrant === 3) return 'canine';\n if (positionInQuadrant <= 5) return 'premolar';\n return 'molar';\n}\n\nfunction primaryAnatomy(positionInQuadrant: number): Anatomy {\n if (positionInQuadrant <= 2) return 'incisor';\n if (positionInQuadrant === 3) return 'canine';\n // Primary dentition has no premolars — positions 4 & 5 are molars.\n return 'molar';\n}\n\n/* ------------------------------------------------------------------ */\n/* Palmer helper */\n/* ------------------------------------------------------------------ */\n\nfunction palmerCode(quadrant: Quadrant, position: number): string {\n // Permanent: 1=UR, 2=UL, 3=LL, 4=LR. Primary: 5=UR, 6=UL, 7=LL, 8=LR.\n // Mapping is identical within each dentition ring; the number is what\n // changes, so we fold 5→1, 6→2, 7→3, 8→4 to derive the quadrant code.\n const normalised = ((quadrant - 1) % 4) + 1;\n const code =\n normalised === 1 ? 'UR'\n : normalised === 2 ? 'UL'\n : normalised === 3 ? 'LL'\n : 'LR';\n return `${code}${position}`;\n}\n\n/* ------------------------------------------------------------------ */\n/* Universal (ADA) conversion */\n/* */\n/* Permanent — Universal numbers 1..32 run clockwise from the */\n/* patient's upper-right 3rd molar (FDI 18 = Universal 1), across */\n/* the upper arch (18→11 = 1→8, 21→28 = 9→16), then down the left */\n/* lower arch (38→31 = 17→24) and across the right lower (41→48 = */\n/* 25→32). */\n/* */\n/* Primary — Universal letters A..T walk the same path: */\n/* 55→51 = A→E, 61→65 = F→J, 75→71 = K→O, 81→85 = P→T. */\n/* ------------------------------------------------------------------ */\n\nfunction permanentUniversal(quadrant: Quadrant, position: number): string {\n switch (quadrant) {\n case 1:\n // 18→1, 17→2, …, 11→8\n return String(1 + (8 - position));\n case 2:\n // 21→9, 22→10, …, 28→16\n return String(8 + position);\n case 3:\n // 38→17, 37→18, …, 31→24\n return String(17 + (8 - position));\n case 4:\n // 41→25, 42→26, …, 48→32\n return String(24 + position);\n default:\n return '';\n }\n}\n\nfunction primaryUniversal(quadrant: Quadrant, position: number): string {\n const letters = 'ABCDEFGHIJKLMNOPQRST';\n let index = -1;\n switch (quadrant) {\n case 5:\n // 55→A, 54→B, 53→C, 52→D, 51→E\n index = 0 + (5 - position);\n break;\n case 6:\n // 61→F, 62→G, 63→H, 64→I, 65→J\n index = 5 + (position - 1);\n break;\n case 7:\n // 75→K, 74→L, 73→M, 72→N, 71→O\n index = 10 + (5 - position);\n break;\n case 8:\n // 81→P, 82→Q, 83→R, 84→S, 85→T\n index = 15 + (position - 1);\n break;\n default:\n index = -1;\n }\n return index >= 0 ? letters[index] : '';\n}\n\n/* ------------------------------------------------------------------ */\n/* Meta builder */\n/* ------------------------------------------------------------------ */\n\nfunction buildMeta(fdi: FdiId): ToothMeta {\n const quadrant = Number(fdi[0]) as Quadrant;\n const position = Number(fdi[1]);\n const isPrimary = quadrant >= 5;\n const anatomy = isPrimary ? primaryAnatomy(position) : permanentAnatomy(position);\n const arch: Arch = quadrant === 1 || quadrant === 2 || quadrant === 5 || quadrant === 6 ? 'upper' : 'lower';\n const side: Side = quadrant === 1 || quadrant === 4 || quadrant === 5 || quadrant === 8 ? 'right' : 'left';\n const universal = isPrimary ? primaryUniversal(quadrant, position) : permanentUniversal(quadrant, position);\n const palmer = palmerCode(quadrant, position);\n return {\n fdi,\n universal,\n palmer,\n quadrant,\n positionInQuadrant: position,\n anatomy,\n arch,\n side,\n dentition: isPrimary ? 'primary' : 'permanent',\n };\n}\n\nexport const FDI_TO_META: Record<FdiId, ToothMeta> = [\n ...PERMANENT_TEETH,\n ...PRIMARY_TEETH,\n].reduce<Record<FdiId, ToothMeta>>((acc, fdi) => {\n acc[fdi] = buildMeta(fdi);\n return acc;\n}, {});\n\nexport const FDI_TO_UNIVERSAL: Record<FdiId, string> = Object.fromEntries(\n Object.entries(FDI_TO_META).map(([fdi, meta]) => [fdi, meta.universal]),\n);\n\nexport const FDI_TO_PALMER: Record<FdiId, string> = Object.fromEntries(\n Object.entries(FDI_TO_META).map(([fdi, meta]) => [fdi, meta.palmer]),\n);\n\n/* ------------------------------------------------------------------ */\n/* Condition → token + pattern */\n/* */\n/* Every condition carries BOTH a colour AND a fill pattern so */\n/* colour-blind clinicians can distinguish states in the SVG (and in */\n/* black-and-white prints) — colour is never the sole channel. */\n/* ------------------------------------------------------------------ */\n\n/** CSS variable name (without the leading `var(...)`) that drives a condition's colour. */\nexport const CONDITION_TOKENS: Record<ToothCondition, string> = {\n caries: '--destructive',\n filled: '--info',\n crowned: '--warning',\n missing: '--muted-foreground',\n implant: '--accent',\n rootCanal: '--primary',\n};\n\n/**\n * Convenience: the `fill=\"var(--destructive)\"` string ready to paste onto\n * an SVG attribute. Kept in sync with `CONDITION_TOKENS`.\n */\nexport const CONDITION_COLORS: Record<ToothCondition, string> = {\n caries: 'var(--destructive)',\n filled: 'var(--info)',\n crowned: 'var(--warning)',\n missing: 'var(--muted-foreground)',\n implant: 'var(--accent)',\n rootCanal: 'var(--primary)',\n};\n\n/** SVG `<pattern>` ids declared in `<defs>`. `missing` renders an X glyph instead of a pattern. */\nexport const CONDITION_PATTERNS: Record<ToothCondition, string> = {\n caries: 'tooth-scheme-pattern-diagonal',\n filled: 'tooth-scheme-pattern-dots',\n crowned: 'tooth-scheme-pattern-crosshatch',\n missing: 'tooth-scheme-pattern-blank',\n implant: 'tooth-scheme-pattern-vertical',\n rootCanal: 'tooth-scheme-pattern-horizontal',\n};\n\n/* ------------------------------------------------------------------ */\n/* SVG shape paths — one per anatomy class */\n/* */\n/* Artistry is deliberately minimal for this first pass: each tooth is */\n/* a rounded rectangle with a small anatomy cue (cusp notch for */\n/* molars, pointed tip for canines, narrower body for incisors). The */\n/* shapes accept a `translate(x, y)` on the enclosing `<g>` and draw */\n/* within a 30×50 bounding box (22×50 for incisors). */\n/* ------------------------------------------------------------------ */\n\nexport interface ToothShape {\n /** Visual width inside the enclosing `<g>`. */\n width: number;\n /** Visual height inside the enclosing `<g>`. */\n height: number;\n /** SVG `d` attribute for the crown outline. */\n path: string;\n /** Optional inner decorative path (occlusal surface on molars, etc.). */\n innerPath?: string;\n}\n\nexport const TOOTH_SHAPES: Record<Anatomy, ToothShape> = {\n incisor: {\n width: 22,\n height: 50,\n // Narrow rounded rectangle — slightly wider at the occlusal/biting edge.\n path: 'M4 4 C4 2 6 0 8 0 L14 0 C16 0 18 2 18 4 L20 44 C20 47 18 50 14 50 L8 50 C4 50 2 47 2 44 Z',\n },\n canine: {\n width: 26,\n height: 50,\n // Pointed cusp at the bottom — canines present a single prominent cusp.\n path: 'M4 4 C4 2 6 0 8 0 L18 0 C20 0 22 2 22 4 L24 40 L13 50 L2 40 Z',\n },\n premolar: {\n width: 28,\n height: 50,\n // Wider rounded rectangle with a subtle horizontal notch hinting at two cusps.\n path: 'M4 4 C4 2 6 0 8 0 L20 0 C22 0 24 2 24 4 L26 44 C26 47 24 50 20 50 L8 50 C4 50 2 47 2 44 Z',\n innerPath: 'M6 34 L22 34',\n },\n molar: {\n width: 32,\n height: 50,\n // Widest rounded rectangle with a cross-notch hinting at the occlusal fissure pattern.\n path: 'M4 4 C4 2 6 0 8 0 L24 0 C26 0 28 2 28 4 L30 44 C30 47 28 50 24 50 L8 50 C4 50 2 47 2 44 Z',\n innerPath: 'M6 32 L26 32 M16 22 L16 42',\n },\n};\n\n/* ------------------------------------------------------------------ */\n/* Viewport layout helpers */\n/* ------------------------------------------------------------------ */\n\nexport const TOOTH_GAP = 4;\nexport const TOOTH_ROW_GAP = 24;\nexport const TOOTH_HIT_PAD = 4;\n\nexport interface PositionedTooth {\n fdi: FdiId;\n meta: ToothMeta;\n shape: ToothShape;\n x: number;\n y: number;\n}\n\n/**\n * Produce the positioned list of teeth for a given dentition. The layout\n * is a two-row grid reading viewer-left-to-right across the upper arch,\n * then viewer-left-to-right across the lower arch. Clinical convention\n * is preserved: the patient's right sits on the viewer's left in both\n * arches, regardless of document direction.\n */\nexport function layoutTeeth(dentition: Dentition): {\n teeth: PositionedTooth[];\n width: number;\n height: number;\n} {\n const ids: FdiId[] =\n dentition === 'primary'\n ? PRIMARY_TEETH\n : dentition === 'mixed'\n ? [...PERMANENT_TEETH, ...PRIMARY_TEETH]\n : PERMANENT_TEETH;\n\n // Upper / lower partition is inferred from meta.arch.\n const upper: FdiId[] = [];\n const lower: FdiId[] = [];\n for (const id of ids) {\n const meta = FDI_TO_META[id];\n if (meta.arch === 'upper') upper.push(id);\n else lower.push(id);\n }\n\n const rowHeight = 50;\n const maxPerRow = Math.max(upper.length, lower.length);\n\n // Centre each row on the widest one.\n function positionRow(row: FdiId[], yOffset: number): PositionedTooth[] {\n const rowWidth = row.reduce((acc, id) => {\n const shape = TOOTH_SHAPES[FDI_TO_META[id].anatomy];\n return acc + shape.width + TOOTH_GAP;\n }, 0);\n const fullRowWidth = row.length > 0 ? rowWidth - TOOTH_GAP : 0;\n\n const maxRowWidth = (function computeMax(): number {\n const bigger = upper.length >= lower.length ? upper : lower;\n const biggerWidth = bigger.reduce((acc, id) => {\n const shape = TOOTH_SHAPES[FDI_TO_META[id].anatomy];\n return acc + shape.width + TOOTH_GAP;\n }, 0);\n return biggerWidth - TOOTH_GAP;\n })();\n\n const startX = (maxRowWidth - fullRowWidth) / 2;\n let cursor = startX;\n return row.map((id) => {\n const meta = FDI_TO_META[id];\n const shape = TOOTH_SHAPES[meta.anatomy];\n const tooth: PositionedTooth = {\n fdi: id,\n meta,\n shape,\n x: cursor,\n y: yOffset,\n };\n cursor += shape.width + TOOTH_GAP;\n return tooth;\n });\n }\n\n const upperRow = positionRow(upper, 0);\n const lowerRow = positionRow(lower, rowHeight + TOOTH_ROW_GAP);\n\n // Compute the true content width / height for the viewBox.\n const maxRowWidth = Math.max(\n upperRow.reduce((acc, t) => Math.max(acc, t.x + t.shape.width), 0),\n lowerRow.reduce((acc, t) => Math.max(acc, t.x + t.shape.width), 0),\n );\n\n // Fall back to a reasonable default if a row is empty.\n const width = maxRowWidth > 0 ? maxRowWidth : maxPerRow * 30;\n const height = rowHeight * 2 + TOOTH_ROW_GAP;\n\n return { teeth: [...upperRow, ...lowerRow], width, height };\n}\n\n/* ------------------------------------------------------------------ */\n/* Labels by numbering */\n/* ------------------------------------------------------------------ */\n\nexport function labelFor(id: FdiId, numbering: Numbering): string {\n switch (numbering) {\n case 'universal':\n return FDI_TO_UNIVERSAL[id] ?? id;\n case 'palmer':\n return FDI_TO_PALMER[id] ?? id;\n case 'fdi':\n default:\n return id;\n }\n}\n\n/* ------------------------------------------------------------------ */\n/* Chart helpers */\n/* ------------------------------------------------------------------ */\n\nexport function emptyChart(): ToothChart {\n return {};\n}\n\nexport function chartFromConditions(\n conditions: Partial<Record<FdiId, ToothCondition[]>>,\n): ToothChart {\n const chart: ToothChart = {};\n for (const [id, list] of Object.entries(conditions)) {\n if (!list || list.length === 0) continue;\n chart[id] = { conditions: list, surfaces: [] };\n }\n return chart;\n}\n","/* ------------------------------------------------------------------ */\n/* ToothScheme — in-house SVG dental chart. */\n/* */\n/* - The ONLY Domain-Specific component without a third-party engine. */\n/* No DentalChart.js, no toothchart, no canvas — `src/docs/08-third- */\n/* party.mdx §Dental chart` flags every candidate as rejected (jQuery */\n/* dependency, abandoned, poor a11y), so we draw SVG ourselves. */\n/* */\n/* - Colour-not-alone: each `ToothCondition` has a translated label, a */\n/* token colour AND a `<pattern>` fill. Pattern ids live in */\n/* `CONDITION_PATTERNS`; the six `<pattern>` nodes live once per */\n/* mounted chart inside a single `<defs>` block. */\n/* */\n/* - Chart orientation is FIXED to clinical convention: the patient's */\n/* right side sits on the viewer's left in both upper and lower */\n/* arches, regardless of document `dir`. RTL mirrors the surrounding */\n/* chrome (labels, controls) but NEVER the anatomy — a mirrored */\n/* chart would invert every diagnosis recorded against it. */\n/* */\n/* TODO: */\n/* - SVG artistry is deliberately minimal (rounded-rect crowns with */\n/* anatomy cues). A future pass should replace each `<path>` with */\n/* a clinically-correct crown outline from the dental design team. */\n/* - Surface selection sub-control (mesial / distal / occlusal / …) */\n/* is stubbed via the data model only; the interactive surface */\n/* picker will land in a follow-up. */\n/* - Condition editing is a single-step cycle on Space/Enter — good */\n/* enough for demos and stories; a richer picker belongs in a */\n/* popover and will reuse Radix Popover when it's wired in. */\n/* ------------------------------------------------------------------ */\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent,\n} from 'react';\nimport * as RadixTooltip from '@radix-ui/react-tooltip';\nimport { cva } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\n\nimport {\n CONDITION_COLORS,\n CONDITION_PATTERNS,\n CONDITION_TOKENS,\n FDI_TO_META,\n PERMANENT_TEETH,\n PRIMARY_TEETH,\n TOOTH_HIT_PAD,\n chartFromConditions,\n labelFor,\n layoutTeeth,\n type Dentition,\n type FdiId,\n type Numbering,\n type ToothChart,\n type ToothCondition,\n type ToothMode,\n type ToothState,\n} from './tooth-data';\n\n/* ------------------------------------------------------------------ */\n/* Public types */\n/* ------------------------------------------------------------------ */\n\nexport interface ToothSchemeProps {\n /** Which teeth to render. Default `'permanent'`. */\n dentition?: Dentition;\n /** Numbering system shown on each tooth label. Default `'fdi'`. */\n numbering?: Numbering;\n /** `'interactive'` enables selection + keyboard nav; `'display'` is read-only. Default `'interactive'`. */\n mode?: ToothMode;\n /** Controlled chart state — the full `Record<FdiId, ToothState>`. */\n value?: ToothChart;\n /** Uncontrolled initial chart state. */\n defaultValue?: ToothChart;\n /** Fired whenever the chart changes — always emits FDI ids. */\n onChange?: (next: ToothChart) => void;\n /** Fired when a tooth is focused or clicked. */\n onSelect?: (toothId: FdiId) => void;\n /** Shortcut for pre-populating conditions: `{ '16': ['caries'], '36': ['filled'] }`. */\n conditions?: Partial<Record<FdiId, ToothCondition[]>>;\n /** Accessible label override for the chart region. */\n ariaLabel?: string;\n /** Extra class names on the wrapper. */\n className?: string;\n}\n\nexport interface ToothSchemeHandle {\n /** Programmatically focus a tooth by FDI id. */\n focusTooth: (id: FdiId) => void;\n /** Read the current chart state. */\n getChart: () => ToothChart;\n}\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nconst rootVariants = cva(\n [\n 'ds:tooth-scheme-alfadocs',\n 'ds:inline-flex ds:flex-col',\n 'ds:gap-[var(--spacing-sm)]',\n 'ds:bg-[var(--background)] ds:text-[var(--foreground)]',\n ].join(' '),\n);\n\nconst svgVariants = cva(\n [\n 'ds:block ds:max-w-full ds:h-auto',\n 'ds:text-[var(--foreground)]',\n ].join(' '),\n);\n\n// `group` here is load-bearing — the focus-ring <rect> below uses\n// `group-focus-visible:opacity-100` to appear when the parent <g> takes\n// focus. Without this class the selector never fires and keyboard users\n// see no focus indicator (a11y-critical-fixes.mdx).\nconst toothGroupVariants = cva(\n [\n 'ds:group ds:cursor-pointer',\n 'ds:focus:outline-none',\n 'ds:transition-[fill,stroke,opacity] ds:duration-[var(--animation-duration)]',\n 'ds:motion-reduce:transition-none',\n ].join(' '),\n);\n\nconst legendVariants = cva(\n [\n 'ds:flex ds:flex-wrap ds:items-center',\n 'ds:gap-[var(--spacing-sm)]',\n 'type-meta ds:text-[var(--muted-foreground)]',\n ].join(' '),\n);\n\nconst legendItemVariants = cva(\n [\n 'ds:inline-flex ds:items-center',\n 'ds:gap-[var(--spacing-xs)]',\n ].join(' '),\n);\n\nconst legendSwatchVariants = cva(\n [\n 'ds:inline-block',\n 'ds:inline-size-[0.75rem] ds:block-size-[0.75rem]',\n 'ds:rounded-[var(--radius-xs)]',\n 'ds:border ds:border-[color:var(--border)]',\n ].join(' '),\n);\n\nconst liveRegionVariants = cva(\n [\n 'ds:sr-only',\n ].join(' '),\n);\n\n/* ------------------------------------------------------------------ */\n/* Helpers */\n/* ------------------------------------------------------------------ */\n\nfunction isControlled(props: Pick<ToothSchemeProps, 'value'>): boolean {\n return props.value !== undefined;\n}\n\nfunction emptyState(): ToothState {\n return { conditions: [], surfaces: [] };\n}\n\nfunction cycleCondition(current: ToothCondition[] | undefined): ToothCondition[] {\n if (!current || current.length === 0) return ['caries'];\n return [];\n}\n\nfunction sanitiseIdSuffix(raw: string): string {\n return raw.replace(/[^a-zA-Z0-9-_]/g, '');\n}\n\n/* ------------------------------------------------------------------ */\n/* Patterns <defs> */\n/* ------------------------------------------------------------------ */\n\ninterface PatternDefsProps {\n idPrefix: string;\n}\n\nfunction PatternDefs({ idPrefix }: PatternDefsProps): JSX.Element {\n // We namespace pattern ids per-instance so multiple ToothScheme charts\n // on a page don't collide. References inside the component use\n // `${idPrefix}-${CONDITION_PATTERNS[condition]}`.\n const pid = (id: string): string => `${idPrefix}-${id}`;\n return (\n <defs>\n <pattern\n id={pid(CONDITION_PATTERNS.caries)}\n patternUnits=\"userSpaceOnUse\"\n width=\"6\"\n height=\"6\"\n patternTransform=\"rotate(45)\"\n >\n <line\n x1=\"0\"\n y1=\"0\"\n x2=\"0\"\n y2=\"6\"\n stroke={CONDITION_COLORS.caries}\n strokeWidth=\"2\"\n />\n </pattern>\n <pattern\n id={pid(CONDITION_PATTERNS.filled)}\n patternUnits=\"userSpaceOnUse\"\n width=\"5\"\n height=\"5\"\n >\n <circle cx=\"2.5\" cy=\"2.5\" r=\"1\" fill={CONDITION_COLORS.filled} />\n </pattern>\n <pattern\n id={pid(CONDITION_PATTERNS.crowned)}\n patternUnits=\"userSpaceOnUse\"\n width=\"6\"\n height=\"6\"\n >\n <path\n d=\"M0 0 L6 6 M6 0 L0 6\"\n stroke={CONDITION_COLORS.crowned}\n strokeWidth=\"1\"\n />\n </pattern>\n <pattern\n id={pid(CONDITION_PATTERNS.implant)}\n patternUnits=\"userSpaceOnUse\"\n width=\"4\"\n height=\"4\"\n >\n <line\n x1=\"1\"\n y1=\"0\"\n x2=\"1\"\n y2=\"4\"\n stroke={CONDITION_COLORS.implant}\n strokeWidth=\"1\"\n />\n </pattern>\n <pattern\n id={pid(CONDITION_PATTERNS.rootCanal)}\n patternUnits=\"userSpaceOnUse\"\n width=\"4\"\n height=\"4\"\n >\n <line\n x1=\"0\"\n y1=\"1\"\n x2=\"4\"\n y2=\"1\"\n stroke={CONDITION_COLORS.rootCanal}\n strokeWidth=\"1\"\n />\n </pattern>\n {/* `missing` does not need a pattern — the renderer draws an X glyph. */}\n </defs>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Legend */\n/* ------------------------------------------------------------------ */\n\nconst ALL_CONDITIONS: ToothCondition[] = [\n 'caries',\n 'filled',\n 'crowned',\n 'missing',\n 'implant',\n 'rootCanal',\n];\n\ninterface LegendProps {\n idPrefix: string;\n}\n\nfunction Legend({ idPrefix: _idPrefix }: LegendProps): JSX.Element {\n const { t } = useTranslation();\n return (\n <ul\n className={legendVariants()}\n data-testid=\"tooth-scheme-legend\"\n aria-label={t('toothScheme.legendLabel')}\n >\n {ALL_CONDITIONS.map((condition) => (\n <li key={condition} className={legendItemVariants()}>\n <span\n className={legendSwatchVariants()}\n aria-hidden=\"true\"\n data-condition={condition}\n data-token={CONDITION_TOKENS[condition]}\n data-testid={`tooth-scheme-legend-swatch-${condition}`}\n />\n <span>{t(`toothScheme.condition.${condition}`)}</span>\n </li>\n ))}\n </ul>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport const ToothScheme = forwardRef<ToothSchemeHandle, ToothSchemeProps>(\n (\n {\n dentition = 'permanent',\n numbering = 'fdi',\n mode = 'interactive',\n value,\n defaultValue,\n onChange,\n onSelect,\n conditions,\n ariaLabel,\n className,\n },\n ref,\n ) => {\n const { t } = useTranslation();\n\n const rawId = useId();\n const idPrefix = useMemo(\n () => `ts-${sanitiseIdSuffix(rawId)}`,\n [rawId],\n );\n\n // Chart state — controlled / uncontrolled.\n const [internalChart, setInternalChart] = useState<ToothChart>(() => {\n if (value !== undefined) return value;\n if (defaultValue !== undefined) return defaultValue;\n if (conditions) return chartFromConditions(conditions);\n return {};\n });\n\n const chart: ToothChart = isControlled({ value }) ? (value as ToothChart) : internalChart;\n\n // Keep uncontrolled chart in sync when `conditions` shortcut updates.\n useEffect(() => {\n if (isControlled({ value })) return;\n if (conditions === undefined) return;\n setInternalChart(chartFromConditions(conditions));\n // Intentionally depend on a stable key — swapping the entire prop\n // snapshot is the consumer's signal to reset.\n }, [conditions, value]);\n\n // Layout.\n const layout = useMemo(() => layoutTeeth(dentition), [dentition]);\n const visibleIds = useMemo(\n () => layout.teeth.map((t) => t.fdi),\n [layout],\n );\n\n // Roving tabindex: which tooth owns `tabindex=\"0\"`.\n const firstId: FdiId | undefined = visibleIds[0];\n const [focusedId, setFocusedId] = useState<FdiId | undefined>(firstId);\n useEffect(() => {\n if (!focusedId || !visibleIds.includes(focusedId)) {\n setFocusedId(firstId);\n }\n }, [firstId, focusedId, visibleIds]);\n\n // Live-region announcement text.\n const [announcement, setAnnouncement] = useState<string>('');\n\n const svgRef = useRef<SVGSVGElement>(null);\n\n const commitChart = useCallback(\n (next: ToothChart) => {\n if (!isControlled({ value })) {\n setInternalChart(next);\n }\n onChange?.(next);\n },\n [onChange, value],\n );\n\n const focusToothById = useCallback((id: FdiId) => {\n const svg = svgRef.current;\n if (!svg) return;\n const node = svg.querySelector<SVGGElement>(`g[data-fdi=\"${id}\"]`);\n if (node) {\n node.focus();\n setFocusedId(id);\n }\n }, []);\n\n useImperativeHandle(\n ref,\n (): ToothSchemeHandle => ({\n focusTooth: (id: FdiId) => {\n focusToothById(id);\n },\n getChart: () => chart,\n }),\n [chart, focusToothById],\n );\n\n /* ------------------------------------------------------------- */\n /* Keyboard navigation */\n /* ------------------------------------------------------------- */\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<SVGGElement>, id: FdiId) => {\n if (mode !== 'interactive') return;\n const meta = FDI_TO_META[id];\n if (!meta) return;\n\n // Resolve the ordered list of visible ids for Left/Right.\n const order = visibleIds;\n const currentIndex = order.indexOf(id);\n\n switch (event.key) {\n case 'ArrowRight': {\n event.preventDefault();\n const nextIndex = Math.min(order.length - 1, currentIndex + 1);\n focusToothById(order[nextIndex]);\n return;\n }\n case 'ArrowLeft': {\n event.preventDefault();\n const nextIndex = Math.max(0, currentIndex - 1);\n focusToothById(order[nextIndex]);\n return;\n }\n case 'ArrowUp':\n case 'ArrowDown': {\n event.preventDefault();\n // Map to the opposing arch, same FDI position where possible.\n // Permanent: 1 ↔ 4, 2 ↔ 3. Primary: 5 ↔ 8, 6 ↔ 7.\n const quadrantMap: Record<number, number> = {\n 1: 4,\n 4: 1,\n 2: 3,\n 3: 2,\n 5: 8,\n 8: 5,\n 6: 7,\n 7: 6,\n };\n const targetQuadrant = quadrantMap[meta.quadrant];\n if (!targetQuadrant) return;\n const candidate = `${targetQuadrant}${meta.positionInQuadrant}`;\n if (order.includes(candidate)) {\n focusToothById(candidate);\n }\n return;\n }\n case 'Home': {\n event.preventDefault();\n // First tooth in the same quadrant (visible).\n const inQuadrant = order.filter(\n (other) => FDI_TO_META[other].quadrant === meta.quadrant,\n );\n if (inQuadrant.length > 0) {\n focusToothById(inQuadrant[0]);\n }\n return;\n }\n case 'End': {\n event.preventDefault();\n const inQuadrant = order.filter(\n (other) => FDI_TO_META[other].quadrant === meta.quadrant,\n );\n if (inQuadrant.length > 0) {\n focusToothById(inQuadrant[inQuadrant.length - 1]);\n }\n return;\n }\n case ' ':\n case 'Enter': {\n event.preventDefault();\n const prev = chart[id]?.conditions;\n const nextConditions = cycleCondition(prev);\n const next: ToothChart = { ...chart };\n if (nextConditions.length === 0) {\n delete next[id];\n setAnnouncement(t('toothScheme.deselected', { id }));\n } else {\n next[id] = {\n ...(chart[id] ?? emptyState()),\n conditions: nextConditions,\n };\n setAnnouncement(\n t('toothScheme.selected', {\n id,\n conditions: nextConditions\n .map((c) => t(`toothScheme.condition.${c}`))\n .join(', '),\n }),\n );\n }\n commitChart(next);\n onSelect?.(id);\n return;\n }\n default:\n return;\n }\n },\n [chart, commitChart, focusToothById, mode, onSelect, t, visibleIds],\n );\n\n const handleClick = useCallback(\n (id: FdiId) => {\n if (mode !== 'interactive') return;\n setFocusedId(id);\n const prev = chart[id]?.conditions;\n const nextConditions = cycleCondition(prev);\n const next: ToothChart = { ...chart };\n if (nextConditions.length === 0) {\n delete next[id];\n setAnnouncement(t('toothScheme.deselected', { id }));\n } else {\n next[id] = {\n ...(chart[id] ?? emptyState()),\n conditions: nextConditions,\n };\n setAnnouncement(\n t('toothScheme.selected', {\n id,\n conditions: nextConditions\n .map((c) => t(`toothScheme.condition.${c}`))\n .join(', '),\n }),\n );\n }\n commitChart(next);\n onSelect?.(id);\n },\n [chart, commitChart, mode, onSelect, t],\n );\n\n /* ------------------------------------------------------------- */\n /* Rendering */\n /* ------------------------------------------------------------- */\n\n const viewBoxWidth = layout.width + 4; // small padding for stroke\n const viewBoxHeight = layout.height + 4;\n\n const regionLabel = ariaLabel ?? t('toothScheme.ariaLabel');\n\n return (\n <RadixTooltip.Provider delayDuration={200}>\n <div\n role=\"region\"\n aria-label={regionLabel}\n className={[rootVariants(), className].filter(Boolean).join(' ')}\n data-component=\"tooth-scheme\"\n data-testid=\"tooth-scheme-root\"\n data-dentition={dentition}\n data-numbering={numbering}\n data-mode={mode}\n >\n <svg\n ref={svgRef}\n viewBox={`0 0 ${viewBoxWidth} ${viewBoxHeight}`}\n className={svgVariants()}\n role=\"group\"\n aria-label={regionLabel}\n data-testid=\"tooth-scheme-svg\"\n focusable=\"false\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <PatternDefs idPrefix={idPrefix} />\n\n {layout.teeth.map((tooth) => {\n const state = chart[tooth.fdi];\n const primaryCondition: ToothCondition | undefined =\n state?.conditions?.[0];\n\n const anatomyLabel = t(\n `toothScheme.anatomy.${tooth.meta.anatomy}`,\n );\n const conditionSuffix = primaryCondition\n ? `, ${t(`toothScheme.condition.${primaryCondition}`)}`\n : '';\n const label = t('toothScheme.toothLabel', {\n id: labelFor(tooth.fdi, numbering),\n anatomy: anatomyLabel,\n conditionSuffix,\n });\n\n const tabIndex =\n mode === 'interactive'\n ? tooth.fdi === focusedId\n ? 0\n : -1\n : undefined;\n\n const fillColour: string =\n primaryCondition && primaryCondition !== 'missing'\n ? `url(#${idPrefix}-${CONDITION_PATTERNS[primaryCondition]})`\n : 'var(--muted)';\n\n const strokeColour: string =\n primaryCondition && primaryCondition !== 'missing'\n ? CONDITION_COLORS[primaryCondition]\n : 'var(--border)';\n\n const tooltipContent = primaryCondition\n ? `${anatomyLabel} · ${t(`toothScheme.condition.${primaryCondition}`)}`\n : anatomyLabel;\n\n const labelText = labelFor(tooth.fdi, numbering);\n\n return (\n <RadixTooltip.Root key={tooth.fdi}>\n <RadixTooltip.Trigger asChild>\n <g\n data-fdi={tooth.fdi}\n data-anatomy={tooth.meta.anatomy}\n data-arch={tooth.meta.arch}\n data-side={tooth.meta.side}\n data-quadrant={tooth.meta.quadrant}\n data-condition={primaryCondition ?? 'none'}\n data-testid={`tooth-${tooth.fdi}`}\n transform={`translate(${tooth.x}, ${tooth.y})`}\n tabIndex={tabIndex}\n role={mode === 'interactive' ? 'button' : undefined}\n aria-label={label}\n aria-pressed={\n mode === 'interactive'\n ? (primaryCondition !== undefined)\n : undefined\n }\n className={toothGroupVariants()}\n onClick={\n mode === 'interactive'\n ? () => handleClick(tooth.fdi)\n : undefined\n }\n onFocus={() => setFocusedId(tooth.fdi)}\n onKeyDown={(event) => handleKeyDown(event, tooth.fdi)}\n >\n {/* Invisible ≥44px hit target (WCAG 2.5.5). */}\n <rect\n x={-TOOTH_HIT_PAD}\n y={-TOOTH_HIT_PAD}\n width={tooth.shape.width + TOOTH_HIT_PAD * 2}\n height={tooth.shape.height + TOOTH_HIT_PAD * 2}\n fill=\"transparent\"\n />\n {/* Crown outline. */}\n <path\n d={tooth.shape.path}\n fill={fillColour}\n stroke={strokeColour}\n strokeWidth=\"1.5\"\n vectorEffect=\"non-scaling-stroke\"\n />\n {tooth.shape.innerPath ? (\n <path\n d={tooth.shape.innerPath}\n fill=\"none\"\n stroke=\"var(--muted-foreground)\"\n strokeWidth=\"0.75\"\n vectorEffect=\"non-scaling-stroke\"\n />\n ) : null}\n {/* Focus ring — drawn as a rect so it tracks the hit\n area. `group` on the parent <g> makes this\n group-focus-visible selector fire when a keyboard\n user focuses the tooth. In forced-colors mode the\n ring falls back to `CanvasText` so it stays visible\n when `var(--ring)` is remapped. */}\n <rect\n x={-TOOTH_HIT_PAD}\n y={-TOOTH_HIT_PAD}\n width={tooth.shape.width + TOOTH_HIT_PAD * 2}\n height={tooth.shape.height + TOOTH_HIT_PAD * 2}\n fill=\"none\"\n stroke=\"var(--ring)\"\n strokeWidth=\"var(--focus-ring-width)\"\n className={[\n 'ds:opacity-0',\n 'ds:group-focus-visible:opacity-100',\n 'ds:forced-colors:stroke-[CanvasText]',\n ].join(' ')}\n data-testid={`tooth-${tooth.fdi}-focus-ring`}\n pointerEvents=\"none\"\n rx=\"4\"\n />\n {/* Missing-tooth X glyph. */}\n {primaryCondition === 'missing' ? (\n <path\n d={`M2 2 L${tooth.shape.width - 2} ${tooth.shape.height - 2} M${tooth.shape.width - 2} 2 L2 ${tooth.shape.height - 2}`}\n stroke={CONDITION_COLORS.missing}\n strokeWidth=\"2\"\n fill=\"none\"\n />\n ) : null}\n {/* Tooth number label — sits below (upper arch) or above (lower) the crown. */}\n <text\n x={tooth.shape.width / 2}\n y={\n tooth.meta.arch === 'upper'\n ? tooth.shape.height + 12\n : -4\n }\n textAnchor=\"middle\"\n fontSize=\"10\"\n fill=\"var(--muted-foreground)\"\n className=\"ds:select-none\"\n aria-hidden=\"true\"\n >\n {labelText}\n </text>\n </g>\n </RadixTooltip.Trigger>\n <RadixTooltip.Portal>\n <RadixTooltip.Content\n side=\"top\"\n sideOffset={6}\n className=\"ds:z-[var(--z-tooltip)] ds:rounded-[var(--radius-sm)] ds:bg-[var(--foreground)] ds:text-[var(--background)] ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)] ds:pt-[calc(var(--spacing-xs)/2)] ds:pb-[calc(var(--spacing-xs)/2)] ds:text-[var(--font-size-xs)]\"\n >\n {tooltipContent}\n <RadixTooltip.Arrow className=\"ds:fill-[var(--foreground)]\" />\n </RadixTooltip.Content>\n </RadixTooltip.Portal>\n </RadixTooltip.Root>\n );\n })}\n </svg>\n\n <Legend idPrefix={idPrefix} />\n\n <div\n className={liveRegionVariants()}\n aria-live=\"polite\"\n aria-atomic=\"true\"\n data-testid=\"tooth-scheme-live\"\n >\n {announcement}\n </div>\n </div>\n </RadixTooltip.Provider>\n );\n },\n);\n\nToothScheme.displayName = 'ToothScheme';\n\n/* ------------------------------------------------------------------ */\n/* Re-exports */\n/* ------------------------------------------------------------------ */\n\nexport {\n rootVariants as toothSchemeVariants,\n PERMANENT_TEETH,\n PRIMARY_TEETH,\n};\nexport type {\n Dentition,\n FdiId,\n Numbering,\n ToothChart,\n ToothCondition,\n ToothMode,\n ToothState,\n} from './tooth-data';\n","import type { AgentAdapter } from '../../agent/types';\nimport type { ToothSchemeHandle } from './tooth-scheme';\n\nexport const toothSchemeAgent: AgentAdapter<ToothSchemeHandle> = {\n id: 'tooth-scheme',\n capabilities: ['pick'],\n state: {\n chart: {\n type: 'object',\n description: 'Current dental chart state — tooth conditions keyed by FDI id.',\n read: (handle) => handle.getChart(),\n },\n },\n actions: {\n focus_tooth: {\n safety: 'read',\n argsType: '{ id: FdiId }',\n description: 'Move focus to a specific tooth by FDI id.',\n invoke: (handle, args: { id: Parameters<ToothSchemeHandle['focusTooth']>[0] }) => {\n handle.focusTooth(args.id);\n },\n },\n },\n domHooks: {\n root: { attr: 'data-component', value: 'tooth-scheme' },\n item: {\n attr: 'data-fdi',\n description: 'Each rendered tooth `<g>` carries its FDI id as `data-fdi`. Tests still use `data-testid=\"tooth-<fdi>\"` separately.',\n },\n },\n};\n"],"names":["PERMANENT_TEETH","PRIMARY_TEETH","permanentAnatomy","positionInQuadrant","primaryAnatomy","palmerCode","quadrant","position","normalised","permanentUniversal","primaryUniversal","letters","index","buildMeta","fdi","isPrimary","anatomy","arch","side","universal","palmer","FDI_TO_META","acc","FDI_TO_UNIVERSAL","meta","FDI_TO_PALMER","CONDITION_TOKENS","CONDITION_COLORS","CONDITION_PATTERNS","TOOTH_SHAPES","TOOTH_GAP","TOOTH_ROW_GAP","TOOTH_HIT_PAD","layoutTeeth","dentition","ids","upper","lower","id","rowHeight","maxPerRow","positionRow","row","yOffset","rowWidth","shape","fullRowWidth","cursor","tooth","upperRow","lowerRow","maxRowWidth","t","width","height","labelFor","numbering","emptyChart","chartFromConditions","conditions","chart","list","rootVariants","cva","svgVariants","toothGroupVariants","legendVariants","legendItemVariants","legendSwatchVariants","liveRegionVariants","isControlled","props","emptyState","cycleCondition","current","sanitiseIdSuffix","raw","PatternDefs","idPrefix","pid","jsx","ALL_CONDITIONS","Legend","_idPrefix","useTranslation","condition","ToothScheme","forwardRef","mode","value","defaultValue","onChange","onSelect","ariaLabel","className","ref","rawId","useId","useMemo","internalChart","setInternalChart","useState","useEffect","layout","visibleIds","firstId","focusedId","setFocusedId","announcement","setAnnouncement","svgRef","useRef","commitChart","useCallback","next","focusToothById","svg","node","useImperativeHandle","handleKeyDown","event","order","currentIndex","nextIndex","targetQuadrant","candidate","inQuadrant","other","prev","_a","nextConditions","c","handleClick","viewBoxWidth","viewBoxHeight","regionLabel","RadixTooltip","jsxs","state","primaryCondition","anatomyLabel","conditionSuffix","label","tabIndex","fillColour","strokeColour","tooltipContent","labelText","toothSchemeAgent","handle","args"],"mappings":"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"}
@@ -0,0 +1,265 @@
1
+ import { jsxs as m, jsx as n } from "react/jsx-runtime";
2
+ import { forwardRef as $, useState as j, useRef as I, useId as A, useMemo as z, useCallback as L, useLayoutEffect as K, Fragment as P } from "react";
3
+ import { c as D } from "./index-D2ZczOXr.js";
4
+ import { useTranslation as O } from "react-i18next";
5
+ import { u as V } from "./index-CJE9uQmb.js";
6
+ import { S as F } from "./search-input-D6rarD0_.js";
7
+ const _ = D(
8
+ [
9
+ "ds:flex ds:flex-col ds:w-full ds:min-h-0",
10
+ "ds:rounded-[var(--radius-md)] ds:border ds:border-border",
11
+ "ds:bg-background",
12
+ "ds:focus-within:border-[color:var(--primary)]"
13
+ ].join(" "),
14
+ {
15
+ variants: {
16
+ size: {
17
+ sm: "ds:text-[length:var(--font-size-sm)]",
18
+ md: "ds:text-[length:var(--font-size-base)]",
19
+ lg: "ds:text-[length:var(--font-size-lg)]"
20
+ }
21
+ },
22
+ defaultVariants: { size: "md" }
23
+ }
24
+ ), H = 100, B = 8;
25
+ function M(s, r) {
26
+ const o = Math.max(0, Math.floor(s)), i = Math.floor(o / 60), c = o % 60, l = (h) => new Intl.NumberFormat(r, { minimumIntegerDigits: 2 }).format(h);
27
+ return `${l(i)}:${l(c)}`;
28
+ }
29
+ function U(s) {
30
+ const r = Math.max(0, Math.floor(s)), o = Math.floor(r / 60), i = r % 60;
31
+ return `PT${o}M${i}S`;
32
+ }
33
+ function G(s) {
34
+ let r = 0;
35
+ for (let o = 0; o < s.length; o += 1)
36
+ r = r * 31 + s.charCodeAt(o) | 0;
37
+ return Math.abs(r) % B;
38
+ }
39
+ const Q = [
40
+ "bg-[color:var(--transcript-speaker-1)]",
41
+ "bg-[color:var(--transcript-speaker-2)]",
42
+ "bg-[color:var(--transcript-speaker-3)]",
43
+ "bg-[color:var(--transcript-speaker-4)]",
44
+ "bg-[color:var(--transcript-speaker-5)]",
45
+ "bg-[color:var(--transcript-speaker-6)]",
46
+ "bg-[color:var(--transcript-speaker-7)]",
47
+ "bg-[color:var(--transcript-speaker-8)]"
48
+ ];
49
+ function Y(s) {
50
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
51
+ }
52
+ function Z({ text: s, query: r }) {
53
+ if (!r) return s;
54
+ const o = Y(r);
55
+ return s.split(new RegExp(`(${o})`, "gi")).map(
56
+ (c, l) => c.toLocaleLowerCase() === r.toLocaleLowerCase() ? /* @__PURE__ */ n(
57
+ "mark",
58
+ {
59
+ className: "ds:bg-[color:var(--warning)]/30 ds:text-[color:var(--foreground)] ds:rounded-[var(--radius-sm)]",
60
+ children: c
61
+ },
62
+ l
63
+ ) : /* @__PURE__ */ n(P, { children: c }, l)
64
+ );
65
+ }
66
+ const q = $(
67
+ ({
68
+ lines: s,
69
+ currentTime: r = 0,
70
+ onSeek: o,
71
+ searchable: i = !0,
72
+ size: c = "md",
73
+ className: l,
74
+ ...h
75
+ }, R) => {
76
+ const { t: d, i18n: v } = O(), [u, T] = j(""), b = I(null), w = I(null), S = A(), p = z(() => {
77
+ if (!Number.isFinite(r)) return -1;
78
+ for (let e = 0; e < s.length; e += 1) {
79
+ const t = s[e];
80
+ if (t.start <= r && r < t.end) return e;
81
+ }
82
+ return -1;
83
+ }, [s, r]), y = z(() => {
84
+ if (!u) return 0;
85
+ let e = 0;
86
+ const t = u.toLocaleLowerCase();
87
+ for (const a of s) {
88
+ const f = a.text.toLocaleLowerCase();
89
+ let g = f.indexOf(t);
90
+ for (; g !== -1; )
91
+ e += 1, g = f.indexOf(t, g + t.length);
92
+ }
93
+ return e;
94
+ }, [s, u]), E = L(
95
+ (e) => {
96
+ var t;
97
+ (e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "f" && i && (e.preventDefault(), (t = w.current) == null || t.focus());
98
+ },
99
+ [i]
100
+ ), N = s.length > H, x = V({
101
+ count: N ? s.length : 0,
102
+ getScrollElement: () => b.current,
103
+ estimateSize: () => 48,
104
+ overscan: 8,
105
+ getItemKey: (e) => e
106
+ });
107
+ K(() => {
108
+ if (p < 0) return;
109
+ const e = b.current;
110
+ if (!e) return;
111
+ const t = e.querySelector(
112
+ `[data-line-index="${p}"]`
113
+ );
114
+ if (!t) return;
115
+ const a = typeof window < "u" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
116
+ t.scrollIntoView({
117
+ block: "center",
118
+ behavior: a ? "auto" : "smooth"
119
+ });
120
+ }, [p]);
121
+ const k = L(
122
+ (e, t, a) => {
123
+ const f = t === p, g = G(e.speaker), C = typeof e.confidence == "number" && e.confidence < 0.6;
124
+ return /* @__PURE__ */ m(
125
+ "li",
126
+ {
127
+ ref: a == null ? void 0 : a.liRef,
128
+ "data-line-index": t,
129
+ "data-index": a == null ? void 0 : a.dataIndex,
130
+ "aria-current": f ? "true" : void 0,
131
+ style: a == null ? void 0 : a.style,
132
+ className: [
133
+ "ds:flex ds:items-start ds:gap-[var(--spacing-sm)]",
134
+ "ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-sm)]",
135
+ f ? "ds:bg-[color:var(--accent)]/12 ds:border-s-[3px] ds:border-[color:var(--primary)]" : "ds:border-s-[3px] ds:border-transparent",
136
+ a != null && a.absolutePositioning ? "ds:absolute ds:start-0 ds:end-0 ds:top-0 ds:w-full" : ""
137
+ ].filter(Boolean).join(" "),
138
+ children: [
139
+ /* @__PURE__ */ n(
140
+ "button",
141
+ {
142
+ type: "button",
143
+ onClick: () => o == null ? void 0 : o(e.start),
144
+ "aria-label": d("ui.chat.transcript.seekTo", {
145
+ time: M(e.start, v.language)
146
+ }),
147
+ className: [
148
+ "ds:inline-flex ds:items-center ds:justify-center",
149
+ "ds:font-[family-name:var(--font-mono)]",
150
+ "type-meta",
151
+ "ds:tabular-nums",
152
+ "ds:text-[color:var(--muted-foreground)]",
153
+ "ds:rounded-[var(--radius-sm)]",
154
+ "ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)] ds:pt-[var(--spacing-xs)] ds:pb-[var(--spacing-xs)]",
155
+ // WCAG 2.5.5 target size: 44×44 default, 48×48 in accessible
156
+ // themes — matches the DS `--min-target-size` token.
157
+ "ds:min-h-[var(--min-target-size)] ds:min-w-[var(--min-target-size)]",
158
+ "ds:hover:bg-muted/20",
159
+ "ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid",
160
+ "ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]",
161
+ "ds:forced-colors:focus-visible:outline-[CanvasText]"
162
+ ].join(" "),
163
+ children: /* @__PURE__ */ n("time", { dir: "ltr", dateTime: U(e.start), children: M(e.start, v.language) })
164
+ }
165
+ ),
166
+ /* @__PURE__ */ m("div", { className: "ds:flex-1 ds:min-w-0", children: [
167
+ /* @__PURE__ */ m("div", { className: "ds:flex ds:items-center ds:gap-[var(--spacing-xs)]", children: [
168
+ /* @__PURE__ */ n(
169
+ "span",
170
+ {
171
+ "aria-hidden": "true",
172
+ className: `ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ${Q[g]}`
173
+ }
174
+ ),
175
+ /* @__PURE__ */ n("strong", { className: "type-title-item", children: d("ui.chat.transcript.speaker", { name: e.speaker }) }),
176
+ C ? /* @__PURE__ */ m("span", { className: "type-meta ds:italic ds:text-[color:var(--muted-foreground)]", children: [
177
+ "(",
178
+ d("ui.chat.transcript.lowConfidence"),
179
+ ")"
180
+ ] }) : null
181
+ ] }),
182
+ /* @__PURE__ */ n(
183
+ "span",
184
+ {
185
+ dir: "auto",
186
+ className: [
187
+ "ds:block",
188
+ C ? "ds:underline ds:decoration-dotted ds:decoration-[color:var(--muted-foreground)]" : ""
189
+ ].join(" "),
190
+ children: /* @__PURE__ */ n(Z, { text: e.text, query: u })
191
+ }
192
+ )
193
+ ] })
194
+ ]
195
+ },
196
+ t
197
+ );
198
+ },
199
+ [p, v.language, o, u, d]
200
+ );
201
+ return /* @__PURE__ */ m(
202
+ "div",
203
+ {
204
+ ref: R,
205
+ onKeyDown: E,
206
+ className: _({ size: c, className: l }),
207
+ ...h,
208
+ children: [
209
+ i ? /* @__PURE__ */ m("div", { className: "ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-xs)] ds:flex ds:items-center ds:gap-[var(--spacing-sm)] ds:border-b ds:border-border", children: [
210
+ /* @__PURE__ */ n(
211
+ F,
212
+ {
213
+ ref: w,
214
+ id: S,
215
+ placeholder: d("ui.chat.transcript.search"),
216
+ "aria-label": d("ui.chat.transcript.search"),
217
+ "aria-keyshortcuts": "Meta+F Control+F",
218
+ debounceMs: 150,
219
+ onChange: (e) => T(e),
220
+ size: "sm"
221
+ }
222
+ ),
223
+ u ? /* @__PURE__ */ n(
224
+ "span",
225
+ {
226
+ role: "status",
227
+ "aria-live": "polite",
228
+ className: "type-meta ds:text-[color:var(--muted-foreground)] ds:tabular-nums",
229
+ children: y === 0 ? d("ui.chat.transcript.noMatches") : d("ui.chat.transcript.matchCount", { count: y })
230
+ }
231
+ ) : null
232
+ ] }) : null,
233
+ /* @__PURE__ */ n(
234
+ "div",
235
+ {
236
+ ref: b,
237
+ className: "ds:flex-1 ds:min-h-0 ds:overflow-y-auto ds:overflow-x-hidden",
238
+ children: N ? /* @__PURE__ */ n(
239
+ "ol",
240
+ {
241
+ className: "ds:relative ds:list-none ds:ps-0 ds:m-0",
242
+ style: { blockSize: `${x.getTotalSize()}px` },
243
+ children: x.getVirtualItems().map((e) => {
244
+ const t = s[e.index];
245
+ return t ? k(t, e.index, {
246
+ liRef: x.measureElement,
247
+ dataIndex: e.index,
248
+ style: { transform: `translateY(${e.start}px)` },
249
+ absolutePositioning: !0
250
+ }) : null;
251
+ })
252
+ }
253
+ ) : /* @__PURE__ */ n("ol", { className: "ds:list-none ds:ps-0 ds:m-0", children: s.map((e, t) => k(e, t)) })
254
+ }
255
+ )
256
+ ]
257
+ }
258
+ );
259
+ }
260
+ );
261
+ q.displayName = "TranscriptPanel";
262
+ export {
263
+ q as T
264
+ };
265
+ //# sourceMappingURL=transcript-panel-Bg1BTMSr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcript-panel-Bg1BTMSr.js","sources":["../../src/components/transcript-panel/transcript-panel.tsx"],"sourcesContent":["import {\n forwardRef,\n Fragment,\n useCallback,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type KeyboardEvent,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { useVirtualizer } from '@tanstack/react-virtual';\nimport { SearchInput } from '../search-input';\n\n/* ------------------------------------------------------------------ */\n/* Public types */\n/* ------------------------------------------------------------------ */\n\nexport interface TranscriptLine {\n speaker: string;\n text: string;\n /** Seconds, floating-point — matches HTMLMediaElement.currentTime. */\n start: number;\n end: number;\n /** 0..1 — rendered with a dotted underline when below 0.6. */\n confidence?: number;\n}\n\nexport interface TranscriptPanelProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'children'>,\n VariantProps<typeof rootVariants> {\n lines: TranscriptLine[];\n /** Current playback time in seconds — drives the active line highlight. */\n currentTime?: number;\n onSeek?: (seconds: number) => void;\n /** Show the search bar. */\n searchable?: boolean;\n}\n\nconst rootVariants = cva(\n [\n 'ds:flex ds:flex-col ds:w-full ds:min-h-0',\n 'ds:rounded-[var(--radius-md)] ds:border ds:border-border',\n 'ds:bg-background',\n 'ds:focus-within:border-[color:var(--primary)]',\n ].join(' '),\n {\n variants: {\n size: {\n sm: 'ds:text-[length:var(--font-size-sm)]',\n md: 'ds:text-[length:var(--font-size-base)]',\n lg: 'ds:text-[length:var(--font-size-lg)]',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\nconst VIRTUALIZATION_THRESHOLD = 100;\nconst SPEAKER_SLOT_COUNT = 8;\n\nfunction formatTime(seconds: number, locale: string): string {\n const total = Math.max(0, Math.floor(seconds));\n const m = Math.floor(total / 60);\n const s = total % 60;\n const fmt = (n: number) =>\n new Intl.NumberFormat(locale, { minimumIntegerDigits: 2 }).format(n);\n return `${fmt(m)}:${fmt(s)}`;\n}\n\n/** ISO-8601 duration (PT#M#S) for <time datetime=\"...\">. */\nfunction secondsToIsoDuration(seconds: number): string {\n const total = Math.max(0, Math.floor(seconds));\n const m = Math.floor(total / 60);\n const s = total % 60;\n return `PT${m}M${s}S`;\n}\n\n/** Deterministic speaker slot (0..7) based on name hash. */\nfunction speakerSlot(name: string): number {\n let hash = 0;\n for (let i = 0; i < name.length; i += 1) {\n hash = (hash * 31 + name.charCodeAt(i)) | 0;\n }\n return Math.abs(hash) % SPEAKER_SLOT_COUNT;\n}\n\n/** Per-slot background classes. Indices align with the speaker palette tokens\n * in tokens/index.css. Static strings so Tailwind can pick them up at build. */\nconst SPEAKER_BG_CLASSES = [\n 'bg-[color:var(--transcript-speaker-1)]',\n 'bg-[color:var(--transcript-speaker-2)]',\n 'bg-[color:var(--transcript-speaker-3)]',\n 'bg-[color:var(--transcript-speaker-4)]',\n 'bg-[color:var(--transcript-speaker-5)]',\n 'bg-[color:var(--transcript-speaker-6)]',\n 'bg-[color:var(--transcript-speaker-7)]',\n 'bg-[color:var(--transcript-speaker-8)]',\n] as const;\n\n/** Escape a literal string for safe use inside a RegExp. */\nfunction escapeRegExp(input: string): string {\n return input.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\ninterface HighlightedTextProps {\n text: string;\n query: string;\n}\n\n/** Renders text with <mark> wrapping around case-insensitive matches.\n * No dangerouslySetInnerHTML — splits text nodes only. */\nfunction HighlightedText({ text, query }: HighlightedTextProps): ReactNode {\n if (!query) return text;\n const escaped = escapeRegExp(query);\n const parts = text.split(new RegExp(`(${escaped})`, 'gi'));\n return parts.map((part, i) =>\n part.toLocaleLowerCase() === query.toLocaleLowerCase() ? (\n <mark\n key={i}\n className=\"ds:bg-[color:var(--warning)]/30 ds:text-[color:var(--foreground)] ds:rounded-[var(--radius-sm)]\"\n >\n {part}\n </mark>\n ) : (\n <Fragment key={i}>{part}</Fragment>\n ),\n );\n}\n\nexport const TranscriptPanel = forwardRef<HTMLDivElement, TranscriptPanelProps>(\n (\n {\n lines,\n currentTime = 0,\n onSeek,\n searchable = true,\n size = 'md',\n className,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const [query, setQuery] = useState('');\n const scrollRef = useRef<HTMLDivElement | null>(null);\n const searchInputRef = useRef<HTMLInputElement | null>(null);\n const searchId = useId();\n\n /* Active-line index */\n const activeIndex = useMemo(() => {\n if (!Number.isFinite(currentTime)) return -1;\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i];\n if (line.start <= currentTime && currentTime < line.end) return i;\n }\n return -1;\n }, [lines, currentTime]);\n\n /* Match count for search */\n const matchCount = useMemo(() => {\n if (!query) return 0;\n let count = 0;\n const needle = query.toLocaleLowerCase();\n for (const line of lines) {\n const hay = line.text.toLocaleLowerCase();\n let idx = hay.indexOf(needle);\n while (idx !== -1) {\n count += 1;\n idx = hay.indexOf(needle, idx + needle.length);\n }\n }\n return count;\n }, [lines, query]);\n\n /* Ctrl/Cmd+F shortcut — focuses the search field when focus is within. */\n const handleRootKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'f' && searchable) {\n e.preventDefault();\n searchInputRef.current?.focus();\n }\n },\n [searchable],\n );\n\n /* Virtualisation above 100 lines */\n const virtualized = lines.length > VIRTUALIZATION_THRESHOLD;\n const virtualizer = useVirtualizer({\n count: virtualized ? lines.length : 0,\n getScrollElement: () => scrollRef.current,\n estimateSize: () => 48,\n overscan: 8,\n getItemKey: (i) => i,\n });\n\n /* Auto-scroll active line into view when it changes. */\n useLayoutEffect(() => {\n if (activeIndex < 0) return;\n const scroller = scrollRef.current;\n if (!scroller) return;\n const target = scroller.querySelector(\n `[data-line-index=\"${activeIndex}\"]`,\n ) as HTMLElement | null;\n if (!target) return;\n const prefersReducedMotion =\n typeof window !== 'undefined' &&\n window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n target.scrollIntoView({\n block: 'center',\n behavior: prefersReducedMotion ? 'auto' : 'smooth',\n });\n }, [activeIndex]);\n\n const renderLine = useCallback(\n (\n line: TranscriptLine,\n index: number,\n extras?: {\n liRef?: (node: HTMLLIElement | null) => void;\n dataIndex?: number;\n // Style is an inline-style escape hatch — virtualizer offsets\n // cannot be expressed as static Tailwind. See 23-constraints §4.\n style?: CSSProperties;\n absolutePositioning?: boolean;\n },\n ) => {\n const isActive = index === activeIndex;\n const slot = speakerSlot(line.speaker);\n const lowConfidence =\n typeof line.confidence === 'number' && line.confidence < 0.6;\n return (\n <li\n key={index}\n ref={extras?.liRef}\n data-line-index={index}\n data-index={extras?.dataIndex}\n aria-current={isActive ? 'true' : undefined}\n // Inline style — permitted per 23-constraints\n // §Runtime-computed dimensions (virtualizer translateY).\n style={extras?.style}\n className={[\n 'ds:flex ds:items-start ds:gap-[var(--spacing-sm)]',\n 'ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-sm)]',\n isActive\n ? 'ds:bg-[color:var(--accent)]/12 ds:border-s-[3px] ds:border-[color:var(--primary)]'\n : 'ds:border-s-[3px] ds:border-transparent',\n extras?.absolutePositioning\n ? 'ds:absolute ds:start-0 ds:end-0 ds:top-0 ds:w-full'\n : '',\n ]\n .filter(Boolean)\n .join(' ')}\n >\n <button\n type=\"button\"\n onClick={() => onSeek?.(line.start)}\n aria-label={t('ui.chat.transcript.seekTo', {\n time: formatTime(line.start, i18n.language),\n })}\n className={[\n 'ds:inline-flex ds:items-center ds:justify-center',\n 'ds:font-[family-name:var(--font-mono)]',\n 'type-meta',\n 'ds:tabular-nums',\n 'ds:text-[color:var(--muted-foreground)]',\n 'ds:rounded-[var(--radius-sm)]',\n 'ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)] ds:pt-[var(--spacing-xs)] ds:pb-[var(--spacing-xs)]',\n // WCAG 2.5.5 target size: 44×44 default, 48×48 in accessible\n // themes — matches the DS `--min-target-size` token.\n 'ds:min-h-[var(--min-target-size)] ds:min-w-[var(--min-target-size)]',\n 'ds:hover:bg-muted/20',\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 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n ].join(' ')}\n >\n <time dir=\"ltr\" dateTime={secondsToIsoDuration(line.start)}>\n {formatTime(line.start, i18n.language)}\n </time>\n </button>\n <div className=\"ds:flex-1 ds:min-w-0\">\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-xs)]\">\n <span\n aria-hidden=\"true\"\n className={`ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ${SPEAKER_BG_CLASSES[slot]}`}\n />\n <strong className=\"type-title-item\">\n {t('ui.chat.transcript.speaker', { name: line.speaker })}\n </strong>\n {lowConfidence ? (\n <span className=\"type-meta ds:italic ds:text-[color:var(--muted-foreground)]\">\n ({t('ui.chat.transcript.lowConfidence')})\n </span>\n ) : null}\n </div>\n <span\n dir=\"auto\"\n className={[\n 'ds:block',\n lowConfidence\n ? 'ds:underline ds:decoration-dotted ds:decoration-[color:var(--muted-foreground)]'\n : '',\n ].join(' ')}\n >\n <HighlightedText text={line.text} query={query} />\n </span>\n </div>\n </li>\n );\n },\n [activeIndex, i18n.language, onSeek, query, t],\n );\n\n return (\n <div\n ref={ref}\n onKeyDown={handleRootKeyDown}\n className={rootVariants({ size, className })}\n {...rest}\n >\n {searchable ? (\n <div className=\"ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-xs)] ds:flex ds:items-center ds:gap-[var(--spacing-sm)] ds:border-b ds:border-border\">\n <SearchInput\n ref={searchInputRef}\n id={searchId}\n placeholder={t('ui.chat.transcript.search')}\n aria-label={t('ui.chat.transcript.search')}\n aria-keyshortcuts=\"Meta+F Control+F\"\n debounceMs={150}\n onChange={(v) => setQuery(v)}\n size=\"sm\"\n />\n {query ? (\n <span\n role=\"status\"\n aria-live=\"polite\"\n className=\"type-meta ds:text-[color:var(--muted-foreground)] ds:tabular-nums\"\n >\n {matchCount === 0\n ? t('ui.chat.transcript.noMatches')\n : t('ui.chat.transcript.matchCount', { count: matchCount })}\n </span>\n ) : null}\n </div>\n ) : null}\n\n <div\n ref={scrollRef}\n className=\"ds:flex-1 ds:min-h-0 ds:overflow-y-auto ds:overflow-x-hidden\"\n >\n {virtualized ? (\n <ol\n className=\"ds:relative ds:list-none ds:ps-0 ds:m-0\"\n // Inline style — permitted per 23-constraints\n // §Runtime-computed dimensions (virtualizer total size).\n style={{ blockSize: `${virtualizer.getTotalSize()}px` }}\n >\n {virtualizer.getVirtualItems().map((vi) => {\n const line = lines[vi.index];\n if (!line) return null;\n // Put the measureElement ref on the <li> directly — a <div>\n // child of <ol> is invalid and breaks list semantics for\n // assistive tech.\n return renderLine(line, vi.index, {\n liRef: virtualizer.measureElement,\n dataIndex: vi.index,\n style: { transform: `translateY(${vi.start}px)` },\n absolutePositioning: true,\n });\n })}\n </ol>\n ) : (\n <ol className=\"ds:list-none ds:ps-0 ds:m-0\">\n {lines.map((line, index) => renderLine(line, index))}\n </ol>\n )}\n </div>\n </div>\n );\n },\n);\n\nTranscriptPanel.displayName = 'TranscriptPanel';\n"],"names":["rootVariants","cva","VIRTUALIZATION_THRESHOLD","SPEAKER_SLOT_COUNT","formatTime","seconds","locale","total","m","s","fmt","n","secondsToIsoDuration","speakerSlot","name","hash","i","SPEAKER_BG_CLASSES","escapeRegExp","input","HighlightedText","text","query","escaped","part","jsx","Fragment","TranscriptPanel","forwardRef","lines","currentTime","onSeek","searchable","size","className","rest","ref","t","i18n","useTranslation","setQuery","useState","scrollRef","useRef","searchInputRef","searchId","useId","activeIndex","useMemo","line","matchCount","count","needle","hay","idx","handleRootKeyDown","useCallback","_a","virtualized","virtualizer","useVirtualizer","useLayoutEffect","scroller","target","prefersReducedMotion","renderLine","index","extras","isActive","slot","lowConfidence","jsxs","SearchInput","v","vi"],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ import { jsxs as o, jsx as s } from "react/jsx-runtime";
2
+ import { forwardRef as f } from "react";
3
+ import { c as e } from "./index-D2ZczOXr.js";
4
+ import { useTranslation as u } from "react-i18next";
5
+ const g = e(
6
+ [
7
+ "ds:inline-flex ds:items-center",
8
+ "ds:text-[color:var(--muted-foreground)]"
9
+ ].join(" "),
10
+ {
11
+ variants: {
12
+ density: {
13
+ default: "ds:gap-[var(--spacing-sm)]",
14
+ compact: "ds:gap-[var(--spacing-xs)]"
15
+ },
16
+ size: {
17
+ sm: "ds:text-[length:var(--font-size-xs)]",
18
+ md: "ds:text-[length:var(--font-size-sm)]",
19
+ lg: "ds:text-[length:var(--font-size-base)]"
20
+ }
21
+ },
22
+ defaultVariants: { density: "default", size: "md" }
23
+ }
24
+ ), v = e("ds:inline-flex ds:items-center", {
25
+ variants: {
26
+ density: {
27
+ default: "ds:gap-[var(--spacing-xs)]",
28
+ compact: "ds:gap-[calc(var(--spacing)*0.75)]"
29
+ }
30
+ },
31
+ defaultVariants: { density: "default" }
32
+ }), t = e(
33
+ [
34
+ "ds:inline-block ds:rounded-[var(--radius-full)]",
35
+ "ds:bg-[color:currentColor]",
36
+ // Accessible themes set --animation-duration:0ms; reduced-motion falls back via media query below
37
+ "ds:motion-safe:animate-[typing-indicator-dot_var(--animation-duration-typing,1.4s)_ease-in-out_infinite]",
38
+ "ds:motion-reduce:opacity-60",
39
+ "ds:[.theme-accessible_&]:animate-none ds:[.theme-accessible_&]:opacity-60",
40
+ "ds:forced-colors:bg-[CanvasText]"
41
+ ].join(" "),
42
+ {
43
+ variants: {
44
+ size: {
45
+ sm: "ds:size-[6px]",
46
+ md: "ds:size-[8px]",
47
+ lg: "ds:size-[10px]"
48
+ }
49
+ },
50
+ defaultVariants: { size: "md" }
51
+ }
52
+ ), x = f(
53
+ ({ size: a = "md", density: i = "default", label: r, hideLabel: n = !1, className: l, ...c }, m) => {
54
+ const { t: p } = u(), d = r ?? p("ui.chat.typing");
55
+ return /* @__PURE__ */ o(
56
+ "div",
57
+ {
58
+ ref: m,
59
+ role: "status",
60
+ "aria-live": "polite",
61
+ "aria-atomic": "true",
62
+ "aria-label": n ? d : void 0,
63
+ className: g({ size: a, density: i, className: l }),
64
+ ...c,
65
+ children: [
66
+ /* @__PURE__ */ o("span", { "aria-hidden": "true", className: v({ density: i }), children: [
67
+ /* @__PURE__ */ s("span", { className: `${t({ size: a })} ds:[animation-delay:0ms]` }),
68
+ /* @__PURE__ */ s("span", { className: `${t({ size: a })} ds:[animation-delay:200ms]` }),
69
+ /* @__PURE__ */ s("span", { className: `${t({ size: a })} ds:[animation-delay:400ms]` })
70
+ ] }),
71
+ n ? null : /* @__PURE__ */ s("span", { className: "ds:font-medium", children: d })
72
+ ]
73
+ }
74
+ );
75
+ }
76
+ );
77
+ x.displayName = "TypingIndicator";
78
+ export {
79
+ x as T
80
+ };
81
+ //# sourceMappingURL=typing-indicator-BRg22Rqr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typing-indicator-BRg22Rqr.js","sources":["../../src/components/typing-indicator/typing-indicator.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\n\nconst indicatorVariants = cva(\n [\n 'ds:inline-flex ds:items-center',\n 'ds:text-[color:var(--muted-foreground)]',\n ].join(' '),\n {\n variants: {\n density: {\n default: 'ds:gap-[var(--spacing-sm)]',\n compact: 'ds:gap-[var(--spacing-xs)]',\n },\n size: {\n sm: 'ds:text-[length:var(--font-size-xs)]',\n md: 'ds:text-[length:var(--font-size-sm)]',\n lg: 'ds:text-[length:var(--font-size-base)]',\n },\n },\n defaultVariants: { density: 'default', size: 'md' },\n },\n);\n\nconst dotsVariants = cva('ds:inline-flex ds:items-center', {\n variants: {\n density: {\n default: 'ds:gap-[var(--spacing-xs)]',\n compact: 'ds:gap-[calc(var(--spacing)*0.75)]',\n },\n },\n defaultVariants: { density: 'default' },\n});\n\nconst dotVariants = cva(\n [\n 'ds:inline-block ds:rounded-[var(--radius-full)]',\n 'ds:bg-[color:currentColor]',\n // Accessible themes set --animation-duration:0ms; reduced-motion falls back via media query below\n 'ds:motion-safe:animate-[typing-indicator-dot_var(--animation-duration-typing,1.4s)_ease-in-out_infinite]',\n 'ds:motion-reduce:opacity-60',\n 'ds:[.theme-accessible_&]:animate-none ds:[.theme-accessible_&]:opacity-60',\n 'ds:forced-colors:bg-[CanvasText]',\n ].join(' '),\n {\n variants: {\n size: {\n sm: 'ds:size-[6px]',\n md: 'ds:size-[8px]',\n lg: 'ds:size-[10px]',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\ntype NativeDivProps = Omit<HTMLAttributes<HTMLDivElement>, 'role' | 'aria-live' | 'aria-atomic'>;\n\nexport interface TypingIndicatorProps\n extends NativeDivProps,\n VariantProps<typeof indicatorVariants> {\n /** Optional override. When omitted, uses t('ui.chat.typing'). */\n label?: string;\n /** Hide the visible label, keeping only the animated dots. The label still\n * carries the accessible name via aria-label. */\n hideLabel?: boolean;\n}\n\nexport const TypingIndicator = forwardRef<HTMLDivElement, TypingIndicatorProps>(\n (\n { size = 'md', density = 'default', label, hideLabel = false, className, ...rest },\n ref,\n ) => {\n const { t } = useTranslation();\n const resolvedLabel = label ?? t('ui.chat.typing');\n\n return (\n <div\n ref={ref}\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n aria-label={hideLabel ? resolvedLabel : undefined}\n className={indicatorVariants({ size, density, className })}\n {...rest}\n >\n <span aria-hidden=\"true\" className={dotsVariants({ density })}>\n <span className={`${dotVariants({ size })} ds:[animation-delay:0ms]`} />\n <span className={`${dotVariants({ size })} ds:[animation-delay:200ms]`} />\n <span className={`${dotVariants({ size })} ds:[animation-delay:400ms]`} />\n </span>\n {!hideLabel ? (\n <span className=\"ds:font-medium\">{resolvedLabel}</span>\n ) : null}\n </div>\n );\n },\n);\n\nTypingIndicator.displayName = 'TypingIndicator';\n"],"names":["indicatorVariants","cva","dotsVariants","dotVariants","TypingIndicator","forwardRef","size","density","label","hideLabel","className","rest","ref","t","useTranslation","resolvedLabel","jsxs","jsx"],"mappings":";;;;AAIA,MAAMA,IAAoBC;AAAA,EACxB;AAAA,IACE;AAAA,IACA;AAAA,EAAA,EACA,KAAK,GAAG;AAAA,EACV;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SAAS;AAAA,QACT,SAAS;AAAA,MAAA;AAAA,MAEX,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,MAAA;AAAA,IACN;AAAA,IAEF,iBAAiB,EAAE,SAAS,WAAW,MAAM,KAAA;AAAA,EAAK;AAEtD,GAEMC,IAAeD,EAAI,kCAAkC;AAAA,EACzD,UAAU;AAAA,IACR,SAAS;AAAA,MACP,SAAS;AAAA,MACT,SAAS;AAAA,IAAA;AAAA,EACX;AAAA,EAEF,iBAAiB,EAAE,SAAS,UAAA;AAC9B,CAAC,GAEKE,IAAcF;AAAA,EAClB;AAAA,IACE;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,EACA,KAAK,GAAG;AAAA,EACV;AAAA,IACE,UAAU;AAAA,MACR,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,MAAA;AAAA,IACN;AAAA,IAEF,iBAAiB,EAAE,MAAM,KAAA;AAAA,EAAK;AAElC,GAcaG,IAAkBC;AAAA,EAC7B,CACE,EAAE,MAAAC,IAAO,MAAM,SAAAC,IAAU,WAAW,OAAAC,GAAO,WAAAC,IAAY,IAAO,WAAAC,GAAW,GAAGC,EAAA,GAC5EC,MACG;AACH,UAAM,EAAE,GAAAC,EAAA,IAAMC,EAAA,GACRC,IAAgBP,KAASK,EAAE,gBAAgB;AAEjD,WACE,gBAAAG;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAAJ;AAAA,QACA,MAAK;AAAA,QACL,aAAU;AAAA,QACV,eAAY;AAAA,QACZ,cAAYH,IAAYM,IAAgB;AAAA,QACxC,WAAWf,EAAkB,EAAE,MAAAM,GAAM,SAAAC,GAAS,WAAAG,GAAW;AAAA,QACxD,GAAGC;AAAA,QAEJ,UAAA;AAAA,UAAA,gBAAAK,EAAC,QAAA,EAAK,eAAY,QAAO,WAAWd,EAAa,EAAE,SAAAK,GAAS,GAC1D,UAAA;AAAA,YAAA,gBAAAU,EAAC,QAAA,EAAK,WAAW,GAAGd,EAAY,EAAE,MAAAG,EAAA,CAAM,CAAC,6BAA6B;AAAA,YACtE,gBAAAW,EAAC,UAAK,WAAW,GAAGd,EAAY,EAAE,MAAAG,EAAA,CAAM,CAAC,+BAA+B;AAAA,YACxE,gBAAAW,EAAC,UAAK,WAAW,GAAGd,EAAY,EAAE,MAAAG,EAAA,CAAM,CAAC,8BAAA,CAA+B;AAAA,UAAA,GAC1E;AAAA,UACEG,IAEE,OADF,gBAAAQ,EAAC,UAAK,WAAU,kBAAkB,aAAc;AAAA,QAC9C;AAAA,MAAA;AAAA,IAAA;AAAA,EAGV;AACF;AAEAb,EAAgB,cAAc;"}
@@ -0,0 +1,32 @@
1
+ import { useState as c, useEffect as s, useCallback as u } from "react";
2
+ import "../i18n/config.js";
3
+ import a from "i18next";
4
+ function l(n) {
5
+ const [o, i] = c(() => typeof window > "u" || typeof window.matchMedia != "function" ? !1 : window.matchMedia(n).matches);
6
+ return s(() => {
7
+ if (typeof window > "u" || typeof window.matchMedia != "function")
8
+ return;
9
+ const t = window.matchMedia(n);
10
+ i(t.matches);
11
+ const e = (r) => i(r.matches);
12
+ return typeof t.addEventListener == "function" ? (t.addEventListener("change", e), () => t.removeEventListener("change", e)) : (t.addListener(e), () => t.removeListener(e));
13
+ }, [n]), o;
14
+ }
15
+ function h() {
16
+ const [n, o] = c(() => a.language);
17
+ s(() => {
18
+ const e = (r) => o(r);
19
+ return a.on("languageChanged", e), a.language !== n && o(a.language), () => {
20
+ a.off("languageChanged", e);
21
+ };
22
+ }, [n]);
23
+ const i = u(async (e) => {
24
+ await a.changeLanguage(e);
25
+ }, []), t = a.dir(n) === "rtl" ? "rtl" : "ltr";
26
+ return { locale: n, dir: t, setLocale: i };
27
+ }
28
+ export {
29
+ l as a,
30
+ h as u
31
+ };
32
+ //# sourceMappingURL=use-locale-BuXR_Zl9.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-locale-BuXR_Zl9.js","sources":["../../src/hooks/use-media-query.ts","../../src/hooks/use-locale.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\n\n/**\n * Subscribe to an arbitrary media query and return whether it currently\n * matches. Useful for responsive components that need to branch on\n * viewport size without hand-rolling `matchMedia` plumbing.\n *\n * SSR-safe: returns `false` when `window` is undefined. The effect is\n * client-only, so hydration mismatches are avoided without a null\n * sentinel at the type level.\n *\n * Mirrors the shape of `usePrefersReducedMotion` — Safari < 14 fallback\n * via `addListener` / `removeListener` is included.\n *\n * @example\n * const isMobile = useMediaQuery('(max-width: 640px)');\n */\nexport function useMediaQuery(query: string): boolean {\n const [matches, setMatches] = useState<boolean>(() => {\n if (\n typeof window === 'undefined' ||\n typeof window.matchMedia !== 'function'\n ) {\n return false;\n }\n return window.matchMedia(query).matches;\n });\n\n useEffect(() => {\n if (\n typeof window === 'undefined' ||\n typeof window.matchMedia !== 'function'\n ) {\n return;\n }\n const mql = window.matchMedia(query);\n // Re-sync in case the query (or the OS state) changed between the\n // initial useState read and the first effect.\n setMatches(mql.matches);\n const onChange = (ev: MediaQueryListEvent) => setMatches(ev.matches);\n if (typeof mql.addEventListener === 'function') {\n mql.addEventListener('change', onChange);\n return () => mql.removeEventListener('change', onChange);\n }\n mql.addListener(onChange);\n return () => mql.removeListener(onChange);\n }, [query]);\n\n return matches;\n}\n","import { useCallback, useEffect, useState } from 'react';\nimport i18n from '../i18n/config';\n\nexport type LocaleDir = 'ltr' | 'rtl';\n\nexport interface UseLocaleReturn {\n /** Current i18next language tag (e.g. `'en'`, `'ar'`, `'ja'`). */\n locale: string;\n /** Reading direction for the current locale, derived from `i18n.dir(locale)`. */\n dir: LocaleDir;\n /** Change the active i18next language. */\n setLocale: (next: string) => Promise<void>;\n}\n\n/**\n * Read the active i18n locale and its reading direction. Subscribes to\n * `i18next.on('languageChanged')` so consumers re-render when the locale\n * changes (e.g. via `<select>` in a settings page).\n *\n * SSR-safe: the initial state is read synchronously from the i18next\n * instance, which itself ships an SSR-friendly default. The\n * `languageChanged` listener attaches in `useEffect`, so it only runs\n * client-side.\n *\n * Thin wrapper — the underlying source of truth is the i18next instance\n * exported from `src/i18n/config.ts`. Use this hook over reading\n * `i18n.language` directly so components can:\n * 1. Re-render on language change without ad-hoc state.\n * 2. Get `dir` derived correctly (RTL for `ar`).\n */\nexport function useLocale(): UseLocaleReturn {\n const [locale, setLocaleState] = useState<string>(() => i18n.language);\n\n useEffect(() => {\n const onChange = (next: string) => setLocaleState(next);\n i18n.on('languageChanged', onChange);\n // Re-sync — the `i18n.language` may have settled between the\n // initial useState read and effect attach (async language load).\n if (i18n.language !== locale) setLocaleState(i18n.language);\n return () => {\n i18n.off('languageChanged', onChange);\n };\n }, [locale]);\n\n const setLocale = useCallback(async (next: string) => {\n await i18n.changeLanguage(next);\n }, []);\n\n // `i18n.dir(locale)` returns `'ltr' | 'rtl'` — the i18next types declare\n // it as `string`, so we narrow.\n const dir = (i18n.dir(locale) === 'rtl' ? 'rtl' : 'ltr') as LocaleDir;\n\n return { locale, dir, setLocale };\n}\n"],"names":["useMediaQuery","query","matches","setMatches","useState","useEffect","mql","onChange","ev","useLocale","locale","setLocaleState","i18n","next","setLocale","useCallback","dir"],"mappings":";;;AAiBO,SAASA,EAAcC,GAAwB;AACpD,QAAM,CAACC,GAASC,CAAU,IAAIC,EAAkB,MAE5C,OAAO,SAAW,OAClB,OAAO,OAAO,cAAe,aAEtB,KAEF,OAAO,WAAWH,CAAK,EAAE,OACjC;AAED,SAAAI,EAAU,MAAM;AACd,QACE,OAAO,SAAW,OAClB,OAAO,OAAO,cAAe;AAE7B;AAEF,UAAMC,IAAM,OAAO,WAAWL,CAAK;AAGnC,IAAAE,EAAWG,EAAI,OAAO;AACtB,UAAMC,IAAW,CAACC,MAA4BL,EAAWK,EAAG,OAAO;AACnE,WAAI,OAAOF,EAAI,oBAAqB,cAClCA,EAAI,iBAAiB,UAAUC,CAAQ,GAChC,MAAMD,EAAI,oBAAoB,UAAUC,CAAQ,MAEzDD,EAAI,YAAYC,CAAQ,GACjB,MAAMD,EAAI,eAAeC,CAAQ;AAAA,EAC1C,GAAG,CAACN,CAAK,CAAC,GAEHC;AACT;ACnBO,SAASO,IAA6B;AAC3C,QAAM,CAACC,GAAQC,CAAc,IAAIP,EAAiB,MAAMQ,EAAK,QAAQ;AAErE,EAAAP,EAAU,MAAM;AACd,UAAME,IAAW,CAACM,MAAiBF,EAAeE,CAAI;AACtDD,WAAAA,EAAK,GAAG,mBAAmBL,CAAQ,GAG/BK,EAAK,aAAaF,KAAQC,EAAeC,EAAK,QAAQ,GACnD,MAAM;AACXA,MAAAA,EAAK,IAAI,mBAAmBL,CAAQ;AAAA,IACtC;AAAA,EACF,GAAG,CAACG,CAAM,CAAC;AAEX,QAAMI,IAAYC,EAAY,OAAOF,MAAiB;AACpD,UAAMD,EAAK,eAAeC,CAAI;AAAA,EAChC,GAAG,CAAA,CAAE,GAICG,IAAOJ,EAAK,IAAIF,CAAM,MAAM,QAAQ,QAAQ;AAElD,SAAO,EAAE,QAAAA,GAAQ,KAAAM,GAAK,WAAAF,EAAA;AACxB;"}