@alfadocs/ui-kit-debug 1.90.1 → 1.90.2

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 (151) hide show
  1. package/dist/_chunks/address-autocomplete-DR4Xn23Z.js +519 -0
  2. package/dist/_chunks/address-autocomplete-DR4Xn23Z.js.map +1 -0
  3. package/dist/_chunks/{address-fields-CfnXK8RT.js → address-fields-qxQkGwPf.js} +5 -5
  4. package/dist/_chunks/{address-fields-CfnXK8RT.js.map → address-fields-qxQkGwPf.js.map} +1 -1
  5. package/dist/_chunks/{ai-permission-notice-CoW22jx1.js → ai-permission-notice-VNh3W57L.js} +2 -2
  6. package/dist/_chunks/{ai-permission-notice-CoW22jx1.js.map → ai-permission-notice-VNh3W57L.js.map} +1 -1
  7. package/dist/_chunks/{anamnesis-summary-rows-fbEQwcuW.js → anamnesis-summary-rows-EKPfVCmh.js} +2 -2
  8. package/dist/_chunks/{anamnesis-summary-rows-fbEQwcuW.js.map → anamnesis-summary-rows-EKPfVCmh.js.map} +1 -1
  9. package/dist/_chunks/autocomplete-BXMcNoa3.js +562 -0
  10. package/dist/_chunks/autocomplete-BXMcNoa3.js.map +1 -0
  11. package/dist/_chunks/{balance-badge-cell-C4FYwmrZ.js → balance-badge-cell-D9VhFp0L.js} +3 -3
  12. package/dist/_chunks/{balance-badge-cell-C4FYwmrZ.js.map → balance-badge-cell-D9VhFp0L.js.map} +1 -1
  13. package/dist/_chunks/{booking-C0FoW8Yx.js → booking-CcE_oviI.js} +1896 -1859
  14. package/dist/_chunks/booking-CcE_oviI.js.map +1 -0
  15. package/dist/_chunks/{calendar-appointment-editor-dialog-C1v2MNE4.js → calendar-appointment-editor-dialog-CiAGAX2t.js} +7 -7
  16. package/dist/_chunks/{calendar-appointment-editor-dialog-C1v2MNE4.js.map → calendar-appointment-editor-dialog-CiAGAX2t.js.map} +1 -1
  17. package/dist/_chunks/{checkbox-BjDv-r3o.js → checkbox-a7Sh-dXb.js} +32 -31
  18. package/dist/_chunks/checkbox-a7Sh-dXb.js.map +1 -0
  19. package/dist/_chunks/{checkbox-group-Cw2H5qaq.js → checkbox-group-eGdv9fZc.js} +2 -2
  20. package/dist/_chunks/{checkbox-group-Cw2H5qaq.js.map → checkbox-group-eGdv9fZc.js.map} +1 -1
  21. package/dist/_chunks/{city-input-CJVGswxp.js → city-input-CA59CjnV.js} +2 -2
  22. package/dist/_chunks/{city-input-CJVGswxp.js.map → city-input-CA59CjnV.js.map} +1 -1
  23. package/dist/_chunks/{combobox-CDPm_n0p.js → combobox-D-gpgEDy.js} +314 -294
  24. package/dist/_chunks/combobox-D-gpgEDy.js.map +1 -0
  25. package/dist/_chunks/{country-select-DQ8q7ypA.js → country-select-BfmX9y5i.js} +2 -2
  26. package/dist/_chunks/{country-select-DQ8q7ypA.js.map → country-select-BfmX9y5i.js.map} +1 -1
  27. package/dist/_chunks/{data-table-B_IcS0Ke.js → data-table-CqQgoNsU.js} +2 -2
  28. package/dist/_chunks/{data-table-B_IcS0Ke.js.map → data-table-CqQgoNsU.js.map} +1 -1
  29. package/dist/_chunks/{face-scheme-BgJGFDTa.js → face-scheme-BKSE98bo.js} +2 -2
  30. package/dist/_chunks/{face-scheme-BgJGFDTa.js.map → face-scheme-BKSE98bo.js.map} +1 -1
  31. package/dist/_chunks/{file-manager-C9YOT-Dn.js → file-manager-BnI1ZWZu.js} +2 -2
  32. package/dist/_chunks/{file-manager-C9YOT-Dn.js.map → file-manager-BnI1ZWZu.js.map} +1 -1
  33. package/dist/_chunks/{header-settings-BVhjndtV.js → header-settings-BeA2fnol.js} +24 -23
  34. package/dist/_chunks/header-settings-BeA2fnol.js.map +1 -0
  35. package/dist/_chunks/{heatmap-HTz3Jvzy.js → heatmap-fCxwNz2o.js} +2 -2
  36. package/dist/_chunks/{heatmap-HTz3Jvzy.js.map → heatmap-fCxwNz2o.js.map} +1 -1
  37. package/dist/_chunks/{link-cell-renderer-C0ynDik7.js → link-cell-renderer-CnCgudvq.js} +3 -3
  38. package/dist/_chunks/{link-cell-renderer-C0ynDik7.js.map → link-cell-renderer-CnCgudvq.js.map} +1 -1
  39. package/dist/_chunks/{login-dev-card-DYwbL5sD.js → login-dev-card-BZBR58mu.js} +2 -2
  40. package/dist/_chunks/{login-dev-card-DYwbL5sD.js.map → login-dev-card-BZBR58mu.js.map} +1 -1
  41. package/dist/_chunks/{map-view-Bvi06RVh.js → map-view-UrNgxBy3.js} +260 -237
  42. package/dist/_chunks/map-view-UrNgxBy3.js.map +1 -0
  43. package/dist/_chunks/{index.modern-BeRXQVdc.js → maps-core-gate-DkNqGkm3.js} +360 -319
  44. package/dist/_chunks/maps-core-gate-DkNqGkm3.js.map +1 -0
  45. package/dist/_chunks/{marketplace-app-shell-x_abo_ye.js → marketplace-app-shell-CmZx0Oam.js} +2 -2
  46. package/dist/_chunks/{marketplace-app-shell-x_abo_ye.js.map → marketplace-app-shell-CmZx0Oam.js.map} +1 -1
  47. package/dist/_chunks/{notes-panel-CZO1qsD-.js → notes-panel-Dywo6R55.js} +2 -2
  48. package/dist/_chunks/{notes-panel-CZO1qsD-.js.map → notes-panel-Dywo6R55.js.map} +1 -1
  49. package/dist/_chunks/{patient-details-D0P5eog5.js → patient-details-BUr7Oemx.js} +2 -2
  50. package/dist/_chunks/{patient-details-D0P5eog5.js.map → patient-details-BUr7Oemx.js.map} +1 -1
  51. package/dist/_chunks/{patient-field-row-R73dZJY6.js → patient-field-row-bJk8Q_qr.js} +5 -5
  52. package/dist/_chunks/{patient-field-row-R73dZJY6.js.map → patient-field-row-bJk8Q_qr.js.map} +1 -1
  53. package/dist/_chunks/patient-search-Ca9j5cWb.js +1306 -0
  54. package/dist/_chunks/patient-search-Ca9j5cWb.js.map +1 -0
  55. package/dist/_chunks/{patient-summary-card-6U74-ivO.js → patient-summary-card-CCQZ8X37.js} +2 -2
  56. package/dist/_chunks/{patient-summary-card-6U74-ivO.js.map → patient-summary-card-CCQZ8X37.js.map} +1 -1
  57. package/dist/_chunks/{periodontal-diagnosis-B9YmmVmM.js → periodontal-diagnosis-DD3hrg9X.js} +2 -2
  58. package/dist/_chunks/{periodontal-diagnosis-B9YmmVmM.js.map → periodontal-diagnosis-DD3hrg9X.js.map} +1 -1
  59. package/dist/_chunks/{permission-matrix-BoQjENGV.js → permission-matrix-BJ6sg3Zp.js} +3 -3
  60. package/dist/_chunks/{permission-matrix-BoQjENGV.js.map → permission-matrix-BJ6sg3Zp.js.map} +1 -1
  61. package/dist/_chunks/{practice-results-B61krlwu.js → practice-results-DQuOfqej.js} +2 -2
  62. package/dist/_chunks/{practice-results-B61krlwu.js.map → practice-results-DQuOfqej.js.map} +1 -1
  63. package/dist/_chunks/{province-select-CPdTwEob.js → province-select-D64whw0m.js} +2 -2
  64. package/dist/_chunks/{province-select-CPdTwEob.js.map → province-select-D64whw0m.js.map} +1 -1
  65. package/dist/_chunks/{registration-form-DmP3YUfp.js → registration-form-CRm6DaB9.js} +2 -2
  66. package/dist/_chunks/{registration-form-DmP3YUfp.js.map → registration-form-CRm6DaB9.js.map} +1 -1
  67. package/dist/_chunks/{reviews-panel-UzQQLb-z.js → reviews-panel-B7InBORU.js} +42 -41
  68. package/dist/_chunks/reviews-panel-B7InBORU.js.map +1 -0
  69. package/dist/_chunks/{rich-text-editor-BZTyxQBd.js → rich-text-editor-BfEu57dk.js} +3 -3
  70. package/dist/_chunks/{rich-text-editor-BZTyxQBd.js.map → rich-text-editor-BfEu57dk.js.map} +1 -1
  71. package/dist/_chunks/{slot-grid-Bb13H9Rl.js → slot-grid-DrwynKeN.js} +91 -89
  72. package/dist/_chunks/{slot-grid-Bb13H9Rl.js.map → slot-grid-DrwynKeN.js.map} +1 -1
  73. package/dist/_chunks/{task-editor-BOETWbjU.js → task-editor-CI1SKUNc.js} +2 -2
  74. package/dist/_chunks/{task-editor-BOETWbjU.js.map → task-editor-CI1SKUNc.js.map} +1 -1
  75. package/dist/_chunks/{use-appointment-details-read-CWN-nJCZ.js → use-appointment-details-read-BytPq3Ux.js} +2 -2
  76. package/dist/_chunks/{use-appointment-details-read-CWN-nJCZ.js.map → use-appointment-details-read-BytPq3Ux.js.map} +1 -1
  77. package/dist/_chunks/{voice-control-panel-BziKE-KK.js → voice-control-panel-BP0otrID.js} +2 -2
  78. package/dist/_chunks/{voice-control-panel-BziKE-KK.js.map → voice-control-panel-BP0otrID.js.map} +1 -1
  79. package/dist/_chunks/{workflow-webhook-action-DulCpCRz.js → workflow-webhook-action-B_nBJ_ht.js} +2 -2
  80. package/dist/_chunks/{workflow-webhook-action-DulCpCRz.js.map → workflow-webhook-action-B_nBJ_ht.js.map} +1 -1
  81. package/dist/agent-catalog.json +2 -2
  82. package/dist/agent-i18n/en.json +1 -1
  83. package/dist/components/_shared/maps-core-gate.d.ts +18 -0
  84. package/dist/components/_shared/maps-core-gate.d.ts.map +1 -0
  85. package/dist/components/address-autocomplete/address-autocomplete.d.ts.map +1 -1
  86. package/dist/components/address-autocomplete/index.js +1 -1
  87. package/dist/components/address-fields/index.js +1 -1
  88. package/dist/components/ai-permission-notice/index.js +1 -1
  89. package/dist/components/appointment-details-panel/index.js +1 -1
  90. package/dist/components/autocomplete/autocomplete.agent.d.ts.map +1 -1
  91. package/dist/components/autocomplete/autocomplete.d.ts.map +1 -1
  92. package/dist/components/autocomplete/index.js +1 -1
  93. package/dist/components/booking/booking.d.ts.map +1 -1
  94. package/dist/components/booking/details-form.d.ts.map +1 -1
  95. package/dist/components/booking/index.js +1 -1
  96. package/dist/components/checkbox/checkbox.d.ts.map +1 -1
  97. package/dist/components/checkbox/index.js +1 -1
  98. package/dist/components/checkbox-group/index.js +1 -1
  99. package/dist/components/city-input/index.js +1 -1
  100. package/dist/components/combobox/combobox.d.ts.map +1 -1
  101. package/dist/components/combobox/index.js +1 -1
  102. package/dist/components/country-select/index.js +1 -1
  103. package/dist/components/data-table/index.js +2 -2
  104. package/dist/components/face-scheme/index.js +1 -1
  105. package/dist/components/file-manager/index.js +1 -1
  106. package/dist/components/header-settings/header-settings.d.ts +2 -1
  107. package/dist/components/header-settings/header-settings.d.ts.map +1 -1
  108. package/dist/components/header-settings/index.js +1 -1
  109. package/dist/components/heatmap/index.js +1 -1
  110. package/dist/components/map-view/index.js +1 -1
  111. package/dist/components/map-view/map-view.d.ts.map +1 -1
  112. package/dist/components/notes-panel/index.js +1 -1
  113. package/dist/components/patient-details/index.js +1 -1
  114. package/dist/components/patient-search/index.js +1 -1
  115. package/dist/components/patient-search/patient-search.d.ts.map +1 -1
  116. package/dist/components/patient-summary-card/index.js +1 -1
  117. package/dist/components/patient-table/index.js +1 -1
  118. package/dist/components/periodontal-diagnosis/index.js +1 -1
  119. package/dist/components/permission-matrix/index.js +1 -1
  120. package/dist/components/practice-results/index.js +1 -1
  121. package/dist/components/province-select/index.js +1 -1
  122. package/dist/components/reviews-panel/index.js +1 -1
  123. package/dist/components/reviews-panel/reviews-panel.d.ts.map +1 -1
  124. package/dist/components/rich-text-editor/index.js +1 -1
  125. package/dist/components/slot-grid/index.js +1 -1
  126. package/dist/components/slot-grid/slot-grid.d.ts.map +1 -1
  127. package/dist/components/task-editor/index.js +1 -1
  128. package/dist/components/voice-control-panel/index.js +1 -1
  129. package/dist/host-compat.css +1 -10
  130. package/dist/index.js +37 -37
  131. package/dist/patterns/anamnesis/index.js +1 -1
  132. package/dist/patterns/calendar/index.js +1 -1
  133. package/dist/patterns/login-form/index.js +1 -1
  134. package/dist/patterns/marketplace-app-shell/index.js +1 -1
  135. package/dist/patterns/registration-form/index.js +1 -1
  136. package/dist/patterns/workflow-webhook-action/index.js +1 -1
  137. package/dist/tokens.css +1 -1
  138. package/package.json +1 -1
  139. package/dist/_chunks/address-autocomplete-bPR2mNBt.js +0 -498
  140. package/dist/_chunks/address-autocomplete-bPR2mNBt.js.map +0 -1
  141. package/dist/_chunks/autocomplete-CnB1mHGc.js +0 -539
  142. package/dist/_chunks/autocomplete-CnB1mHGc.js.map +0 -1
  143. package/dist/_chunks/booking-C0FoW8Yx.js.map +0 -1
  144. package/dist/_chunks/checkbox-BjDv-r3o.js.map +0 -1
  145. package/dist/_chunks/combobox-CDPm_n0p.js.map +0 -1
  146. package/dist/_chunks/header-settings-BVhjndtV.js.map +0 -1
  147. package/dist/_chunks/index.modern-BeRXQVdc.js.map +0 -1
  148. package/dist/_chunks/map-view-Bvi06RVh.js.map +0 -1
  149. package/dist/_chunks/patient-search-CCOUZABE.js +0 -1196
  150. package/dist/_chunks/patient-search-CCOUZABE.js.map +0 -1
  151. package/dist/_chunks/reviews-panel-UzQQLb-z.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"voice-control-panel-BziKE-KK.js","sources":["../../node_modules/lucide-react/dist/esm/icons/audio-lines.mjs","../../src/components/voice-control-panel/voice-control-panel.agent.ts","../../src/components/voice-control-panel/voice-control-panel.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\"path\", { d: \"M2 10v3\", key: \"1fnikh\" }],\n [\"path\", { d: \"M6 6v11\", key: \"11sgs0\" }],\n [\"path\", { d: \"M10 3v18\", key: \"yhl04a\" }],\n [\"path\", { d: \"M14 8v7\", key: \"3a1oy3\" }],\n [\"path\", { d: \"M18 5v13\", key: \"123xd1\" }],\n [\"path\", { d: \"M22 10v3\", key: \"154ddg\" }]\n];\nconst AudioLines = createLucideIcon(\"audio-lines\", __iconNode);\n\nexport { __iconNode, AudioLines as default };\n//# sourceMappingURL=audio-lines.mjs.map\n","/* -------------------------------------------------------------------- */\n/* Agent adapter: VoiceControlPanel. */\n/* */\n/* The panel draws a host-driven voice session; the host owns the */\n/* microphone and the speech service. `start_listening` / `stop_listening`*/\n/* call the same `onToggle` the button calls, but only in the direction */\n/* asked, so an agent never starts by mistake. They are `capture` and */\n/* `write` (start opens the microphone). Picking a match hands the host */\n/* an id through `onCandidatePick`, which the host turns into a write on */\n/* the patient's record, so it is `phi`. The commands disclosure is a */\n/* plain `expand`. In the floating layout the panel above the pill opens */\n/* and closes (`open` / `close`, `read`): closing never stops listening. */\n/* */\n/* No state slot returns the transcript, the target line or a message: */\n/* those can carry patient data the host put there. */\n/* */\n/* AI credits: `start_listening` is gated by `aiCredits.exhausted` after */\n/* the consent check, so out of credits it shows the out-of-credits */\n/* notice instead. The notice's buy, upgrade and support buttons are */\n/* host navigation an agent never presses; it can only read the gate and */\n/* close the notice (`close_credits_notice`, `read`). */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { VoiceControlPanelHandle } from './voice-control-panel';\n\nexport const voiceControlPanelAgent: AgentAdapter<VoiceControlPanelHandle> = {\n id: 'voice-control-panel',\n capabilities: ['capture', 'pick', 'expand', 'open', 'close'],\n state: {\n status: {\n type: \"'idle' | 'starting' | 'listening' | 'paused' | 'error'\",\n descriptionKey: 'ui.agent.voiceControlPanel.state.status',\n description:\n 'Where the host voice session is: idle, starting, listening, paused or error.',\n read: (handle) => handle.getStatus(),\n },\n candidateIds: {\n type: 'string[]',\n descriptionKey: 'ui.agent.voiceControlPanel.state.candidateIds',\n description:\n 'Ids of the matches currently offered, in spoken order (option 1 first). Empty when there is nothing to choose.',\n read: (handle) => handle.getCandidateIds(),\n },\n commandsOpen: {\n type: 'boolean',\n descriptionKey: 'ui.agent.voiceControlPanel.state.commandsOpen',\n description: 'True when the voice commands list is expanded.',\n read: (handle) => handle.isCommandsOpen(),\n },\n panelOpen: {\n type: 'boolean',\n descriptionKey: 'ui.agent.voiceControlPanel.state.panelOpen',\n description:\n 'Floating layout: true when the panel above the pill is open. Always false inline.',\n read: (handle) => handle.isPanelOpen(),\n },\n creditsExhausted: {\n type: 'boolean',\n descriptionKey: 'ui.agent.voiceControlPanel.state.creditsExhausted',\n description:\n 'True when the practice is out of AI credits: every start shows the out-of-credits notice instead of listening.',\n read: (handle) => handle.isCreditsExhausted(),\n },\n creditsNoticeOpen: {\n type: 'boolean',\n descriptionKey: 'ui.agent.voiceControlPanel.state.creditsNoticeOpen',\n description:\n 'True when the out-of-credits notice is showing in the panel, in place of the controls.',\n read: (handle) => handle.isCreditsNoticeOpen(),\n },\n },\n actions: {\n start_listening: {\n safety: 'write',\n idempotent: true,\n // The host streams the patient's audio to its transcription service.\n openWorld: true,\n descriptionKey: 'ui.agent.voiceControlPanel.actions.startListening',\n description:\n 'Ask the host to start listening (it opens the microphone). Does nothing when already starting or listening, or while the panel is disabled. Without patient AI consent it shows the consent banner instead; out of AI credits (creditsExhausted) it shows the out-of-credits notice instead.',\n errors: ['Microphone permission denied', 'Speech service unavailable'],\n invoke: (handle) => {\n handle.start();\n },\n },\n stop_listening: {\n safety: 'write',\n idempotent: true,\n descriptionKey: 'ui.agent.voiceControlPanel.actions.stopListening',\n description:\n 'Ask the host to stop listening, cancel a start or end a paused session. Never gated by consent or AI credits. Does nothing when idle or stopped.',\n invoke: (handle) => {\n handle.stop();\n },\n },\n pick_candidate: {\n // The host turns the pick into a write on the patient's record (the\n // care plan adds a treatment line), so it is gated as PHI.\n safety: 'phi',\n argsType: '{ id: string }',\n argsSchema: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description: 'One of the ids in the candidateIds state slot.',\n },\n },\n required: ['id'],\n additionalProperties: false,\n },\n descriptionKey: 'ui.agent.voiceControlPanel.actions.pickCandidate',\n description:\n 'Pick one of the offered matches by id, the same as saying its option number or pressing it. Unknown ids are ignored.',\n examples: [\n { args: { id: 'otve' }, note: 'Pick the match with id otve.' },\n ],\n invoke: (handle, args: { id: string }) => {\n handle.pickCandidate(args.id);\n },\n },\n open_commands: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.voiceControlPanel.actions.openCommands',\n description: 'Expand the voice commands list.',\n invoke: (handle) => {\n handle.openCommands();\n },\n },\n close_commands: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.voiceControlPanel.actions.closeCommands',\n description: 'Collapse the voice commands list.',\n invoke: (handle) => {\n handle.closeCommands();\n },\n },\n open_panel: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.voiceControlPanel.actions.openPanel',\n description:\n 'Floating layout: open the panel above the pill. Does nothing inline.',\n invoke: (handle) => {\n handle.openPanel();\n },\n },\n close_panel: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.voiceControlPanel.actions.closePanel',\n description:\n 'Floating layout: close the panel above the pill. Listening carries on. Does nothing inline.',\n invoke: (handle) => {\n handle.closePanel();\n },\n },\n close_credits_notice: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.voiceControlPanel.actions.closeCreditsNotice',\n description:\n 'Close the out-of-credits notice and go back to the controls. Credits stay out: the next start shows it again.',\n invoke: (handle) => {\n handle.closeCreditsNotice();\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'voice-control-panel',\n description: 'Marks the VoiceControlPanel root.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop on VoiceControlPanel.',\n },\n item: {\n attr: 'data-candidate-id',\n description:\n 'On each match button, the candidate id that pick_candidate takes.',\n },\n },\n};\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { cva } from 'class-variance-authority';\nimport { Trans, useTranslation } from 'react-i18next';\nimport {\n AudioLines,\n ChevronDown,\n CircleAlert,\n CircleCheck,\n Info,\n LoaderCircle,\n SlidersHorizontal,\n Square,\n TriangleAlert,\n X,\n} from 'lucide-react';\nimport {\n AiCreditsExhaustedNotice,\n AiCreditsWarning,\n type AiCreditsInfo,\n type AiCreditsNoticeProps,\n} from '../ai-credits-notice';\nimport {\n useDismissibleAiCreditsNotice,\n withAiCreditsNotice,\n} from '../ai-credits-notice/ai-credits-notice';\nimport {\n AiConsentBanner,\n type AiConsentBannerHeadingLevel,\n type AiConsentGate,\n} from '../ai-permission-notice';\nimport { AiSparkle } from '../ai-sparkle';\nimport { Badge } from '../badge';\nimport { Button, IconButton } from '../button';\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from '../collapsible';\nimport { FormField } from '../form-field';\nimport { Kbd, type KbdNamedKey } from '../kbd';\nimport { List } from '../list';\nimport { LiveRegion } from '../live-region';\nimport { Select, type SelectOption } from '../select';\nimport {\n aiFabDividerClass,\n aiFabPillClass,\n aiFabSegmentVariants,\n aiFabSweepClass,\n} from '../_shared/ai-fab-pill';\nimport { composeRefs } from '../_shared/compose-refs';\nimport {\n glassEntranceClass,\n glassSurfaceClass,\n} from '../_shared/glass-surface';\nimport { publishFloatingClearance } from '../_shared/floating-clearance';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { useAiSparkle } from '../../hooks/use-ai-sparkle';\nimport { useIsomorphicLayoutEffect } from '../../hooks/use-isomorphic-layout-effect';\nimport { voiceControlPanelAgent } from './voice-control-panel.agent';\n\n/* -------------------------------------------------------------------- */\n/* VoiceControlPanel */\n/* */\n/* The kit draws the voice-control UI; the host owns the microphone and */\n/* the speech service and drives the panel through props. The same split */\n/* as RichTextEditor's `onDictate`: no audio, no network, no parsing in */\n/* the kit. The panel is compact so it can sit inline above a chart today */\n/* and move into a tool rail later: a container query stacks it in a */\n/* narrow container. */\n/* */\n/* `layout=\"floating\"` draws the same panel as a non-modal companion */\n/* above the RichTextEditor's AI pill (shared recipe in */\n/* `_shared/ai-fab-pill.ts`), fixed to a viewport corner: mic | panel. */\n/* */\n/* `aiConsent` puts every start behind the patient AI-consent gate, the */\n/* same contract, banner and resume-after-grant as RichTextEditor. */\n/* `aiCredits` puts it behind the practice's AI credits too, checked */\n/* after consent, with the shared AiCreditsExhaustedNotice. */\n/* -------------------------------------------------------------------- */\n\n/* -------------------------------------------------------------------- */\n/* Public types */\n/* -------------------------------------------------------------------- */\n\n/** Where the host's voice session is. */\nexport type VoiceControlStatus =\n | 'idle'\n | 'starting'\n | 'listening'\n | 'paused'\n | 'error';\n\n/** How a message reads: the tone decides the colour and the icon. */\nexport type VoiceControlTone = 'info' | 'success' | 'warning' | 'error';\n\n/** Generic failures the kit words itself, translated. */\nexport type VoiceControlError =\n | 'microphoneDenied'\n | 'connectionLost'\n | 'startFailed'\n | 'recordingFailed'\n | 'unsupported';\n\n/** A host message, already translated by the host. */\nexport interface VoiceControlMessage {\n tone: VoiceControlTone;\n text: string;\n /**\n * Change it to announce the same words again (the live region only speaks\n * when the message changes), e.g. a counter for \"Not recognised\" twice.\n */\n id?: string | number;\n}\n\n/** What the speech service heard. `partial` may still change. */\nexport interface VoiceControlTranscript {\n final?: string;\n partial?: string;\n}\n\n/** One dictation language the host's speech service accepts. */\nexport interface VoiceControlLanguage {\n value: string;\n label: string;\n}\n\n/** One match the user can pick by voice (\"option 1\") or by hand. */\nexport interface VoiceControlCandidate {\n id: string;\n label: string;\n detail?: string;\n}\n\n/** One voice command shown in the commands disclosure. */\nexport interface VoiceControlCommand {\n phrase: ReactNode;\n description: ReactNode;\n}\n\n/** What a press asks the host to do: start (or resume), or stop (or cancel\n * a start). */\nexport type VoiceControlToggle = 'start' | 'stop';\n\n/** Inline in the page flow, or a floating pill that opens the panel. */\nexport type VoiceControlLayout = 'inline' | 'floating';\n\n/** The viewport corner of the floating pill. Logical: mirrors in RTL. */\nexport type VoiceControlPosition = 'bottom-end' | 'bottom-start';\n\n/**\n * The practice's AI credits, as the platform keeps them (a yearly allowance\n * that expires, then welcome and bought credits that do not), plus whether\n * they have run out. See `AiCreditsInfo` for the allowance, the dates and\n * the next steps the host can offer.\n */\nexport interface VoiceControlAiCredits extends AiCreditsInfo {\n /**\n * Out of AI credits. Every way of starting shows the out-of-credits\n * notice in the panel instead of calling `onToggle`; stopping is never\n * gated. Set while a session runs or is paused, the panel stops it\n * (`onToggle('stop')`, once) and shows the notice on its own.\n */\n exhausted?: boolean;\n}\n\n/** Curated imperative handle for agent / external automation. */\nexport interface VoiceControlPanelHandle {\n getStatus: () => VoiceControlStatus;\n /** Ask the host to start (`onToggle('start')`) unless already starting\n * or listening, or disabled. Gated by `aiConsent` like a press. */\n start: () => void;\n /** Ask the host to stop, cancel a start or end a paused session\n * (`onToggle('stop')`); a no-op when idle or stopped. Never gated. */\n stop: () => void;\n getCandidateIds: () => string[];\n pickCandidate: (id: string) => void;\n isCommandsOpen: () => boolean;\n openCommands: () => void;\n closeCommands: () => void;\n /** Floating layout: whether the panel above the pill is open. Always\n * false inline. */\n isPanelOpen: () => boolean;\n /** Floating layout: open the panel (no focus move). A no-op inline. */\n openPanel: () => void;\n /** Floating layout: close the panel; listening carries on. A no-op inline. */\n closePanel: () => void;\n /** Whether `aiCredits.exhausted` is gating every start. */\n isCreditsExhausted: () => boolean;\n /** Whether the out-of-credits notice is showing. */\n isCreditsNoticeOpen: () => boolean;\n /** Close the out-of-credits notice, back to the controls. */\n closeCreditsNotice: () => void;\n}\n\nexport interface VoiceControlPanelProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title' | 'children' | 'onToggle'\n> {\n /** Where the host's voice session is. Drives the button, dot and status. */\n status: VoiceControlStatus;\n /**\n * The start/stop button, the pill's voice segment, the shortcut and the\n * agent call this, saying which way: `'start'` when idle, paused or\n * stopped, `'stop'` when starting or listening (stop, or cancel a start).\n * Every stop is passed on, so act on the argument rather than flip your\n * own state. A host that ignores the argument keeps working.\n */\n onToggle: (next: VoiceControlToggle) => void;\n /**\n * Stops a session from starting: disables the button, the shortcut, the\n * settings and the matches. A session already starting or listening can\n * still be stopped, so the microphone is never trapped on.\n */\n disabled?: boolean;\n /** Panel title. Default: the kit's \"Voice control\". */\n title?: ReactNode;\n /** Shows a \"Beta\" badge beside the title. */\n beta?: boolean;\n /** A host notice under the title, e.g. \"Free during the beta.\" */\n betaNotice?: ReactNode;\n /** The current dictation language (a `languages` value). */\n language?: string;\n /** Dictation languages. The select shows only with two or more. */\n languages?: VoiceControlLanguage[];\n onLanguageChange?: (value: string) => void;\n /** Extra settings beside the language, e.g. the periodontal route select. */\n extraControls?: ReactNode;\n /** The bold \"where am I\" line: the field or elements voice acts on. */\n target?: ReactNode;\n /** What the speech service heard: `final` reads normal, `partial` muted. */\n transcript?: VoiceControlTranscript;\n /** A host message. The tone sets colour and icon; not every message is red. */\n message?: VoiceControlMessage | null;\n /** A generic failure, worded and translated by the kit (tone: error). */\n error?: VoiceControlError | null;\n /** Several matches to choose from, numbered as they are spoken. */\n candidates?: VoiceControlCandidate[];\n /** Host label above the matches, e.g. \"Several matches: say 'option 1'…\". */\n candidatesLabel?: string;\n onCandidatePick?: (id: string) => void;\n /** Voice commands listed in the disclosure under the panel. */\n commands?: VoiceControlCommand[];\n /** Start with the commands disclosure open. */\n defaultCommandsOpen?: boolean;\n /**\n * Keyboard shortcut the kit binds document-wide to toggle, e.g. `\"Alt+V\"`\n * (default). Modifiers: Alt, Control, Shift, Meta. `false` turns it off.\n */\n shortcut?: string | false;\n /** Opaque instance id: `data-component-id` and agent registration. */\n id?: string;\n /**\n * `inline` (default): the panel sits in the page flow, above the chart.\n * `floating`: the RichTextEditor's AI pill (mic | panel) fixed to a\n * viewport corner; the second segment opens this panel above it, non-modal.\n */\n layout?: VoiceControlLayout;\n /**\n * Floating only: the viewport corner. Default `bottom-end`; `bottom-start`\n * moves the pill away from another floating widget (a chat bubble).\n */\n position?: VoiceControlPosition;\n /** Floating only: whether the panel above the pill is open (controlled). */\n open?: boolean;\n /** Floating only: the open state when uncontrolled. Default `false`. */\n defaultOpen?: boolean;\n /**\n * Floating only: called when the panel opens or closes, by the pill, the\n * close button, Escape, or on its own for an error or a set of matches.\n */\n onOpenChange?: (open: boolean) => void;\n /**\n * Floating only: the pill's sparkle, as on the RichTextEditor's pill. It\n * plays once when the pill first shows and again when a success message\n * arrives (a write landed); never in a loop while listening. Default `true`.\n */\n aiSparkle?: boolean;\n /**\n * Patient AI-consent gate: voice sends patient audio to the host's speech\n * service. Same contract as RichTextEditor's `aiConsent`. While present and\n * not granted, every way of starting shows the `AiConsentBanner` instead of\n * calling `onToggle`; stopping is never gated. Omit for ungated behaviour.\n */\n aiConsent?: AiConsentGate;\n /**\n * Heading level for the `AiConsentBanner` title and the out-of-credits\n * notice title, which show in the same place. Default 5.\n */\n aiConsentHeadingLevel?: AiConsentBannerHeadingLevel;\n /**\n * The practice's AI credits. With `exhausted`, every way of starting (the\n * Start button, the pill's mic, the shortcut, the agent's\n * `start_listening`) shows the out-of-credits notice instead of calling\n * `onToggle`, after the consent check; stopping is never gated. Running\n * low (80% used, or `runningLow`) and `expiring` show as quiet lines in\n * the panel and never block. Omit for ungated behaviour.\n */\n aiCredits?: VoiceControlAiCredits;\n /**\n * Floating only: the `AiCreditsNotice` hover card on the pill's mic, as on\n * the RichTextEditor's AI triggers. It never takes the click: the mic\n * still starts, stops or opens a gate exactly as without it.\n */\n aiCreditsNotice?: AiCreditsNoticeProps;\n}\n\n/* -------------------------------------------------------------------- */\n/* CVA */\n/* -------------------------------------------------------------------- */\n\nconst rootVariants = cva(\n [\n // The container query lives on the root and is read by descendants only,\n // never by a flex parent of its own (see the grid below).\n 'ds:@container/voice-control',\n 'ds:block ds:w-full ds:min-w-0',\n // The edge is --surface-border-width (1px, 2px in the accessible themes),\n // at zero specificity so the nearest theme root decides it.\n 'ds:rounded-[var(--radius-lg)] ds:[:where(&)]:border-[length:var(--surface-border-width,var(--border-width-sm))]',\n 'ds:bg-[var(--card)] ds:text-[color:var(--foreground)]',\n 'ds:p-[var(--spacing-md)]',\n 'ds:print:hidden',\n ].join(' '),\n {\n variants: {\n // The border colour lives only here, never beside a base colour, so\n // the listening ring can't lose a race on stylesheet order.\n status: {\n idle: 'ds:border-[color:var(--card-border)]',\n starting: 'ds:border-[color:var(--card-border)]',\n listening: 'ds:border-[color:var(--ring)]',\n paused: 'ds:border-[color:var(--card-border)]',\n error: 'ds:border-[color:var(--card-border)]',\n },\n },\n defaultVariants: { status: 'idle' },\n },\n);\n\nconst statusDotVariants = cva(\n [\n 'ds:relative ds:inline-flex ds:size-2.5 ds:shrink-0 ds:rounded-[var(--radius-full)]',\n 'ds:forced-colors:border ds:forced-colors:border-[CanvasText]',\n ].join(' '),\n {\n variants: {\n status: {\n idle: 'ds:border ds:border-[color:var(--muted-foreground)]',\n starting: 'ds:bg-[color:var(--muted-foreground)]',\n listening: 'ds:bg-[color:var(--destructive)]',\n paused: 'ds:bg-[color:var(--warning)]',\n error: 'ds:bg-[color:var(--error)]',\n },\n },\n defaultVariants: { status: 'idle' },\n },\n);\n\nconst messageVariants = cva(\n 'ds:m-0 ds:flex ds:items-start ds:gap-[var(--spacing-xs)] type-body-sm',\n {\n variants: {\n tone: {\n info: 'ds:text-[color:var(--info-foreground)]',\n success: 'ds:text-[color:var(--success-foreground)]',\n warning: 'ds:text-[color:var(--warning-foreground)]',\n error: 'ds:text-[color:var(--error-foreground)]',\n },\n },\n defaultVariants: { tone: 'info' },\n },\n);\n\nconst focusRing =\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]';\n\nconst candidateButtonVariants = cva(\n [\n 'ds:flex ds:w-full ds:items-center',\n 'ds:min-h-[var(--min-target-size)] ds:rounded-[var(--radius-sm)]',\n 'ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)] ds:py-[var(--spacing-2xs)]',\n 'ds:text-start ds:text-[color:var(--foreground)] ds:cursor-pointer',\n 'ds:hover:bg-[color:var(--muted)] ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n 'ds:disabled:cursor-not-allowed ds:disabled:opacity-50',\n focusRing,\n ].join(' '),\n);\n\nconst commandsTriggerVariants = cva(\n [\n 'ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]',\n 'ds:min-h-[var(--min-target-size)] ds:py-[var(--spacing-2xs)]',\n 'ds:rounded-[var(--radius-sm)] type-meta ds:text-[color:var(--muted-foreground)] ds:text-start',\n 'ds:hover:text-[color:var(--foreground)] ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n focusRing,\n ].join(' '),\n);\n\n/* ---- Floating layout ------------------------------------------------ */\n\n// The fixed cluster: the panel stacked above the pill, in one viewport\n// corner. `--z-fixed` is the kit's layer for viewport-fixed controls (the\n// FloatingActionButton's): above sticky chrome, below a modal's backdrop, so\n// an open Dialog covers the pill and the page together. The Select listbox\n// the panel opens portals to <body> on its own `--z-dropdown` layer, above\n// this one. The cluster is as wide as the open panel; its empty corner lets\n// clicks through to the page.\nconst floatingRootVariants = cva(\n [\n 'ds:fixed ds:z-[var(--z-fixed)]',\n // `--voice-control-offset` (default: the kit's corner gap) lifts the\n // cluster, for example above the page's own floating button; the\n // panel's height cap below reads the same property.\n 'ds:bottom-[calc(var(--voice-control-offset,var(--spacing-lg))+env(safe-area-inset-bottom,0px))]',\n 'ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]',\n 'ds:pointer-events-none ds:[&>*]:pointer-events-auto',\n 'ds:print:hidden',\n ].join(' '),\n {\n variants: {\n position: {\n 'bottom-end': 'ds:end-[var(--spacing-lg)] ds:items-end',\n 'bottom-start': 'ds:start-[var(--spacing-lg)] ds:items-start',\n },\n },\n defaultVariants: { position: 'bottom-end' },\n },\n);\n\n// The panel above the pill: the Help me write card's liquid glass (shared\n// recipe in `_shared/glass-surface.ts`, with its opaque fallback for the\n// accessible themes, more contrast, reduced transparency and forced\n// colours), so AI tools share one look. Compact: never wider than the\n// viewport minus the gutter on both sides, never taller than the room above\n// the pill; longer content scrolls inside. It grows out of the pill's\n// corner. `transform-origin` has no logical keywords, so the corner is\n// picked per position and direction: bottom-end is the bottom-right corner\n// in LTR and the bottom-left in RTL.\nconst floatingPanelVariants = cva(\n [\n 'ds:@container/voice-control',\n // Closed is `hidden`: never let a display utility win over it.\n 'ds:min-w-0 ds:[&[hidden]]:hidden',\n 'ds:[inline-size:min(24rem,calc(100vw-(var(--spacing-lg)*2)))]',\n 'ds:[max-block-size:calc(100dvh-var(--min-target-size)-var(--voice-control-offset,var(--spacing-lg))-var(--spacing-lg)-var(--spacing-sm)-env(safe-area-inset-bottom,0px))]',\n // On a phone the panel spans the screen, so it stops at half the height\n // (scrolling inside) and the voice cursor row above it stays in view.\n 'ds:max-sm:[max-block-size:calc(50dvh-var(--min-target-size)-var(--voice-control-offset,var(--spacing-lg))-var(--spacing-lg)-var(--spacing-sm)-env(safe-area-inset-bottom,0px))]',\n 'ds:overflow-y-auto ds:overscroll-contain',\n glassSurfaceClass,\n glassEntranceClass,\n 'ds:text-[color:var(--foreground)]',\n focusRing,\n ].join(' '),\n {\n variants: {\n position: {\n 'bottom-end': 'ds:origin-bottom-right ds:rtl:origin-bottom-left',\n 'bottom-start': 'ds:origin-bottom-left ds:rtl:origin-bottom-right',\n },\n },\n defaultVariants: { position: 'bottom-end' },\n },\n);\n\n// The pill's mic segment. While listening it carries the word \"Listening\"\n// beside the stop icon, so it widens like an extended FAB.\nconst micSegmentVariants = cva(\n 'ds:disabled:cursor-not-allowed ds:disabled:opacity-50',\n {\n variants: {\n extended: {\n true: 'ds:gap-[var(--spacing-xs)] ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-md)]',\n false: '',\n },\n },\n defaultVariants: { extended: false },\n },\n);\n\n/* -------------------------------------------------------------------- */\n/* Shortcut */\n/* -------------------------------------------------------------------- */\n\ntype ShortcutModifier = Extract<\n KbdNamedKey,\n 'Alt' | 'Control' | 'Shift' | 'Meta'\n>;\n\ninterface ParsedShortcut {\n modifiers: Set<ShortcutModifier>;\n key: string;\n /** `aria-keyshortcuts` value, e.g. `Alt+V`. */\n aria: string;\n /** Keys for the kit Kbd, e.g. `['Alt', 'V']`. */\n keys: string[];\n}\n\nconst MODIFIER_ALIASES: Record<string, ShortcutModifier> = {\n alt: 'Alt',\n option: 'Alt',\n ctrl: 'Control',\n control: 'Control',\n shift: 'Shift',\n meta: 'Meta',\n cmd: 'Meta',\n command: 'Meta',\n};\n\n/** Parse `\"Alt+V\"`-style shortcuts. Returns null for an unusable string. */\nexport function parseVoiceShortcut(shortcut: string): ParsedShortcut | null {\n const parts = shortcut\n .split('+')\n .map((part) => part.trim())\n .filter(Boolean);\n if (parts.length === 0) return null;\n const rawKey = parts[parts.length - 1];\n const modifiers = new Set<ShortcutModifier>();\n for (const part of parts.slice(0, -1)) {\n const modifier = MODIFIER_ALIASES[part.toLowerCase()];\n if (!modifier) return null;\n modifiers.add(modifier);\n }\n const key = rawKey.length === 1 ? rawKey.toUpperCase() : rawKey;\n const ordered = (['Control', 'Alt', 'Shift', 'Meta'] as const).filter((m) =>\n modifiers.has(m),\n );\n return {\n modifiers,\n key,\n aria: [...ordered, key].join('+'),\n keys: [...ordered, key],\n };\n}\n\n/** Whether a keydown matches a parsed shortcut. Letters and digits match on\n * the physical key, so Alt+V still matches on macOS, where Option+V types\n * a different character. */\nfunction matchesShortcut(event: KeyboardEvent, parsed: ParsedShortcut) {\n if (event.altKey !== parsed.modifiers.has('Alt')) return false;\n if (event.ctrlKey !== parsed.modifiers.has('Control')) return false;\n if (event.shiftKey !== parsed.modifiers.has('Shift')) return false;\n if (event.metaKey !== parsed.modifiers.has('Meta')) return false;\n const { key } = parsed;\n if (/^[A-Z]$/.test(key)) return event.code === `Key${key}`;\n if (/^[0-9]$/.test(key)) return event.code === `Digit${key}`;\n return event.key.toLowerCase() === key.toLowerCase();\n}\n\n/* -------------------------------------------------------------------- */\n/* Component */\n/* -------------------------------------------------------------------- */\n\ntype FocusTarget =\n | 'panel'\n | 'gate'\n | 'credits'\n | 'toggle'\n | 'launcher'\n | 'return';\n\n/** The shortcut as kit Kbd pills. `<Trans>` hands a placeholder component\n * `children` (null for a self-closing tag), which would switch Kbd to its\n * children path and render an empty pill, so this wrapper drops them. */\nfunction ShortcutKeys({ keys }: { keys: string[] }) {\n return <Kbd keys={keys} separator=\"plus\" size=\"sm\" />;\n}\n\nconst TONE_ICON: Record<VoiceControlTone, typeof Info> = {\n info: Info,\n success: CircleCheck,\n warning: TriangleAlert,\n error: CircleAlert,\n};\n\nconst NO_CANDIDATES: VoiceControlCandidate[] = [];\n\n// The kit's target size (44px, 48px in the accessible themes) on every\n// control in the panel, at every width: the panel is touch-first.\nconst TARGET_HEIGHT = 'ds:min-h-[var(--min-target-size)]';\nconst TARGET_SQUARE =\n 'ds:min-h-[var(--min-target-size)] ds:min-w-[var(--min-target-size)]';\n\n/** A part that already ends a sentence, in Latin, CJK or Arabic script. */\nconst SENTENCE_END = /[.!?…:。.!?؟।]$/u;\n\n/** How soon after a session ends that running out of credits still shows\n * the notice on its own. Later, it waits for the next start. */\nconst CREDITS_GRACE_MS = 10_000;\n\n/** Appended on every other announcement so a repeat still changes the\n * region's text (and is read again). Invisible, and not trimmed away. */\nconst NO_BREAK_SPACE = '\\u00a0';\n\n/**\n * VoiceControlPanel: the kit UI for a host-driven voice control session.\n * The host keeps the microphone and the speech service; the panel shows the\n * state, what was heard, where the voice acts, messages and matches, and\n * calls `onToggle` from its button and its keyboard shortcut.\n */\nexport const VoiceControlPanel = forwardRef<\n HTMLDivElement,\n VoiceControlPanelProps\n>(\n (\n {\n status,\n onToggle,\n disabled = false,\n title,\n beta = false,\n betaNotice,\n language,\n languages,\n onLanguageChange,\n extraControls,\n target,\n transcript,\n message,\n error,\n candidates = NO_CANDIDATES,\n candidatesLabel,\n onCandidatePick,\n commands,\n defaultCommandsOpen = false,\n shortcut = 'Alt+V',\n id,\n layout = 'inline',\n position = 'bottom-end',\n open,\n defaultOpen = false,\n onOpenChange,\n aiSparkle = true,\n aiConsent,\n aiConsentHeadingLevel,\n aiCredits,\n aiCreditsNotice: aiCreditsNoticeProp,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation('ui');\n const titleId = useId();\n const candidatesLabelId = useId();\n const panelId = useId();\n const [commandsOpen, setCommandsOpen] = useState(defaultCommandsOpen);\n const floating = layout === 'floating';\n\n const rootRef = useRef<HTMLDivElement>(null);\n const composedRef = useMemo(() => composeRefs(ref, rootRef), [ref]);\n const panelRef = useRef<HTMLDivElement>(null);\n const gateRef = useRef<HTMLDivElement>(null);\n const creditsRef = useRef<HTMLDivElement>(null);\n const toggleRef = useRef<HTMLButtonElement>(null);\n const launcherRef = useRef<HTMLButtonElement>(null);\n const pillRef = useRef<HTMLDivElement>(null);\n\n const parsedShortcut = useMemo(\n () => (shortcut === false ? null : parseVoiceShortcut(shortcut)),\n [shortcut],\n );\n useEffect(() => {\n if (import.meta.env.DEV && shortcut !== false && !parsedShortcut) {\n console.warn(\n `VoiceControlPanel: shortcut \"${shortcut}\" is not a valid shortcut ` +\n '(use modifiers Alt, Control, Shift, Meta and one key, e.g. \"Alt+V\"); it is off.',\n );\n }\n }, [shortcut, parsedShortcut]);\n\n /* ---- Floating panel: open state ---------------------------------- */\n const [openState, setOpenState] = useState(defaultOpen);\n const controlled = open !== undefined;\n const panelOpen = floating && (controlled ? open : openState);\n\n /* ---- AI consent gate --------------------------------------------- */\n // Hard stop, whatever the UI state: no consent, no start.\n const aiBlocked = aiConsent !== undefined && !aiConsent.granted;\n const [gateOpen, setGateOpen] = useState(false);\n const showGate = gateOpen && aiBlocked;\n // A start was gated: resume it if consent is granted from this banner.\n const resumeAfterGrantRef = useRef(false);\n // Set only by a Grant click HERE. A `granted` that flips for any other\n // reason (another patient, another tab) must not start listening.\n const grantRequestedRef = useRef(false);\n const [localGrantPending, setLocalGrantPending] = useState(false);\n const grantPending = (aiConsent?.pending ?? false) || localGrantPending;\n\n /* ---- AI credits gate (checked after consent) ---------------------- */\n const creditsBlocked = aiCredits?.exhausted === true;\n const [creditsOpen, setCreditsOpen] = useState(false);\n const showCredits = creditsOpen && creditsBlocked;\n // Set when the notice opens on its own (no focus move), so the live\n // region says why the panel changed. The ref lets the announcement join\n // a status change in the same commit (\"Not listening. You've run out…\");\n // the counter re-runs the announcer when nothing else changed.\n const creditsAnnouncePendingRef = useRef(false);\n const [creditsAnnounce, setCreditsAnnounce] = useState(0);\n // Mirrors `creditsOpen`, set at once by `openCredits` so a focus request\n // made from an effect survives until the notice renders.\n const creditsOpenRef = useRef(creditsOpen);\n creditsOpenRef.current = creditsOpen;\n // The body the gates replace: the controls, or a gate. Focus in here is\n // lost when the body swaps.\n const bodyRef = useRef<HTMLDivElement>(null);\n // Whether focus was last inside the notice: still true when the notice\n // unmounts with focus in it (no blur fires), false once focus left it.\n const noticeFocusRef = useRef(false);\n\n /* ---- Latest values for listeners, callbacks and the agent handle -- */\n const latest = useRef({\n status,\n onToggle,\n disabled,\n candidates,\n onCandidatePick,\n commandsOpen,\n aiBlocked,\n creditsBlocked,\n showCredits,\n floating,\n panelOpen,\n controlled,\n onOpenChange,\n });\n latest.current = {\n status,\n onToggle,\n disabled,\n candidates,\n onCandidatePick,\n commandsOpen,\n aiBlocked,\n creditsBlocked,\n showCredits,\n floating,\n panelOpen,\n controlled,\n onOpenChange,\n };\n\n // A focus move waits for the DOM to show its target; asking for one\n // always re-renders, so it runs now, never at some later render.\n const pendingFocusRef = useRef<FocusTarget | null>(null);\n const [, setFocusRequest] = useState(0);\n const requestFocus = useCallback((target: FocusTarget) => {\n pendingFocusRef.current = target;\n setFocusRequest((n) => n + 1);\n }, []);\n // Where focus was when a start was gated, to go back on Close.\n const gateReturnRef = useRef<HTMLElement | null>(null);\n // Consent withdrawn mid-session: the one automatic stop already sent.\n const stopSentRef = useRef(false);\n\n const setPanelOpen = useCallback((next: boolean) => {\n const now = latest.current;\n if (!now.floating || now.panelOpen === next) return;\n if (!now.controlled) setOpenState(next);\n now.onOpenChange?.(next);\n }, []);\n\n const closeGate = useCallback(() => {\n resumeAfterGrantRef.current = false;\n grantRequestedRef.current = false;\n setGateOpen(false);\n }, []);\n\n /** Where focus is now, to go back to on Close (never <body>). */\n const captureReturn = useCallback(() => {\n const focused =\n typeof document === 'undefined' ? null : document.activeElement;\n gateReturnRef.current =\n focused instanceof HTMLElement && focused !== document.body\n ? focused\n : null;\n }, []);\n\n const openGate = useCallback(() => {\n captureReturn();\n resumeAfterGrantRef.current = true;\n // Consent first: a credits notice still up gives way to the banner.\n setCreditsOpen(false);\n setGateOpen(true);\n setPanelOpen(true);\n requestFocus('gate');\n }, [captureReturn, setPanelOpen, requestFocus]);\n\n /** Whether focus sits in the body the gates replace (or is already\n * lost), so a swap must hand it on. */\n const focusInBody = useCallback(() => {\n if (typeof document === 'undefined') return false;\n const focused = document.activeElement;\n return (\n !focused ||\n focused === document.body ||\n Boolean(bodyRef.current?.contains(focused))\n );\n }, []);\n\n // Closing the notice with focus inside it sends focus to Start.\n const closeCredits = useCallback(() => {\n const inside = Boolean(\n creditsRef.current?.contains(document.activeElement),\n );\n noticeFocusRef.current = false;\n setCreditsOpen(false);\n if (inside) requestFocus('toggle');\n }, [requestFocus]);\n\n // A start met the credits gate: the notice takes the body and focus, as\n // the consent banner does. `keepReturn`: the start came through the\n // consent banner, which already holds where it was asked from.\n const openCredits = useCallback(\n (keepReturn = false) => {\n if (!keepReturn) captureReturn();\n creditsOpenRef.current = true;\n setCreditsOpen(true);\n setPanelOpen(true);\n requestFocus('credits');\n },\n [captureReturn, setPanelOpen, requestFocus],\n );\n\n // The one way in for the button, the pill's mic, the shortcut and the\n // agent: stopping (or cancelling a start) is never gated; starting is,\n // while consent is missing, then while credits are out.\n const requestToggle = useCallback(() => {\n const {\n status: now,\n disabled: off,\n aiBlocked: blocked,\n creditsBlocked: outOfCredits,\n } = latest.current;\n if (now === 'starting' || now === 'listening') {\n // Every stop reaches the host, never swallowed: it says 'stop', so a\n // repeat cannot be read as a start.\n latest.current.onToggle('stop');\n return;\n }\n if (off) return;\n if (blocked) {\n openGate();\n return;\n }\n if (outOfCredits) {\n openCredits();\n return;\n }\n latest.current.onToggle('start');\n }, [openGate, openCredits]);\n const requestToggleRef = useRef(requestToggle);\n requestToggleRef.current = requestToggle;\n\n const handleGrantConsent = useCallback(() => {\n grantRequestedRef.current = true;\n const result = aiConsent?.onGrant?.();\n if (result && typeof (result as Promise<void>).then === 'function') {\n setLocalGrantPending(true);\n Promise.resolve(result)\n .catch(() => {\n // The host shows its own failure UI (`pending`, a fresh\n // `granted: false`). This click no longer counts as the grant,\n // so a later grant from elsewhere will not start listening.\n grantRequestedRef.current = false;\n })\n .finally(() => setLocalGrantPending(false));\n }\n }, [aiConsent]);\n\n // Granted after a Grant click here: start listening, as the editor\n // resumes the action it gated. Granted any other way: close the banner.\n const prevGrantedRef = useRef(aiConsent?.granted ?? true);\n useEffect(() => {\n const wasGranted = prevGrantedRef.current;\n const nowGranted = aiConsent?.granted ?? true;\n prevGrantedRef.current = nowGranted;\n if (wasGranted || !nowGranted) return;\n const resume = resumeAfterGrantRef.current;\n const requestedHere = grantRequestedRef.current;\n resumeAfterGrantRef.current = false;\n grantRequestedRef.current = false;\n setGateOpen(false);\n if (!resume) return;\n const focusLost =\n typeof document === 'undefined' ||\n !document.activeElement ||\n document.activeElement === document.body;\n if (requestedHere) {\n const {\n status: now,\n disabled: off,\n creditsBlocked: outOfCredits,\n } = latest.current;\n if (!off && now !== 'starting' && now !== 'listening') {\n if (outOfCredits) {\n // Consent first, then credits: the resumed start meets the\n // credits gate instead of starting. Close still goes back to\n // where the start was asked from.\n openCredits(true);\n return;\n }\n latest.current.onToggle('start');\n }\n gateReturnRef.current = null;\n requestFocus('toggle');\n } else if (focusLost) {\n requestFocus('toggle');\n }\n }, [aiConsent?.granted, requestFocus, openCredits]);\n\n // Consent withdrawn (or the patient changed), or credits run out,\n // mid-session: stop, once. Paused counts: the host still holds the\n // session, and with a gate closed Resume is refused, so only this stop\n // (or the agent's) can end it.\n useEffect(() => {\n const held =\n status === 'starting' || status === 'listening' || status === 'paused';\n if (!held || !(aiBlocked || creditsBlocked)) {\n stopSentRef.current = false;\n return;\n }\n if (stopSentRef.current) return;\n stopSentRef.current = true;\n latest.current.onToggle('stop');\n }, [aiBlocked, creditsBlocked, status]);\n\n // Out of credits during a session, or within CREDITS_GRACE_MS of its\n // end (the host stopped first): once it has stopped, the notice shows\n // on its own, once per run-out, and the floating panel opens, as for an\n // error. While a session still runs, Stop stays in view. Credits that\n // load already exhausted, or run out long after the last session, wait\n // for the next start. Focus moves only if it was in the body the\n // notice replaces (the Stop the user pressed), never from the page.\n const sessionRef = useRef({ live: false, endedAt: -Infinity });\n const creditsShownRef = useRef(false);\n useEffect(() => {\n const session = sessionRef.current;\n const live =\n status === 'starting' || status === 'listening' || status === 'paused';\n if (live) {\n session.live = true;\n } else if (session.live) {\n session.live = false;\n session.endedAt = Date.now();\n }\n if (!creditsBlocked) {\n creditsShownRef.current = false;\n if (creditsOpenRef.current) {\n // Credits are back: the notice goes; focus in it goes to Start.\n if (noticeFocusRef.current && focusInBody()) requestFocus('toggle');\n noticeFocusRef.current = false;\n gateReturnRef.current = null;\n setCreditsOpen(false);\n }\n return;\n }\n if (creditsShownRef.current) return;\n if (status === 'starting' || status === 'listening') return;\n const recent =\n status === 'paused' || Date.now() - session.endedAt <= CREDITS_GRACE_MS;\n if (!recent) return;\n creditsShownRef.current = true;\n // Close goes back to Start: no earlier start's origin applies.\n gateReturnRef.current = null;\n if (focusInBody() && document.activeElement !== document.body) {\n // Focus moves with the notice, which then reads itself.\n requestFocus('credits');\n } else {\n // Focus stays on the page: the live region says what changed.\n noticeFocusRef.current = false;\n creditsAnnouncePendingRef.current = true;\n setCreditsAnnounce((n) => n + 1);\n }\n creditsOpenRef.current = true;\n setCreditsOpen(true);\n setPanelOpen(true);\n }, [creditsBlocked, status, setPanelOpen, focusInBody, requestFocus]);\n\n // Closing the floating panel closes the banner or notice inside it too.\n useEffect(() => {\n if (floating && !panelOpen && gateOpen) closeGate();\n if (floating && !panelOpen && creditsOpen) setCreditsOpen(false);\n }, [floating, panelOpen, gateOpen, closeGate, creditsOpen]);\n\n /* ---- Document-wide shortcut ------------------------------------- */\n useEffect(() => {\n if (!parsedShortcut || typeof document === 'undefined') return;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.defaultPrevented || event.repeat || event.isComposing) {\n return;\n }\n if (!matchesShortcut(event, parsedShortcut)) return;\n const { status: now, disabled: off } = latest.current;\n // Disabled stops a start, never a stop.\n if (off && now !== 'starting' && now !== 'listening') return;\n event.preventDefault();\n requestToggleRef.current();\n };\n document.addEventListener('keydown', onKeyDown);\n return () => document.removeEventListener('keydown', onKeyDown);\n }, [parsedShortcut]);\n\n /* ---- Agent handle ----------------------------------------------- */\n const handle = useMemo<VoiceControlPanelHandle>(\n () => ({\n getStatus: () => latest.current.status,\n start: () => {\n const { status: now, disabled: off } = latest.current;\n if (off || now === 'starting' || now === 'listening') return;\n // Gated like a user: without consent it shows the banner.\n requestToggleRef.current();\n },\n stop: () => {\n const { status: now } = latest.current;\n if (now !== 'starting' && now !== 'listening' && now !== 'paused') {\n return;\n }\n // Never gated, always 'stop', also for a paused session (whose\n // button would resume).\n latest.current.onToggle('stop');\n },\n getCandidateIds: () => latest.current.candidates.map((c) => c.id),\n pickCandidate: (candidateId: string) => {\n if (latest.current.disabled) return;\n if (!latest.current.candidates.some((c) => c.id === candidateId)) {\n return;\n }\n latest.current.onCandidatePick?.(candidateId);\n },\n isCommandsOpen: () => latest.current.commandsOpen,\n openCommands: () => setCommandsOpen(true),\n closeCommands: () => setCommandsOpen(false),\n isPanelOpen: () => latest.current.panelOpen,\n openPanel: () => setPanelOpen(true),\n closePanel: () => setPanelOpen(false),\n isCreditsExhausted: () => latest.current.creditsBlocked,\n isCreditsNoticeOpen: () => latest.current.showCredits,\n closeCreditsNotice: () => closeCredits(),\n }),\n [setPanelOpen, closeCredits],\n );\n useAgentRegistration(voiceControlPanelAgent, handle, id);\n\n /* ---- Strings ---------------------------------------------------- */\n const statusLabel = t(`voiceControl.status.${status}`);\n const errorText = error ? t(`voiceControl.error.${error}`) : '';\n const messageText = message?.text ?? '';\n // The id lets a host repeat the same words and still be heard.\n const messageKey = messageText\n ? `${message?.id ?? ''}\\u0000${messageText}`\n : '';\n const hasCandidates = candidates.length > 0;\n const candidatesKey = candidates.map((c) => c.id).join('\\u0000');\n const active = status === 'starting' || status === 'listening';\n\n // The label always says what pressing does. While starting, pressing\n // cancels; the spinner, aria-busy and the \"Connecting\" status carry the\n // busy state.\n const buttonLabel =\n status === 'listening'\n ? t('voiceControl.action.stop')\n : status === 'starting'\n ? t('common.cancel')\n : status === 'paused'\n ? t('voiceControl.action.resume')\n : t('voiceControl.action.start');\n\n /* ---- One live region: status, error, message, matches ------------ */\n const [announcement, setAnnouncement] = useState({ text: '', count: 0 });\n const announced = useRef({\n status,\n errorText,\n messageKey,\n candidatesKey,\n });\n useEffect(() => {\n const before = announced.current;\n const parts: string[] = [];\n if (status !== before.status) parts.push(statusLabel);\n if (errorText && errorText !== before.errorText) parts.push(errorText);\n if (messageKey && messageKey !== before.messageKey) {\n parts.push(messageText);\n }\n // A new set of matches, not only the first one.\n if (candidatesKey && candidatesKey !== before.candidatesKey) {\n parts.push(candidatesLabel || t('voiceControl.candidates'));\n }\n // The out-of-credits notice opened on its own (no focus move).\n if (creditsAnnouncePendingRef.current) {\n creditsAnnouncePendingRef.current = false;\n parts.push(t('aiCredits.title'));\n }\n announced.current = { status, errorText, messageKey, candidatesKey };\n if (parts.length > 0) {\n setAnnouncement((prev) => ({\n // A sentence break between parts gives the screen reader a pause:\n // \"Stopped. Microphone access is blocked…\". The break is a\n // translated template (Japanese ends a sentence with 。), skipped\n // when a part already ends one.\n text: parts.reduce((joined, part) =>\n SENTENCE_END.test(joined)\n ? `${joined} ${part}`\n : t('voiceControl.announcementJoin', {\n first: joined,\n next: part,\n }),\n ),\n count: prev.count + 1,\n }));\n }\n }, [\n t,\n status,\n statusLabel,\n errorText,\n messageKey,\n messageText,\n candidatesKey,\n candidatesLabel,\n creditsAnnounce,\n ]);\n\n /* ---- Floating: open on its own when the user must act ------------ */\n // A new error (the session stopped) or a new set of matches (a choice\n // is waiting) opens the panel, without moving focus.\n const attention = useRef({ error, candidatesKey });\n useEffect(() => {\n const before = attention.current;\n attention.current = { error, candidatesKey };\n if (!floating) return;\n if (\n (error && error !== before.error) ||\n (candidatesKey && candidatesKey !== before.candidatesKey)\n ) {\n setPanelOpen(true);\n }\n }, [floating, error, candidatesKey, setPanelOpen]);\n\n /* ---- Floating: clearance for scrolled-to and focused content ----- */\n // See `_shared/floating-clearance.ts`. Closed: how far the pill reaches\n // up from the viewport's bottom edge, plus a gap. Open: the panel too,\n // but never more than half the viewport, so a tall panel on a short\n // screen cannot push the cursor's whole working area off the page.\n // Layout heights (offsetHeight), so the panel's grow-in zoom does not\n // shrink the reading.\n useEffect(() => {\n const el = rootRef.current;\n if (!floating || !el) return undefined;\n const owner = {};\n const measure = () => {\n const style = window.getComputedStyle(el);\n const gap = Number.parseFloat(style.rowGap) || 0;\n const closed =\n (Number.parseFloat(style.bottom) || 0) +\n (pillRef.current?.offsetHeight ?? 0) +\n gap;\n const panel = panelRef.current;\n const panelHeight = panel && !panel.hidden ? panel.offsetHeight : 0;\n publishFloatingClearance(\n owner,\n panelHeight > 0\n ? Math.max(\n closed,\n Math.min(closed + panelHeight + gap, window.innerHeight / 2),\n )\n : closed,\n );\n };\n measure();\n const observer =\n typeof ResizeObserver === 'undefined'\n ? null\n : new ResizeObserver(measure);\n observer?.observe(el);\n window.addEventListener('resize', measure);\n return () => {\n observer?.disconnect();\n window.removeEventListener('resize', measure);\n publishFloatingClearance(owner, null);\n };\n }, [floating]);\n\n /* ---- Floating: the credits hover card on the mic ------------------ */\n // \"Don't show again\" hides it for the rest of this mount, per mode, as\n // on the RichTextEditor.\n const creditsNotice = useDismissibleAiCreditsNotice(\n floating ? aiCreditsNoticeProp : undefined,\n );\n\n /* ---- Floating: the pill's sparkle -------------------------------- */\n const sparkleOn = floating && aiSparkle;\n const {\n state: sparkleState,\n props: sparkleProps,\n land: landSparkle,\n } = useAiSparkle({ enabled: sparkleOn });\n const successKey = message?.tone === 'success' ? messageKey : '';\n const sparkledKey = useRef(successKey);\n useEffect(() => {\n if (successKey === sparkledKey.current) return;\n sparkledKey.current = successKey;\n if (sparkleOn && successKey) landSparkle();\n }, [sparkleOn, successKey, landSparkle]);\n\n /* ---- Focus after a pick ------------------------------------------ */\n // Picking a match usually makes the host clear the matches, which\n // unmounts the focused button and drops focus to <body>. Send it back to\n // the start/stop button instead. Only for a pick made on a match button.\n const restoreFocusRef = useRef(false);\n useEffect(() => {\n if (hasCandidates || !restoreFocusRef.current) return;\n restoreFocusRef.current = false;\n const focused =\n typeof document === 'undefined' ? null : document.activeElement;\n if (!focused || focused === document.body) toggleRef.current?.focus();\n }, [hasCandidates]);\n\n /* ---- Floating: Escape closes the panel ---------------------------- */\n // A native listener on the cluster: it hears keys from the pill and the\n // panel only, not from a portalled Select listbox, which closes itself.\n const closePanelRef = useRef<(refocus: boolean) => void>(() => {});\n useEffect(() => {\n const el = rootRef.current;\n if (!floating || !el) return undefined;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Escape' || event.defaultPrevented) return;\n if (!latest.current.panelOpen) return;\n event.preventDefault();\n closePanelRef.current(true);\n };\n el.addEventListener('keydown', onKeyDown);\n return () => el.removeEventListener('keydown', onKeyDown);\n }, [floating]);\n\n /* ---- Floating: a close from anywhere keeps focus on the page ------ */\n // Closed by the host (`open={false}`) while focus is inside: send it to\n // the panel segment, not to <body>. Read before the browser moves it.\n const wasOpenRef = useRef(panelOpen);\n useIsomorphicLayoutEffect(() => {\n const wasOpen = wasOpenRef.current;\n wasOpenRef.current = panelOpen;\n if (!wasOpen || panelOpen || pendingFocusRef.current) return;\n const focused = document.activeElement;\n if (focused && panelRef.current?.contains(focused)) {\n requestFocus('launcher');\n }\n }, [panelOpen, requestFocus]);\n\n /* ---- Pending focus moves (declared last: runs after the others) -- */\n // Focus moves wait for the DOM to show the target (the panel and the\n // banner mount or unhide in the same commit that asks for focus).\n useEffect(() => {\n const target = pendingFocusRef.current;\n if (!target) return;\n // Opened from an effect (a start resumed after a grant), the notice\n // shows a render later: keep the request until it does, unless the\n // credits came back in the meantime.\n if (target === 'credits' && !creditsRef.current) {\n if (!latest.current.creditsBlocked || !creditsOpenRef.current) {\n pendingFocusRef.current = null;\n }\n return;\n }\n pendingFocusRef.current = null;\n if (target === 'gate') {\n const gate = gateRef.current;\n // The banner's confirm checkbox, or the banner itself when the\n // reader may not grant (there is nothing to tick).\n const focusable =\n gate?.querySelector<HTMLElement>(\n '[role=\"checkbox\"]:not([disabled])',\n ) ??\n gate?.querySelector<HTMLElement>(\n '[data-component=\"ai-consent-banner\"]',\n );\n focusable?.focus();\n } else if (target === 'credits') {\n // The notice itself, so it is read from its title.\n creditsRef.current\n ?.querySelector<HTMLElement>(\n '[data-component=\"ai-credits-exhausted-notice\"]',\n )\n ?.focus();\n } else if (target === 'return') {\n const origin = gateReturnRef.current;\n gateReturnRef.current = null;\n // Still on the page and shown (a hidden panel has no boxes).\n if (origin?.isConnected && origin.getClientRects().length > 0) {\n origin.focus();\n } else if (latest.current.floating && !latest.current.panelOpen) {\n launcherRef.current?.focus();\n } else {\n toggleRef.current?.focus();\n }\n } else if (target === 'panel') {\n panelRef.current?.focus();\n } else if (target === 'toggle') {\n toggleRef.current?.focus();\n } else {\n launcherRef.current?.focus();\n }\n });\n\n /* ---- Settings --------------------------------------------------- */\n const languageOptions: SelectOption[] = (languages ?? []).map((l) => ({\n value: l.value,\n label: l.label,\n }));\n const showLanguage = languageOptions.length > 1;\n const hasSettings =\n showLanguage || (extraControls != null && extraControls !== false);\n\n const showTranscript =\n Boolean(transcript?.final) || Boolean(transcript?.partial);\n const busy = status === 'starting';\n\n const ToneIcon = message ? TONE_ICON[message.tone] : Info;\n\n /* ---- Pieces shared by both layouts ------------------------------ */\n // Escape and the close button send focus back where the panel was\n // opened from: the field Alt+V was pressed in (if it is still there),\n // or else the panel segment.\n const closePanel = (refocus: boolean) => {\n setPanelOpen(false);\n closeGate();\n setCreditsOpen(false);\n if (refocus) requestFocus(gateReturnRef.current ? 'return' : 'launcher');\n };\n closePanelRef.current = closePanel;\n\n const header = (\n <div className=\"ds:flex ds:flex-wrap ds:items-center ds:gap-x-[var(--spacing-xs)] ds:gap-y-[var(--spacing-2xs)]\">\n <p\n id={titleId}\n className=\"ds:m-0 type-label ds:font-semibold ds:text-[color:var(--foreground)]\"\n >\n {title ?? t('voiceControl.title')}\n </p>\n {beta ? (\n <Badge variant=\"brand\" size=\"sm\">\n {t('voiceControl.beta')}\n </Badge>\n ) : null}\n <span className=\"ds:ms-auto ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)] type-meta ds:text-[color:var(--muted-foreground)]\">\n <span aria-hidden=\"true\" className={statusDotVariants({ status })}>\n {status === 'listening' ? (\n <span\n className={[\n 'ds:absolute ds:inset-0 ds:rounded-[var(--radius-full)]',\n 'ds:bg-[color:var(--destructive)]',\n // No pulse in the accessible themes (--accessible-off).\n 'ds:motion-safe:animate-[var(--accessible-off,recorder-pulse_1.2s_ease-out_infinite)]',\n ].join(' ')}\n />\n ) : null}\n </span>\n <span data-voice-status=\"\">{statusLabel}</span>\n </span>\n {floating ? (\n <IconButton\n intent=\"ghost\"\n size=\"md\"\n icon={<X />}\n aria-label={t('common.close')}\n onClick={() => closePanel(true)}\n className={TARGET_SQUARE}\n data-voice-close=\"\"\n />\n ) : null}\n </div>\n );\n\n const betaNoticeLine = betaNotice ? (\n <p className=\"ds:m-0 ds:flex ds:items-start ds:gap-[var(--spacing-2xs)] type-meta ds:text-[color:var(--muted-foreground)]\">\n <Info aria-hidden=\"true\" className=\"ds:mt-px ds:size-4 ds:shrink-0\" />\n <span>{betaNotice}</span>\n </p>\n ) : null;\n\n // Without consent the banner takes the body: the same banner, Grant\n // control and busy state as the RichTextEditor's gate.\n const consentGate =\n showGate && aiConsent ? (\n <div\n ref={gateRef}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\"\n data-voice-consent=\"\"\n >\n <AiConsentBanner\n // Focus lands here when the reader may not grant (nothing to tick).\n tabIndex={-1}\n granted={aiConsent.granted}\n grantedAt={aiConsent.grantedAt}\n grantedBy={aiConsent.grantedBy}\n // No handler, no Grant control: never a dead button.\n canGrant={\n (aiConsent.canGrant ?? true) && aiConsent.onGrant !== undefined\n }\n pending={grantPending}\n onGrantConsent={handleGrantConsent}\n headingLevel={aiConsentHeadingLevel}\n />\n <div className=\"ds:flex ds:justify-end\">\n <Button\n intent=\"ghost\"\n size=\"md\"\n onClick={() => {\n closeGate();\n requestFocus('return');\n }}\n className={TARGET_HEIGHT}\n >\n {t('common.close')}\n </Button>\n </div>\n </div>\n ) : null;\n\n // Out of credits, after consent: the shared notice takes the body, with\n // the same Close as the consent banner. The panel announces it itself.\n const creditsGate =\n showCredits && !consentGate ? (\n <div\n ref={creditsRef}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\"\n data-voice-credits=\"\"\n onFocusCapture={() => {\n noticeFocusRef.current = true;\n }}\n onBlurCapture={() => {\n noticeFocusRef.current = false;\n }}\n >\n <AiCreditsExhaustedNotice\n // Focus lands here when a start meets the gate, with the kit\n // focus ring.\n tabIndex={-1}\n className={focusRing}\n credits={aiCredits}\n headingLevel={aiConsentHeadingLevel ?? 5}\n announce=\"off\"\n />\n <div className=\"ds:flex ds:justify-end\">\n <Button\n intent=\"ghost\"\n size=\"md\"\n onClick={() => {\n closeCredits();\n requestFocus('return');\n }}\n className={TARGET_HEIGHT}\n >\n {t('common.close')}\n </Button>\n </div>\n </div>\n ) : null;\n\n // Running low, expiring, or out of credits at rest: quiet lines that\n // never block. Renders nothing when none applies.\n const creditsLines = aiCredits ? (\n <AiCreditsWarning credits={aiCredits} exhausted={creditsBlocked} />\n ) : null;\n\n const controls = (\n <>\n {/* Body: action | readout | settings. One column in a narrow\n container (the tool rail, the floating panel), three from 560px\n of panel width. */}\n <div className=\"ds:grid ds:grid-cols-1 ds:items-start ds:gap-[var(--spacing-sm)] ds:@[560px]/voice-control:grid-cols-[auto_minmax(0,1fr)_auto]\">\n <Button\n ref={toggleRef}\n intent={status === 'listening' ? 'outline' : 'primary'}\n background=\"surface\"\n size=\"md\"\n // Disabled stops a start, never a stop.\n disabled={disabled && !active}\n aria-busy={busy || undefined}\n aria-keyshortcuts={parsedShortcut?.aria}\n onClick={() => requestToggle()}\n startIcon={\n busy ? (\n <LoaderCircle className=\"ds:animate-[var(--accessible-off,spin_1s_linear_infinite)] ds:motion-reduce:animate-none\" />\n ) : status === 'listening' ? (\n <Square className=\"ds:fill-current\" />\n ) : (\n <AudioLines />\n )\n }\n className={`${TARGET_HEIGHT} ds:w-full ds:@[560px]/voice-control:w-auto`}\n data-voice-toggle=\"\"\n >\n {buttonLabel}\n </Button>\n\n <div className=\"ds:flex ds:min-w-0 ds:flex-col ds:gap-[var(--spacing-2xs)]\">\n {target != null && target !== '' ? (\n <p className=\"ds:m-0 type-body-sm ds:font-semibold ds:[overflow-wrap:anywhere]\">\n {target}\n </p>\n ) : null}\n {showTranscript ? (\n <p\n className=\"ds:m-0 type-body-sm ds:[overflow-wrap:anywhere]\"\n data-voice-transcript=\"\"\n >\n <span className=\"ds:sr-only\">{t('voiceControl.heard')} </span>\n {transcript?.final ? (\n <span className=\"ds:text-[color:var(--foreground)]\">\n {transcript.final}\n </span>\n ) : null}\n {transcript?.final && transcript?.partial ? ' ' : null}\n {transcript?.partial ? (\n <span\n className=\"ds:italic ds:text-[color:var(--muted-foreground)]\"\n data-voice-partial=\"\"\n >\n {transcript.partial}\n </span>\n ) : null}\n </p>\n ) : status === 'listening' ? (\n // Holds the line while nothing has been heard yet, so the first\n // words do not push the panel down.\n <p\n aria-hidden=\"true\"\n className=\"ds:m-0 type-body-sm ds:min-h-[1lh]\"\n />\n ) : null}\n {error ? (\n <p className={messageVariants({ tone: 'error' })}>\n <CircleAlert\n aria-hidden=\"true\"\n className=\"ds:mt-px ds:size-4 ds:shrink-0\"\n />\n <span>{errorText}</span>\n </p>\n ) : null}\n {message && message.text ? (\n <p\n className={messageVariants({ tone: message.tone })}\n data-tone={message.tone}\n >\n <ToneIcon\n aria-hidden=\"true\"\n className=\"ds:mt-px ds:size-4 ds:shrink-0\"\n />\n <span>{message.text}</span>\n </p>\n ) : null}\n </div>\n\n {hasSettings ? (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)] ds:@[560px]/voice-control:flex-row ds:@[560px]/voice-control:flex-wrap ds:@[560px]/voice-control:items-end\">\n {showLanguage ? (\n // A visible, associated label (the kit FormField pattern).\n <FormField label={t('voiceControl.language')}>\n <Select\n options={languageOptions}\n value={language ?? ''}\n onValueChange={(next) => {\n if (next) onLanguageChange?.(next);\n }}\n disabled={disabled}\n />\n </FormField>\n ) : null}\n {extraControls}\n </div>\n ) : null}\n </div>\n\n {/* Matches, numbered as they are spoken (\"option 1\") */}\n {hasCandidates ? (\n <div\n role=\"group\"\n aria-labelledby={candidatesLabel ? candidatesLabelId : undefined}\n aria-label={\n candidatesLabel ? undefined : t('voiceControl.candidates')\n }\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)]\"\n data-voice-candidates=\"\"\n >\n {candidatesLabel ? (\n <p\n id={candidatesLabelId}\n className=\"ds:m-0 type-body-sm ds:text-[color:var(--foreground)]\"\n >\n {candidatesLabel}\n </p>\n ) : null}\n <List as=\"ol\" density=\"compact\">\n {candidates.map((candidate, index) => (\n <List.Item\n key={candidate.id}\n title={\n <button\n type=\"button\"\n disabled={disabled}\n onClick={() => {\n restoreFocusRef.current = true;\n onCandidatePick?.(candidate.id);\n }}\n className={candidateButtonVariants()}\n data-candidate-id={candidate.id}\n >\n {/* One inline run, so the accessible name keeps\n its spaces: \"Option 1: Composite filling OTVE\". */}\n <span>\n <span className=\"ds:sr-only\">\n {t('voiceControl.candidate', {\n number: index + 1,\n })}\n </span>{' '}\n <span className=\"ds:font-medium\">\n {candidate.label}\n </span>\n {candidate.detail ? (\n <>\n {' '}\n <span className=\"ds:ms-[var(--spacing-2xs)] type-meta ds:text-[color:var(--muted-foreground)]\">\n {candidate.detail}\n </span>\n </>\n ) : null}\n </span>\n </button>\n }\n />\n ))}\n </List>\n </div>\n ) : null}\n\n {/* Voice commands, titled with the shortcut */}\n {commands && commands.length > 0 ? (\n <Collapsible open={commandsOpen} onOpenChange={setCommandsOpen}>\n <CollapsibleTrigger className={commandsTriggerVariants()}>\n <ChevronDown\n aria-hidden=\"true\"\n className=\"ds:size-4 ds:shrink-0 ds:transition-transform ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none ds:[[data-state=open]_&]:rotate-180\"\n />\n <span>\n {parsedShortcut ? (\n <Trans\n ns=\"ui\"\n i18nKey=\"voiceControl.commandsWithShortcut\"\n components={{\n shortcut: <ShortcutKeys keys={parsedShortcut.keys} />,\n }}\n />\n ) : (\n t('voiceControl.commands')\n )}\n </span>\n </CollapsibleTrigger>\n <CollapsibleContent\n aria-label={t('voiceControl.commands')}\n className=\"ds:pt-[var(--spacing-xs)]\"\n >\n <List density=\"compact\">\n {commands.map((command, index) => (\n <List.Item\n key={index}\n title={command.phrase}\n description={command.description}\n />\n ))}\n </List>\n </CollapsibleContent>\n </Collapsible>\n ) : null}\n </>\n );\n\n const content = (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n {header}\n {betaNoticeLine}\n <div ref={bodyRef} className=\"ds:contents\">\n {consentGate ?? creditsGate ?? (\n <>\n {creditsLines}\n {controls}\n </>\n )}\n </div>\n </div>\n );\n\n // Always mounted, polite, and outside anything that collapses: it keeps\n // speaking while the floating panel is closed. See the docs for why\n // errors are polite.\n const liveRegion = (\n <LiveRegion politeness=\"polite\" data-voice-announcer=\"\">\n {announcement.text\n ? // Alternate a trailing no-break space so the same words said\n // twice in a row still change the region and are announced.\n `${announcement.text}${announcement.count % 2 ? NO_BREAK_SPACE : ''}`\n : ''}\n </LiveRegion>\n );\n\n if (!floating) {\n return (\n <div\n ref={composedRef}\n role=\"group\"\n aria-labelledby={titleId}\n data-component=\"voice-control-panel\"\n data-component-id={id}\n data-status={status}\n id={id}\n {...rest}\n className={rootVariants({ status, className })}\n >\n {content}\n {liveRegion}\n </div>\n );\n }\n\n /* ---- Floating layout -------------------------------------------- */\n const listening = status === 'listening';\n // The state in words on the pill, so it never rests on colour alone:\n // \"Listening\" on the red segment, \"Paused\" on the plain one (paused and\n // idle would otherwise look the same with the panel closed).\n const showWord = listening || status === 'paused';\n // The visible word must stay inside the name (WCAG 2.5.3). English\n // \"Listening\" sits in \"Stop listening\"; \"Paused\" is not in \"Resume\n // listening\", nor is a translation always, so the word then leads the\n // name through a translated template: \"Paused: Resume listening\".\n const micLabel =\n showWord &&\n !buttonLabel.toLocaleLowerCase().includes(statusLabel.toLocaleLowerCase())\n ? t('voiceControl.statusWithAction', {\n status: statusLabel,\n action: buttonLabel,\n })\n : buttonLabel;\n return (\n <div\n ref={composedRef}\n data-component=\"voice-control-panel\"\n data-component-id={id}\n data-status={status}\n data-layout=\"floating\"\n data-position={position}\n data-open={panelOpen ? '' : undefined}\n id={id}\n {...rest}\n className={floatingRootVariants({ position, className })}\n >\n {/* Non-modal: no focus trap, no backdrop; the page stays usable. */}\n <div\n ref={panelRef}\n id={panelId}\n role=\"group\"\n aria-labelledby={titleId}\n tabIndex={-1}\n hidden={!panelOpen}\n className={floatingPanelVariants({ position })}\n data-voice-panel=\"\"\n >\n {content}\n </div>\n\n {/* The RichTextEditor's AI pill: mic | panel. */}\n <div\n ref={pillRef}\n className=\"ds:relative\"\n data-ai-sparkle={sparkleProps['data-ai-sparkle']}\n >\n <div className={aiFabPillClass} data-voice-pill=\"\">\n {withAiCreditsNotice(\n <button\n type=\"button\"\n onClick={() => requestToggle()}\n disabled={(disabled && !active) || undefined}\n aria-label={micLabel}\n aria-busy={busy || undefined}\n aria-keyshortcuts={parsedShortcut?.aria}\n className={aiFabSegmentVariants({\n // The word on the red needs 7:1 in the accessible themes:\n // `labelled` holds the hover red there.\n recording: listening ? 'labelled' : false,\n className: micSegmentVariants({ extended: showWord }),\n })}\n data-voice-mic=\"\"\n >\n {busy ? (\n <LoaderCircle\n aria-hidden=\"true\"\n className=\"ds:size-5 ds:animate-[var(--accessible-off,spin_1s_linear_infinite)] ds:motion-reduce:animate-none\"\n />\n ) : listening ? (\n <Square\n aria-hidden=\"true\"\n className=\"ds:size-5 ds:fill-current\"\n />\n ) : (\n <AudioLines aria-hidden=\"true\" className=\"ds:size-5\" />\n )}\n {/* The state in words, not colour alone; see micLabel. */}\n {showWord ? (\n <span className=\"type-label ds:whitespace-nowrap\">\n {statusLabel}\n </span>\n ) : null}\n </button>,\n creditsNotice,\n // The pill sits at the foot of the viewport: the card opens up.\n 'top',\n )}\n <span aria-hidden=\"true\" className={aiFabDividerClass} />\n <button\n ref={launcherRef}\n type=\"button\"\n onClick={() => {\n if (panelOpen) {\n closePanel(false);\n } else {\n // Opened from here: Escape comes back here.\n gateReturnRef.current = null;\n setPanelOpen(true);\n requestFocus('panel');\n }\n }}\n aria-label={t('voiceControl.panelToggle')}\n aria-expanded={panelOpen}\n aria-controls={panelId}\n className={aiFabSegmentVariants({ recording: false })}\n data-voice-launcher=\"\"\n >\n {/* Fixed glyph: aria-expanded carries open or closed. */}\n <SlidersHorizontal aria-hidden=\"true\" className=\"ds:size-5\" />\n </button>\n {sparkleState ? (\n <span\n aria-hidden=\"true\"\n className={`${aiFabSweepClass} ${sparkleProps.className}`}\n />\n ) : null}\n </div>\n <AiSparkle state={sparkleState} />\n </div>\n\n {liveRegion}\n </div>\n );\n },\n);\n\nVoiceControlPanel.displayName = 'VoiceControlPanel';\n"],"names":["__iconNode","AudioLines","createLucideIcon","voiceControlPanelAgent","handle","args","rootVariants","cva","statusDotVariants","messageVariants","focusRing","candidateButtonVariants","commandsTriggerVariants","floatingRootVariants","floatingPanelVariants","glassSurfaceClass","glassEntranceClass","micSegmentVariants","MODIFIER_ALIASES","parseVoiceShortcut","shortcut","parts","part","rawKey","modifiers","modifier","key","ordered","m","matchesShortcut","event","parsed","ShortcutKeys","keys","Kbd","TONE_ICON","Info","CircleCheck","TriangleAlert","CircleAlert","NO_CANDIDATES","TARGET_HEIGHT","TARGET_SQUARE","SENTENCE_END","CREDITS_GRACE_MS","NO_BREAK_SPACE","VoiceControlPanel","forwardRef","status","onToggle","disabled","title","beta","betaNotice","language","languages","onLanguageChange","extraControls","target","transcript","message","error","candidates","candidatesLabel","onCandidatePick","commands","defaultCommandsOpen","id","layout","position","open","defaultOpen","onOpenChange","aiSparkle","aiConsent","aiConsentHeadingLevel","aiCredits","aiCreditsNoticeProp","className","rest","ref","t","useTranslation","titleId","useId","candidatesLabelId","panelId","commandsOpen","setCommandsOpen","useState","floating","rootRef","useRef","composedRef","useMemo","composeRefs","panelRef","gateRef","creditsRef","toggleRef","launcherRef","pillRef","parsedShortcut","useEffect","openState","setOpenState","controlled","panelOpen","aiBlocked","gateOpen","setGateOpen","showGate","resumeAfterGrantRef","grantRequestedRef","localGrantPending","setLocalGrantPending","grantPending","creditsBlocked","creditsOpen","setCreditsOpen","showCredits","creditsAnnouncePendingRef","creditsAnnounce","setCreditsAnnounce","creditsOpenRef","bodyRef","noticeFocusRef","latest","pendingFocusRef","setFocusRequest","requestFocus","useCallback","gateReturnRef","stopSentRef","setPanelOpen","next","now","_a","closeGate","captureReturn","focused","openGate","focusInBody","closeCredits","inside","openCredits","keepReturn","requestToggle","off","blocked","outOfCredits","requestToggleRef","handleGrantConsent","result","prevGrantedRef","wasGranted","nowGranted","resume","requestedHere","focusLost","sessionRef","creditsShownRef","session","n","onKeyDown","c","candidateId","_b","useAgentRegistration","statusLabel","errorText","messageText","messageKey","hasCandidates","candidatesKey","active","buttonLabel","announcement","setAnnouncement","announced","before","prev","joined","attention","el","owner","measure","style","gap","closed","panel","panelHeight","publishFloatingClearance","observer","creditsNotice","useDismissibleAiCreditsNotice","sparkleOn","sparkleState","sparkleProps","landSparkle","useAiSparkle","successKey","sparkledKey","restoreFocusRef","closePanelRef","wasOpenRef","useIsomorphicLayoutEffect","wasOpen","gate","focusable","origin","_c","_d","_e","_f","_g","languageOptions","l","showLanguage","hasSettings","showTranscript","busy","ToneIcon","closePanel","refocus","header","jsxs","jsx","Badge","IconButton","X","betaNoticeLine","consentGate","AiConsentBanner","Button","creditsGate","AiCreditsExhaustedNotice","creditsLines","AiCreditsWarning","controls","Fragment","LoaderCircle","Square","FormField","Select","List","candidate","index","Collapsible","CollapsibleTrigger","ChevronDown","Trans","CollapsibleContent","command","content","liveRegion","LiveRegion","listening","showWord","micLabel","aiFabPillClass","withAiCreditsNotice","aiFabSegmentVariants","aiFabDividerClass","SlidersHorizontal","aiFabSweepClass","AiSparkle"],"mappings":"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","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"voice-control-panel-BP0otrID.js","sources":["../../node_modules/lucide-react/dist/esm/icons/audio-lines.mjs","../../src/components/voice-control-panel/voice-control-panel.agent.ts","../../src/components/voice-control-panel/voice-control-panel.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\"path\", { d: \"M2 10v3\", key: \"1fnikh\" }],\n [\"path\", { d: \"M6 6v11\", key: \"11sgs0\" }],\n [\"path\", { d: \"M10 3v18\", key: \"yhl04a\" }],\n [\"path\", { d: \"M14 8v7\", key: \"3a1oy3\" }],\n [\"path\", { d: \"M18 5v13\", key: \"123xd1\" }],\n [\"path\", { d: \"M22 10v3\", key: \"154ddg\" }]\n];\nconst AudioLines = createLucideIcon(\"audio-lines\", __iconNode);\n\nexport { __iconNode, AudioLines as default };\n//# sourceMappingURL=audio-lines.mjs.map\n","/* -------------------------------------------------------------------- */\n/* Agent adapter: VoiceControlPanel. */\n/* */\n/* The panel draws a host-driven voice session; the host owns the */\n/* microphone and the speech service. `start_listening` / `stop_listening`*/\n/* call the same `onToggle` the button calls, but only in the direction */\n/* asked, so an agent never starts by mistake. They are `capture` and */\n/* `write` (start opens the microphone). Picking a match hands the host */\n/* an id through `onCandidatePick`, which the host turns into a write on */\n/* the patient's record, so it is `phi`. The commands disclosure is a */\n/* plain `expand`. In the floating layout the panel above the pill opens */\n/* and closes (`open` / `close`, `read`): closing never stops listening. */\n/* */\n/* No state slot returns the transcript, the target line or a message: */\n/* those can carry patient data the host put there. */\n/* */\n/* AI credits: `start_listening` is gated by `aiCredits.exhausted` after */\n/* the consent check, so out of credits it shows the out-of-credits */\n/* notice instead. The notice's buy, upgrade and support buttons are */\n/* host navigation an agent never presses; it can only read the gate and */\n/* close the notice (`close_credits_notice`, `read`). */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { VoiceControlPanelHandle } from './voice-control-panel';\n\nexport const voiceControlPanelAgent: AgentAdapter<VoiceControlPanelHandle> = {\n id: 'voice-control-panel',\n capabilities: ['capture', 'pick', 'expand', 'open', 'close'],\n state: {\n status: {\n type: \"'idle' | 'starting' | 'listening' | 'paused' | 'error'\",\n descriptionKey: 'ui.agent.voiceControlPanel.state.status',\n description:\n 'Where the host voice session is: idle, starting, listening, paused or error.',\n read: (handle) => handle.getStatus(),\n },\n candidateIds: {\n type: 'string[]',\n descriptionKey: 'ui.agent.voiceControlPanel.state.candidateIds',\n description:\n 'Ids of the matches currently offered, in spoken order (option 1 first). Empty when there is nothing to choose.',\n read: (handle) => handle.getCandidateIds(),\n },\n commandsOpen: {\n type: 'boolean',\n descriptionKey: 'ui.agent.voiceControlPanel.state.commandsOpen',\n description: 'True when the voice commands list is expanded.',\n read: (handle) => handle.isCommandsOpen(),\n },\n panelOpen: {\n type: 'boolean',\n descriptionKey: 'ui.agent.voiceControlPanel.state.panelOpen',\n description:\n 'Floating layout: true when the panel above the pill is open. Always false inline.',\n read: (handle) => handle.isPanelOpen(),\n },\n creditsExhausted: {\n type: 'boolean',\n descriptionKey: 'ui.agent.voiceControlPanel.state.creditsExhausted',\n description:\n 'True when the practice is out of AI credits: every start shows the out-of-credits notice instead of listening.',\n read: (handle) => handle.isCreditsExhausted(),\n },\n creditsNoticeOpen: {\n type: 'boolean',\n descriptionKey: 'ui.agent.voiceControlPanel.state.creditsNoticeOpen',\n description:\n 'True when the out-of-credits notice is showing in the panel, in place of the controls.',\n read: (handle) => handle.isCreditsNoticeOpen(),\n },\n },\n actions: {\n start_listening: {\n safety: 'write',\n idempotent: true,\n // The host streams the patient's audio to its transcription service.\n openWorld: true,\n descriptionKey: 'ui.agent.voiceControlPanel.actions.startListening',\n description:\n 'Ask the host to start listening (it opens the microphone). Does nothing when already starting or listening, or while the panel is disabled. Without patient AI consent it shows the consent banner instead; out of AI credits (creditsExhausted) it shows the out-of-credits notice instead.',\n errors: ['Microphone permission denied', 'Speech service unavailable'],\n invoke: (handle) => {\n handle.start();\n },\n },\n stop_listening: {\n safety: 'write',\n idempotent: true,\n descriptionKey: 'ui.agent.voiceControlPanel.actions.stopListening',\n description:\n 'Ask the host to stop listening, cancel a start or end a paused session. Never gated by consent or AI credits. Does nothing when idle or stopped.',\n invoke: (handle) => {\n handle.stop();\n },\n },\n pick_candidate: {\n // The host turns the pick into a write on the patient's record (the\n // care plan adds a treatment line), so it is gated as PHI.\n safety: 'phi',\n argsType: '{ id: string }',\n argsSchema: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description: 'One of the ids in the candidateIds state slot.',\n },\n },\n required: ['id'],\n additionalProperties: false,\n },\n descriptionKey: 'ui.agent.voiceControlPanel.actions.pickCandidate',\n description:\n 'Pick one of the offered matches by id, the same as saying its option number or pressing it. Unknown ids are ignored.',\n examples: [\n { args: { id: 'otve' }, note: 'Pick the match with id otve.' },\n ],\n invoke: (handle, args: { id: string }) => {\n handle.pickCandidate(args.id);\n },\n },\n open_commands: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.voiceControlPanel.actions.openCommands',\n description: 'Expand the voice commands list.',\n invoke: (handle) => {\n handle.openCommands();\n },\n },\n close_commands: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.voiceControlPanel.actions.closeCommands',\n description: 'Collapse the voice commands list.',\n invoke: (handle) => {\n handle.closeCommands();\n },\n },\n open_panel: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.voiceControlPanel.actions.openPanel',\n description:\n 'Floating layout: open the panel above the pill. Does nothing inline.',\n invoke: (handle) => {\n handle.openPanel();\n },\n },\n close_panel: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.voiceControlPanel.actions.closePanel',\n description:\n 'Floating layout: close the panel above the pill. Listening carries on. Does nothing inline.',\n invoke: (handle) => {\n handle.closePanel();\n },\n },\n close_credits_notice: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.voiceControlPanel.actions.closeCreditsNotice',\n description:\n 'Close the out-of-credits notice and go back to the controls. Credits stay out: the next start shows it again.',\n invoke: (handle) => {\n handle.closeCreditsNotice();\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'voice-control-panel',\n description: 'Marks the VoiceControlPanel root.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop on VoiceControlPanel.',\n },\n item: {\n attr: 'data-candidate-id',\n description:\n 'On each match button, the candidate id that pick_candidate takes.',\n },\n },\n};\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { cva } from 'class-variance-authority';\nimport { Trans, useTranslation } from 'react-i18next';\nimport {\n AudioLines,\n ChevronDown,\n CircleAlert,\n CircleCheck,\n Info,\n LoaderCircle,\n SlidersHorizontal,\n Square,\n TriangleAlert,\n X,\n} from 'lucide-react';\nimport {\n AiCreditsExhaustedNotice,\n AiCreditsWarning,\n type AiCreditsInfo,\n type AiCreditsNoticeProps,\n} from '../ai-credits-notice';\nimport {\n useDismissibleAiCreditsNotice,\n withAiCreditsNotice,\n} from '../ai-credits-notice/ai-credits-notice';\nimport {\n AiConsentBanner,\n type AiConsentBannerHeadingLevel,\n type AiConsentGate,\n} from '../ai-permission-notice';\nimport { AiSparkle } from '../ai-sparkle';\nimport { Badge } from '../badge';\nimport { Button, IconButton } from '../button';\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from '../collapsible';\nimport { FormField } from '../form-field';\nimport { Kbd, type KbdNamedKey } from '../kbd';\nimport { List } from '../list';\nimport { LiveRegion } from '../live-region';\nimport { Select, type SelectOption } from '../select';\nimport {\n aiFabDividerClass,\n aiFabPillClass,\n aiFabSegmentVariants,\n aiFabSweepClass,\n} from '../_shared/ai-fab-pill';\nimport { composeRefs } from '../_shared/compose-refs';\nimport {\n glassEntranceClass,\n glassSurfaceClass,\n} from '../_shared/glass-surface';\nimport { publishFloatingClearance } from '../_shared/floating-clearance';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { useAiSparkle } from '../../hooks/use-ai-sparkle';\nimport { useIsomorphicLayoutEffect } from '../../hooks/use-isomorphic-layout-effect';\nimport { voiceControlPanelAgent } from './voice-control-panel.agent';\n\n/* -------------------------------------------------------------------- */\n/* VoiceControlPanel */\n/* */\n/* The kit draws the voice-control UI; the host owns the microphone and */\n/* the speech service and drives the panel through props. The same split */\n/* as RichTextEditor's `onDictate`: no audio, no network, no parsing in */\n/* the kit. The panel is compact so it can sit inline above a chart today */\n/* and move into a tool rail later: a container query stacks it in a */\n/* narrow container. */\n/* */\n/* `layout=\"floating\"` draws the same panel as a non-modal companion */\n/* above the RichTextEditor's AI pill (shared recipe in */\n/* `_shared/ai-fab-pill.ts`), fixed to a viewport corner: mic | panel. */\n/* */\n/* `aiConsent` puts every start behind the patient AI-consent gate, the */\n/* same contract, banner and resume-after-grant as RichTextEditor. */\n/* `aiCredits` puts it behind the practice's AI credits too, checked */\n/* after consent, with the shared AiCreditsExhaustedNotice. */\n/* -------------------------------------------------------------------- */\n\n/* -------------------------------------------------------------------- */\n/* Public types */\n/* -------------------------------------------------------------------- */\n\n/** Where the host's voice session is. */\nexport type VoiceControlStatus =\n | 'idle'\n | 'starting'\n | 'listening'\n | 'paused'\n | 'error';\n\n/** How a message reads: the tone decides the colour and the icon. */\nexport type VoiceControlTone = 'info' | 'success' | 'warning' | 'error';\n\n/** Generic failures the kit words itself, translated. */\nexport type VoiceControlError =\n | 'microphoneDenied'\n | 'connectionLost'\n | 'startFailed'\n | 'recordingFailed'\n | 'unsupported';\n\n/** A host message, already translated by the host. */\nexport interface VoiceControlMessage {\n tone: VoiceControlTone;\n text: string;\n /**\n * Change it to announce the same words again (the live region only speaks\n * when the message changes), e.g. a counter for \"Not recognised\" twice.\n */\n id?: string | number;\n}\n\n/** What the speech service heard. `partial` may still change. */\nexport interface VoiceControlTranscript {\n final?: string;\n partial?: string;\n}\n\n/** One dictation language the host's speech service accepts. */\nexport interface VoiceControlLanguage {\n value: string;\n label: string;\n}\n\n/** One match the user can pick by voice (\"option 1\") or by hand. */\nexport interface VoiceControlCandidate {\n id: string;\n label: string;\n detail?: string;\n}\n\n/** One voice command shown in the commands disclosure. */\nexport interface VoiceControlCommand {\n phrase: ReactNode;\n description: ReactNode;\n}\n\n/** What a press asks the host to do: start (or resume), or stop (or cancel\n * a start). */\nexport type VoiceControlToggle = 'start' | 'stop';\n\n/** Inline in the page flow, or a floating pill that opens the panel. */\nexport type VoiceControlLayout = 'inline' | 'floating';\n\n/** The viewport corner of the floating pill. Logical: mirrors in RTL. */\nexport type VoiceControlPosition = 'bottom-end' | 'bottom-start';\n\n/**\n * The practice's AI credits, as the platform keeps them (a yearly allowance\n * that expires, then welcome and bought credits that do not), plus whether\n * they have run out. See `AiCreditsInfo` for the allowance, the dates and\n * the next steps the host can offer.\n */\nexport interface VoiceControlAiCredits extends AiCreditsInfo {\n /**\n * Out of AI credits. Every way of starting shows the out-of-credits\n * notice in the panel instead of calling `onToggle`; stopping is never\n * gated. Set while a session runs or is paused, the panel stops it\n * (`onToggle('stop')`, once) and shows the notice on its own.\n */\n exhausted?: boolean;\n}\n\n/** Curated imperative handle for agent / external automation. */\nexport interface VoiceControlPanelHandle {\n getStatus: () => VoiceControlStatus;\n /** Ask the host to start (`onToggle('start')`) unless already starting\n * or listening, or disabled. Gated by `aiConsent` like a press. */\n start: () => void;\n /** Ask the host to stop, cancel a start or end a paused session\n * (`onToggle('stop')`); a no-op when idle or stopped. Never gated. */\n stop: () => void;\n getCandidateIds: () => string[];\n pickCandidate: (id: string) => void;\n isCommandsOpen: () => boolean;\n openCommands: () => void;\n closeCommands: () => void;\n /** Floating layout: whether the panel above the pill is open. Always\n * false inline. */\n isPanelOpen: () => boolean;\n /** Floating layout: open the panel (no focus move). A no-op inline. */\n openPanel: () => void;\n /** Floating layout: close the panel; listening carries on. A no-op inline. */\n closePanel: () => void;\n /** Whether `aiCredits.exhausted` is gating every start. */\n isCreditsExhausted: () => boolean;\n /** Whether the out-of-credits notice is showing. */\n isCreditsNoticeOpen: () => boolean;\n /** Close the out-of-credits notice, back to the controls. */\n closeCreditsNotice: () => void;\n}\n\nexport interface VoiceControlPanelProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'title' | 'children' | 'onToggle'\n> {\n /** Where the host's voice session is. Drives the button, dot and status. */\n status: VoiceControlStatus;\n /**\n * The start/stop button, the pill's voice segment, the shortcut and the\n * agent call this, saying which way: `'start'` when idle, paused or\n * stopped, `'stop'` when starting or listening (stop, or cancel a start).\n * Every stop is passed on, so act on the argument rather than flip your\n * own state. A host that ignores the argument keeps working.\n */\n onToggle: (next: VoiceControlToggle) => void;\n /**\n * Stops a session from starting: disables the button, the shortcut, the\n * settings and the matches. A session already starting or listening can\n * still be stopped, so the microphone is never trapped on.\n */\n disabled?: boolean;\n /** Panel title. Default: the kit's \"Voice control\". */\n title?: ReactNode;\n /** Shows a \"Beta\" badge beside the title. */\n beta?: boolean;\n /** A host notice under the title, e.g. \"Free during the beta.\" */\n betaNotice?: ReactNode;\n /** The current dictation language (a `languages` value). */\n language?: string;\n /** Dictation languages. The select shows only with two or more. */\n languages?: VoiceControlLanguage[];\n onLanguageChange?: (value: string) => void;\n /** Extra settings beside the language, e.g. the periodontal route select. */\n extraControls?: ReactNode;\n /** The bold \"where am I\" line: the field or elements voice acts on. */\n target?: ReactNode;\n /** What the speech service heard: `final` reads normal, `partial` muted. */\n transcript?: VoiceControlTranscript;\n /** A host message. The tone sets colour and icon; not every message is red. */\n message?: VoiceControlMessage | null;\n /** A generic failure, worded and translated by the kit (tone: error). */\n error?: VoiceControlError | null;\n /** Several matches to choose from, numbered as they are spoken. */\n candidates?: VoiceControlCandidate[];\n /** Host label above the matches, e.g. \"Several matches: say 'option 1'…\". */\n candidatesLabel?: string;\n onCandidatePick?: (id: string) => void;\n /** Voice commands listed in the disclosure under the panel. */\n commands?: VoiceControlCommand[];\n /** Start with the commands disclosure open. */\n defaultCommandsOpen?: boolean;\n /**\n * Keyboard shortcut the kit binds document-wide to toggle, e.g. `\"Alt+V\"`\n * (default). Modifiers: Alt, Control, Shift, Meta. `false` turns it off.\n */\n shortcut?: string | false;\n /** Opaque instance id: `data-component-id` and agent registration. */\n id?: string;\n /**\n * `inline` (default): the panel sits in the page flow, above the chart.\n * `floating`: the RichTextEditor's AI pill (mic | panel) fixed to a\n * viewport corner; the second segment opens this panel above it, non-modal.\n */\n layout?: VoiceControlLayout;\n /**\n * Floating only: the viewport corner. Default `bottom-end`; `bottom-start`\n * moves the pill away from another floating widget (a chat bubble).\n */\n position?: VoiceControlPosition;\n /** Floating only: whether the panel above the pill is open (controlled). */\n open?: boolean;\n /** Floating only: the open state when uncontrolled. Default `false`. */\n defaultOpen?: boolean;\n /**\n * Floating only: called when the panel opens or closes, by the pill, the\n * close button, Escape, or on its own for an error or a set of matches.\n */\n onOpenChange?: (open: boolean) => void;\n /**\n * Floating only: the pill's sparkle, as on the RichTextEditor's pill. It\n * plays once when the pill first shows and again when a success message\n * arrives (a write landed); never in a loop while listening. Default `true`.\n */\n aiSparkle?: boolean;\n /**\n * Patient AI-consent gate: voice sends patient audio to the host's speech\n * service. Same contract as RichTextEditor's `aiConsent`. While present and\n * not granted, every way of starting shows the `AiConsentBanner` instead of\n * calling `onToggle`; stopping is never gated. Omit for ungated behaviour.\n */\n aiConsent?: AiConsentGate;\n /**\n * Heading level for the `AiConsentBanner` title and the out-of-credits\n * notice title, which show in the same place. Default 5.\n */\n aiConsentHeadingLevel?: AiConsentBannerHeadingLevel;\n /**\n * The practice's AI credits. With `exhausted`, every way of starting (the\n * Start button, the pill's mic, the shortcut, the agent's\n * `start_listening`) shows the out-of-credits notice instead of calling\n * `onToggle`, after the consent check; stopping is never gated. Running\n * low (80% used, or `runningLow`) and `expiring` show as quiet lines in\n * the panel and never block. Omit for ungated behaviour.\n */\n aiCredits?: VoiceControlAiCredits;\n /**\n * Floating only: the `AiCreditsNotice` hover card on the pill's mic, as on\n * the RichTextEditor's AI triggers. It never takes the click: the mic\n * still starts, stops or opens a gate exactly as without it.\n */\n aiCreditsNotice?: AiCreditsNoticeProps;\n}\n\n/* -------------------------------------------------------------------- */\n/* CVA */\n/* -------------------------------------------------------------------- */\n\nconst rootVariants = cva(\n [\n // The container query lives on the root and is read by descendants only,\n // never by a flex parent of its own (see the grid below).\n 'ds:@container/voice-control',\n 'ds:block ds:w-full ds:min-w-0',\n // The edge is --surface-border-width (1px, 2px in the accessible themes),\n // at zero specificity so the nearest theme root decides it.\n 'ds:rounded-[var(--radius-lg)] ds:[:where(&)]:border-[length:var(--surface-border-width,var(--border-width-sm))]',\n 'ds:bg-[var(--card)] ds:text-[color:var(--foreground)]',\n 'ds:p-[var(--spacing-md)]',\n 'ds:print:hidden',\n ].join(' '),\n {\n variants: {\n // The border colour lives only here, never beside a base colour, so\n // the listening ring can't lose a race on stylesheet order.\n status: {\n idle: 'ds:border-[color:var(--card-border)]',\n starting: 'ds:border-[color:var(--card-border)]',\n listening: 'ds:border-[color:var(--ring)]',\n paused: 'ds:border-[color:var(--card-border)]',\n error: 'ds:border-[color:var(--card-border)]',\n },\n },\n defaultVariants: { status: 'idle' },\n },\n);\n\nconst statusDotVariants = cva(\n [\n 'ds:relative ds:inline-flex ds:size-2.5 ds:shrink-0 ds:rounded-[var(--radius-full)]',\n 'ds:forced-colors:border ds:forced-colors:border-[CanvasText]',\n ].join(' '),\n {\n variants: {\n status: {\n idle: 'ds:border ds:border-[color:var(--muted-foreground)]',\n starting: 'ds:bg-[color:var(--muted-foreground)]',\n listening: 'ds:bg-[color:var(--destructive)]',\n paused: 'ds:bg-[color:var(--warning)]',\n error: 'ds:bg-[color:var(--error)]',\n },\n },\n defaultVariants: { status: 'idle' },\n },\n);\n\nconst messageVariants = cva(\n 'ds:m-0 ds:flex ds:items-start ds:gap-[var(--spacing-xs)] type-body-sm',\n {\n variants: {\n tone: {\n info: 'ds:text-[color:var(--info-foreground)]',\n success: 'ds:text-[color:var(--success-foreground)]',\n warning: 'ds:text-[color:var(--warning-foreground)]',\n error: 'ds:text-[color:var(--error-foreground)]',\n },\n },\n defaultVariants: { tone: 'info' },\n },\n);\n\nconst focusRing =\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]';\n\nconst candidateButtonVariants = cva(\n [\n 'ds:flex ds:w-full ds:items-center',\n 'ds:min-h-[var(--min-target-size)] ds:rounded-[var(--radius-sm)]',\n 'ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)] ds:py-[var(--spacing-2xs)]',\n 'ds:text-start ds:text-[color:var(--foreground)] ds:cursor-pointer',\n 'ds:hover:bg-[color:var(--muted)] ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n 'ds:disabled:cursor-not-allowed ds:disabled:opacity-50',\n focusRing,\n ].join(' '),\n);\n\nconst commandsTriggerVariants = cva(\n [\n 'ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]',\n 'ds:min-h-[var(--min-target-size)] ds:py-[var(--spacing-2xs)]',\n 'ds:rounded-[var(--radius-sm)] type-meta ds:text-[color:var(--muted-foreground)] ds:text-start',\n 'ds:hover:text-[color:var(--foreground)] ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n focusRing,\n ].join(' '),\n);\n\n/* ---- Floating layout ------------------------------------------------ */\n\n// The fixed cluster: the panel stacked above the pill, in one viewport\n// corner. `--z-fixed` is the kit's layer for viewport-fixed controls (the\n// FloatingActionButton's): above sticky chrome, below a modal's backdrop, so\n// an open Dialog covers the pill and the page together. The Select listbox\n// the panel opens portals to <body> on its own `--z-dropdown` layer, above\n// this one. The cluster is as wide as the open panel; its empty corner lets\n// clicks through to the page.\nconst floatingRootVariants = cva(\n [\n 'ds:fixed ds:z-[var(--z-fixed)]',\n // `--voice-control-offset` (default: the kit's corner gap) lifts the\n // cluster, for example above the page's own floating button; the\n // panel's height cap below reads the same property.\n 'ds:bottom-[calc(var(--voice-control-offset,var(--spacing-lg))+env(safe-area-inset-bottom,0px))]',\n 'ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]',\n 'ds:pointer-events-none ds:[&>*]:pointer-events-auto',\n 'ds:print:hidden',\n ].join(' '),\n {\n variants: {\n position: {\n 'bottom-end': 'ds:end-[var(--spacing-lg)] ds:items-end',\n 'bottom-start': 'ds:start-[var(--spacing-lg)] ds:items-start',\n },\n },\n defaultVariants: { position: 'bottom-end' },\n },\n);\n\n// The panel above the pill: the Help me write card's liquid glass (shared\n// recipe in `_shared/glass-surface.ts`, with its opaque fallback for the\n// accessible themes, more contrast, reduced transparency and forced\n// colours), so AI tools share one look. Compact: never wider than the\n// viewport minus the gutter on both sides, never taller than the room above\n// the pill; longer content scrolls inside. It grows out of the pill's\n// corner. `transform-origin` has no logical keywords, so the corner is\n// picked per position and direction: bottom-end is the bottom-right corner\n// in LTR and the bottom-left in RTL.\nconst floatingPanelVariants = cva(\n [\n 'ds:@container/voice-control',\n // Closed is `hidden`: never let a display utility win over it.\n 'ds:min-w-0 ds:[&[hidden]]:hidden',\n 'ds:[inline-size:min(24rem,calc(100vw-(var(--spacing-lg)*2)))]',\n 'ds:[max-block-size:calc(100dvh-var(--min-target-size)-var(--voice-control-offset,var(--spacing-lg))-var(--spacing-lg)-var(--spacing-sm)-env(safe-area-inset-bottom,0px))]',\n // On a phone the panel spans the screen, so it stops at half the height\n // (scrolling inside) and the voice cursor row above it stays in view.\n 'ds:max-sm:[max-block-size:calc(50dvh-var(--min-target-size)-var(--voice-control-offset,var(--spacing-lg))-var(--spacing-lg)-var(--spacing-sm)-env(safe-area-inset-bottom,0px))]',\n 'ds:overflow-y-auto ds:overscroll-contain',\n glassSurfaceClass,\n glassEntranceClass,\n 'ds:text-[color:var(--foreground)]',\n focusRing,\n ].join(' '),\n {\n variants: {\n position: {\n 'bottom-end': 'ds:origin-bottom-right ds:rtl:origin-bottom-left',\n 'bottom-start': 'ds:origin-bottom-left ds:rtl:origin-bottom-right',\n },\n },\n defaultVariants: { position: 'bottom-end' },\n },\n);\n\n// The pill's mic segment. While listening it carries the word \"Listening\"\n// beside the stop icon, so it widens like an extended FAB.\nconst micSegmentVariants = cva(\n 'ds:disabled:cursor-not-allowed ds:disabled:opacity-50',\n {\n variants: {\n extended: {\n true: 'ds:gap-[var(--spacing-xs)] ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-md)]',\n false: '',\n },\n },\n defaultVariants: { extended: false },\n },\n);\n\n/* -------------------------------------------------------------------- */\n/* Shortcut */\n/* -------------------------------------------------------------------- */\n\ntype ShortcutModifier = Extract<\n KbdNamedKey,\n 'Alt' | 'Control' | 'Shift' | 'Meta'\n>;\n\ninterface ParsedShortcut {\n modifiers: Set<ShortcutModifier>;\n key: string;\n /** `aria-keyshortcuts` value, e.g. `Alt+V`. */\n aria: string;\n /** Keys for the kit Kbd, e.g. `['Alt', 'V']`. */\n keys: string[];\n}\n\nconst MODIFIER_ALIASES: Record<string, ShortcutModifier> = {\n alt: 'Alt',\n option: 'Alt',\n ctrl: 'Control',\n control: 'Control',\n shift: 'Shift',\n meta: 'Meta',\n cmd: 'Meta',\n command: 'Meta',\n};\n\n/** Parse `\"Alt+V\"`-style shortcuts. Returns null for an unusable string. */\nexport function parseVoiceShortcut(shortcut: string): ParsedShortcut | null {\n const parts = shortcut\n .split('+')\n .map((part) => part.trim())\n .filter(Boolean);\n if (parts.length === 0) return null;\n const rawKey = parts[parts.length - 1];\n const modifiers = new Set<ShortcutModifier>();\n for (const part of parts.slice(0, -1)) {\n const modifier = MODIFIER_ALIASES[part.toLowerCase()];\n if (!modifier) return null;\n modifiers.add(modifier);\n }\n const key = rawKey.length === 1 ? rawKey.toUpperCase() : rawKey;\n const ordered = (['Control', 'Alt', 'Shift', 'Meta'] as const).filter((m) =>\n modifiers.has(m),\n );\n return {\n modifiers,\n key,\n aria: [...ordered, key].join('+'),\n keys: [...ordered, key],\n };\n}\n\n/** Whether a keydown matches a parsed shortcut. Letters and digits match on\n * the physical key, so Alt+V still matches on macOS, where Option+V types\n * a different character. */\nfunction matchesShortcut(event: KeyboardEvent, parsed: ParsedShortcut) {\n if (event.altKey !== parsed.modifiers.has('Alt')) return false;\n if (event.ctrlKey !== parsed.modifiers.has('Control')) return false;\n if (event.shiftKey !== parsed.modifiers.has('Shift')) return false;\n if (event.metaKey !== parsed.modifiers.has('Meta')) return false;\n const { key } = parsed;\n if (/^[A-Z]$/.test(key)) return event.code === `Key${key}`;\n if (/^[0-9]$/.test(key)) return event.code === `Digit${key}`;\n return event.key.toLowerCase() === key.toLowerCase();\n}\n\n/* -------------------------------------------------------------------- */\n/* Component */\n/* -------------------------------------------------------------------- */\n\ntype FocusTarget =\n | 'panel'\n | 'gate'\n | 'credits'\n | 'toggle'\n | 'launcher'\n | 'return';\n\n/** The shortcut as kit Kbd pills. `<Trans>` hands a placeholder component\n * `children` (null for a self-closing tag), which would switch Kbd to its\n * children path and render an empty pill, so this wrapper drops them. */\nfunction ShortcutKeys({ keys }: { keys: string[] }) {\n return <Kbd keys={keys} separator=\"plus\" size=\"sm\" />;\n}\n\nconst TONE_ICON: Record<VoiceControlTone, typeof Info> = {\n info: Info,\n success: CircleCheck,\n warning: TriangleAlert,\n error: CircleAlert,\n};\n\nconst NO_CANDIDATES: VoiceControlCandidate[] = [];\n\n// The kit's target size (44px, 48px in the accessible themes) on every\n// control in the panel, at every width: the panel is touch-first.\nconst TARGET_HEIGHT = 'ds:min-h-[var(--min-target-size)]';\nconst TARGET_SQUARE =\n 'ds:min-h-[var(--min-target-size)] ds:min-w-[var(--min-target-size)]';\n\n/** A part that already ends a sentence, in Latin, CJK or Arabic script. */\nconst SENTENCE_END = /[.!?…:。.!?؟।]$/u;\n\n/** How soon after a session ends that running out of credits still shows\n * the notice on its own. Later, it waits for the next start. */\nconst CREDITS_GRACE_MS = 10_000;\n\n/** Appended on every other announcement so a repeat still changes the\n * region's text (and is read again). Invisible, and not trimmed away. */\nconst NO_BREAK_SPACE = '\\u00a0';\n\n/**\n * VoiceControlPanel: the kit UI for a host-driven voice control session.\n * The host keeps the microphone and the speech service; the panel shows the\n * state, what was heard, where the voice acts, messages and matches, and\n * calls `onToggle` from its button and its keyboard shortcut.\n */\nexport const VoiceControlPanel = forwardRef<\n HTMLDivElement,\n VoiceControlPanelProps\n>(\n (\n {\n status,\n onToggle,\n disabled = false,\n title,\n beta = false,\n betaNotice,\n language,\n languages,\n onLanguageChange,\n extraControls,\n target,\n transcript,\n message,\n error,\n candidates = NO_CANDIDATES,\n candidatesLabel,\n onCandidatePick,\n commands,\n defaultCommandsOpen = false,\n shortcut = 'Alt+V',\n id,\n layout = 'inline',\n position = 'bottom-end',\n open,\n defaultOpen = false,\n onOpenChange,\n aiSparkle = true,\n aiConsent,\n aiConsentHeadingLevel,\n aiCredits,\n aiCreditsNotice: aiCreditsNoticeProp,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation('ui');\n const titleId = useId();\n const candidatesLabelId = useId();\n const panelId = useId();\n const [commandsOpen, setCommandsOpen] = useState(defaultCommandsOpen);\n const floating = layout === 'floating';\n\n const rootRef = useRef<HTMLDivElement>(null);\n const composedRef = useMemo(() => composeRefs(ref, rootRef), [ref]);\n const panelRef = useRef<HTMLDivElement>(null);\n const gateRef = useRef<HTMLDivElement>(null);\n const creditsRef = useRef<HTMLDivElement>(null);\n const toggleRef = useRef<HTMLButtonElement>(null);\n const launcherRef = useRef<HTMLButtonElement>(null);\n const pillRef = useRef<HTMLDivElement>(null);\n\n const parsedShortcut = useMemo(\n () => (shortcut === false ? null : parseVoiceShortcut(shortcut)),\n [shortcut],\n );\n useEffect(() => {\n if (import.meta.env.DEV && shortcut !== false && !parsedShortcut) {\n console.warn(\n `VoiceControlPanel: shortcut \"${shortcut}\" is not a valid shortcut ` +\n '(use modifiers Alt, Control, Shift, Meta and one key, e.g. \"Alt+V\"); it is off.',\n );\n }\n }, [shortcut, parsedShortcut]);\n\n /* ---- Floating panel: open state ---------------------------------- */\n const [openState, setOpenState] = useState(defaultOpen);\n const controlled = open !== undefined;\n const panelOpen = floating && (controlled ? open : openState);\n\n /* ---- AI consent gate --------------------------------------------- */\n // Hard stop, whatever the UI state: no consent, no start.\n const aiBlocked = aiConsent !== undefined && !aiConsent.granted;\n const [gateOpen, setGateOpen] = useState(false);\n const showGate = gateOpen && aiBlocked;\n // A start was gated: resume it if consent is granted from this banner.\n const resumeAfterGrantRef = useRef(false);\n // Set only by a Grant click HERE. A `granted` that flips for any other\n // reason (another patient, another tab) must not start listening.\n const grantRequestedRef = useRef(false);\n const [localGrantPending, setLocalGrantPending] = useState(false);\n const grantPending = (aiConsent?.pending ?? false) || localGrantPending;\n\n /* ---- AI credits gate (checked after consent) ---------------------- */\n const creditsBlocked = aiCredits?.exhausted === true;\n const [creditsOpen, setCreditsOpen] = useState(false);\n const showCredits = creditsOpen && creditsBlocked;\n // Set when the notice opens on its own (no focus move), so the live\n // region says why the panel changed. The ref lets the announcement join\n // a status change in the same commit (\"Not listening. You've run out…\");\n // the counter re-runs the announcer when nothing else changed.\n const creditsAnnouncePendingRef = useRef(false);\n const [creditsAnnounce, setCreditsAnnounce] = useState(0);\n // Mirrors `creditsOpen`, set at once by `openCredits` so a focus request\n // made from an effect survives until the notice renders.\n const creditsOpenRef = useRef(creditsOpen);\n creditsOpenRef.current = creditsOpen;\n // The body the gates replace: the controls, or a gate. Focus in here is\n // lost when the body swaps.\n const bodyRef = useRef<HTMLDivElement>(null);\n // Whether focus was last inside the notice: still true when the notice\n // unmounts with focus in it (no blur fires), false once focus left it.\n const noticeFocusRef = useRef(false);\n\n /* ---- Latest values for listeners, callbacks and the agent handle -- */\n const latest = useRef({\n status,\n onToggle,\n disabled,\n candidates,\n onCandidatePick,\n commandsOpen,\n aiBlocked,\n creditsBlocked,\n showCredits,\n floating,\n panelOpen,\n controlled,\n onOpenChange,\n });\n latest.current = {\n status,\n onToggle,\n disabled,\n candidates,\n onCandidatePick,\n commandsOpen,\n aiBlocked,\n creditsBlocked,\n showCredits,\n floating,\n panelOpen,\n controlled,\n onOpenChange,\n };\n\n // A focus move waits for the DOM to show its target; asking for one\n // always re-renders, so it runs now, never at some later render.\n const pendingFocusRef = useRef<FocusTarget | null>(null);\n const [, setFocusRequest] = useState(0);\n const requestFocus = useCallback((target: FocusTarget) => {\n pendingFocusRef.current = target;\n setFocusRequest((n) => n + 1);\n }, []);\n // Where focus was when a start was gated, to go back on Close.\n const gateReturnRef = useRef<HTMLElement | null>(null);\n // Consent withdrawn mid-session: the one automatic stop already sent.\n const stopSentRef = useRef(false);\n\n const setPanelOpen = useCallback((next: boolean) => {\n const now = latest.current;\n if (!now.floating || now.panelOpen === next) return;\n if (!now.controlled) setOpenState(next);\n now.onOpenChange?.(next);\n }, []);\n\n const closeGate = useCallback(() => {\n resumeAfterGrantRef.current = false;\n grantRequestedRef.current = false;\n setGateOpen(false);\n }, []);\n\n /** Where focus is now, to go back to on Close (never <body>). */\n const captureReturn = useCallback(() => {\n const focused =\n typeof document === 'undefined' ? null : document.activeElement;\n gateReturnRef.current =\n focused instanceof HTMLElement && focused !== document.body\n ? focused\n : null;\n }, []);\n\n const openGate = useCallback(() => {\n captureReturn();\n resumeAfterGrantRef.current = true;\n // Consent first: a credits notice still up gives way to the banner.\n setCreditsOpen(false);\n setGateOpen(true);\n setPanelOpen(true);\n requestFocus('gate');\n }, [captureReturn, setPanelOpen, requestFocus]);\n\n /** Whether focus sits in the body the gates replace (or is already\n * lost), so a swap must hand it on. */\n const focusInBody = useCallback(() => {\n if (typeof document === 'undefined') return false;\n const focused = document.activeElement;\n return (\n !focused ||\n focused === document.body ||\n Boolean(bodyRef.current?.contains(focused))\n );\n }, []);\n\n // Closing the notice with focus inside it sends focus to Start.\n const closeCredits = useCallback(() => {\n const inside = Boolean(\n creditsRef.current?.contains(document.activeElement),\n );\n noticeFocusRef.current = false;\n setCreditsOpen(false);\n if (inside) requestFocus('toggle');\n }, [requestFocus]);\n\n // A start met the credits gate: the notice takes the body and focus, as\n // the consent banner does. `keepReturn`: the start came through the\n // consent banner, which already holds where it was asked from.\n const openCredits = useCallback(\n (keepReturn = false) => {\n if (!keepReturn) captureReturn();\n creditsOpenRef.current = true;\n setCreditsOpen(true);\n setPanelOpen(true);\n requestFocus('credits');\n },\n [captureReturn, setPanelOpen, requestFocus],\n );\n\n // The one way in for the button, the pill's mic, the shortcut and the\n // agent: stopping (or cancelling a start) is never gated; starting is,\n // while consent is missing, then while credits are out.\n const requestToggle = useCallback(() => {\n const {\n status: now,\n disabled: off,\n aiBlocked: blocked,\n creditsBlocked: outOfCredits,\n } = latest.current;\n if (now === 'starting' || now === 'listening') {\n // Every stop reaches the host, never swallowed: it says 'stop', so a\n // repeat cannot be read as a start.\n latest.current.onToggle('stop');\n return;\n }\n if (off) return;\n if (blocked) {\n openGate();\n return;\n }\n if (outOfCredits) {\n openCredits();\n return;\n }\n latest.current.onToggle('start');\n }, [openGate, openCredits]);\n const requestToggleRef = useRef(requestToggle);\n requestToggleRef.current = requestToggle;\n\n const handleGrantConsent = useCallback(() => {\n grantRequestedRef.current = true;\n const result = aiConsent?.onGrant?.();\n if (result && typeof (result as Promise<void>).then === 'function') {\n setLocalGrantPending(true);\n Promise.resolve(result)\n .catch(() => {\n // The host shows its own failure UI (`pending`, a fresh\n // `granted: false`). This click no longer counts as the grant,\n // so a later grant from elsewhere will not start listening.\n grantRequestedRef.current = false;\n })\n .finally(() => setLocalGrantPending(false));\n }\n }, [aiConsent]);\n\n // Granted after a Grant click here: start listening, as the editor\n // resumes the action it gated. Granted any other way: close the banner.\n const prevGrantedRef = useRef(aiConsent?.granted ?? true);\n useEffect(() => {\n const wasGranted = prevGrantedRef.current;\n const nowGranted = aiConsent?.granted ?? true;\n prevGrantedRef.current = nowGranted;\n if (wasGranted || !nowGranted) return;\n const resume = resumeAfterGrantRef.current;\n const requestedHere = grantRequestedRef.current;\n resumeAfterGrantRef.current = false;\n grantRequestedRef.current = false;\n setGateOpen(false);\n if (!resume) return;\n const focusLost =\n typeof document === 'undefined' ||\n !document.activeElement ||\n document.activeElement === document.body;\n if (requestedHere) {\n const {\n status: now,\n disabled: off,\n creditsBlocked: outOfCredits,\n } = latest.current;\n if (!off && now !== 'starting' && now !== 'listening') {\n if (outOfCredits) {\n // Consent first, then credits: the resumed start meets the\n // credits gate instead of starting. Close still goes back to\n // where the start was asked from.\n openCredits(true);\n return;\n }\n latest.current.onToggle('start');\n }\n gateReturnRef.current = null;\n requestFocus('toggle');\n } else if (focusLost) {\n requestFocus('toggle');\n }\n }, [aiConsent?.granted, requestFocus, openCredits]);\n\n // Consent withdrawn (or the patient changed), or credits run out,\n // mid-session: stop, once. Paused counts: the host still holds the\n // session, and with a gate closed Resume is refused, so only this stop\n // (or the agent's) can end it.\n useEffect(() => {\n const held =\n status === 'starting' || status === 'listening' || status === 'paused';\n if (!held || !(aiBlocked || creditsBlocked)) {\n stopSentRef.current = false;\n return;\n }\n if (stopSentRef.current) return;\n stopSentRef.current = true;\n latest.current.onToggle('stop');\n }, [aiBlocked, creditsBlocked, status]);\n\n // Out of credits during a session, or within CREDITS_GRACE_MS of its\n // end (the host stopped first): once it has stopped, the notice shows\n // on its own, once per run-out, and the floating panel opens, as for an\n // error. While a session still runs, Stop stays in view. Credits that\n // load already exhausted, or run out long after the last session, wait\n // for the next start. Focus moves only if it was in the body the\n // notice replaces (the Stop the user pressed), never from the page.\n const sessionRef = useRef({ live: false, endedAt: -Infinity });\n const creditsShownRef = useRef(false);\n useEffect(() => {\n const session = sessionRef.current;\n const live =\n status === 'starting' || status === 'listening' || status === 'paused';\n if (live) {\n session.live = true;\n } else if (session.live) {\n session.live = false;\n session.endedAt = Date.now();\n }\n if (!creditsBlocked) {\n creditsShownRef.current = false;\n if (creditsOpenRef.current) {\n // Credits are back: the notice goes; focus in it goes to Start.\n if (noticeFocusRef.current && focusInBody()) requestFocus('toggle');\n noticeFocusRef.current = false;\n gateReturnRef.current = null;\n setCreditsOpen(false);\n }\n return;\n }\n if (creditsShownRef.current) return;\n if (status === 'starting' || status === 'listening') return;\n const recent =\n status === 'paused' || Date.now() - session.endedAt <= CREDITS_GRACE_MS;\n if (!recent) return;\n creditsShownRef.current = true;\n // Close goes back to Start: no earlier start's origin applies.\n gateReturnRef.current = null;\n if (focusInBody() && document.activeElement !== document.body) {\n // Focus moves with the notice, which then reads itself.\n requestFocus('credits');\n } else {\n // Focus stays on the page: the live region says what changed.\n noticeFocusRef.current = false;\n creditsAnnouncePendingRef.current = true;\n setCreditsAnnounce((n) => n + 1);\n }\n creditsOpenRef.current = true;\n setCreditsOpen(true);\n setPanelOpen(true);\n }, [creditsBlocked, status, setPanelOpen, focusInBody, requestFocus]);\n\n // Closing the floating panel closes the banner or notice inside it too.\n useEffect(() => {\n if (floating && !panelOpen && gateOpen) closeGate();\n if (floating && !panelOpen && creditsOpen) setCreditsOpen(false);\n }, [floating, panelOpen, gateOpen, closeGate, creditsOpen]);\n\n /* ---- Document-wide shortcut ------------------------------------- */\n useEffect(() => {\n if (!parsedShortcut || typeof document === 'undefined') return;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.defaultPrevented || event.repeat || event.isComposing) {\n return;\n }\n if (!matchesShortcut(event, parsedShortcut)) return;\n const { status: now, disabled: off } = latest.current;\n // Disabled stops a start, never a stop.\n if (off && now !== 'starting' && now !== 'listening') return;\n event.preventDefault();\n requestToggleRef.current();\n };\n document.addEventListener('keydown', onKeyDown);\n return () => document.removeEventListener('keydown', onKeyDown);\n }, [parsedShortcut]);\n\n /* ---- Agent handle ----------------------------------------------- */\n const handle = useMemo<VoiceControlPanelHandle>(\n () => ({\n getStatus: () => latest.current.status,\n start: () => {\n const { status: now, disabled: off } = latest.current;\n if (off || now === 'starting' || now === 'listening') return;\n // Gated like a user: without consent it shows the banner.\n requestToggleRef.current();\n },\n stop: () => {\n const { status: now } = latest.current;\n if (now !== 'starting' && now !== 'listening' && now !== 'paused') {\n return;\n }\n // Never gated, always 'stop', also for a paused session (whose\n // button would resume).\n latest.current.onToggle('stop');\n },\n getCandidateIds: () => latest.current.candidates.map((c) => c.id),\n pickCandidate: (candidateId: string) => {\n if (latest.current.disabled) return;\n if (!latest.current.candidates.some((c) => c.id === candidateId)) {\n return;\n }\n latest.current.onCandidatePick?.(candidateId);\n },\n isCommandsOpen: () => latest.current.commandsOpen,\n openCommands: () => setCommandsOpen(true),\n closeCommands: () => setCommandsOpen(false),\n isPanelOpen: () => latest.current.panelOpen,\n openPanel: () => setPanelOpen(true),\n closePanel: () => setPanelOpen(false),\n isCreditsExhausted: () => latest.current.creditsBlocked,\n isCreditsNoticeOpen: () => latest.current.showCredits,\n closeCreditsNotice: () => closeCredits(),\n }),\n [setPanelOpen, closeCredits],\n );\n useAgentRegistration(voiceControlPanelAgent, handle, id);\n\n /* ---- Strings ---------------------------------------------------- */\n const statusLabel = t(`voiceControl.status.${status}`);\n const errorText = error ? t(`voiceControl.error.${error}`) : '';\n const messageText = message?.text ?? '';\n // The id lets a host repeat the same words and still be heard.\n const messageKey = messageText\n ? `${message?.id ?? ''}\\u0000${messageText}`\n : '';\n const hasCandidates = candidates.length > 0;\n const candidatesKey = candidates.map((c) => c.id).join('\\u0000');\n const active = status === 'starting' || status === 'listening';\n\n // The label always says what pressing does. While starting, pressing\n // cancels; the spinner, aria-busy and the \"Connecting\" status carry the\n // busy state.\n const buttonLabel =\n status === 'listening'\n ? t('voiceControl.action.stop')\n : status === 'starting'\n ? t('common.cancel')\n : status === 'paused'\n ? t('voiceControl.action.resume')\n : t('voiceControl.action.start');\n\n /* ---- One live region: status, error, message, matches ------------ */\n const [announcement, setAnnouncement] = useState({ text: '', count: 0 });\n const announced = useRef({\n status,\n errorText,\n messageKey,\n candidatesKey,\n });\n useEffect(() => {\n const before = announced.current;\n const parts: string[] = [];\n if (status !== before.status) parts.push(statusLabel);\n if (errorText && errorText !== before.errorText) parts.push(errorText);\n if (messageKey && messageKey !== before.messageKey) {\n parts.push(messageText);\n }\n // A new set of matches, not only the first one.\n if (candidatesKey && candidatesKey !== before.candidatesKey) {\n parts.push(candidatesLabel || t('voiceControl.candidates'));\n }\n // The out-of-credits notice opened on its own (no focus move).\n if (creditsAnnouncePendingRef.current) {\n creditsAnnouncePendingRef.current = false;\n parts.push(t('aiCredits.title'));\n }\n announced.current = { status, errorText, messageKey, candidatesKey };\n if (parts.length > 0) {\n setAnnouncement((prev) => ({\n // A sentence break between parts gives the screen reader a pause:\n // \"Stopped. Microphone access is blocked…\". The break is a\n // translated template (Japanese ends a sentence with 。), skipped\n // when a part already ends one.\n text: parts.reduce((joined, part) =>\n SENTENCE_END.test(joined)\n ? `${joined} ${part}`\n : t('voiceControl.announcementJoin', {\n first: joined,\n next: part,\n }),\n ),\n count: prev.count + 1,\n }));\n }\n }, [\n t,\n status,\n statusLabel,\n errorText,\n messageKey,\n messageText,\n candidatesKey,\n candidatesLabel,\n creditsAnnounce,\n ]);\n\n /* ---- Floating: open on its own when the user must act ------------ */\n // A new error (the session stopped) or a new set of matches (a choice\n // is waiting) opens the panel, without moving focus.\n const attention = useRef({ error, candidatesKey });\n useEffect(() => {\n const before = attention.current;\n attention.current = { error, candidatesKey };\n if (!floating) return;\n if (\n (error && error !== before.error) ||\n (candidatesKey && candidatesKey !== before.candidatesKey)\n ) {\n setPanelOpen(true);\n }\n }, [floating, error, candidatesKey, setPanelOpen]);\n\n /* ---- Floating: clearance for scrolled-to and focused content ----- */\n // See `_shared/floating-clearance.ts`. Closed: how far the pill reaches\n // up from the viewport's bottom edge, plus a gap. Open: the panel too,\n // but never more than half the viewport, so a tall panel on a short\n // screen cannot push the cursor's whole working area off the page.\n // Layout heights (offsetHeight), so the panel's grow-in zoom does not\n // shrink the reading.\n useEffect(() => {\n const el = rootRef.current;\n if (!floating || !el) return undefined;\n const owner = {};\n const measure = () => {\n const style = window.getComputedStyle(el);\n const gap = Number.parseFloat(style.rowGap) || 0;\n const closed =\n (Number.parseFloat(style.bottom) || 0) +\n (pillRef.current?.offsetHeight ?? 0) +\n gap;\n const panel = panelRef.current;\n const panelHeight = panel && !panel.hidden ? panel.offsetHeight : 0;\n publishFloatingClearance(\n owner,\n panelHeight > 0\n ? Math.max(\n closed,\n Math.min(closed + panelHeight + gap, window.innerHeight / 2),\n )\n : closed,\n );\n };\n measure();\n const observer =\n typeof ResizeObserver === 'undefined'\n ? null\n : new ResizeObserver(measure);\n observer?.observe(el);\n window.addEventListener('resize', measure);\n return () => {\n observer?.disconnect();\n window.removeEventListener('resize', measure);\n publishFloatingClearance(owner, null);\n };\n }, [floating]);\n\n /* ---- Floating: the credits hover card on the mic ------------------ */\n // \"Don't show again\" hides it for the rest of this mount, per mode, as\n // on the RichTextEditor.\n const creditsNotice = useDismissibleAiCreditsNotice(\n floating ? aiCreditsNoticeProp : undefined,\n );\n\n /* ---- Floating: the pill's sparkle -------------------------------- */\n const sparkleOn = floating && aiSparkle;\n const {\n state: sparkleState,\n props: sparkleProps,\n land: landSparkle,\n } = useAiSparkle({ enabled: sparkleOn });\n const successKey = message?.tone === 'success' ? messageKey : '';\n const sparkledKey = useRef(successKey);\n useEffect(() => {\n if (successKey === sparkledKey.current) return;\n sparkledKey.current = successKey;\n if (sparkleOn && successKey) landSparkle();\n }, [sparkleOn, successKey, landSparkle]);\n\n /* ---- Focus after a pick ------------------------------------------ */\n // Picking a match usually makes the host clear the matches, which\n // unmounts the focused button and drops focus to <body>. Send it back to\n // the start/stop button instead. Only for a pick made on a match button.\n const restoreFocusRef = useRef(false);\n useEffect(() => {\n if (hasCandidates || !restoreFocusRef.current) return;\n restoreFocusRef.current = false;\n const focused =\n typeof document === 'undefined' ? null : document.activeElement;\n if (!focused || focused === document.body) toggleRef.current?.focus();\n }, [hasCandidates]);\n\n /* ---- Floating: Escape closes the panel ---------------------------- */\n // A native listener on the cluster: it hears keys from the pill and the\n // panel only, not from a portalled Select listbox, which closes itself.\n const closePanelRef = useRef<(refocus: boolean) => void>(() => {});\n useEffect(() => {\n const el = rootRef.current;\n if (!floating || !el) return undefined;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Escape' || event.defaultPrevented) return;\n if (!latest.current.panelOpen) return;\n event.preventDefault();\n closePanelRef.current(true);\n };\n el.addEventListener('keydown', onKeyDown);\n return () => el.removeEventListener('keydown', onKeyDown);\n }, [floating]);\n\n /* ---- Floating: a close from anywhere keeps focus on the page ------ */\n // Closed by the host (`open={false}`) while focus is inside: send it to\n // the panel segment, not to <body>. Read before the browser moves it.\n const wasOpenRef = useRef(panelOpen);\n useIsomorphicLayoutEffect(() => {\n const wasOpen = wasOpenRef.current;\n wasOpenRef.current = panelOpen;\n if (!wasOpen || panelOpen || pendingFocusRef.current) return;\n const focused = document.activeElement;\n if (focused && panelRef.current?.contains(focused)) {\n requestFocus('launcher');\n }\n }, [panelOpen, requestFocus]);\n\n /* ---- Pending focus moves (declared last: runs after the others) -- */\n // Focus moves wait for the DOM to show the target (the panel and the\n // banner mount or unhide in the same commit that asks for focus).\n useEffect(() => {\n const target = pendingFocusRef.current;\n if (!target) return;\n // Opened from an effect (a start resumed after a grant), the notice\n // shows a render later: keep the request until it does, unless the\n // credits came back in the meantime.\n if (target === 'credits' && !creditsRef.current) {\n if (!latest.current.creditsBlocked || !creditsOpenRef.current) {\n pendingFocusRef.current = null;\n }\n return;\n }\n pendingFocusRef.current = null;\n if (target === 'gate') {\n const gate = gateRef.current;\n // The banner's confirm checkbox, or the banner itself when the\n // reader may not grant (there is nothing to tick).\n const focusable =\n gate?.querySelector<HTMLElement>(\n '[role=\"checkbox\"]:not([disabled])',\n ) ??\n gate?.querySelector<HTMLElement>(\n '[data-component=\"ai-consent-banner\"]',\n );\n focusable?.focus();\n } else if (target === 'credits') {\n // The notice itself, so it is read from its title.\n creditsRef.current\n ?.querySelector<HTMLElement>(\n '[data-component=\"ai-credits-exhausted-notice\"]',\n )\n ?.focus();\n } else if (target === 'return') {\n const origin = gateReturnRef.current;\n gateReturnRef.current = null;\n // Still on the page and shown (a hidden panel has no boxes).\n if (origin?.isConnected && origin.getClientRects().length > 0) {\n origin.focus();\n } else if (latest.current.floating && !latest.current.panelOpen) {\n launcherRef.current?.focus();\n } else {\n toggleRef.current?.focus();\n }\n } else if (target === 'panel') {\n panelRef.current?.focus();\n } else if (target === 'toggle') {\n toggleRef.current?.focus();\n } else {\n launcherRef.current?.focus();\n }\n });\n\n /* ---- Settings --------------------------------------------------- */\n const languageOptions: SelectOption[] = (languages ?? []).map((l) => ({\n value: l.value,\n label: l.label,\n }));\n const showLanguage = languageOptions.length > 1;\n const hasSettings =\n showLanguage || (extraControls != null && extraControls !== false);\n\n const showTranscript =\n Boolean(transcript?.final) || Boolean(transcript?.partial);\n const busy = status === 'starting';\n\n const ToneIcon = message ? TONE_ICON[message.tone] : Info;\n\n /* ---- Pieces shared by both layouts ------------------------------ */\n // Escape and the close button send focus back where the panel was\n // opened from: the field Alt+V was pressed in (if it is still there),\n // or else the panel segment.\n const closePanel = (refocus: boolean) => {\n setPanelOpen(false);\n closeGate();\n setCreditsOpen(false);\n if (refocus) requestFocus(gateReturnRef.current ? 'return' : 'launcher');\n };\n closePanelRef.current = closePanel;\n\n const header = (\n <div className=\"ds:flex ds:flex-wrap ds:items-center ds:gap-x-[var(--spacing-xs)] ds:gap-y-[var(--spacing-2xs)]\">\n <p\n id={titleId}\n className=\"ds:m-0 type-label ds:font-semibold ds:text-[color:var(--foreground)]\"\n >\n {title ?? t('voiceControl.title')}\n </p>\n {beta ? (\n <Badge variant=\"brand\" size=\"sm\">\n {t('voiceControl.beta')}\n </Badge>\n ) : null}\n <span className=\"ds:ms-auto ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)] type-meta ds:text-[color:var(--muted-foreground)]\">\n <span aria-hidden=\"true\" className={statusDotVariants({ status })}>\n {status === 'listening' ? (\n <span\n className={[\n 'ds:absolute ds:inset-0 ds:rounded-[var(--radius-full)]',\n 'ds:bg-[color:var(--destructive)]',\n // No pulse in the accessible themes (--accessible-off).\n 'ds:motion-safe:animate-[var(--accessible-off,recorder-pulse_1.2s_ease-out_infinite)]',\n ].join(' ')}\n />\n ) : null}\n </span>\n <span data-voice-status=\"\">{statusLabel}</span>\n </span>\n {floating ? (\n <IconButton\n intent=\"ghost\"\n size=\"md\"\n icon={<X />}\n aria-label={t('common.close')}\n onClick={() => closePanel(true)}\n className={TARGET_SQUARE}\n data-voice-close=\"\"\n />\n ) : null}\n </div>\n );\n\n const betaNoticeLine = betaNotice ? (\n <p className=\"ds:m-0 ds:flex ds:items-start ds:gap-[var(--spacing-2xs)] type-meta ds:text-[color:var(--muted-foreground)]\">\n <Info aria-hidden=\"true\" className=\"ds:mt-px ds:size-4 ds:shrink-0\" />\n <span>{betaNotice}</span>\n </p>\n ) : null;\n\n // Without consent the banner takes the body: the same banner, Grant\n // control and busy state as the RichTextEditor's gate.\n const consentGate =\n showGate && aiConsent ? (\n <div\n ref={gateRef}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\"\n data-voice-consent=\"\"\n >\n <AiConsentBanner\n // Focus lands here when the reader may not grant (nothing to tick).\n tabIndex={-1}\n granted={aiConsent.granted}\n grantedAt={aiConsent.grantedAt}\n grantedBy={aiConsent.grantedBy}\n // No handler, no Grant control: never a dead button.\n canGrant={\n (aiConsent.canGrant ?? true) && aiConsent.onGrant !== undefined\n }\n pending={grantPending}\n onGrantConsent={handleGrantConsent}\n headingLevel={aiConsentHeadingLevel}\n />\n <div className=\"ds:flex ds:justify-end\">\n <Button\n intent=\"ghost\"\n size=\"md\"\n onClick={() => {\n closeGate();\n requestFocus('return');\n }}\n className={TARGET_HEIGHT}\n >\n {t('common.close')}\n </Button>\n </div>\n </div>\n ) : null;\n\n // Out of credits, after consent: the shared notice takes the body, with\n // the same Close as the consent banner. The panel announces it itself.\n const creditsGate =\n showCredits && !consentGate ? (\n <div\n ref={creditsRef}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\"\n data-voice-credits=\"\"\n onFocusCapture={() => {\n noticeFocusRef.current = true;\n }}\n onBlurCapture={() => {\n noticeFocusRef.current = false;\n }}\n >\n <AiCreditsExhaustedNotice\n // Focus lands here when a start meets the gate, with the kit\n // focus ring.\n tabIndex={-1}\n className={focusRing}\n credits={aiCredits}\n headingLevel={aiConsentHeadingLevel ?? 5}\n announce=\"off\"\n />\n <div className=\"ds:flex ds:justify-end\">\n <Button\n intent=\"ghost\"\n size=\"md\"\n onClick={() => {\n closeCredits();\n requestFocus('return');\n }}\n className={TARGET_HEIGHT}\n >\n {t('common.close')}\n </Button>\n </div>\n </div>\n ) : null;\n\n // Running low, expiring, or out of credits at rest: quiet lines that\n // never block. Renders nothing when none applies.\n const creditsLines = aiCredits ? (\n <AiCreditsWarning credits={aiCredits} exhausted={creditsBlocked} />\n ) : null;\n\n const controls = (\n <>\n {/* Body: action | readout | settings. One column in a narrow\n container (the tool rail, the floating panel), three from 560px\n of panel width. */}\n <div className=\"ds:grid ds:grid-cols-1 ds:items-start ds:gap-[var(--spacing-sm)] ds:@[560px]/voice-control:grid-cols-[auto_minmax(0,1fr)_auto]\">\n <Button\n ref={toggleRef}\n intent={status === 'listening' ? 'outline' : 'primary'}\n background=\"surface\"\n size=\"md\"\n // Disabled stops a start, never a stop.\n disabled={disabled && !active}\n aria-busy={busy || undefined}\n aria-keyshortcuts={parsedShortcut?.aria}\n onClick={() => requestToggle()}\n startIcon={\n busy ? (\n <LoaderCircle className=\"ds:animate-[var(--accessible-off,spin_1s_linear_infinite)] ds:motion-reduce:animate-none\" />\n ) : status === 'listening' ? (\n <Square className=\"ds:fill-current\" />\n ) : (\n <AudioLines />\n )\n }\n className={`${TARGET_HEIGHT} ds:w-full ds:@[560px]/voice-control:w-auto`}\n data-voice-toggle=\"\"\n >\n {buttonLabel}\n </Button>\n\n <div className=\"ds:flex ds:min-w-0 ds:flex-col ds:gap-[var(--spacing-2xs)]\">\n {target != null && target !== '' ? (\n <p className=\"ds:m-0 type-body-sm ds:font-semibold ds:[overflow-wrap:anywhere]\">\n {target}\n </p>\n ) : null}\n {showTranscript ? (\n <p\n className=\"ds:m-0 type-body-sm ds:[overflow-wrap:anywhere]\"\n data-voice-transcript=\"\"\n >\n <span className=\"ds:sr-only\">{t('voiceControl.heard')} </span>\n {transcript?.final ? (\n <span className=\"ds:text-[color:var(--foreground)]\">\n {transcript.final}\n </span>\n ) : null}\n {transcript?.final && transcript?.partial ? ' ' : null}\n {transcript?.partial ? (\n <span\n className=\"ds:italic ds:text-[color:var(--muted-foreground)]\"\n data-voice-partial=\"\"\n >\n {transcript.partial}\n </span>\n ) : null}\n </p>\n ) : status === 'listening' ? (\n // Holds the line while nothing has been heard yet, so the first\n // words do not push the panel down.\n <p\n aria-hidden=\"true\"\n className=\"ds:m-0 type-body-sm ds:min-h-[1lh]\"\n />\n ) : null}\n {error ? (\n <p className={messageVariants({ tone: 'error' })}>\n <CircleAlert\n aria-hidden=\"true\"\n className=\"ds:mt-px ds:size-4 ds:shrink-0\"\n />\n <span>{errorText}</span>\n </p>\n ) : null}\n {message && message.text ? (\n <p\n className={messageVariants({ tone: message.tone })}\n data-tone={message.tone}\n >\n <ToneIcon\n aria-hidden=\"true\"\n className=\"ds:mt-px ds:size-4 ds:shrink-0\"\n />\n <span>{message.text}</span>\n </p>\n ) : null}\n </div>\n\n {hasSettings ? (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)] ds:@[560px]/voice-control:flex-row ds:@[560px]/voice-control:flex-wrap ds:@[560px]/voice-control:items-end\">\n {showLanguage ? (\n // A visible, associated label (the kit FormField pattern).\n <FormField label={t('voiceControl.language')}>\n <Select\n options={languageOptions}\n value={language ?? ''}\n onValueChange={(next) => {\n if (next) onLanguageChange?.(next);\n }}\n disabled={disabled}\n />\n </FormField>\n ) : null}\n {extraControls}\n </div>\n ) : null}\n </div>\n\n {/* Matches, numbered as they are spoken (\"option 1\") */}\n {hasCandidates ? (\n <div\n role=\"group\"\n aria-labelledby={candidatesLabel ? candidatesLabelId : undefined}\n aria-label={\n candidatesLabel ? undefined : t('voiceControl.candidates')\n }\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)]\"\n data-voice-candidates=\"\"\n >\n {candidatesLabel ? (\n <p\n id={candidatesLabelId}\n className=\"ds:m-0 type-body-sm ds:text-[color:var(--foreground)]\"\n >\n {candidatesLabel}\n </p>\n ) : null}\n <List as=\"ol\" density=\"compact\">\n {candidates.map((candidate, index) => (\n <List.Item\n key={candidate.id}\n title={\n <button\n type=\"button\"\n disabled={disabled}\n onClick={() => {\n restoreFocusRef.current = true;\n onCandidatePick?.(candidate.id);\n }}\n className={candidateButtonVariants()}\n data-candidate-id={candidate.id}\n >\n {/* One inline run, so the accessible name keeps\n its spaces: \"Option 1: Composite filling OTVE\". */}\n <span>\n <span className=\"ds:sr-only\">\n {t('voiceControl.candidate', {\n number: index + 1,\n })}\n </span>{' '}\n <span className=\"ds:font-medium\">\n {candidate.label}\n </span>\n {candidate.detail ? (\n <>\n {' '}\n <span className=\"ds:ms-[var(--spacing-2xs)] type-meta ds:text-[color:var(--muted-foreground)]\">\n {candidate.detail}\n </span>\n </>\n ) : null}\n </span>\n </button>\n }\n />\n ))}\n </List>\n </div>\n ) : null}\n\n {/* Voice commands, titled with the shortcut */}\n {commands && commands.length > 0 ? (\n <Collapsible open={commandsOpen} onOpenChange={setCommandsOpen}>\n <CollapsibleTrigger className={commandsTriggerVariants()}>\n <ChevronDown\n aria-hidden=\"true\"\n className=\"ds:size-4 ds:shrink-0 ds:transition-transform ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none ds:[[data-state=open]_&]:rotate-180\"\n />\n <span>\n {parsedShortcut ? (\n <Trans\n ns=\"ui\"\n i18nKey=\"voiceControl.commandsWithShortcut\"\n components={{\n shortcut: <ShortcutKeys keys={parsedShortcut.keys} />,\n }}\n />\n ) : (\n t('voiceControl.commands')\n )}\n </span>\n </CollapsibleTrigger>\n <CollapsibleContent\n aria-label={t('voiceControl.commands')}\n className=\"ds:pt-[var(--spacing-xs)]\"\n >\n <List density=\"compact\">\n {commands.map((command, index) => (\n <List.Item\n key={index}\n title={command.phrase}\n description={command.description}\n />\n ))}\n </List>\n </CollapsibleContent>\n </Collapsible>\n ) : null}\n </>\n );\n\n const content = (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n {header}\n {betaNoticeLine}\n <div ref={bodyRef} className=\"ds:contents\">\n {consentGate ?? creditsGate ?? (\n <>\n {creditsLines}\n {controls}\n </>\n )}\n </div>\n </div>\n );\n\n // Always mounted, polite, and outside anything that collapses: it keeps\n // speaking while the floating panel is closed. See the docs for why\n // errors are polite.\n const liveRegion = (\n <LiveRegion politeness=\"polite\" data-voice-announcer=\"\">\n {announcement.text\n ? // Alternate a trailing no-break space so the same words said\n // twice in a row still change the region and are announced.\n `${announcement.text}${announcement.count % 2 ? NO_BREAK_SPACE : ''}`\n : ''}\n </LiveRegion>\n );\n\n if (!floating) {\n return (\n <div\n ref={composedRef}\n role=\"group\"\n aria-labelledby={titleId}\n data-component=\"voice-control-panel\"\n data-component-id={id}\n data-status={status}\n id={id}\n {...rest}\n className={rootVariants({ status, className })}\n >\n {content}\n {liveRegion}\n </div>\n );\n }\n\n /* ---- Floating layout -------------------------------------------- */\n const listening = status === 'listening';\n // The state in words on the pill, so it never rests on colour alone:\n // \"Listening\" on the red segment, \"Paused\" on the plain one (paused and\n // idle would otherwise look the same with the panel closed).\n const showWord = listening || status === 'paused';\n // The visible word must stay inside the name (WCAG 2.5.3). English\n // \"Listening\" sits in \"Stop listening\"; \"Paused\" is not in \"Resume\n // listening\", nor is a translation always, so the word then leads the\n // name through a translated template: \"Paused: Resume listening\".\n const micLabel =\n showWord &&\n !buttonLabel.toLocaleLowerCase().includes(statusLabel.toLocaleLowerCase())\n ? t('voiceControl.statusWithAction', {\n status: statusLabel,\n action: buttonLabel,\n })\n : buttonLabel;\n return (\n <div\n ref={composedRef}\n data-component=\"voice-control-panel\"\n data-component-id={id}\n data-status={status}\n data-layout=\"floating\"\n data-position={position}\n data-open={panelOpen ? '' : undefined}\n id={id}\n {...rest}\n className={floatingRootVariants({ position, className })}\n >\n {/* Non-modal: no focus trap, no backdrop; the page stays usable. */}\n <div\n ref={panelRef}\n id={panelId}\n role=\"group\"\n aria-labelledby={titleId}\n tabIndex={-1}\n hidden={!panelOpen}\n className={floatingPanelVariants({ position })}\n data-voice-panel=\"\"\n >\n {content}\n </div>\n\n {/* The RichTextEditor's AI pill: mic | panel. */}\n <div\n ref={pillRef}\n className=\"ds:relative\"\n data-ai-sparkle={sparkleProps['data-ai-sparkle']}\n >\n <div className={aiFabPillClass} data-voice-pill=\"\">\n {withAiCreditsNotice(\n <button\n type=\"button\"\n onClick={() => requestToggle()}\n disabled={(disabled && !active) || undefined}\n aria-label={micLabel}\n aria-busy={busy || undefined}\n aria-keyshortcuts={parsedShortcut?.aria}\n className={aiFabSegmentVariants({\n // The word on the red needs 7:1 in the accessible themes:\n // `labelled` holds the hover red there.\n recording: listening ? 'labelled' : false,\n className: micSegmentVariants({ extended: showWord }),\n })}\n data-voice-mic=\"\"\n >\n {busy ? (\n <LoaderCircle\n aria-hidden=\"true\"\n className=\"ds:size-5 ds:animate-[var(--accessible-off,spin_1s_linear_infinite)] ds:motion-reduce:animate-none\"\n />\n ) : listening ? (\n <Square\n aria-hidden=\"true\"\n className=\"ds:size-5 ds:fill-current\"\n />\n ) : (\n <AudioLines aria-hidden=\"true\" className=\"ds:size-5\" />\n )}\n {/* The state in words, not colour alone; see micLabel. */}\n {showWord ? (\n <span className=\"type-label ds:whitespace-nowrap\">\n {statusLabel}\n </span>\n ) : null}\n </button>,\n creditsNotice,\n // The pill sits at the foot of the viewport: the card opens up.\n 'top',\n )}\n <span aria-hidden=\"true\" className={aiFabDividerClass} />\n <button\n ref={launcherRef}\n type=\"button\"\n onClick={() => {\n if (panelOpen) {\n closePanel(false);\n } else {\n // Opened from here: Escape comes back here.\n gateReturnRef.current = null;\n setPanelOpen(true);\n requestFocus('panel');\n }\n }}\n aria-label={t('voiceControl.panelToggle')}\n aria-expanded={panelOpen}\n aria-controls={panelId}\n className={aiFabSegmentVariants({ recording: false })}\n data-voice-launcher=\"\"\n >\n {/* Fixed glyph: aria-expanded carries open or closed. */}\n <SlidersHorizontal aria-hidden=\"true\" className=\"ds:size-5\" />\n </button>\n {sparkleState ? (\n <span\n aria-hidden=\"true\"\n className={`${aiFabSweepClass} ${sparkleProps.className}`}\n />\n ) : null}\n </div>\n <AiSparkle state={sparkleState} />\n </div>\n\n {liveRegion}\n </div>\n );\n },\n);\n\nVoiceControlPanel.displayName = 'VoiceControlPanel';\n"],"names":["__iconNode","AudioLines","createLucideIcon","voiceControlPanelAgent","handle","args","rootVariants","cva","statusDotVariants","messageVariants","focusRing","candidateButtonVariants","commandsTriggerVariants","floatingRootVariants","floatingPanelVariants","glassSurfaceClass","glassEntranceClass","micSegmentVariants","MODIFIER_ALIASES","parseVoiceShortcut","shortcut","parts","part","rawKey","modifiers","modifier","key","ordered","m","matchesShortcut","event","parsed","ShortcutKeys","keys","Kbd","TONE_ICON","Info","CircleCheck","TriangleAlert","CircleAlert","NO_CANDIDATES","TARGET_HEIGHT","TARGET_SQUARE","SENTENCE_END","CREDITS_GRACE_MS","NO_BREAK_SPACE","VoiceControlPanel","forwardRef","status","onToggle","disabled","title","beta","betaNotice","language","languages","onLanguageChange","extraControls","target","transcript","message","error","candidates","candidatesLabel","onCandidatePick","commands","defaultCommandsOpen","id","layout","position","open","defaultOpen","onOpenChange","aiSparkle","aiConsent","aiConsentHeadingLevel","aiCredits","aiCreditsNoticeProp","className","rest","ref","t","useTranslation","titleId","useId","candidatesLabelId","panelId","commandsOpen","setCommandsOpen","useState","floating","rootRef","useRef","composedRef","useMemo","composeRefs","panelRef","gateRef","creditsRef","toggleRef","launcherRef","pillRef","parsedShortcut","useEffect","openState","setOpenState","controlled","panelOpen","aiBlocked","gateOpen","setGateOpen","showGate","resumeAfterGrantRef","grantRequestedRef","localGrantPending","setLocalGrantPending","grantPending","creditsBlocked","creditsOpen","setCreditsOpen","showCredits","creditsAnnouncePendingRef","creditsAnnounce","setCreditsAnnounce","creditsOpenRef","bodyRef","noticeFocusRef","latest","pendingFocusRef","setFocusRequest","requestFocus","useCallback","gateReturnRef","stopSentRef","setPanelOpen","next","now","_a","closeGate","captureReturn","focused","openGate","focusInBody","closeCredits","inside","openCredits","keepReturn","requestToggle","off","blocked","outOfCredits","requestToggleRef","handleGrantConsent","result","prevGrantedRef","wasGranted","nowGranted","resume","requestedHere","focusLost","sessionRef","creditsShownRef","session","n","onKeyDown","c","candidateId","_b","useAgentRegistration","statusLabel","errorText","messageText","messageKey","hasCandidates","candidatesKey","active","buttonLabel","announcement","setAnnouncement","announced","before","prev","joined","attention","el","owner","measure","style","gap","closed","panel","panelHeight","publishFloatingClearance","observer","creditsNotice","useDismissibleAiCreditsNotice","sparkleOn","sparkleState","sparkleProps","landSparkle","useAiSparkle","successKey","sparkledKey","restoreFocusRef","closePanelRef","wasOpenRef","useIsomorphicLayoutEffect","wasOpen","gate","focusable","origin","_c","_d","_e","_f","_g","languageOptions","l","showLanguage","hasSettings","showTranscript","busy","ToneIcon","closePanel","refocus","header","jsxs","jsx","Badge","IconButton","X","betaNoticeLine","consentGate","AiConsentBanner","Button","creditsGate","AiCreditsExhaustedNotice","creditsLines","AiCreditsWarning","controls","Fragment","LoaderCircle","Square","FormField","Select","List","candidate","index","Collapsible","CollapsibleTrigger","ChevronDown","Trans","CollapsibleContent","command","content","liveRegion","LiveRegion","listening","showWord","micLabel","aiFabPillClass","withAiCreditsNotice","aiFabSegmentVariants","aiFabDividerClass","SlidersHorizontal","aiFabSweepClass","AiSparkle"],"mappings":"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","x_google_ignoreList":[0]}