@elabs-ai/components-ai 4.2.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{_audio-player-media-chrome-T3XVXWRZ.js → _audio-player-media-chrome.js} +1 -1
- package/dist/_code-block-theme.js +12 -0
- package/dist/_code-block-theme.js.map +1 -0
- package/dist/{_flow-boundary-SHNWLQG5.js → _flow-boundary.js} +1 -1
- package/dist/_flow-boundary.js.map +1 -0
- package/dist/_flow-lazy.js +8 -0
- package/dist/_flow-lazy.js.map +1 -0
- package/dist/_lazy-boundary-conformance.js +2 -0
- package/dist/_lazy-boundary-conformance.js.map +1 -0
- package/dist/_lazy-cjk.js +13 -0
- package/dist/_lazy-cjk.js.map +1 -0
- package/dist/_lazy-engine-boundary.js +9 -0
- package/dist/_lazy-engine-boundary.js.map +1 -0
- package/dist/_lazy-math.js +13 -0
- package/dist/_lazy-math.js.map +1 -0
- package/dist/_lazy-mermaid.js +12 -0
- package/dist/_lazy-mermaid.js.map +1 -0
- package/dist/_mermaid-error-panel.js +9 -0
- package/dist/_mermaid-error-panel.js.map +1 -0
- package/dist/{_persona-rive-JEG44YHX.js → _persona-rive.js} +19 -24
- package/dist/_persona-rive.js.map +1 -0
- package/dist/_streamdown-i18n.js +21 -0
- package/dist/_streamdown-i18n.js.map +1 -0
- package/dist/_streamdown-safety.js +10 -0
- package/dist/_streamdown-safety.js.map +1 -0
- package/dist/_theme-scope-store.js +13 -0
- package/dist/_theme-scope-store.js.map +1 -0
- package/dist/a2ui/a2ui-surface.js +17 -0
- package/dist/a2ui/a2ui-surface.js.map +1 -0
- package/dist/a2ui/core/catalog.generated.js +10 -0
- package/dist/a2ui/core/catalog.generated.js.map +1 -0
- package/dist/a2ui/core/complete-json.js +10 -0
- package/dist/a2ui/core/complete-json.js.map +1 -0
- package/dist/a2ui/core/index.js +37 -0
- package/dist/a2ui/core/index.js.map +1 -0
- package/dist/a2ui/core/schema.js +11 -0
- package/dist/a2ui/core/schema.js.map +1 -0
- package/dist/a2ui/core/spec.js +10 -0
- package/dist/a2ui/core/spec.js.map +1 -0
- package/dist/a2ui/core/validate.js +16 -0
- package/dist/a2ui/core/validate.js.map +1 -0
- package/dist/a2ui/index.js +54 -0
- package/dist/a2ui/index.js.map +1 -0
- package/dist/a2ui/ui-catalog.js +18 -0
- package/dist/a2ui/ui-catalog.js.map +1 -0
- package/dist/agent-event.js +10 -0
- package/dist/agent-event.js.map +1 -0
- package/dist/agent-timeline.js +11 -0
- package/dist/agent-timeline.js.map +1 -0
- package/dist/agent.js +25 -0
- package/dist/agent.js.map +1 -0
- package/dist/artifact.js +23 -0
- package/dist/artifact.js.map +1 -0
- package/dist/asset-preview.js +22 -0
- package/dist/asset-preview.js.map +1 -0
- package/dist/attachments.js +33 -0
- package/dist/attachments.js.map +1 -0
- package/dist/audio-player.js +30 -0
- package/dist/audio-player.js.map +1 -0
- package/dist/audio-visualizer.js +9 -0
- package/dist/audio-visualizer.js.map +1 -0
- package/dist/canvas.js +10 -0
- package/dist/canvas.js.map +1 -0
- package/dist/chain-of-thought.js +22 -0
- package/dist/chain-of-thought.js.map +1 -0
- package/dist/chat-greeting.js +8 -0
- package/dist/chat-greeting.js.map +1 -0
- package/dist/chat-shell.js +9 -0
- package/dist/chat-shell.js.map +1 -0
- package/dist/checkpoint.js +13 -0
- package/dist/checkpoint.js.map +1 -0
- package/dist/chunk-2XOIG5TG.js +72 -0
- package/dist/chunk-2XOIG5TG.js.map +1 -0
- package/dist/chunk-34CQ2VHM.js +172 -0
- package/dist/chunk-34CQ2VHM.js.map +1 -0
- package/dist/chunk-3TDHPXZZ.js +60 -0
- package/dist/chunk-3TDHPXZZ.js.map +1 -0
- package/dist/chunk-3XNPMDZY.js +478 -0
- package/dist/chunk-3XNPMDZY.js.map +1 -0
- package/dist/chunk-3YPT3V7L.js +56 -0
- package/dist/chunk-3YPT3V7L.js.map +1 -0
- package/dist/chunk-475UEU2Q.js +87 -0
- package/dist/chunk-475UEU2Q.js.map +1 -0
- package/dist/chunk-4CBTII2I.js +95 -0
- package/dist/chunk-4CBTII2I.js.map +1 -0
- package/dist/chunk-4XRNIZMG.js +22 -0
- package/dist/chunk-4XRNIZMG.js.map +1 -0
- package/dist/chunk-5NNWUKK5.js +2 -0
- package/dist/chunk-5NNWUKK5.js.map +1 -0
- package/dist/chunk-5UOMU27Z.js +164 -0
- package/dist/chunk-5UOMU27Z.js.map +1 -0
- package/dist/chunk-5VGYZ62I.js +264 -0
- package/dist/chunk-5VGYZ62I.js.map +1 -0
- package/dist/chunk-6IQPH2OS.js +21 -0
- package/dist/chunk-6IQPH2OS.js.map +1 -0
- package/dist/chunk-6PB2MXCI.js +482 -0
- package/dist/chunk-6PB2MXCI.js.map +1 -0
- package/dist/chunk-7FD5CZEU.js +215 -0
- package/dist/chunk-7FD5CZEU.js.map +1 -0
- package/dist/chunk-7RL6KZDF.js +266 -0
- package/dist/chunk-7RL6KZDF.js.map +1 -0
- package/dist/chunk-ADOYAGGK.js +482 -0
- package/dist/chunk-ADOYAGGK.js.map +1 -0
- package/dist/chunk-AXMNJMMH.js +984 -0
- package/dist/chunk-AXMNJMMH.js.map +1 -0
- package/dist/chunk-BA3AEMOU.js +21 -0
- package/dist/chunk-BA3AEMOU.js.map +1 -0
- package/dist/chunk-BBHFOYML.js +336 -0
- package/dist/chunk-BBHFOYML.js.map +1 -0
- package/dist/chunk-BHUVISIY.js +221 -0
- package/dist/chunk-BHUVISIY.js.map +1 -0
- package/dist/chunk-BM2LWERA.js +100 -0
- package/dist/chunk-BM2LWERA.js.map +1 -0
- package/dist/chunk-CMOYXNPJ.js +211 -0
- package/dist/chunk-CMOYXNPJ.js.map +1 -0
- package/dist/chunk-CZP3RHC4.js +74 -0
- package/dist/chunk-CZP3RHC4.js.map +1 -0
- package/dist/chunk-DDG5JHQT.js +112 -0
- package/dist/chunk-DDG5JHQT.js.map +1 -0
- package/dist/chunk-DJODXO7I.js +38 -0
- package/dist/chunk-DJODXO7I.js.map +1 -0
- package/dist/chunk-DWJDJLPU.js +192 -0
- package/dist/chunk-DWJDJLPU.js.map +1 -0
- package/dist/chunk-DZW6LM22.js +45 -0
- package/dist/chunk-DZW6LM22.js.map +1 -0
- package/dist/chunk-ELGCZLZF.js +305 -0
- package/dist/chunk-ELGCZLZF.js.map +1 -0
- package/dist/chunk-EV3U6ZZB.js +209 -0
- package/dist/chunk-EV3U6ZZB.js.map +1 -0
- package/dist/chunk-EY7LVUKQ.js +87 -0
- package/dist/chunk-EY7LVUKQ.js.map +1 -0
- package/dist/chunk-F4C74F7M.js +230 -0
- package/dist/chunk-F4C74F7M.js.map +1 -0
- package/dist/chunk-FERYDFZA.js +108 -0
- package/dist/chunk-FERYDFZA.js.map +1 -0
- package/dist/chunk-FUIYG2DS.js +166 -0
- package/dist/chunk-FUIYG2DS.js.map +1 -0
- package/dist/chunk-GKYZT3LA.js +136 -0
- package/dist/chunk-GKYZT3LA.js.map +1 -0
- package/dist/chunk-H42LRUQ4.js +102 -0
- package/dist/chunk-H42LRUQ4.js.map +1 -0
- package/dist/chunk-HVXM2G5F.js +222 -0
- package/dist/chunk-HVXM2G5F.js.map +1 -0
- package/dist/chunk-I4RDAGVY.js +53 -0
- package/dist/chunk-I4RDAGVY.js.map +1 -0
- package/dist/chunk-IQJG4IDX.js +208 -0
- package/dist/chunk-IQJG4IDX.js.map +1 -0
- package/dist/chunk-IX2PXG4Y.js +144 -0
- package/dist/chunk-IX2PXG4Y.js.map +1 -0
- package/dist/chunk-IXICSOOP.js +287 -0
- package/dist/chunk-IXICSOOP.js.map +1 -0
- package/dist/chunk-JY5HSN4D.js +84 -0
- package/dist/chunk-JY5HSN4D.js.map +1 -0
- package/dist/chunk-LMNSBOBG.js +19 -0
- package/dist/chunk-LMNSBOBG.js.map +1 -0
- package/dist/chunk-MBI7D6IL.js +42 -0
- package/dist/chunk-MBI7D6IL.js.map +1 -0
- package/dist/chunk-MLFMYCPH.js +192 -0
- package/dist/chunk-MLFMYCPH.js.map +1 -0
- package/dist/chunk-MOK66734.js +33 -0
- package/dist/chunk-MOK66734.js.map +1 -0
- package/dist/chunk-MXTAWPYM.js +196 -0
- package/dist/chunk-MXTAWPYM.js.map +1 -0
- package/dist/chunk-NCL2UZKI.js +96 -0
- package/dist/chunk-NCL2UZKI.js.map +1 -0
- package/dist/chunk-NET4YLRY.js +107 -0
- package/dist/chunk-NET4YLRY.js.map +1 -0
- package/dist/chunk-NIG67CFM.js +329 -0
- package/dist/chunk-NIG67CFM.js.map +1 -0
- package/dist/chunk-NMVNNO3L.js +10 -0
- package/dist/chunk-NMVNNO3L.js.map +1 -0
- package/dist/chunk-NP7VID4K.js +2 -0
- package/dist/chunk-NP7VID4K.js.map +1 -0
- package/dist/chunk-NPUCMAG2.js +382 -0
- package/dist/chunk-NPUCMAG2.js.map +1 -0
- package/dist/chunk-NSCLRHES.js +102 -0
- package/dist/chunk-NSCLRHES.js.map +1 -0
- package/dist/chunk-OEUXHVFW.js +86 -0
- package/dist/chunk-OEUXHVFW.js.map +1 -0
- package/dist/chunk-ONBHFIMZ.js +137 -0
- package/dist/chunk-ONBHFIMZ.js.map +1 -0
- package/dist/chunk-OPH2HHL2.js +228 -0
- package/dist/chunk-OPH2HHL2.js.map +1 -0
- package/dist/chunk-OQERHIQP.js +287 -0
- package/dist/chunk-OQERHIQP.js.map +1 -0
- package/dist/chunk-P6YJAZFG.js +103 -0
- package/dist/chunk-P6YJAZFG.js.map +1 -0
- package/dist/chunk-PG7ERDOD.js +65 -0
- package/dist/chunk-PG7ERDOD.js.map +1 -0
- package/dist/chunk-PIB5JBMA.js +90 -0
- package/dist/chunk-PIB5JBMA.js.map +1 -0
- package/dist/chunk-PQ5BPIWQ.js +22 -0
- package/dist/chunk-PQ5BPIWQ.js.map +1 -0
- package/dist/chunk-PQBF6ZIQ.js +55 -0
- package/dist/chunk-PQBF6ZIQ.js.map +1 -0
- package/dist/chunk-PTWCTSZL.js +173 -0
- package/dist/chunk-PTWCTSZL.js.map +1 -0
- package/dist/chunk-QJUNPVOS.js +101 -0
- package/dist/chunk-QJUNPVOS.js.map +1 -0
- package/dist/chunk-QUDZOTRV.js +43 -0
- package/dist/chunk-QUDZOTRV.js.map +1 -0
- package/dist/chunk-RB7G65JC.js +213 -0
- package/dist/chunk-RB7G65JC.js.map +1 -0
- package/dist/chunk-RPHAOR7P.js +45 -0
- package/dist/chunk-RPHAOR7P.js.map +1 -0
- package/dist/chunk-RTCSH2AN.js +331 -0
- package/dist/chunk-RTCSH2AN.js.map +1 -0
- package/dist/chunk-S3ISPW3A.js +28 -0
- package/dist/chunk-S3ISPW3A.js.map +1 -0
- package/dist/chunk-SEVN656N.js +36 -0
- package/dist/chunk-SEVN656N.js.map +1 -0
- package/dist/chunk-SKVG672B.js +64 -0
- package/dist/chunk-SKVG672B.js.map +1 -0
- package/dist/chunk-SPQIU7GA.js +236 -0
- package/dist/chunk-SPQIU7GA.js.map +1 -0
- package/dist/chunk-SSGJ6MEM.js +1076 -0
- package/dist/chunk-SSGJ6MEM.js.map +1 -0
- package/dist/chunk-SSKZ2AED.js +118 -0
- package/dist/chunk-SSKZ2AED.js.map +1 -0
- package/dist/chunk-T56JFGXI.js +561 -0
- package/dist/chunk-T56JFGXI.js.map +1 -0
- package/dist/chunk-TCB6O74A.js +137 -0
- package/dist/chunk-TCB6O74A.js.map +1 -0
- package/dist/chunk-TTEYPO6X.js +57 -0
- package/dist/chunk-TTEYPO6X.js.map +1 -0
- package/dist/chunk-TWPHAJNM.js +462 -0
- package/dist/chunk-TWPHAJNM.js.map +1 -0
- package/dist/chunk-TXJJLP4O.js +336 -0
- package/dist/chunk-TXJJLP4O.js.map +1 -0
- package/dist/chunk-TZ6TB7VM.js +245 -0
- package/dist/chunk-TZ6TB7VM.js.map +1 -0
- package/dist/chunk-UBLKJJ7K.js +278 -0
- package/dist/chunk-UBLKJJ7K.js.map +1 -0
- package/dist/chunk-UCJ6IUFS.js +52 -0
- package/dist/chunk-UCJ6IUFS.js.map +1 -0
- package/dist/chunk-UCKFJ3QF.js +197 -0
- package/dist/chunk-UCKFJ3QF.js.map +1 -0
- package/dist/chunk-UPXSLILN.js +20 -0
- package/dist/chunk-UPXSLILN.js.map +1 -0
- package/dist/chunk-V4DORGWJ.js +92 -0
- package/dist/chunk-V4DORGWJ.js.map +1 -0
- package/dist/chunk-V6E5DYZD.js +80 -0
- package/dist/chunk-V6E5DYZD.js.map +1 -0
- package/dist/chunk-VLLYKF3P.js +96 -0
- package/dist/chunk-VLLYKF3P.js.map +1 -0
- package/dist/chunk-VLNBTOH6.js +43 -0
- package/dist/chunk-VLNBTOH6.js.map +1 -0
- package/dist/chunk-WMDSMSPL.js +303 -0
- package/dist/chunk-WMDSMSPL.js.map +1 -0
- package/dist/chunk-WU5BLGTF.js +149 -0
- package/dist/chunk-WU5BLGTF.js.map +1 -0
- package/dist/chunk-WYTZVSGK.js +245 -0
- package/dist/chunk-WYTZVSGK.js.map +1 -0
- package/dist/chunk-X47QMM6K.js +187 -0
- package/dist/chunk-X47QMM6K.js.map +1 -0
- package/dist/chunk-XNBKO32Y.js +174 -0
- package/dist/chunk-XNBKO32Y.js.map +1 -0
- package/dist/chunk-YHUSW7D2.js +40 -0
- package/dist/chunk-YHUSW7D2.js.map +1 -0
- package/dist/chunk-YSKD7UXX.js +126 -0
- package/dist/chunk-YSKD7UXX.js.map +1 -0
- package/dist/chunk-YYRDGWOU.js +72 -0
- package/dist/chunk-YYRDGWOU.js.map +1 -0
- package/dist/chunk-ZIGZMWM7.js +19 -0
- package/dist/chunk-ZIGZMWM7.js.map +1 -0
- package/dist/chunk-ZKRTUKNP.js +40 -0
- package/dist/chunk-ZKRTUKNP.js.map +1 -0
- package/dist/code-block.js +38 -0
- package/dist/code-block.js.map +1 -0
- package/dist/commit.js +51 -0
- package/dist/commit.js.map +1 -0
- package/dist/composer.js +15 -0
- package/dist/composer.js.map +1 -0
- package/dist/confirmation.js +53 -0
- package/dist/confirmation.js.map +1 -0
- package/dist/connection.js +8 -0
- package/dist/connection.js.map +1 -0
- package/dist/context-panel.js +25 -0
- package/dist/context-panel.js.map +1 -0
- package/dist/controls.js +10 -0
- package/dist/controls.js.map +1 -0
- package/dist/conversation.js +19 -0
- package/dist/conversation.js.map +1 -0
- package/dist/diff-view.js +15 -0
- package/dist/diff-view.js.map +1 -0
- package/dist/edge.js +10 -0
- package/dist/edge.js.map +1 -0
- package/dist/environment-variables.js +29 -0
- package/dist/environment-variables.js.map +1 -0
- package/dist/file-tree.js +23 -0
- package/dist/file-tree.js.map +1 -0
- package/dist/gallery.js +9 -0
- package/dist/gallery.js.map +1 -0
- package/dist/grouped-parts.js +21 -0
- package/dist/grouped-parts.js.map +1 -0
- package/dist/image.js +9 -0
- package/dist/image.js.map +1 -0
- package/dist/index.d.ts +366 -98
- package/dist/index.js +672 -13577
- package/dist/index.js.map +1 -1
- package/dist/inline-citation.js +39 -0
- package/dist/inline-citation.js.map +1 -0
- package/dist/jsx-preview.js +17 -0
- package/dist/jsx-preview.js.map +1 -0
- package/dist/markdown-view.js +17 -0
- package/dist/markdown-view.js.map +1 -0
- package/dist/message-compare.js +12 -0
- package/dist/message-compare.js.map +1 -0
- package/dist/message-edit.js +19 -0
- package/dist/message-edit.js.map +1 -0
- package/dist/message-feedback.js +11 -0
- package/dist/message-feedback.js.map +1 -0
- package/dist/message-form-spec.js +38 -0
- package/dist/message-form-spec.js.map +1 -0
- package/dist/message-form.js +26 -0
- package/dist/message-form.js.map +1 -0
- package/dist/message-table-spec.js +26 -0
- package/dist/message-table-spec.js.map +1 -0
- package/dist/message-table.js +12 -0
- package/dist/message-table.js.map +1 -0
- package/dist/message.js +51 -0
- package/dist/message.js.map +1 -0
- package/dist/mic-selector.js +27 -0
- package/dist/mic-selector.js.map +1 -0
- package/dist/model-provider-logo.js +13 -0
- package/dist/model-provider-logo.js.map +1 -0
- package/dist/motion-config.js +9 -0
- package/dist/motion-config.js.map +1 -0
- package/dist/node.js +22 -0
- package/dist/node.js.map +1 -0
- package/dist/open-in-chat.js +31 -0
- package/dist/open-in-chat.js.map +1 -0
- package/dist/package-info.js +25 -0
- package/dist/package-info.js.map +1 -0
- package/dist/panel.js +10 -0
- package/dist/panel.js.map +1 -0
- package/dist/part-groups.js +18 -0
- package/dist/part-groups.js.map +1 -0
- package/dist/permission-mode-select.js +9 -0
- package/dist/permission-mode-select.js.map +1 -0
- package/dist/persona-sources.js +8 -0
- package/dist/persona-sources.js.map +1 -0
- package/dist/persona.js +14 -0
- package/dist/persona.js.map +1 -0
- package/dist/plan.js +32 -0
- package/dist/plan.js.map +1 -0
- package/dist/prompt-input-effort.js +9 -0
- package/dist/prompt-input-effort.js.map +1 -0
- package/dist/prompt-input-mode.js +10 -0
- package/dist/prompt-input-mode.js.map +1 -0
- package/dist/prompt-input-slash.js +12 -0
- package/dist/prompt-input-slash.js.map +1 -0
- package/dist/prompt-input.js +89 -0
- package/dist/prompt-input.js.map +1 -0
- package/dist/queue.js +37 -0
- package/dist/queue.js.map +1 -0
- package/dist/reasoning.js +23 -0
- package/dist/reasoning.js.map +1 -0
- package/dist/sandbox.js +28 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/schema-display.js +33 -0
- package/dist/schema-display.js.map +1 -0
- package/dist/selection-toolbar.js +11 -0
- package/dist/selection-toolbar.js.map +1 -0
- package/dist/session-header.js +9 -0
- package/dist/session-header.js.map +1 -0
- package/dist/session-status-bar.js +9 -0
- package/dist/session-status-bar.js.map +1 -0
- package/dist/shimmer.js +9 -0
- package/dist/shimmer.js.map +1 -0
- package/dist/snippet.js +17 -0
- package/dist/snippet.js.map +1 -0
- package/dist/sources.js +17 -0
- package/dist/sources.js.map +1 -0
- package/dist/speech-input.js +9 -0
- package/dist/speech-input.js.map +1 -0
- package/dist/stack-trace.js +27 -0
- package/dist/stack-trace.js.map +1 -0
- package/dist/suggestion.js +16 -0
- package/dist/suggestion.js.map +1 -0
- package/dist/task.js +18 -0
- package/dist/task.js.map +1 -0
- package/dist/test-results.js +43 -0
- package/dist/test-results.js.map +1 -0
- package/dist/token-usage.js +27 -0
- package/dist/token-usage.js.map +1 -0
- package/dist/tool-result-card.js +9 -0
- package/dist/tool-result-card.js.map +1 -0
- package/dist/tool.js +27 -0
- package/dist/tool.js.map +1 -0
- package/dist/toolbar.js +10 -0
- package/dist/toolbar.js.map +1 -0
- package/dist/transcription.js +11 -0
- package/dist/transcription.js.map +1 -0
- package/dist/turn-status.js +9 -0
- package/dist/turn-status.js.map +1 -0
- package/dist/use-audio-level.js +9 -0
- package/dist/use-audio-level.js.map +1 -0
- package/dist/voice-selector.js +47 -0
- package/dist/voice-selector.js.map +1 -0
- package/dist/web-preview.js +19 -0
- package/dist/web-preview.js.map +1 -0
- package/package.json +11 -9
- package/schemas/a2ui-surface.v1.schema.json +5425 -0
- package/src/_chat-shell-rail.tsx +3 -1
- package/src/_flow-boundary.tsx +2 -2
- package/src/_persona-rive.tsx +27 -41
- package/src/a2ui/a2ui-surface.stories.tsx +662 -0
- package/src/a2ui/a2ui-surface.test.tsx +201 -0
- package/src/a2ui/a2ui-surface.tsx +354 -0
- package/src/a2ui/catalog.source.json +772 -0
- package/src/a2ui/core/a2ui-core.test.ts +178 -0
- package/src/a2ui/core/catalog.generated.ts +1006 -0
- package/src/a2ui/core/complete-json.ts +81 -0
- package/src/a2ui/core/index.ts +10 -0
- package/src/a2ui/core/schema.ts +111 -0
- package/src/a2ui/core/spec.ts +148 -0
- package/src/a2ui/core/validate.ts +266 -0
- package/src/a2ui/index.ts +7 -0
- package/src/a2ui/ui-catalog.tsx +225 -0
- package/src/a2ui/wave1-chart-spec.test.ts +87 -0
- package/src/a2ui/wave2-chart-spec.test.ts +108 -0
- package/src/agentic-workspace.stories.tsx +6 -45
- package/src/chat-shell.test.tsx +40 -10
- package/src/chat-shell.tsx +90 -44
- package/src/chat.stories.tsx +47 -42
- package/src/composer.stories.tsx +4 -5
- package/src/composer.test.tsx +45 -0
- package/src/composer.tsx +104 -21
- package/src/context-panel.stories.tsx +1 -1
- package/src/context-panel.tsx +1 -1
- package/src/conversation.stories.tsx +1 -0
- package/src/conversation.test.tsx +16 -0
- package/src/conversation.tsx +16 -2
- package/src/diff-view.stories.tsx +79 -0
- package/src/diff-view.tsx +3 -2
- package/src/file-tree.tsx +24 -17
- package/src/gallery.stories.tsx +42 -0
- package/src/gallery.tsx +2 -2
- package/src/grouped-parts.stories.tsx +23 -0
- package/src/grouped-parts.tsx +1 -1
- package/src/index.ts +1 -0
- package/src/message-compare.stories.tsx +40 -0
- package/src/message-compare.tsx +2 -1
- package/src/message-form.tsx +1 -1
- package/src/message-table.stories.tsx +24 -0
- package/src/message-table.tsx +1 -1
- package/src/package-info.tsx +12 -9
- package/src/reasoning.stories.tsx +27 -0
- package/src/reasoning.tsx +1 -1
- package/src/session-header.stories.tsx +35 -1
- package/src/session-header.tsx +1 -1
- package/src/sources.stories.tsx +30 -0
- package/src/sources.tsx +4 -1
- package/src/templates-ai-assistant.stories.tsx +151 -87
- package/src/tool.stories.tsx +39 -0
- package/src/tool.tsx +3 -3
- package/src/toolbar.tsx +0 -10
- package/dist/_flow-boundary-SHNWLQG5.js.map +0 -1
- package/dist/_persona-rive-JEG44YHX.js.map +0 -1
- /package/dist/{_audio-player-media-chrome-T3XVXWRZ.js.map → _audio-player-media-chrome.js.map} +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/permission-mode-select.tsx
|
|
4
|
+
import { forwardRef, useId } from "react";
|
|
5
|
+
import { Badge, Kbd, Label, RadioGroup, RadioGroupItem, useLocale } from "@elabs-ai/components-ui";
|
|
6
|
+
import { cn } from "@elabs-ai/components-ui/lib/cn";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
var PermissionModeSelect = forwardRef(
|
|
9
|
+
function PermissionModeSelect2({ modes, value, defaultValue, onValueChange, currentId, className, dir, ...props }, ref) {
|
|
10
|
+
const { t } = useLocale();
|
|
11
|
+
const baseId = useId();
|
|
12
|
+
return /* @__PURE__ */ jsx(
|
|
13
|
+
RadioGroup,
|
|
14
|
+
{
|
|
15
|
+
ref,
|
|
16
|
+
dir,
|
|
17
|
+
defaultValue: defaultValue ?? currentId,
|
|
18
|
+
value,
|
|
19
|
+
onValueChange,
|
|
20
|
+
"data-slot": "permission-mode-select",
|
|
21
|
+
className: cn("grid gap-3", className),
|
|
22
|
+
...props,
|
|
23
|
+
children: modes.map((mode) => {
|
|
24
|
+
const isCurrent = mode.id === currentId;
|
|
25
|
+
const itemId = `${baseId}-${mode.id}`;
|
|
26
|
+
const consequenceId = `${itemId}-consequence`;
|
|
27
|
+
return /* @__PURE__ */ jsxs(
|
|
28
|
+
"div",
|
|
29
|
+
{
|
|
30
|
+
"data-slot": "permission-mode-select-option",
|
|
31
|
+
className: cn(
|
|
32
|
+
"flex items-start gap-3 rounded-lg border border-border bg-card p-4",
|
|
33
|
+
"has-[[data-state=checked]]:border-primary"
|
|
34
|
+
),
|
|
35
|
+
children: [
|
|
36
|
+
/* @__PURE__ */ jsx(
|
|
37
|
+
RadioGroupItem,
|
|
38
|
+
{
|
|
39
|
+
"aria-describedby": consequenceId,
|
|
40
|
+
className: "mt-1",
|
|
41
|
+
"data-slot": "permission-mode-select-input",
|
|
42
|
+
id: itemId,
|
|
43
|
+
value: mode.id
|
|
44
|
+
}
|
|
45
|
+
),
|
|
46
|
+
/* @__PURE__ */ jsxs("div", { className: "grid flex-1 gap-1", children: [
|
|
47
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
48
|
+
/* @__PURE__ */ jsxs(
|
|
49
|
+
Label,
|
|
50
|
+
{
|
|
51
|
+
className: "text-body flex items-center gap-2 font-medium",
|
|
52
|
+
"data-slot": "permission-mode-select-label",
|
|
53
|
+
htmlFor: itemId,
|
|
54
|
+
children: [
|
|
55
|
+
mode.label,
|
|
56
|
+
isCurrent ? /* @__PURE__ */ jsx(Badge, { "data-slot": "permission-mode-select-current", variant: "secondary", children: t("ai.permissionModeSelect.current") }) : null
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
),
|
|
60
|
+
mode.keyHint ? /* @__PURE__ */ jsx(Kbd, { "data-slot": "permission-mode-select-key-hint", children: mode.keyHint }) : null
|
|
61
|
+
] }),
|
|
62
|
+
/* @__PURE__ */ jsx(
|
|
63
|
+
"p",
|
|
64
|
+
{
|
|
65
|
+
className: "text-meta text-muted-foreground",
|
|
66
|
+
"data-slot": "permission-mode-select-consequence",
|
|
67
|
+
id: consequenceId,
|
|
68
|
+
children: mode.consequence
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
] })
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
mode.id
|
|
75
|
+
);
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
PermissionModeSelect.displayName = "PermissionModeSelect";
|
|
82
|
+
|
|
83
|
+
export {
|
|
84
|
+
PermissionModeSelect
|
|
85
|
+
};
|
|
86
|
+
//# sourceMappingURL=chunk-OEUXHVFW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/permission-mode-select.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRef, useId, type ComponentProps } from \"react\";\nimport { Badge, Kbd, Label, RadioGroup, RadioGroupItem, useLocale } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\n/**\n * PermissionModeSelect — the standing-permission-policy chooser (#104).\n *\n * Distinct from `Confirmation`/`ApprovalCard` (#103): those render ONE\n * per-call decision (\"may I run this command?\"). This renders the POLICY\n * that governs how many of those per-call decisions ever occur (\"how much\n * may you do without asking?\"). The two stay separate components with a\n * documented relationship rather than one growing a boolean \"policy mode\"\n * prop — see `.claude/rules/component-api.md` (\"Avoid boolean-prop\n * proliferation\").\n *\n * The mode vocabulary is entirely app-defined: no agent's mode names are\n * hardcoded in this file or in `PermissionMode`/`PermissionModeSelectProps`.\n * `consequence` is a REQUIRED field — a mode whose effect is unstated is\n * exactly the failure this component exists to prevent (three unlabelled\n * radio buttons).\n *\n * Built on `@elabs-ai/components-ui`'s `RadioGroup` (Radix) for roving\n * focus / arrow-key navigation / Space-Enter selection / announcement — this\n * component does not hand-roll any of that. The in-force mode is marked in\n * TEXT: a \"Current\" badge lives inside the `<Label>` associated with that\n * mode's radio input, so it is part of the control's accessible name and\n * recoverable in greyscale — never colour alone (WCAG 1.4.1).\n */\nexport interface PermissionMode {\n id: string;\n label: string;\n /** What this mode actually permits — required, not optional. */\n consequence: string;\n keyHint?: string;\n}\n\nexport interface PermissionModeSelectProps extends ComponentProps<\"div\"> {\n modes: PermissionMode[];\n value?: string;\n defaultValue?: string;\n onValueChange?: (id: string) => void;\n /** The mode currently in force, marked distinctly from the highlighted one. */\n currentId?: string;\n}\n\nexport const PermissionModeSelect = forwardRef<HTMLDivElement, PermissionModeSelectProps>(\n function PermissionModeSelect(\n { modes, value, defaultValue, onValueChange, currentId, className, dir, ...props },\n ref,\n ) {\n const { t } = useLocale();\n const baseId = useId();\n\n return (\n <RadioGroup\n ref={ref}\n // `dir` on a plain <div> is a free-form HTML string; Radix's RadioGroup\n // narrows it to \"ltr\" | \"rtl\". Cast at this single call site rather\n // than widening (and so weakening) the public prop type.\n dir={dir as \"ltr\" | \"rtl\" | undefined}\n // The RadioGroup's own highlighted value is independent of `currentId`\n // (the mode already in force) — they answer different questions, so\n // neither prop ever derives the other except as an uncontrolled\n // starting point: with nothing else specified, pre-select the mode\n // that is currently in force.\n defaultValue={defaultValue ?? currentId}\n value={value}\n onValueChange={onValueChange}\n data-slot=\"permission-mode-select\"\n className={cn(\"grid gap-3\", className)}\n {...props}\n >\n {modes.map((mode) => {\n const isCurrent = mode.id === currentId;\n const itemId = `${baseId}-${mode.id}`;\n // The consequence sentence is the whole point of this component (see\n // the `consequence` prop doc above), so it has to reach assistive\n // tech as a DESCRIPTION of the radio — not merely sit next to it in\n // the DOM. Without this link a screen-reader user arrowing through\n // the modes hears \"Unrestricted\" and never hears what unrestricted\n // costs them. Same wiring as `ApprovalCardOptions` in\n // `confirmation.tsx`, deliberately: one convention, not two.\n const consequenceId = `${itemId}-consequence`;\n\n return (\n <div\n key={mode.id}\n data-slot=\"permission-mode-select-option\"\n className={cn(\n \"flex items-start gap-3 rounded-lg border border-border bg-card p-4\",\n \"has-[[data-state=checked]]:border-primary\",\n )}\n >\n <RadioGroupItem\n aria-describedby={consequenceId}\n className=\"mt-1\"\n data-slot=\"permission-mode-select-input\"\n id={itemId}\n value={mode.id}\n />\n <div className=\"grid flex-1 gap-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <Label\n className=\"text-body flex items-center gap-2 font-medium\"\n data-slot=\"permission-mode-select-label\"\n htmlFor={itemId}\n >\n {mode.label}\n {isCurrent ? (\n <Badge data-slot=\"permission-mode-select-current\" variant=\"secondary\">\n {t(\"ai.permissionModeSelect.current\")}\n </Badge>\n ) : null}\n </Label>\n {mode.keyHint ? (\n <Kbd data-slot=\"permission-mode-select-key-hint\">{mode.keyHint}</Kbd>\n ) : null}\n </div>\n <p\n className=\"text-meta text-muted-foreground\"\n data-slot=\"permission-mode-select-consequence\"\n id={consequenceId}\n >\n {mode.consequence}\n </p>\n </div>\n </div>\n );\n })}\n </RadioGroup>\n );\n },\n);\n\nPermissionModeSelect.displayName = \"PermissionModeSelect\";\n"],"mappings":";;;AAEA,SAAS,YAAY,aAAkC;AACvD,SAAS,OAAO,KAAK,OAAO,YAAY,gBAAgB,iBAAiB;AACzE,SAAS,UAAU;AA2FL,cASI,YATJ;AAhDP,IAAM,uBAAuB;AAAA,EAClC,SAASA,sBACP,EAAE,OAAO,OAAO,cAAc,eAAe,WAAW,WAAW,KAAK,GAAG,MAAM,GACjF,KACA;AACA,UAAM,EAAE,EAAE,IAAI,UAAU;AACxB,UAAM,SAAS,MAAM;AAErB,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QAIA;AAAA,QAMA,cAAc,gBAAgB;AAAA,QAC9B;AAAA,QACA;AAAA,QACA,aAAU;AAAA,QACV,WAAW,GAAG,cAAc,SAAS;AAAA,QACpC,GAAG;AAAA,QAEH,gBAAM,IAAI,CAAC,SAAS;AACnB,gBAAM,YAAY,KAAK,OAAO;AAC9B,gBAAM,SAAS,GAAG,MAAM,IAAI,KAAK,EAAE;AAQnC,gBAAM,gBAAgB,GAAG,MAAM;AAE/B,iBACE;AAAA,YAAC;AAAA;AAAA,cAEC,aAAU;AAAA,cACV,WAAW;AAAA,gBACT;AAAA,gBACA;AAAA,cACF;AAAA,cAEA;AAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,oBAAkB;AAAA,oBAClB,WAAU;AAAA,oBACV,aAAU;AAAA,oBACV,IAAI;AAAA,oBACJ,OAAO,KAAK;AAAA;AAAA,gBACd;AAAA,gBACA,qBAAC,SAAI,WAAU,qBACb;AAAA,uCAAC,SAAI,WAAU,2CACb;AAAA;AAAA,sBAAC;AAAA;AAAA,wBACC,WAAU;AAAA,wBACV,aAAU;AAAA,wBACV,SAAS;AAAA,wBAER;AAAA,+BAAK;AAAA,0BACL,YACC,oBAAC,SAAM,aAAU,kCAAiC,SAAQ,aACvD,YAAE,iCAAiC,GACtC,IACE;AAAA;AAAA;AAAA,oBACN;AAAA,oBACC,KAAK,UACJ,oBAAC,OAAI,aAAU,mCAAmC,eAAK,SAAQ,IAC7D;AAAA,qBACN;AAAA,kBACA;AAAA,oBAAC;AAAA;AAAA,sBACC,WAAU;AAAA,sBACV,aAAU;AAAA,sBACV,IAAI;AAAA,sBAEH,eAAK;AAAA;AAAA,kBACR;AAAA,mBACF;AAAA;AAAA;AAAA,YAvCK,KAAK;AAAA,UAwCZ;AAAA,QAEJ,CAAC;AAAA;AAAA,IACH;AAAA,EAEJ;AACF;AAEA,qBAAqB,cAAc;","names":["PermissionModeSelect"]}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/package-info.tsx
|
|
4
|
+
import { Badge, useLocale } from "@elabs-ai/components-ui";
|
|
5
|
+
import { cn } from "@elabs-ai/components-ui/lib/cn";
|
|
6
|
+
import { ArrowRightIcon, MinusIcon, PackageIcon, PlusIcon } from "lucide-react";
|
|
7
|
+
import { createContext, useContext, useMemo } from "react";
|
|
8
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
var PackageInfoContext = createContext({
|
|
10
|
+
name: ""
|
|
11
|
+
});
|
|
12
|
+
var PackageInfoHeader = ({ className, children, ...props }) => /* @__PURE__ */ jsx("div", { className: cn("flex items-center justify-between gap-2", className), ...props, children });
|
|
13
|
+
var PackageInfoName = ({ className, children, ...props }) => {
|
|
14
|
+
const { name } = useContext(PackageInfoContext);
|
|
15
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-2", className), ...props, children: [
|
|
16
|
+
/* @__PURE__ */ jsx(PackageIcon, { className: "size-4 text-muted-foreground" }),
|
|
17
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium font-mono text-body", children: children ?? name })
|
|
18
|
+
] });
|
|
19
|
+
};
|
|
20
|
+
var changeTypeStyles = {
|
|
21
|
+
added: "bg-info/10 text-info-text",
|
|
22
|
+
major: "bg-destructive/10 text-destructive-text",
|
|
23
|
+
minor: "bg-warning/10 text-warning-text",
|
|
24
|
+
patch: "bg-success/10 text-success-text",
|
|
25
|
+
removed: "bg-muted text-muted-foreground"
|
|
26
|
+
};
|
|
27
|
+
var changeTypeIcons = {
|
|
28
|
+
added: /* @__PURE__ */ jsx(PlusIcon, { className: "size-3" }),
|
|
29
|
+
major: /* @__PURE__ */ jsx(ArrowRightIcon, { className: "size-3" }),
|
|
30
|
+
minor: /* @__PURE__ */ jsx(ArrowRightIcon, { className: "size-3" }),
|
|
31
|
+
patch: /* @__PURE__ */ jsx(ArrowRightIcon, { className: "size-3" }),
|
|
32
|
+
removed: /* @__PURE__ */ jsx(MinusIcon, { className: "size-3" })
|
|
33
|
+
};
|
|
34
|
+
var PackageInfoChangeType = ({
|
|
35
|
+
className,
|
|
36
|
+
children,
|
|
37
|
+
...props
|
|
38
|
+
}) => {
|
|
39
|
+
const { changeType } = useContext(PackageInfoContext);
|
|
40
|
+
if (!changeType) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return /* @__PURE__ */ jsxs(
|
|
44
|
+
Badge,
|
|
45
|
+
{
|
|
46
|
+
className: cn("gap-1 text-meta capitalize", changeTypeStyles[changeType], className),
|
|
47
|
+
variant: "secondary",
|
|
48
|
+
...props,
|
|
49
|
+
children: [
|
|
50
|
+
changeTypeIcons[changeType],
|
|
51
|
+
children ?? changeType
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
var PackageInfoVersion = ({ className, children, ...props }) => {
|
|
57
|
+
const { currentVersion, newVersion } = useContext(PackageInfoContext);
|
|
58
|
+
if (!(currentVersion || newVersion)) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
return /* @__PURE__ */ jsx(
|
|
62
|
+
"div",
|
|
63
|
+
{
|
|
64
|
+
className: cn(
|
|
65
|
+
"mt-2 flex items-center gap-2 font-mono text-muted-foreground text-body",
|
|
66
|
+
className
|
|
67
|
+
),
|
|
68
|
+
...props,
|
|
69
|
+
children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
70
|
+
currentVersion && /* @__PURE__ */ jsx("span", { children: currentVersion }),
|
|
71
|
+
currentVersion && newVersion && /* @__PURE__ */ jsx(ArrowRightIcon, { className: "size-3" }),
|
|
72
|
+
newVersion && /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: newVersion })
|
|
73
|
+
] })
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
var PackageInfo = ({
|
|
78
|
+
name,
|
|
79
|
+
currentVersion,
|
|
80
|
+
newVersion,
|
|
81
|
+
changeType,
|
|
82
|
+
className,
|
|
83
|
+
children,
|
|
84
|
+
...props
|
|
85
|
+
}) => {
|
|
86
|
+
const contextValue = useMemo(
|
|
87
|
+
() => ({ changeType, currentVersion, name, newVersion }),
|
|
88
|
+
[changeType, currentVersion, name, newVersion]
|
|
89
|
+
);
|
|
90
|
+
return /* @__PURE__ */ jsx(PackageInfoContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx("div", { className: cn("rounded-lg border bg-background p-4", className), ...props, children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
91
|
+
/* @__PURE__ */ jsxs(PackageInfoHeader, { children: [
|
|
92
|
+
/* @__PURE__ */ jsx(PackageInfoName, {}),
|
|
93
|
+
changeType && /* @__PURE__ */ jsx(PackageInfoChangeType, {})
|
|
94
|
+
] }),
|
|
95
|
+
(currentVersion || newVersion) && /* @__PURE__ */ jsx(PackageInfoVersion, {})
|
|
96
|
+
] }) }) });
|
|
97
|
+
};
|
|
98
|
+
var PackageInfoDescription = ({
|
|
99
|
+
className,
|
|
100
|
+
children,
|
|
101
|
+
...props
|
|
102
|
+
}) => /* @__PURE__ */ jsx("p", { className: cn("mt-2 text-muted-foreground text-body", className), ...props, children });
|
|
103
|
+
var PackageInfoContent = ({ className, children, ...props }) => /* @__PURE__ */ jsx("div", { className: cn("mt-3 border-t pt-3", className), ...props, children });
|
|
104
|
+
var PackageInfoDependencies = ({
|
|
105
|
+
className,
|
|
106
|
+
children,
|
|
107
|
+
...props
|
|
108
|
+
}) => {
|
|
109
|
+
const { t } = useLocale();
|
|
110
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", className), ...props, children: [
|
|
111
|
+
/* @__PURE__ */ jsx("span", { className: "text-eyebrow uppercase text-muted-foreground", children: t("ai.packageInfo.dependencies") }),
|
|
112
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-1", children })
|
|
113
|
+
] });
|
|
114
|
+
};
|
|
115
|
+
var PackageInfoDependency = ({
|
|
116
|
+
name,
|
|
117
|
+
version,
|
|
118
|
+
className,
|
|
119
|
+
children,
|
|
120
|
+
...props
|
|
121
|
+
}) => /* @__PURE__ */ jsx("div", { className: cn("flex items-center justify-between text-body", className), ...props, children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
122
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-muted-foreground", children: name }),
|
|
123
|
+
version && /* @__PURE__ */ jsx("span", { className: "font-mono text-meta", children: version })
|
|
124
|
+
] }) });
|
|
125
|
+
|
|
126
|
+
export {
|
|
127
|
+
PackageInfoHeader,
|
|
128
|
+
PackageInfoName,
|
|
129
|
+
PackageInfoChangeType,
|
|
130
|
+
PackageInfoVersion,
|
|
131
|
+
PackageInfo,
|
|
132
|
+
PackageInfoDescription,
|
|
133
|
+
PackageInfoContent,
|
|
134
|
+
PackageInfoDependencies,
|
|
135
|
+
PackageInfoDependency
|
|
136
|
+
};
|
|
137
|
+
//# sourceMappingURL=chunk-ONBHFIMZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/package-info.tsx"],"sourcesContent":["\"use client\";\n\nimport { Badge, useLocale } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { ArrowRightIcon, MinusIcon, PackageIcon, PlusIcon } from \"lucide-react\";\nimport type { HTMLAttributes } from \"react\";\nimport { createContext, useContext, useMemo } from \"react\";\n\ntype ChangeType = \"major\" | \"minor\" | \"patch\" | \"added\" | \"removed\";\n\ninterface PackageInfoContextType {\n name: string;\n currentVersion?: string;\n newVersion?: string;\n changeType?: ChangeType;\n}\n\nconst PackageInfoContext = createContext<PackageInfoContextType>({\n name: \"\",\n});\n\nexport type PackageInfoHeaderProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PackageInfoHeader = ({ className, children, ...props }: PackageInfoHeaderProps) => (\n <div className={cn(\"flex items-center justify-between gap-2\", className)} {...props}>\n {children}\n </div>\n);\n\nexport type PackageInfoNameProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PackageInfoName = ({ className, children, ...props }: PackageInfoNameProps) => {\n const { name } = useContext(PackageInfoContext);\n\n return (\n <div className={cn(\"flex items-center gap-2\", className)} {...props}>\n <PackageIcon className=\"size-4 text-muted-foreground\" />\n <span className=\"font-medium font-mono text-body\">{children ?? name}</span>\n </div>\n );\n};\n\nconst changeTypeStyles: Record<ChangeType, string> = {\n added: \"bg-info/10 text-info-text\",\n major: \"bg-destructive/10 text-destructive-text\",\n minor: \"bg-warning/10 text-warning-text\",\n patch: \"bg-success/10 text-success-text\",\n removed: \"bg-muted text-muted-foreground\",\n};\n\nconst changeTypeIcons: Record<ChangeType, React.ReactNode> = {\n added: <PlusIcon className=\"size-3\" />,\n major: <ArrowRightIcon className=\"size-3\" />,\n minor: <ArrowRightIcon className=\"size-3\" />,\n patch: <ArrowRightIcon className=\"size-3\" />,\n removed: <MinusIcon className=\"size-3\" />,\n};\n\nexport type PackageInfoChangeTypeProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PackageInfoChangeType = ({\n className,\n children,\n ...props\n}: PackageInfoChangeTypeProps) => {\n const { changeType } = useContext(PackageInfoContext);\n\n if (!changeType) {\n return null;\n }\n\n return (\n <Badge\n className={cn(\"gap-1 text-meta capitalize\", changeTypeStyles[changeType], className)}\n variant=\"secondary\"\n {...props}\n >\n {changeTypeIcons[changeType]}\n {children ?? changeType}\n </Badge>\n );\n};\n\nexport type PackageInfoVersionProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PackageInfoVersion = ({ className, children, ...props }: PackageInfoVersionProps) => {\n const { currentVersion, newVersion } = useContext(PackageInfoContext);\n\n if (!(currentVersion || newVersion)) {\n return null;\n }\n\n return (\n <div\n className={cn(\n \"mt-2 flex items-center gap-2 font-mono text-muted-foreground text-body\",\n className,\n )}\n {...props}\n >\n {children ?? (\n <>\n {currentVersion && <span>{currentVersion}</span>}\n {currentVersion && newVersion && <ArrowRightIcon className=\"size-3\" />}\n {newVersion && <span className=\"font-medium text-foreground\">{newVersion}</span>}\n </>\n )}\n </div>\n );\n};\n\nexport type PackageInfoProps = HTMLAttributes<HTMLDivElement> & {\n name: string;\n currentVersion?: string;\n newVersion?: string;\n changeType?: ChangeType;\n};\n\nexport const PackageInfo = ({\n name,\n currentVersion,\n newVersion,\n changeType,\n className,\n children,\n ...props\n}: PackageInfoProps) => {\n const contextValue = useMemo(\n () => ({ changeType, currentVersion, name, newVersion }),\n [changeType, currentVersion, name, newVersion],\n );\n\n return (\n <PackageInfoContext.Provider value={contextValue}>\n <div className={cn(\"rounded-lg border bg-background p-4\", className)} {...props}>\n {children ?? (\n <>\n <PackageInfoHeader>\n <PackageInfoName />\n {changeType && <PackageInfoChangeType />}\n </PackageInfoHeader>\n {(currentVersion || newVersion) && <PackageInfoVersion />}\n </>\n )}\n </div>\n </PackageInfoContext.Provider>\n );\n};\n\nexport type PackageInfoDescriptionProps = HTMLAttributes<HTMLParagraphElement>;\n\nexport const PackageInfoDescription = ({\n className,\n children,\n ...props\n}: PackageInfoDescriptionProps) => (\n <p className={cn(\"mt-2 text-muted-foreground text-body\", className)} {...props}>\n {children}\n </p>\n);\n\nexport type PackageInfoContentProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PackageInfoContent = ({ className, children, ...props }: PackageInfoContentProps) => (\n <div className={cn(\"mt-3 border-t pt-3\", className)} {...props}>\n {children}\n </div>\n);\n\nexport type PackageInfoDependenciesProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PackageInfoDependencies = ({\n className,\n children,\n ...props\n}: PackageInfoDependenciesProps) => {\n const { t } = useLocale();\n return (\n <div className={cn(\"space-y-2\", className)} {...props}>\n <span className=\"text-eyebrow uppercase text-muted-foreground\">\n {t(\"ai.packageInfo.dependencies\")}\n </span>\n <div className=\"space-y-1\">{children}</div>\n </div>\n );\n};\n\nexport type PackageInfoDependencyProps = HTMLAttributes<HTMLDivElement> & {\n name: string;\n version?: string;\n};\n\nexport const PackageInfoDependency = ({\n name,\n version,\n className,\n children,\n ...props\n}: PackageInfoDependencyProps) => (\n <div className={cn(\"flex items-center justify-between text-body\", className)} {...props}>\n {children ?? (\n <>\n <span className=\"font-mono text-muted-foreground\">{name}</span>\n {version && <span className=\"font-mono text-meta\">{version}</span>}\n </>\n )}\n </div>\n);\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/speech-input.tsx
|
|
4
|
+
import { Button } from "@elabs-ai/components-ui";
|
|
5
|
+
import { Spinner } from "@elabs-ai/components-ui";
|
|
6
|
+
import { cn } from "@elabs-ai/components-ui/lib/cn";
|
|
7
|
+
import { MicIcon, SquareIcon } from "lucide-react";
|
|
8
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
9
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
var detectSpeechInputMode = () => {
|
|
11
|
+
if (typeof window === "undefined") {
|
|
12
|
+
return "none";
|
|
13
|
+
}
|
|
14
|
+
if ("SpeechRecognition" in window || "webkitSpeechRecognition" in window) {
|
|
15
|
+
return "speech-recognition";
|
|
16
|
+
}
|
|
17
|
+
if ("MediaRecorder" in window && "mediaDevices" in navigator) {
|
|
18
|
+
return "media-recorder";
|
|
19
|
+
}
|
|
20
|
+
return "none";
|
|
21
|
+
};
|
|
22
|
+
var SpeechInput = ({
|
|
23
|
+
className,
|
|
24
|
+
onTranscriptionChange,
|
|
25
|
+
onAudioRecorded,
|
|
26
|
+
lang = "en-US",
|
|
27
|
+
...props
|
|
28
|
+
}) => {
|
|
29
|
+
const [isListening, setIsListening] = useState(false);
|
|
30
|
+
const [isProcessing, setIsProcessing] = useState(false);
|
|
31
|
+
const [mode] = useState(detectSpeechInputMode);
|
|
32
|
+
const [isRecognitionReady, setIsRecognitionReady] = useState(false);
|
|
33
|
+
const recognitionRef = useRef(null);
|
|
34
|
+
const mediaRecorderRef = useRef(null);
|
|
35
|
+
const streamRef = useRef(null);
|
|
36
|
+
const audioChunksRef = useRef([]);
|
|
37
|
+
const onTranscriptionChangeRef = useRef(onTranscriptionChange);
|
|
38
|
+
const onAudioRecordedRef = useRef(onAudioRecorded);
|
|
39
|
+
const isStartingMediaRecorderRef = useRef(false);
|
|
40
|
+
const isMountedRef = useRef(true);
|
|
41
|
+
useEffect(
|
|
42
|
+
() => () => {
|
|
43
|
+
isMountedRef.current = false;
|
|
44
|
+
},
|
|
45
|
+
[]
|
|
46
|
+
);
|
|
47
|
+
onTranscriptionChangeRef.current = onTranscriptionChange;
|
|
48
|
+
onAudioRecordedRef.current = onAudioRecorded;
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (mode !== "speech-recognition") {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
|
|
54
|
+
const speechRecognition = new SpeechRecognition();
|
|
55
|
+
speechRecognition.continuous = true;
|
|
56
|
+
speechRecognition.interimResults = true;
|
|
57
|
+
speechRecognition.lang = lang;
|
|
58
|
+
const handleStart = () => {
|
|
59
|
+
setIsListening(true);
|
|
60
|
+
};
|
|
61
|
+
const handleEnd = () => {
|
|
62
|
+
setIsListening(false);
|
|
63
|
+
};
|
|
64
|
+
const handleResult = (event) => {
|
|
65
|
+
const speechEvent = event;
|
|
66
|
+
let finalTranscript = "";
|
|
67
|
+
for (let i = speechEvent.resultIndex; i < speechEvent.results.length; i += 1) {
|
|
68
|
+
const result = speechEvent.results[i];
|
|
69
|
+
if (result?.isFinal) {
|
|
70
|
+
finalTranscript += result[0]?.transcript ?? "";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (finalTranscript) {
|
|
74
|
+
onTranscriptionChangeRef.current?.(finalTranscript);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const handleError = () => {
|
|
78
|
+
setIsListening(false);
|
|
79
|
+
};
|
|
80
|
+
speechRecognition.addEventListener("start", handleStart);
|
|
81
|
+
speechRecognition.addEventListener("end", handleEnd);
|
|
82
|
+
speechRecognition.addEventListener("result", handleResult);
|
|
83
|
+
speechRecognition.addEventListener("error", handleError);
|
|
84
|
+
recognitionRef.current = speechRecognition;
|
|
85
|
+
setIsRecognitionReady(true);
|
|
86
|
+
return () => {
|
|
87
|
+
speechRecognition.removeEventListener("start", handleStart);
|
|
88
|
+
speechRecognition.removeEventListener("end", handleEnd);
|
|
89
|
+
speechRecognition.removeEventListener("result", handleResult);
|
|
90
|
+
speechRecognition.removeEventListener("error", handleError);
|
|
91
|
+
speechRecognition.stop();
|
|
92
|
+
recognitionRef.current = null;
|
|
93
|
+
setIsRecognitionReady(false);
|
|
94
|
+
};
|
|
95
|
+
}, [mode, lang]);
|
|
96
|
+
useEffect(
|
|
97
|
+
() => () => {
|
|
98
|
+
if (mediaRecorderRef.current?.state === "recording") {
|
|
99
|
+
mediaRecorderRef.current.stop();
|
|
100
|
+
}
|
|
101
|
+
if (streamRef.current) {
|
|
102
|
+
for (const track of streamRef.current.getTracks()) {
|
|
103
|
+
track.stop();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
[]
|
|
108
|
+
);
|
|
109
|
+
const startMediaRecorder = useCallback(async () => {
|
|
110
|
+
if (!onAudioRecordedRef.current || isStartingMediaRecorderRef.current) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
isStartingMediaRecorderRef.current = true;
|
|
114
|
+
try {
|
|
115
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
116
|
+
if (!isMountedRef.current) {
|
|
117
|
+
for (const track of stream.getTracks()) {
|
|
118
|
+
track.stop();
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
streamRef.current = stream;
|
|
123
|
+
const mediaRecorder = new MediaRecorder(stream);
|
|
124
|
+
audioChunksRef.current = [];
|
|
125
|
+
const handleDataAvailable = (event) => {
|
|
126
|
+
if (event.data.size > 0) {
|
|
127
|
+
audioChunksRef.current.push(event.data);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
const handleStop = async () => {
|
|
131
|
+
for (const track of stream.getTracks()) {
|
|
132
|
+
track.stop();
|
|
133
|
+
}
|
|
134
|
+
streamRef.current = null;
|
|
135
|
+
const audioBlob = new Blob(audioChunksRef.current, {
|
|
136
|
+
type: "audio/webm"
|
|
137
|
+
});
|
|
138
|
+
if (audioBlob.size > 0 && onAudioRecordedRef.current) {
|
|
139
|
+
setIsProcessing(true);
|
|
140
|
+
try {
|
|
141
|
+
const transcript = await onAudioRecordedRef.current(audioBlob);
|
|
142
|
+
if (transcript) {
|
|
143
|
+
onTranscriptionChangeRef.current?.(transcript);
|
|
144
|
+
}
|
|
145
|
+
} catch {
|
|
146
|
+
} finally {
|
|
147
|
+
setIsProcessing(false);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
const handleError = () => {
|
|
152
|
+
setIsListening(false);
|
|
153
|
+
for (const track of stream.getTracks()) {
|
|
154
|
+
track.stop();
|
|
155
|
+
}
|
|
156
|
+
streamRef.current = null;
|
|
157
|
+
};
|
|
158
|
+
mediaRecorder.addEventListener("dataavailable", handleDataAvailable);
|
|
159
|
+
mediaRecorder.addEventListener("stop", handleStop);
|
|
160
|
+
mediaRecorder.addEventListener("error", handleError);
|
|
161
|
+
mediaRecorderRef.current = mediaRecorder;
|
|
162
|
+
mediaRecorder.start();
|
|
163
|
+
setIsListening(true);
|
|
164
|
+
} catch {
|
|
165
|
+
setIsListening(false);
|
|
166
|
+
} finally {
|
|
167
|
+
isStartingMediaRecorderRef.current = false;
|
|
168
|
+
}
|
|
169
|
+
}, []);
|
|
170
|
+
const stopMediaRecorder = useCallback(() => {
|
|
171
|
+
if (mediaRecorderRef.current?.state === "recording") {
|
|
172
|
+
mediaRecorderRef.current.stop();
|
|
173
|
+
}
|
|
174
|
+
setIsListening(false);
|
|
175
|
+
}, []);
|
|
176
|
+
const toggleListening = useCallback(() => {
|
|
177
|
+
if (mode === "speech-recognition" && recognitionRef.current) {
|
|
178
|
+
if (isListening) {
|
|
179
|
+
recognitionRef.current.stop();
|
|
180
|
+
} else {
|
|
181
|
+
recognitionRef.current.start();
|
|
182
|
+
}
|
|
183
|
+
} else if (mode === "media-recorder") {
|
|
184
|
+
if (isListening) {
|
|
185
|
+
stopMediaRecorder();
|
|
186
|
+
} else {
|
|
187
|
+
startMediaRecorder();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}, [mode, isListening, startMediaRecorder, stopMediaRecorder]);
|
|
191
|
+
const isDisabled = mode === "none" || mode === "speech-recognition" && !isRecognitionReady || mode === "media-recorder" && !onAudioRecorded || isProcessing;
|
|
192
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative inline-flex items-center justify-center", children: [
|
|
193
|
+
isListening && [0, 1, 2].map((index) => /* @__PURE__ */ jsx(
|
|
194
|
+
"div",
|
|
195
|
+
{
|
|
196
|
+
className: "absolute inset-0 animate-ping rounded-full border-2 border-destructive/30",
|
|
197
|
+
style: {
|
|
198
|
+
animationDelay: `${index * 0.3}s`,
|
|
199
|
+
animationDuration: "2s"
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
index
|
|
203
|
+
)),
|
|
204
|
+
/* @__PURE__ */ jsxs(
|
|
205
|
+
Button,
|
|
206
|
+
{
|
|
207
|
+
className: cn(
|
|
208
|
+
"relative z-10 rounded-full transition-colors duration-slow ease-standard motion-reduce:transition-none",
|
|
209
|
+
isListening ? "bg-destructive text-destructive-foreground hover:bg-destructive/80 hover:text-destructive-foreground" : "bg-primary text-primary-foreground hover:bg-primary/80 hover:text-primary-foreground",
|
|
210
|
+
className
|
|
211
|
+
),
|
|
212
|
+
disabled: isDisabled,
|
|
213
|
+
onClick: toggleListening,
|
|
214
|
+
...props,
|
|
215
|
+
children: [
|
|
216
|
+
isProcessing && /* @__PURE__ */ jsx(Spinner, {}),
|
|
217
|
+
!isProcessing && isListening && /* @__PURE__ */ jsx(SquareIcon, { className: "size-4" }),
|
|
218
|
+
!(isProcessing || isListening) && /* @__PURE__ */ jsx(MicIcon, { className: "size-4" })
|
|
219
|
+
]
|
|
220
|
+
}
|
|
221
|
+
)
|
|
222
|
+
] });
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export {
|
|
226
|
+
SpeechInput
|
|
227
|
+
};
|
|
228
|
+
//# sourceMappingURL=chunk-OPH2HHL2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/speech-input.tsx"],"sourcesContent":["\"use client\";\n\nimport { Button } from \"@elabs-ai/components-ui\";\nimport { Spinner } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { MicIcon, SquareIcon } from \"lucide-react\";\nimport type { ComponentProps } from \"react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\ninterface SpeechRecognition extends EventTarget {\n continuous: boolean;\n interimResults: boolean;\n lang: string;\n start(): void;\n stop(): void;\n onstart: ((this: SpeechRecognition, ev: Event) => void) | null;\n onend: ((this: SpeechRecognition, ev: Event) => void) | null;\n onresult: ((this: SpeechRecognition, ev: SpeechRecognitionEvent) => void) | null;\n onerror: ((this: SpeechRecognition, ev: SpeechRecognitionErrorEvent) => void) | null;\n}\n\ninterface SpeechRecognitionEvent extends Event {\n results: SpeechRecognitionResultList;\n resultIndex: number;\n}\n\ninterface SpeechRecognitionResultList {\n readonly length: number;\n item(index: number): SpeechRecognitionResult;\n [index: number]: SpeechRecognitionResult;\n}\n\ninterface SpeechRecognitionResult {\n readonly length: number;\n item(index: number): SpeechRecognitionAlternative;\n [index: number]: SpeechRecognitionAlternative;\n isFinal: boolean;\n}\n\ninterface SpeechRecognitionAlternative {\n transcript: string;\n confidence: number;\n}\n\ninterface SpeechRecognitionErrorEvent extends Event {\n error: string;\n}\n\ndeclare global {\n interface Window {\n SpeechRecognition: new () => SpeechRecognition;\n webkitSpeechRecognition: new () => SpeechRecognition;\n }\n}\n\ntype SpeechInputMode = \"speech-recognition\" | \"media-recorder\" | \"none\";\n\nexport type SpeechInputProps = ComponentProps<typeof Button> & {\n onTranscriptionChange?: (text: string) => void;\n /**\n * Callback for when audio is recorded using MediaRecorder fallback.\n * This is called in browsers that don't support the Web Speech API (Firefox, Safari).\n * The callback receives an audio Blob that should be sent to a transcription service.\n * Return the transcribed text, which will be passed to onTranscriptionChange.\n */\n onAudioRecorded?: (audioBlob: Blob) => Promise<string>;\n lang?: string;\n};\n\nconst detectSpeechInputMode = (): SpeechInputMode => {\n if (typeof window === \"undefined\") {\n return \"none\";\n }\n\n if (\"SpeechRecognition\" in window || \"webkitSpeechRecognition\" in window) {\n return \"speech-recognition\";\n }\n\n if (\"MediaRecorder\" in window && \"mediaDevices\" in navigator) {\n return \"media-recorder\";\n }\n\n return \"none\";\n};\n\nexport const SpeechInput = ({\n className,\n onTranscriptionChange,\n onAudioRecorded,\n lang = \"en-US\",\n ...props\n}: SpeechInputProps) => {\n const [isListening, setIsListening] = useState(false);\n const [isProcessing, setIsProcessing] = useState(false);\n const [mode] = useState<SpeechInputMode>(detectSpeechInputMode);\n const [isRecognitionReady, setIsRecognitionReady] = useState(false);\n const recognitionRef = useRef<SpeechRecognition | null>(null);\n const mediaRecorderRef = useRef<MediaRecorder | null>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const audioChunksRef = useRef<Blob[]>([]);\n const onTranscriptionChangeRef =\n useRef<SpeechInputProps[\"onTranscriptionChange\"]>(onTranscriptionChange);\n const onAudioRecordedRef = useRef<SpeechInputProps[\"onAudioRecorded\"]>(onAudioRecorded);\n // Guards a pending `getUserMedia()` call against a second click/toggle\n // firing before the first resolves (both see `isListening === false`, since\n // that flips only once the stream is actually granted).\n const isStartingMediaRecorderRef = useRef(false);\n // Flips false on unmount so an in-flight `getUserMedia()` that resolves\n // AFTER teardown stops its just-granted tracks immediately instead of\n // leaving the microphone on with nothing listening.\n const isMountedRef = useRef(true);\n\n useEffect(\n () => () => {\n isMountedRef.current = false;\n },\n [],\n );\n\n // Keep refs in sync\n onTranscriptionChangeRef.current = onTranscriptionChange;\n onAudioRecordedRef.current = onAudioRecorded;\n\n // Initialize Speech Recognition when mode is speech-recognition\n useEffect(() => {\n if (mode !== \"speech-recognition\") {\n return;\n }\n\n const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;\n const speechRecognition = new SpeechRecognition();\n\n speechRecognition.continuous = true;\n speechRecognition.interimResults = true;\n speechRecognition.lang = lang;\n\n const handleStart = () => {\n setIsListening(true);\n };\n\n const handleEnd = () => {\n setIsListening(false);\n };\n\n const handleResult = (event: Event) => {\n const speechEvent = event as SpeechRecognitionEvent;\n let finalTranscript = \"\";\n\n for (let i = speechEvent.resultIndex; i < speechEvent.results.length; i += 1) {\n const result = speechEvent.results[i];\n if (result?.isFinal) {\n finalTranscript += result[0]?.transcript ?? \"\";\n }\n }\n\n if (finalTranscript) {\n onTranscriptionChangeRef.current?.(finalTranscript);\n }\n };\n\n const handleError = () => {\n setIsListening(false);\n };\n\n speechRecognition.addEventListener(\"start\", handleStart);\n speechRecognition.addEventListener(\"end\", handleEnd);\n speechRecognition.addEventListener(\"result\", handleResult);\n speechRecognition.addEventListener(\"error\", handleError);\n\n recognitionRef.current = speechRecognition;\n setIsRecognitionReady(true);\n\n return () => {\n speechRecognition.removeEventListener(\"start\", handleStart);\n speechRecognition.removeEventListener(\"end\", handleEnd);\n speechRecognition.removeEventListener(\"result\", handleResult);\n speechRecognition.removeEventListener(\"error\", handleError);\n speechRecognition.stop();\n recognitionRef.current = null;\n setIsRecognitionReady(false);\n };\n }, [mode, lang]);\n\n // Cleanup MediaRecorder and stream on unmount\n useEffect(\n () => () => {\n if (mediaRecorderRef.current?.state === \"recording\") {\n mediaRecorderRef.current.stop();\n }\n if (streamRef.current) {\n for (const track of streamRef.current.getTracks()) {\n track.stop();\n }\n }\n },\n [],\n );\n\n // Start MediaRecorder recording\n const startMediaRecorder = useCallback(async () => {\n if (!onAudioRecordedRef.current || isStartingMediaRecorderRef.current) {\n return;\n }\n\n isStartingMediaRecorderRef.current = true;\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n\n // Unmounted (or stopped) while getUserMedia was pending — the granted\n // tracks have no listener left to stop them later, so stop them now.\n if (!isMountedRef.current) {\n for (const track of stream.getTracks()) {\n track.stop();\n }\n return;\n }\n\n streamRef.current = stream;\n const mediaRecorder = new MediaRecorder(stream);\n audioChunksRef.current = [];\n\n const handleDataAvailable = (event: BlobEvent) => {\n if (event.data.size > 0) {\n audioChunksRef.current.push(event.data);\n }\n };\n\n const handleStop = async () => {\n for (const track of stream.getTracks()) {\n track.stop();\n }\n streamRef.current = null;\n\n const audioBlob = new Blob(audioChunksRef.current, {\n type: \"audio/webm\",\n });\n\n if (audioBlob.size > 0 && onAudioRecordedRef.current) {\n setIsProcessing(true);\n try {\n const transcript = await onAudioRecordedRef.current(audioBlob);\n if (transcript) {\n onTranscriptionChangeRef.current?.(transcript);\n }\n } catch {\n // Error handling delegated to the onAudioRecorded caller\n } finally {\n setIsProcessing(false);\n }\n }\n };\n\n const handleError = () => {\n setIsListening(false);\n for (const track of stream.getTracks()) {\n track.stop();\n }\n streamRef.current = null;\n };\n\n mediaRecorder.addEventListener(\"dataavailable\", handleDataAvailable);\n mediaRecorder.addEventListener(\"stop\", handleStop);\n mediaRecorder.addEventListener(\"error\", handleError);\n\n mediaRecorderRef.current = mediaRecorder;\n mediaRecorder.start();\n setIsListening(true);\n } catch {\n setIsListening(false);\n } finally {\n isStartingMediaRecorderRef.current = false;\n }\n }, []);\n\n // Stop MediaRecorder recording\n const stopMediaRecorder = useCallback(() => {\n if (mediaRecorderRef.current?.state === \"recording\") {\n mediaRecorderRef.current.stop();\n }\n setIsListening(false);\n }, []);\n\n const toggleListening = useCallback(() => {\n if (mode === \"speech-recognition\" && recognitionRef.current) {\n if (isListening) {\n recognitionRef.current.stop();\n } else {\n recognitionRef.current.start();\n }\n } else if (mode === \"media-recorder\") {\n if (isListening) {\n stopMediaRecorder();\n } else {\n startMediaRecorder();\n }\n }\n }, [mode, isListening, startMediaRecorder, stopMediaRecorder]);\n\n // Determine if button should be disabled\n const isDisabled =\n mode === \"none\" ||\n (mode === \"speech-recognition\" && !isRecognitionReady) ||\n (mode === \"media-recorder\" && !onAudioRecorded) ||\n isProcessing;\n\n return (\n <div className=\"relative inline-flex items-center justify-center\">\n {/* Animated pulse rings */}\n {isListening &&\n [0, 1, 2].map((index) => (\n <div\n className=\"absolute inset-0 animate-ping rounded-full border-2 border-destructive/30\"\n key={index}\n style={{\n animationDelay: `${index * 0.3}s`,\n animationDuration: \"2s\",\n }}\n />\n ))}\n\n {/* Main record button */}\n <Button\n className={cn(\n \"relative z-10 rounded-full transition-colors duration-slow ease-standard motion-reduce:transition-none\",\n isListening\n ? \"bg-destructive text-destructive-foreground hover:bg-destructive/80 hover:text-destructive-foreground\"\n : \"bg-primary text-primary-foreground hover:bg-primary/80 hover:text-primary-foreground\",\n className,\n )}\n disabled={isDisabled}\n onClick={toggleListening}\n {...props}\n >\n {isProcessing && <Spinner />}\n {!isProcessing && isListening && <SquareIcon className=\"size-4\" />}\n {!(isProcessing || isListening) && <MicIcon className=\"size-4\" />}\n </Button>\n </div>\n );\n};\n"],"mappings":"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","names":[]}
|