@athenaintel/react 0.23.2 → 0.24.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 +46 -1
- package/dist/assets/AssetPanel.cjs +1 -1
- package/dist/assets/AssetPanel.cjs.map +1 -1
- package/dist/assets/AssetPanel.js +1 -1
- package/dist/assets/AssetPanel.js.map +1 -1
- package/dist/assets/AthenaAssetEmbed.cjs +1 -1
- package/dist/assets/AthenaAssetEmbed.cjs.map +1 -1
- package/dist/assets/AthenaAssetEmbed.js +1 -1
- package/dist/assets/AthenaAssetEmbed.js.map +1 -1
- package/dist/chat/AthenaChat.cjs +138 -46
- package/dist/chat/AthenaChat.cjs.map +1 -1
- package/dist/chat/AthenaChat.d.ts +37 -1
- package/dist/chat/AthenaChat.js +139 -47
- package/dist/chat/AthenaChat.js.map +1 -1
- package/dist/chat/StatewireApprovalCard.cjs +3 -18
- package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
- package/dist/chat/StatewireApprovalCard.js +3 -18
- package/dist/chat/StatewireApprovalCard.js.map +1 -1
- package/dist/chat/StatewireClientToolBridge.cjs +2 -16
- package/dist/chat/StatewireClientToolBridge.cjs.map +1 -1
- package/dist/chat/StatewireClientToolBridge.js +2 -16
- package/dist/chat/StatewireClientToolBridge.js.map +1 -1
- package/dist/chat/StatewireQueuedMessages.cjs +5 -16
- package/dist/chat/StatewireQueuedMessages.cjs.map +1 -1
- package/dist/chat/StatewireQueuedMessages.js +5 -16
- package/dist/chat/StatewireQueuedMessages.js.map +1 -1
- package/dist/chat/WaitingOnYouBanner.cjs +3 -18
- package/dist/chat/WaitingOnYouBanner.cjs.map +1 -1
- package/dist/chat/WaitingOnYouBanner.js +3 -18
- package/dist/chat/WaitingOnYouBanner.js.map +1 -1
- package/dist/chat/athena-composer-controls.js +1 -1
- package/dist/chat/run-activity.cjs +0 -16
- package/dist/chat/run-activity.cjs.map +1 -1
- package/dist/chat/run-activity.js +0 -16
- package/dist/chat/run-activity.js.map +1 -1
- package/dist/chat/statewire-client-tools.cjs +21 -17
- package/dist/chat/statewire-client-tools.cjs.map +1 -1
- package/dist/chat/statewire-client-tools.d.ts +27 -0
- package/dist/chat/statewire-client-tools.js +21 -17
- package/dist/chat/statewire-client-tools.js.map +1 -1
- package/dist/chat/super-grouping/activity-timeline.cjs +0 -16
- package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
- package/dist/chat/super-grouping/activity-timeline.js +0 -16
- package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
- package/dist/chat/tiptap-text.cjs +1 -17
- package/dist/chat/tiptap-text.cjs.map +1 -1
- package/dist/chat/tiptap-text.js +1 -17
- package/dist/chat/tiptap-text.js.map +1 -1
- package/dist/chat/use-athena-run-live.cjs +0 -15
- package/dist/chat/use-athena-run-live.cjs.map +1 -1
- package/dist/chat/use-athena-run-live.d.ts +7 -0
- package/dist/chat/use-athena-run-live.js +0 -15
- package/dist/chat/use-athena-run-live.js.map +1 -1
- package/dist/chat/use-statewire-approval-request.cjs +4 -16
- package/dist/chat/use-statewire-approval-request.cjs.map +1 -1
- package/dist/chat/use-statewire-approval-request.d.ts +2 -0
- package/dist/chat/use-statewire-approval-request.js +4 -16
- package/dist/chat/use-statewire-approval-request.js.map +1 -1
- package/dist/chat-ui/composer/tiptap-composer.cjs +17 -17
- package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
- package/dist/chat-ui/composer/tiptap-composer.js +17 -17
- package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
- package/dist/chat-ui/index.cjs +0 -16
- package/dist/chat-ui/index.cjs.map +1 -1
- package/dist/chat-ui/index.js +0 -16
- package/dist/chat-ui/index.js.map +1 -1
- package/dist/chat-ui/mentions/mention-chip.cjs +1 -16
- package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
- package/dist/chat-ui/mentions/mention-chip.js +1 -16
- package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.cjs +4 -6
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.cjs.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.js +4 -6
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.js.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/sources/index.cjs +2 -9
- package/dist/chat-ui/mentions/suggestions/data/sources/index.cjs.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/sources/index.js +2 -9
- package/dist/chat-ui/mentions/suggestions/data/sources/index.js.map +1 -1
- package/dist/chat-ui/messages/message-components.cjs +5 -19
- package/dist/chat-ui/messages/message-components.cjs.map +1 -1
- package/dist/chat-ui/messages/message-components.js +5 -19
- package/dist/chat-ui/messages/message-components.js.map +1 -1
- package/dist/chat-ui/messages/runtime-message.cjs +1 -16
- package/dist/chat-ui/messages/runtime-message.cjs.map +1 -1
- package/dist/chat-ui/messages/runtime-message.js +1 -16
- package/dist/chat-ui/messages/runtime-message.js.map +1 -1
- package/dist/chat-ui/messages/statewire-continue-run.cjs +3 -15
- package/dist/chat-ui/messages/statewire-continue-run.cjs.map +1 -1
- package/dist/chat-ui/messages/statewire-continue-run.js +3 -15
- package/dist/chat-ui/messages/statewire-continue-run.js.map +1 -1
- package/dist/chat-ui/messages/user-edit-composer.cjs +0 -16
- package/dist/chat-ui/messages/user-edit-composer.cjs.map +1 -1
- package/dist/chat-ui/messages/user-edit-composer.js +0 -16
- package/dist/chat-ui/messages/user-edit-composer.js.map +1 -1
- package/dist/chat-ui/messages/wake-marker.cjs +1 -16
- package/dist/chat-ui/messages/wake-marker.cjs.map +1 -1
- package/dist/chat-ui/messages/wake-marker.js +1 -16
- package/dist/chat-ui/messages/wake-marker.js.map +1 -1
- package/dist/chat-ui/statewire.cjs +0 -16
- package/dist/chat-ui/statewire.cjs.map +1 -1
- package/dist/chat-ui/statewire.js +0 -16
- package/dist/chat-ui/statewire.js.map +1 -1
- package/dist/chat-ui/thread/statewire-history-edge.cjs +2 -17
- package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
- package/dist/chat-ui/thread/statewire-history-edge.js +2 -17
- package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
- package/dist/composer/composer-handoff.cjs +121 -0
- package/dist/composer/composer-handoff.cjs.map +1 -0
- package/dist/composer/composer-handoff.d.ts +89 -0
- package/dist/composer/composer-handoff.js +121 -0
- package/dist/composer/composer-handoff.js.map +1 -0
- package/dist/composer/index.d.ts +2 -0
- package/dist/composer/useAppendToComposer.cjs +19 -7
- package/dist/composer/useAppendToComposer.cjs.map +1 -1
- package/dist/composer/useAppendToComposer.d.ts +22 -4
- package/dist/composer/useAppendToComposer.js +20 -8
- package/dist/composer/useAppendToComposer.js.map +1 -1
- package/dist/composer/useSendMessage.cjs +26 -8
- package/dist/composer/useSendMessage.cjs.map +1 -1
- package/dist/composer/useSendMessage.d.ts +8 -0
- package/dist/composer/useSendMessage.js +27 -9
- package/dist/composer/useSendMessage.js.map +1 -1
- package/dist/diagnostics/athena-diagnostics.cjs.map +1 -1
- package/dist/diagnostics/athena-diagnostics.d.ts +1 -1
- package/dist/diagnostics/athena-diagnostics.js.map +1 -1
- package/dist/diagnostics/errors.cjs +2 -0
- package/dist/diagnostics/errors.cjs.map +1 -1
- package/dist/diagnostics/errors.d.ts +2 -0
- package/dist/diagnostics/errors.js +2 -0
- package/dist/diagnostics/errors.js.map +1 -1
- package/dist/dictation/DictationStrip.cjs +61 -0
- package/dist/dictation/DictationStrip.cjs.map +1 -0
- package/dist/dictation/DictationStrip.d.ts +27 -0
- package/dist/dictation/DictationStrip.js +61 -0
- package/dist/dictation/DictationStrip.js.map +1 -0
- package/dist/dictation/audio-level-history.cjs +68 -0
- package/dist/dictation/audio-level-history.cjs.map +1 -0
- package/dist/dictation/audio-level-history.d.ts +27 -0
- package/dist/dictation/audio-level-history.js +68 -0
- package/dist/dictation/audio-level-history.js.map +1 -0
- package/dist/dictation/dictation-controls.cjs +60 -0
- package/dist/dictation/dictation-controls.cjs.map +1 -0
- package/dist/dictation/dictation-controls.d.ts +15 -0
- package/dist/dictation/dictation-controls.js +60 -0
- package/dist/dictation/dictation-controls.js.map +1 -0
- package/dist/dictation/dictation-engine.cjs +128 -0
- package/dist/dictation/dictation-engine.cjs.map +1 -0
- package/dist/dictation/dictation-engine.d.ts +95 -0
- package/dist/dictation/dictation-engine.js +128 -0
- package/dist/dictation/dictation-engine.js.map +1 -0
- package/dist/dictation/dictation-state.cjs +68 -0
- package/dist/dictation/dictation-state.cjs.map +1 -0
- package/dist/dictation/dictation-state.d.ts +58 -0
- package/dist/dictation/dictation-state.js +68 -0
- package/dist/dictation/dictation-state.js.map +1 -0
- package/dist/dictation/recorded-dictation-engine.cjs +153 -0
- package/dist/dictation/recorded-dictation-engine.cjs.map +1 -0
- package/dist/dictation/recorded-dictation-engine.d.ts +66 -0
- package/dist/dictation/recorded-dictation-engine.js +153 -0
- package/dist/dictation/recorded-dictation-engine.js.map +1 -0
- package/dist/dictation/use-athena-dictation-engine.cjs +64 -0
- package/dist/dictation/use-athena-dictation-engine.cjs.map +1 -0
- package/dist/dictation/use-athena-dictation-engine.d.ts +25 -0
- package/dist/dictation/use-athena-dictation-engine.js +64 -0
- package/dist/dictation/use-athena-dictation-engine.js.map +1 -0
- package/dist/dictation/use-dictation.cjs +215 -0
- package/dist/dictation/use-dictation.cjs.map +1 -0
- package/dist/dictation/use-dictation.d.ts +63 -0
- package/dist/dictation/use-dictation.js +215 -0
- package/dist/dictation/use-dictation.js.map +1 -0
- package/dist/index.cjs +41 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +14 -0
- package/dist/index.js +23 -21
- package/dist/index.js.map +1 -1
- package/dist/packages/agent-runtime/src/edit-command.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/edit-command.js.map +1 -1
- package/dist/packages/agent-runtime/src/errors.cjs +18 -0
- package/dist/packages/agent-runtime/src/errors.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/errors.js +19 -1
- package/dist/packages/agent-runtime/src/errors.js.map +1 -1
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs +1 -0
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.js +1 -0
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
- package/dist/packages/agent-runtime/src/interrupts.cjs +61 -12
- package/dist/packages/agent-runtime/src/interrupts.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/interrupts.js +61 -12
- package/dist/packages/agent-runtime/src/interrupts.js.map +1 -1
- package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs +49 -0
- package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js +49 -0
- package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js.map +1 -1
- package/dist/packages/agent-runtime/src/thread-status.js +1 -1
- package/dist/packages/agent-runtime/src/tool-metadata.cjs +1 -37
- package/dist/packages/agent-runtime/src/tool-metadata.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/tool-metadata.js +1 -37
- package/dist/packages/agent-runtime/src/tool-metadata.js.map +1 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-config.cjs +0 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-config.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-config.js +0 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-config.js.map +1 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
- package/dist/packages/react/package.json.cjs +1 -1
- package/dist/packages/react/package.json.js +1 -1
- package/dist/provider/AthenaProvider.cjs +45 -20
- package/dist/provider/AthenaProvider.cjs.map +1 -1
- package/dist/provider/AthenaProvider.d.ts +1 -1
- package/dist/provider/AthenaProvider.js +46 -21
- package/dist/provider/AthenaProvider.js.map +1 -1
- package/dist/runtime/statewire-lifecycle.cjs +0 -16
- package/dist/runtime/statewire-lifecycle.cjs.map +1 -1
- package/dist/runtime/statewire-lifecycle.js +0 -16
- package/dist/runtime/statewire-lifecycle.js.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.cjs +3 -17
- package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.js +3 -17
- package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/threads/statewire-thread-list.cjs +4 -17
- package/dist/threads/statewire-thread-list.cjs.map +1 -1
- package/dist/threads/statewire-thread-list.js +4 -17
- package/dist/threads/statewire-thread-list.js.map +1 -1
- package/dist/threads/use-rest-conversation-history.cjs +2 -16
- package/dist/threads/use-rest-conversation-history.cjs.map +1 -1
- package/dist/threads/use-rest-conversation-history.js +2 -16
- package/dist/threads/use-rest-conversation-history.js.map +1 -1
- package/dist/threads/use-statewire-conversation-history.cjs +2 -16
- package/dist/threads/use-statewire-conversation-history.cjs.map +1 -1
- package/dist/threads/use-statewire-conversation-history.js +2 -16
- package/dist/threads/use-statewire-conversation-history.js.map +1 -1
- package/dist/toolkits/index.cjs +0 -7
- package/dist/toolkits/index.cjs.map +1 -1
- package/dist/toolkits/index.d.ts +0 -2
- package/dist/toolkits/index.js +0 -7
- package/dist/toolkits/index.js.map +1 -1
- package/dist/tools/index.cjs +4 -20
- package/dist/tools/index.cjs.map +1 -1
- package/dist/tools/index.js +1 -17
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/misc-tool-uis.cjs +2 -17
- package/dist/tools/misc-tool-uis.cjs.map +1 -1
- package/dist/tools/misc-tool-uis.js +2 -17
- package/dist/tools/misc-tool-uis.js.map +1 -1
- package/dist/tools/primitives.cjs +1 -16
- package/dist/tools/primitives.cjs.map +1 -1
- package/dist/tools/primitives.js +1 -16
- package/dist/tools/primitives.js.map +1 -1
- package/dist/tools/tool-fallback.cjs +2 -17
- package/dist/tools/tool-fallback.cjs.map +1 -1
- package/dist/tools/tool-fallback.js +2 -17
- package/dist/tools/tool-fallback.js.map +1 -1
- package/dist/tools/voice-update-tool-ui.cjs +1 -17
- package/dist/tools/voice-update-tool-ui.cjs.map +1 -1
- package/dist/tools/voice-update-tool-ui.js +1 -17
- package/dist/tools/voice-update-tool-ui.js.map +1 -1
- package/package.json +1 -1
- package/dist/node_modules/qrcode-generator/dist/qrcode.cjs +0 -32
- package/dist/node_modules/qrcode-generator/dist/qrcode.cjs.map +0 -1
- package/dist/node_modules/qrcode-generator/dist/qrcode.js +0 -31
- package/dist/node_modules/qrcode-generator/dist/qrcode.js.map +0 -1
- package/dist/node_modules/zod/v4/classic/errors.cjs +0 -44
- package/dist/node_modules/zod/v4/classic/errors.cjs.map +0 -1
- package/dist/node_modules/zod/v4/classic/errors.js +0 -44
- package/dist/node_modules/zod/v4/classic/errors.js.map +0 -1
- package/dist/node_modules/zod/v4/classic/iso.cjs +0 -43
- package/dist/node_modules/zod/v4/classic/iso.cjs.map +0 -1
- package/dist/node_modules/zod/v4/classic/iso.js +0 -43
- package/dist/node_modules/zod/v4/classic/iso.js.map +0 -1
- package/dist/node_modules/zod/v4/classic/parse.cjs +0 -29
- package/dist/node_modules/zod/v4/classic/parse.cjs.map +0 -1
- package/dist/node_modules/zod/v4/classic/parse.js +0 -29
- package/dist/node_modules/zod/v4/classic/parse.js.map +0 -1
- package/dist/node_modules/zod/v4/classic/schemas.cjs +0 -871
- package/dist/node_modules/zod/v4/classic/schemas.cjs.map +0 -1
- package/dist/node_modules/zod/v4/classic/schemas.js +0 -871
- package/dist/node_modules/zod/v4/classic/schemas.js.map +0 -1
- package/dist/node_modules/zod/v4/core/api.cjs +0 -570
- package/dist/node_modules/zod/v4/core/api.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/api.js +0 -570
- package/dist/node_modules/zod/v4/core/api.js.map +0 -1
- package/dist/node_modules/zod/v4/core/checks.cjs +0 -411
- package/dist/node_modules/zod/v4/core/checks.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/checks.js +0 -411
- package/dist/node_modules/zod/v4/core/checks.js.map +0 -1
- package/dist/node_modules/zod/v4/core/core.cjs +0 -77
- package/dist/node_modules/zod/v4/core/core.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/core.js +0 -77
- package/dist/node_modules/zod/v4/core/core.js.map +0 -1
- package/dist/node_modules/zod/v4/core/doc.cjs +0 -38
- package/dist/node_modules/zod/v4/core/doc.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/doc.js +0 -38
- package/dist/node_modules/zod/v4/core/doc.js.map +0 -1
- package/dist/node_modules/zod/v4/core/errors.cjs +0 -76
- package/dist/node_modules/zod/v4/core/errors.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/errors.js +0 -76
- package/dist/node_modules/zod/v4/core/errors.js.map +0 -1
- package/dist/node_modules/zod/v4/core/json-schema-processors.cjs +0 -312
- package/dist/node_modules/zod/v4/core/json-schema-processors.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/json-schema-processors.js +0 -312
- package/dist/node_modules/zod/v4/core/json-schema-processors.js.map +0 -1
- package/dist/node_modules/zod/v4/core/parse.cjs +0 -96
- package/dist/node_modules/zod/v4/core/parse.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/parse.js +0 -96
- package/dist/node_modules/zod/v4/core/parse.js.map +0 -1
- package/dist/node_modules/zod/v4/core/regexes.cjs +0 -86
- package/dist/node_modules/zod/v4/core/regexes.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/regexes.js +0 -86
- package/dist/node_modules/zod/v4/core/regexes.js.map +0 -1
- package/dist/node_modules/zod/v4/core/registries.cjs +0 -52
- package/dist/node_modules/zod/v4/core/registries.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/registries.js +0 -52
- package/dist/node_modules/zod/v4/core/registries.js.map +0 -1
- package/dist/node_modules/zod/v4/core/schemas.cjs +0 -1412
- package/dist/node_modules/zod/v4/core/schemas.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/schemas.js +0 -1413
- package/dist/node_modules/zod/v4/core/schemas.js.map +0 -1
- package/dist/node_modules/zod/v4/core/to-json-schema.cjs +0 -370
- package/dist/node_modules/zod/v4/core/to-json-schema.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/to-json-schema.js +0 -370
- package/dist/node_modules/zod/v4/core/to-json-schema.js.map +0 -1
- package/dist/node_modules/zod/v4/core/util.cjs +0 -444
- package/dist/node_modules/zod/v4/core/util.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/util.js +0 -444
- package/dist/node_modules/zod/v4/core/util.js.map +0 -1
- package/dist/node_modules/zod/v4/core/versions.cjs +0 -9
- package/dist/node_modules/zod/v4/core/versions.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/versions.js +0 -9
- package/dist/node_modules/zod/v4/core/versions.js.map +0 -1
- package/dist/packages/agent-runtime/src/thread-handoff.cjs +0 -85
- package/dist/packages/agent-runtime/src/thread-handoff.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/thread-handoff.js +0 -85
- package/dist/packages/agent-runtime/src/thread-handoff.js.map +0 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { FC } from 'react';
|
|
2
|
+
import { type AudioLevelHistory } from './audio-level-history';
|
|
3
|
+
import type { DictationStatus, DictationTranscript } from './dictation-state';
|
|
4
|
+
export interface DictationStripProps {
|
|
5
|
+
status: DictationStatus;
|
|
6
|
+
transcript: DictationTranscript;
|
|
7
|
+
/** Microphone stream to visualize; null draws the resting baseline. */
|
|
8
|
+
stream: MediaStream | null;
|
|
9
|
+
/** Number of bars across the strip. Defaults to 48. */
|
|
10
|
+
bars?: number;
|
|
11
|
+
/** Status while the engine flushes. Defaults to `Finishing…`. */
|
|
12
|
+
stoppingLabel?: string;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The bars. A rolling level history renders newest-right: unfilled slots are
|
|
17
|
+
* the dotted baseline, filled slots grow with the microphone level.
|
|
18
|
+
*/
|
|
19
|
+
export declare const DictationWaveform: FC<{
|
|
20
|
+
history: AudioLevelHistory;
|
|
21
|
+
className?: string;
|
|
22
|
+
}>;
|
|
23
|
+
/**
|
|
24
|
+
* The composer's recording mode: a live waveform in place of the text editor
|
|
25
|
+
* and the interim transcript as a caption under it.
|
|
26
|
+
*/
|
|
27
|
+
export declare const DictationStrip: FC<DictationStripProps>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useAudioLevelHistory } from "./audio-level-history.js";
|
|
3
|
+
import { cn } from "cnfast";
|
|
4
|
+
const MIN_BAR_HEIGHT_PX = 3;
|
|
5
|
+
const MAX_BAR_HEIGHT_PX = 24;
|
|
6
|
+
function barHeight(level) {
|
|
7
|
+
return Math.round(MIN_BAR_HEIGHT_PX + level * (MAX_BAR_HEIGHT_PX - MIN_BAR_HEIGHT_PX));
|
|
8
|
+
}
|
|
9
|
+
const DictationWaveform = ({
|
|
10
|
+
history,
|
|
11
|
+
className
|
|
12
|
+
}) => /* @__PURE__ */ jsx(
|
|
13
|
+
"div",
|
|
14
|
+
{
|
|
15
|
+
className: cn("aui-dictation-waveform flex h-6 items-center gap-[3px]", className),
|
|
16
|
+
"aria-hidden": "true",
|
|
17
|
+
children: history.map((level, index) => /* @__PURE__ */ jsx(
|
|
18
|
+
"span",
|
|
19
|
+
{
|
|
20
|
+
className: cn(
|
|
21
|
+
"aui-dictation-bar w-[3px] shrink-0 rounded-full transition-[height] duration-75",
|
|
22
|
+
level === null ? "bg-muted-foreground/30" : "bg-foreground/80"
|
|
23
|
+
),
|
|
24
|
+
style: { height: level === null ? MIN_BAR_HEIGHT_PX : barHeight(level) }
|
|
25
|
+
},
|
|
26
|
+
index
|
|
27
|
+
))
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
const DictationStrip = ({
|
|
31
|
+
status,
|
|
32
|
+
transcript,
|
|
33
|
+
stream,
|
|
34
|
+
bars = 48,
|
|
35
|
+
stoppingLabel = "Finishing…",
|
|
36
|
+
className
|
|
37
|
+
}) => {
|
|
38
|
+
const history = useAudioLevelHistory(stream, { size: bars });
|
|
39
|
+
const caption = [transcript.final, transcript.interim].filter(Boolean).join(" ");
|
|
40
|
+
const statusLabel = status === "starting" ? "Starting microphone…" : status === "stopping" ? stoppingLabel : "Listening…";
|
|
41
|
+
return /* @__PURE__ */ jsxs(
|
|
42
|
+
"div",
|
|
43
|
+
{
|
|
44
|
+
className: cn("aui-dictation-strip flex min-h-10 flex-col gap-1 px-1.5 py-2", className),
|
|
45
|
+
role: "status",
|
|
46
|
+
"aria-live": "polite",
|
|
47
|
+
children: [
|
|
48
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
49
|
+
/* @__PURE__ */ jsx("span", { className: "aui-dictation-status text-muted-foreground shrink-0 text-xs", children: statusLabel }),
|
|
50
|
+
/* @__PURE__ */ jsx(DictationWaveform, { history, className: "min-w-0 flex-1 justify-end overflow-hidden" })
|
|
51
|
+
] }),
|
|
52
|
+
caption && /* @__PURE__ */ jsx("p", { className: "aui-dictation-caption text-muted-foreground line-clamp-2 text-sm", children: caption })
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
export {
|
|
58
|
+
DictationStrip,
|
|
59
|
+
DictationWaveform
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=DictationStrip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DictationStrip.js","sources":["../../src/dictation/DictationStrip.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { cn } from '../lib/utils';\nimport { type AudioLevelHistory, useAudioLevelHistory } from './audio-level-history';\nimport type { DictationStatus, DictationTranscript } from './dictation-state';\n\nexport interface DictationStripProps {\n status: DictationStatus;\n transcript: DictationTranscript;\n /** Microphone stream to visualize; null draws the resting baseline. */\n stream: MediaStream | null;\n /** Number of bars across the strip. Defaults to 48. */\n bars?: number;\n /** Status while the engine flushes. Defaults to `Finishing…`. */\n stoppingLabel?: string;\n className?: string;\n}\n\nconst MIN_BAR_HEIGHT_PX = 3;\nconst MAX_BAR_HEIGHT_PX = 24;\n\nfunction barHeight(level: number): number {\n return Math.round(MIN_BAR_HEIGHT_PX + level * (MAX_BAR_HEIGHT_PX - MIN_BAR_HEIGHT_PX));\n}\n\n/**\n * The bars. A rolling level history renders newest-right: unfilled slots are\n * the dotted baseline, filled slots grow with the microphone level.\n */\nexport const DictationWaveform: FC<{ history: AudioLevelHistory; className?: string }> = ({\n history,\n className,\n}) => (\n <div\n className={cn('aui-dictation-waveform flex h-6 items-center gap-[3px]', className)}\n aria-hidden=\"true\"\n >\n {history.map((level, index) => (\n <span\n // Slots are positional; the history is a fixed-size window.\n key={index}\n className={cn(\n 'aui-dictation-bar w-[3px] shrink-0 rounded-full transition-[height] duration-75',\n level === null ? 'bg-muted-foreground/30' : 'bg-foreground/80',\n )}\n style={{ height: level === null ? MIN_BAR_HEIGHT_PX : barHeight(level) }}\n />\n ))}\n </div>\n);\n\n/**\n * The composer's recording mode: a live waveform in place of the text editor\n * and the interim transcript as a caption under it.\n */\nexport const DictationStrip: FC<DictationStripProps> = ({\n status,\n transcript,\n stream,\n bars = 48,\n stoppingLabel = 'Finishing…',\n className,\n}) => {\n const history = useAudioLevelHistory(stream, { size: bars });\n const caption = [transcript.final, transcript.interim].filter(Boolean).join(' ');\n const statusLabel =\n status === 'starting' ? 'Starting microphone…' : status === 'stopping' ? stoppingLabel : 'Listening…';\n\n return (\n <div\n className={cn('aui-dictation-strip flex min-h-10 flex-col gap-1 px-1.5 py-2', className)}\n role=\"status\"\n aria-live=\"polite\"\n >\n <div className=\"flex items-center gap-3\">\n <span className=\"aui-dictation-status text-muted-foreground shrink-0 text-xs\">{statusLabel}</span>\n <DictationWaveform history={history} className=\"min-w-0 flex-1 justify-end overflow-hidden\" />\n </div>\n {caption && (\n <p className=\"aui-dictation-caption text-muted-foreground line-clamp-2 text-sm\">{caption}</p>\n )}\n </div>\n );\n};\n"],"names":[],"mappings":";;;AAiBA,MAAM,oBAAoB;AAC1B,MAAM,oBAAoB;AAE1B,SAAS,UAAU,OAAuB;AACxC,SAAO,KAAK,MAAM,oBAAoB,SAAS,oBAAoB,kBAAkB;AACvF;AAMO,MAAM,oBAA4E,CAAC;AAAA,EACxF;AAAA,EACA;AACF,MACE;AAAA,EAAC;AAAA,EAAA;AAAA,IACC,WAAW,GAAG,0DAA0D,SAAS;AAAA,IACjF,eAAY;AAAA,IAEX,UAAA,QAAQ,IAAI,CAAC,OAAO,UACnB;AAAA,MAAC;AAAA,MAAA;AAAA,QAGC,WAAW;AAAA,UACT;AAAA,UACA,UAAU,OAAO,2BAA2B;AAAA,QAAA;AAAA,QAE9C,OAAO,EAAE,QAAQ,UAAU,OAAO,oBAAoB,UAAU,KAAK,EAAA;AAAA,MAAE;AAAA,MALlE;AAAA,IAAA,CAOR;AAAA,EAAA;AACH;AAOK,MAAM,iBAA0C,CAAC;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB;AACF,MAAM;AACJ,QAAM,UAAU,qBAAqB,QAAQ,EAAE,MAAM,MAAM;AAC3D,QAAM,UAAU,CAAC,WAAW,OAAO,WAAW,OAAO,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAC/E,QAAM,cACJ,WAAW,aAAa,yBAAyB,WAAW,aAAa,gBAAgB;AAE3F,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,GAAG,gEAAgE,SAAS;AAAA,MACvF,MAAK;AAAA,MACL,aAAU;AAAA,MAEV,UAAA;AAAA,QAAA,qBAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,UAAA,oBAAC,QAAA,EAAK,WAAU,+DAA+D,UAAA,aAAY;AAAA,UAC3F,oBAAC,mBAAA,EAAkB,SAAkB,WAAU,6CAAA,CAA6C;AAAA,QAAA,GAC9F;AAAA,QACC,WACC,oBAAC,KAAA,EAAE,WAAU,oEAAoE,UAAA,QAAA,CAAQ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIjG;"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const react = require("react");
|
|
4
|
+
function emptyAudioLevelHistory(size) {
|
|
5
|
+
return new Array(Math.max(0, size)).fill(null);
|
|
6
|
+
}
|
|
7
|
+
function pushAudioLevel(history, level) {
|
|
8
|
+
if (history.length === 0) return history;
|
|
9
|
+
const clamped = Math.min(1, Math.max(0, level));
|
|
10
|
+
return [...history.slice(1), clamped];
|
|
11
|
+
}
|
|
12
|
+
function displayLevelFromSamples(samples, gain = 4) {
|
|
13
|
+
if (samples.length === 0) return 0;
|
|
14
|
+
let sum = 0;
|
|
15
|
+
for (let i = 0; i < samples.length; i += 1) {
|
|
16
|
+
const sample = samples[i] ?? 0;
|
|
17
|
+
sum += sample * sample;
|
|
18
|
+
}
|
|
19
|
+
const rms = Math.sqrt(sum / samples.length);
|
|
20
|
+
return Math.min(1, rms * gain);
|
|
21
|
+
}
|
|
22
|
+
function useAudioLevelHistory(stream, { size, intervalMs = 50 }) {
|
|
23
|
+
const [history, setHistory] = react.useState(() => emptyAudioLevelHistory(size));
|
|
24
|
+
react.useEffect(() => {
|
|
25
|
+
setHistory(emptyAudioLevelHistory(size));
|
|
26
|
+
if (!stream || typeof window === "undefined") return;
|
|
27
|
+
const AudioContextCtor = window.AudioContext ?? window.webkitAudioContext;
|
|
28
|
+
if (!AudioContextCtor) return;
|
|
29
|
+
let audioContext;
|
|
30
|
+
try {
|
|
31
|
+
audioContext = new AudioContextCtor();
|
|
32
|
+
} catch {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const analyser = audioContext.createAnalyser();
|
|
36
|
+
analyser.fftSize = 1024;
|
|
37
|
+
analyser.smoothingTimeConstant = 0;
|
|
38
|
+
let source = null;
|
|
39
|
+
try {
|
|
40
|
+
source = audioContext.createMediaStreamSource(stream);
|
|
41
|
+
source.connect(analyser);
|
|
42
|
+
} catch {
|
|
43
|
+
void audioContext.close();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const samples = new Float32Array(analyser.fftSize);
|
|
47
|
+
const interval = window.setInterval(() => {
|
|
48
|
+
analyser.getFloatTimeDomainData(samples);
|
|
49
|
+
const level = displayLevelFromSamples(samples);
|
|
50
|
+
setHistory((current) => pushAudioLevel(current, level));
|
|
51
|
+
}, intervalMs);
|
|
52
|
+
return () => {
|
|
53
|
+
window.clearInterval(interval);
|
|
54
|
+
try {
|
|
55
|
+
source == null ? void 0 : source.disconnect();
|
|
56
|
+
analyser.disconnect();
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
void audioContext.close();
|
|
60
|
+
};
|
|
61
|
+
}, [stream, size, intervalMs]);
|
|
62
|
+
return history;
|
|
63
|
+
}
|
|
64
|
+
exports.displayLevelFromSamples = displayLevelFromSamples;
|
|
65
|
+
exports.emptyAudioLevelHistory = emptyAudioLevelHistory;
|
|
66
|
+
exports.pushAudioLevel = pushAudioLevel;
|
|
67
|
+
exports.useAudioLevelHistory = useAudioLevelHistory;
|
|
68
|
+
//# sourceMappingURL=audio-level-history.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-level-history.cjs","sources":["../../src/dictation/audio-level-history.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\n\n/**\n * A rolling window of microphone levels (0..1), newest last, for the\n * composer's waveform strip. `size` slots are always present: unfilled slots\n * are `null` so the strip can draw them as the resting dotted baseline.\n */\nexport type AudioLevelHistory = ReadonlyArray<number | null>;\n\nexport function emptyAudioLevelHistory(size: number): AudioLevelHistory {\n return new Array<number | null>(Math.max(0, size)).fill(null);\n}\n\n/** Append one level, dropping the oldest slot. Pure. */\nexport function pushAudioLevel(history: AudioLevelHistory, level: number): AudioLevelHistory {\n if (history.length === 0) return history;\n const clamped = Math.min(1, Math.max(0, level));\n return [...history.slice(1), clamped];\n}\n\n/**\n * RMS of a PCM sample window scaled to 0..1 for display. Speech peaks sit\n * around 0.1-0.3 RMS, so a gain lifts them into the visible range while the\n * noise floor (about 0.005) stays flat.\n */\nexport function displayLevelFromSamples(samples: ArrayLike<number>, gain = 4): number {\n if (samples.length === 0) return 0;\n let sum = 0;\n for (let i = 0; i < samples.length; i += 1) {\n const sample = samples[i] ?? 0;\n sum += sample * sample;\n }\n const rms = Math.sqrt(sum / samples.length);\n return Math.min(1, rms * gain);\n}\n\ninterface UseAudioLevelHistoryOptions {\n /** Number of bars in the strip. */\n size: number;\n /** Sample cadence in ms; 50ms gives ~20 bars per second. */\n intervalMs?: number;\n}\n\n/**\n * Sample a `MediaStream` through an `AnalyserNode` into a rolling level\n * history. Returns the resting baseline when `stream` is null.\n */\nexport function useAudioLevelHistory(\n stream: MediaStream | null,\n { size, intervalMs = 50 }: UseAudioLevelHistoryOptions,\n): AudioLevelHistory {\n const [history, setHistory] = useState<AudioLevelHistory>(() => emptyAudioLevelHistory(size));\n\n useEffect(() => {\n setHistory(emptyAudioLevelHistory(size));\n if (!stream || typeof window === 'undefined') return;\n const AudioContextCtor =\n window.AudioContext ??\n (window as typeof window & { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!AudioContextCtor) return;\n\n let audioContext: AudioContext;\n try {\n audioContext = new AudioContextCtor();\n } catch {\n return;\n }\n const analyser = audioContext.createAnalyser();\n analyser.fftSize = 1024;\n analyser.smoothingTimeConstant = 0;\n let source: MediaStreamAudioSourceNode | null = null;\n try {\n source = audioContext.createMediaStreamSource(stream);\n source.connect(analyser);\n } catch {\n void audioContext.close();\n return;\n }\n const samples = new Float32Array(analyser.fftSize);\n const interval = window.setInterval(() => {\n analyser.getFloatTimeDomainData(samples);\n const level = displayLevelFromSamples(samples);\n setHistory((current) => pushAudioLevel(current, level));\n }, intervalMs);\n\n return () => {\n window.clearInterval(interval);\n try {\n source?.disconnect();\n analyser.disconnect();\n } catch {\n // Already disconnected.\n }\n void audioContext.close();\n };\n }, [stream, size, intervalMs]);\n\n return history;\n}\n"],"names":["useState","useEffect"],"mappings":";;;AASO,SAAS,uBAAuB,MAAiC;AACtE,SAAO,IAAI,MAAqB,KAAK,IAAI,GAAG,IAAI,CAAC,EAAE,KAAK,IAAI;AAC9D;AAGO,SAAS,eAAe,SAA4B,OAAkC;AAC3F,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,QAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,CAAC;AAC9C,SAAO,CAAC,GAAG,QAAQ,MAAM,CAAC,GAAG,OAAO;AACtC;AAOO,SAAS,wBAAwB,SAA4B,OAAO,GAAW;AACpF,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,GAAG;AAC1C,UAAM,SAAS,QAAQ,CAAC,KAAK;AAC7B,WAAO,SAAS;AAAA,EAClB;AACA,QAAM,MAAM,KAAK,KAAK,MAAM,QAAQ,MAAM;AAC1C,SAAO,KAAK,IAAI,GAAG,MAAM,IAAI;AAC/B;AAaO,SAAS,qBACd,QACA,EAAE,MAAM,aAAa,MACF;AACnB,QAAM,CAAC,SAAS,UAAU,IAAIA,MAAAA,SAA4B,MAAM,uBAAuB,IAAI,CAAC;AAE5FC,QAAAA,UAAU,MAAM;AACd,eAAW,uBAAuB,IAAI,CAAC;AACvC,QAAI,CAAC,UAAU,OAAO,WAAW,YAAa;AAC9C,UAAM,mBACJ,OAAO,gBACN,OAAwE;AAC3E,QAAI,CAAC,iBAAkB;AAEvB,QAAI;AACJ,QAAI;AACF,qBAAe,IAAI,iBAAA;AAAA,IACrB,QAAQ;AACN;AAAA,IACF;AACA,UAAM,WAAW,aAAa,eAAA;AAC9B,aAAS,UAAU;AACnB,aAAS,wBAAwB;AACjC,QAAI,SAA4C;AAChD,QAAI;AACF,eAAS,aAAa,wBAAwB,MAAM;AACpD,aAAO,QAAQ,QAAQ;AAAA,IACzB,QAAQ;AACN,WAAK,aAAa,MAAA;AAClB;AAAA,IACF;AACA,UAAM,UAAU,IAAI,aAAa,SAAS,OAAO;AACjD,UAAM,WAAW,OAAO,YAAY,MAAM;AACxC,eAAS,uBAAuB,OAAO;AACvC,YAAM,QAAQ,wBAAwB,OAAO;AAC7C,iBAAW,CAAC,YAAY,eAAe,SAAS,KAAK,CAAC;AAAA,IACxD,GAAG,UAAU;AAEb,WAAO,MAAM;AACX,aAAO,cAAc,QAAQ;AAC7B,UAAI;AACF,yCAAQ;AACR,iBAAS,WAAA;AAAA,MACX,QAAQ;AAAA,MAER;AACA,WAAK,aAAa,MAAA;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,QAAQ,MAAM,UAAU,CAAC;AAE7B,SAAO;AACT;;;;;"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A rolling window of microphone levels (0..1), newest last, for the
|
|
3
|
+
* composer's waveform strip. `size` slots are always present: unfilled slots
|
|
4
|
+
* are `null` so the strip can draw them as the resting dotted baseline.
|
|
5
|
+
*/
|
|
6
|
+
export type AudioLevelHistory = ReadonlyArray<number | null>;
|
|
7
|
+
export declare function emptyAudioLevelHistory(size: number): AudioLevelHistory;
|
|
8
|
+
/** Append one level, dropping the oldest slot. Pure. */
|
|
9
|
+
export declare function pushAudioLevel(history: AudioLevelHistory, level: number): AudioLevelHistory;
|
|
10
|
+
/**
|
|
11
|
+
* RMS of a PCM sample window scaled to 0..1 for display. Speech peaks sit
|
|
12
|
+
* around 0.1-0.3 RMS, so a gain lifts them into the visible range while the
|
|
13
|
+
* noise floor (about 0.005) stays flat.
|
|
14
|
+
*/
|
|
15
|
+
export declare function displayLevelFromSamples(samples: ArrayLike<number>, gain?: number): number;
|
|
16
|
+
interface UseAudioLevelHistoryOptions {
|
|
17
|
+
/** Number of bars in the strip. */
|
|
18
|
+
size: number;
|
|
19
|
+
/** Sample cadence in ms; 50ms gives ~20 bars per second. */
|
|
20
|
+
intervalMs?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Sample a `MediaStream` through an `AnalyserNode` into a rolling level
|
|
24
|
+
* history. Returns the resting baseline when `stream` is null.
|
|
25
|
+
*/
|
|
26
|
+
export declare function useAudioLevelHistory(stream: MediaStream | null, { size, intervalMs }: UseAudioLevelHistoryOptions): AudioLevelHistory;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
function emptyAudioLevelHistory(size) {
|
|
3
|
+
return new Array(Math.max(0, size)).fill(null);
|
|
4
|
+
}
|
|
5
|
+
function pushAudioLevel(history, level) {
|
|
6
|
+
if (history.length === 0) return history;
|
|
7
|
+
const clamped = Math.min(1, Math.max(0, level));
|
|
8
|
+
return [...history.slice(1), clamped];
|
|
9
|
+
}
|
|
10
|
+
function displayLevelFromSamples(samples, gain = 4) {
|
|
11
|
+
if (samples.length === 0) return 0;
|
|
12
|
+
let sum = 0;
|
|
13
|
+
for (let i = 0; i < samples.length; i += 1) {
|
|
14
|
+
const sample = samples[i] ?? 0;
|
|
15
|
+
sum += sample * sample;
|
|
16
|
+
}
|
|
17
|
+
const rms = Math.sqrt(sum / samples.length);
|
|
18
|
+
return Math.min(1, rms * gain);
|
|
19
|
+
}
|
|
20
|
+
function useAudioLevelHistory(stream, { size, intervalMs = 50 }) {
|
|
21
|
+
const [history, setHistory] = useState(() => emptyAudioLevelHistory(size));
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
setHistory(emptyAudioLevelHistory(size));
|
|
24
|
+
if (!stream || typeof window === "undefined") return;
|
|
25
|
+
const AudioContextCtor = window.AudioContext ?? window.webkitAudioContext;
|
|
26
|
+
if (!AudioContextCtor) return;
|
|
27
|
+
let audioContext;
|
|
28
|
+
try {
|
|
29
|
+
audioContext = new AudioContextCtor();
|
|
30
|
+
} catch {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const analyser = audioContext.createAnalyser();
|
|
34
|
+
analyser.fftSize = 1024;
|
|
35
|
+
analyser.smoothingTimeConstant = 0;
|
|
36
|
+
let source = null;
|
|
37
|
+
try {
|
|
38
|
+
source = audioContext.createMediaStreamSource(stream);
|
|
39
|
+
source.connect(analyser);
|
|
40
|
+
} catch {
|
|
41
|
+
void audioContext.close();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const samples = new Float32Array(analyser.fftSize);
|
|
45
|
+
const interval = window.setInterval(() => {
|
|
46
|
+
analyser.getFloatTimeDomainData(samples);
|
|
47
|
+
const level = displayLevelFromSamples(samples);
|
|
48
|
+
setHistory((current) => pushAudioLevel(current, level));
|
|
49
|
+
}, intervalMs);
|
|
50
|
+
return () => {
|
|
51
|
+
window.clearInterval(interval);
|
|
52
|
+
try {
|
|
53
|
+
source == null ? void 0 : source.disconnect();
|
|
54
|
+
analyser.disconnect();
|
|
55
|
+
} catch {
|
|
56
|
+
}
|
|
57
|
+
void audioContext.close();
|
|
58
|
+
};
|
|
59
|
+
}, [stream, size, intervalMs]);
|
|
60
|
+
return history;
|
|
61
|
+
}
|
|
62
|
+
export {
|
|
63
|
+
displayLevelFromSamples,
|
|
64
|
+
emptyAudioLevelHistory,
|
|
65
|
+
pushAudioLevel,
|
|
66
|
+
useAudioLevelHistory
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=audio-level-history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-level-history.js","sources":["../../src/dictation/audio-level-history.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\n\n/**\n * A rolling window of microphone levels (0..1), newest last, for the\n * composer's waveform strip. `size` slots are always present: unfilled slots\n * are `null` so the strip can draw them as the resting dotted baseline.\n */\nexport type AudioLevelHistory = ReadonlyArray<number | null>;\n\nexport function emptyAudioLevelHistory(size: number): AudioLevelHistory {\n return new Array<number | null>(Math.max(0, size)).fill(null);\n}\n\n/** Append one level, dropping the oldest slot. Pure. */\nexport function pushAudioLevel(history: AudioLevelHistory, level: number): AudioLevelHistory {\n if (history.length === 0) return history;\n const clamped = Math.min(1, Math.max(0, level));\n return [...history.slice(1), clamped];\n}\n\n/**\n * RMS of a PCM sample window scaled to 0..1 for display. Speech peaks sit\n * around 0.1-0.3 RMS, so a gain lifts them into the visible range while the\n * noise floor (about 0.005) stays flat.\n */\nexport function displayLevelFromSamples(samples: ArrayLike<number>, gain = 4): number {\n if (samples.length === 0) return 0;\n let sum = 0;\n for (let i = 0; i < samples.length; i += 1) {\n const sample = samples[i] ?? 0;\n sum += sample * sample;\n }\n const rms = Math.sqrt(sum / samples.length);\n return Math.min(1, rms * gain);\n}\n\ninterface UseAudioLevelHistoryOptions {\n /** Number of bars in the strip. */\n size: number;\n /** Sample cadence in ms; 50ms gives ~20 bars per second. */\n intervalMs?: number;\n}\n\n/**\n * Sample a `MediaStream` through an `AnalyserNode` into a rolling level\n * history. Returns the resting baseline when `stream` is null.\n */\nexport function useAudioLevelHistory(\n stream: MediaStream | null,\n { size, intervalMs = 50 }: UseAudioLevelHistoryOptions,\n): AudioLevelHistory {\n const [history, setHistory] = useState<AudioLevelHistory>(() => emptyAudioLevelHistory(size));\n\n useEffect(() => {\n setHistory(emptyAudioLevelHistory(size));\n if (!stream || typeof window === 'undefined') return;\n const AudioContextCtor =\n window.AudioContext ??\n (window as typeof window & { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!AudioContextCtor) return;\n\n let audioContext: AudioContext;\n try {\n audioContext = new AudioContextCtor();\n } catch {\n return;\n }\n const analyser = audioContext.createAnalyser();\n analyser.fftSize = 1024;\n analyser.smoothingTimeConstant = 0;\n let source: MediaStreamAudioSourceNode | null = null;\n try {\n source = audioContext.createMediaStreamSource(stream);\n source.connect(analyser);\n } catch {\n void audioContext.close();\n return;\n }\n const samples = new Float32Array(analyser.fftSize);\n const interval = window.setInterval(() => {\n analyser.getFloatTimeDomainData(samples);\n const level = displayLevelFromSamples(samples);\n setHistory((current) => pushAudioLevel(current, level));\n }, intervalMs);\n\n return () => {\n window.clearInterval(interval);\n try {\n source?.disconnect();\n analyser.disconnect();\n } catch {\n // Already disconnected.\n }\n void audioContext.close();\n };\n }, [stream, size, intervalMs]);\n\n return history;\n}\n"],"names":[],"mappings":";AASO,SAAS,uBAAuB,MAAiC;AACtE,SAAO,IAAI,MAAqB,KAAK,IAAI,GAAG,IAAI,CAAC,EAAE,KAAK,IAAI;AAC9D;AAGO,SAAS,eAAe,SAA4B,OAAkC;AAC3F,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,QAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,CAAC;AAC9C,SAAO,CAAC,GAAG,QAAQ,MAAM,CAAC,GAAG,OAAO;AACtC;AAOO,SAAS,wBAAwB,SAA4B,OAAO,GAAW;AACpF,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,GAAG;AAC1C,UAAM,SAAS,QAAQ,CAAC,KAAK;AAC7B,WAAO,SAAS;AAAA,EAClB;AACA,QAAM,MAAM,KAAK,KAAK,MAAM,QAAQ,MAAM;AAC1C,SAAO,KAAK,IAAI,GAAG,MAAM,IAAI;AAC/B;AAaO,SAAS,qBACd,QACA,EAAE,MAAM,aAAa,MACF;AACnB,QAAM,CAAC,SAAS,UAAU,IAAI,SAA4B,MAAM,uBAAuB,IAAI,CAAC;AAE5F,YAAU,MAAM;AACd,eAAW,uBAAuB,IAAI,CAAC;AACvC,QAAI,CAAC,UAAU,OAAO,WAAW,YAAa;AAC9C,UAAM,mBACJ,OAAO,gBACN,OAAwE;AAC3E,QAAI,CAAC,iBAAkB;AAEvB,QAAI;AACJ,QAAI;AACF,qBAAe,IAAI,iBAAA;AAAA,IACrB,QAAQ;AACN;AAAA,IACF;AACA,UAAM,WAAW,aAAa,eAAA;AAC9B,aAAS,UAAU;AACnB,aAAS,wBAAwB;AACjC,QAAI,SAA4C;AAChD,QAAI;AACF,eAAS,aAAa,wBAAwB,MAAM;AACpD,aAAO,QAAQ,QAAQ;AAAA,IACzB,QAAQ;AACN,WAAK,aAAa,MAAA;AAClB;AAAA,IACF;AACA,UAAM,UAAU,IAAI,aAAa,SAAS,OAAO;AACjD,UAAM,WAAW,OAAO,YAAY,MAAM;AACxC,eAAS,uBAAuB,OAAO;AACvC,YAAM,QAAQ,wBAAwB,OAAO;AAC7C,iBAAW,CAAC,YAAY,eAAe,SAAS,KAAK,CAAC;AAAA,IACxD,GAAG,UAAU;AAEb,WAAO,MAAM;AACX,aAAO,cAAc,QAAQ;AAC7B,UAAI;AACF,yCAAQ;AACR,iBAAS,WAAA;AAAA,MACX,QAAQ;AAAA,MAER;AACA,WAAK,aAAa,MAAA;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,QAAQ,MAAM,UAAU,CAAC;AAE7B,SAAO;AACT;"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const lucideReact = require("lucide-react");
|
|
5
|
+
const tooltipIconButton = require("../chat/tooltip-icon-button.cjs");
|
|
6
|
+
const cnfast = require("cnfast");
|
|
7
|
+
const DictationMicButton = ({
|
|
8
|
+
onClick,
|
|
9
|
+
disabled,
|
|
10
|
+
className,
|
|
11
|
+
title = "Dictate a message"
|
|
12
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
13
|
+
tooltipIconButton.TooltipIconButton,
|
|
14
|
+
{
|
|
15
|
+
tooltip: title,
|
|
16
|
+
side: "bottom",
|
|
17
|
+
type: "button",
|
|
18
|
+
variant: "ghost",
|
|
19
|
+
size: "icon",
|
|
20
|
+
className: cnfast.cn("aui-composer-dictate size-8 rounded-full text-muted-foreground hover:text-foreground", className),
|
|
21
|
+
"aria-label": title,
|
|
22
|
+
onClick,
|
|
23
|
+
disabled,
|
|
24
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MicIcon, { className: "aui-composer-dictate-icon size-4" })
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
const DictationCancelButton = ({ onClick, disabled, className }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
28
|
+
tooltipIconButton.TooltipIconButton,
|
|
29
|
+
{
|
|
30
|
+
tooltip: "Cancel dictation",
|
|
31
|
+
side: "bottom",
|
|
32
|
+
type: "button",
|
|
33
|
+
variant: "outline",
|
|
34
|
+
size: "icon",
|
|
35
|
+
className: cnfast.cn("aui-composer-dictate-cancel size-8 rounded-full", className),
|
|
36
|
+
"aria-label": "Cancel dictation",
|
|
37
|
+
onClick,
|
|
38
|
+
disabled,
|
|
39
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XIcon, { className: "size-4" })
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
const DictationStopButton = ({ onClick, disabled, className }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
43
|
+
tooltipIconButton.TooltipIconButton,
|
|
44
|
+
{
|
|
45
|
+
tooltip: "Stop and review",
|
|
46
|
+
side: "bottom",
|
|
47
|
+
type: "button",
|
|
48
|
+
variant: "outline",
|
|
49
|
+
size: "icon",
|
|
50
|
+
className: cnfast.cn("aui-composer-dictate-stop size-8 rounded-full", className),
|
|
51
|
+
"aria-label": "Stop dictation",
|
|
52
|
+
onClick,
|
|
53
|
+
disabled,
|
|
54
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SquareIcon, { className: "size-3 fill-current" })
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
exports.DictationCancelButton = DictationCancelButton;
|
|
58
|
+
exports.DictationMicButton = DictationMicButton;
|
|
59
|
+
exports.DictationStopButton = DictationStopButton;
|
|
60
|
+
//# sourceMappingURL=dictation-controls.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dictation-controls.cjs","sources":["../../src/dictation/dictation-controls.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { MicIcon, SquareIcon, XIcon } from 'lucide-react';\nimport { TooltipIconButton } from '../chat/tooltip-icon-button';\nimport { cn } from '../lib/utils';\n\ninterface DictationControlProps {\n onClick: () => void;\n disabled?: boolean;\n className?: string;\n}\n\n/** Toolbar mic: starts a dictation session. */\nexport const DictationMicButton: FC<DictationControlProps & { title?: string }> = ({\n onClick,\n disabled,\n className,\n title = 'Dictate a message',\n}) => (\n <TooltipIconButton\n tooltip={title}\n side=\"bottom\"\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className={cn('aui-composer-dictate size-8 rounded-full text-muted-foreground hover:text-foreground', className)}\n aria-label={title}\n onClick={onClick}\n disabled={disabled}\n >\n <MicIcon className=\"aui-composer-dictate-icon size-4\" />\n </TooltipIconButton>\n);\n\n/** Recording mode, left side: discard the session. */\nexport const DictationCancelButton: FC<DictationControlProps> = ({ onClick, disabled, className }) => (\n <TooltipIconButton\n tooltip=\"Cancel dictation\"\n side=\"bottom\"\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n className={cn('aui-composer-dictate-cancel size-8 rounded-full', className)}\n aria-label=\"Cancel dictation\"\n onClick={onClick}\n disabled={disabled}\n >\n <XIcon className=\"size-4\" />\n </TooltipIconButton>\n);\n\n/** Recording mode, right side: stop and put the words in the composer. */\nexport const DictationStopButton: FC<DictationControlProps> = ({ onClick, disabled, className }) => (\n <TooltipIconButton\n tooltip=\"Stop and review\"\n side=\"bottom\"\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n className={cn('aui-composer-dictate-stop size-8 rounded-full', className)}\n aria-label=\"Stop dictation\"\n onClick={onClick}\n disabled={disabled}\n >\n <SquareIcon className=\"size-3 fill-current\" />\n </TooltipIconButton>\n);\n"],"names":["jsx","TooltipIconButton","cn","MicIcon","XIcon","SquareIcon"],"mappings":";;;;;;AAYO,MAAM,qBAAqE,CAAC;AAAA,EACjF;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AACV,MACEA,2BAAAA;AAAAA,EAACC,kBAAAA;AAAAA,EAAA;AAAA,IACC,SAAS;AAAA,IACT,MAAK;AAAA,IACL,MAAK;AAAA,IACL,SAAQ;AAAA,IACR,MAAK;AAAA,IACL,WAAWC,OAAAA,GAAG,wFAAwF,SAAS;AAAA,IAC/G,cAAY;AAAA,IACZ;AAAA,IACA;AAAA,IAEA,UAAAF,2BAAAA,IAACG,YAAAA,SAAA,EAAQ,WAAU,mCAAA,CAAmC;AAAA,EAAA;AACxD;AAIK,MAAM,wBAAmD,CAAC,EAAE,SAAS,UAAU,gBACpFH,2BAAAA;AAAAA,EAACC,kBAAAA;AAAAA,EAAA;AAAA,IACC,SAAQ;AAAA,IACR,MAAK;AAAA,IACL,MAAK;AAAA,IACL,SAAQ;AAAA,IACR,MAAK;AAAA,IACL,WAAWC,OAAAA,GAAG,mDAAmD,SAAS;AAAA,IAC1E,cAAW;AAAA,IACX;AAAA,IACA;AAAA,IAEA,UAAAF,2BAAAA,IAACI,YAAAA,OAAA,EAAM,WAAU,SAAA,CAAS;AAAA,EAAA;AAC5B;AAIK,MAAM,sBAAiD,CAAC,EAAE,SAAS,UAAU,gBAClFJ,2BAAAA;AAAAA,EAACC,kBAAAA;AAAAA,EAAA;AAAA,IACC,SAAQ;AAAA,IACR,MAAK;AAAA,IACL,MAAK;AAAA,IACL,SAAQ;AAAA,IACR,MAAK;AAAA,IACL,WAAWC,OAAAA,GAAG,iDAAiD,SAAS;AAAA,IACxE,cAAW;AAAA,IACX;AAAA,IACA;AAAA,IAEA,UAAAF,2BAAAA,IAACK,YAAAA,YAAA,EAAW,WAAU,sBAAA,CAAsB;AAAA,EAAA;AAC9C;;;;"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FC } from 'react';
|
|
2
|
+
interface DictationControlProps {
|
|
3
|
+
onClick: () => void;
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
className?: string;
|
|
6
|
+
}
|
|
7
|
+
/** Toolbar mic: starts a dictation session. */
|
|
8
|
+
export declare const DictationMicButton: FC<DictationControlProps & {
|
|
9
|
+
title?: string;
|
|
10
|
+
}>;
|
|
11
|
+
/** Recording mode, left side: discard the session. */
|
|
12
|
+
export declare const DictationCancelButton: FC<DictationControlProps>;
|
|
13
|
+
/** Recording mode, right side: stop and put the words in the composer. */
|
|
14
|
+
export declare const DictationStopButton: FC<DictationControlProps>;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { SquareIcon, MicIcon, XIcon } from "lucide-react";
|
|
3
|
+
import { TooltipIconButton } from "../chat/tooltip-icon-button.js";
|
|
4
|
+
import { cn } from "cnfast";
|
|
5
|
+
const DictationMicButton = ({
|
|
6
|
+
onClick,
|
|
7
|
+
disabled,
|
|
8
|
+
className,
|
|
9
|
+
title = "Dictate a message"
|
|
10
|
+
}) => /* @__PURE__ */ jsx(
|
|
11
|
+
TooltipIconButton,
|
|
12
|
+
{
|
|
13
|
+
tooltip: title,
|
|
14
|
+
side: "bottom",
|
|
15
|
+
type: "button",
|
|
16
|
+
variant: "ghost",
|
|
17
|
+
size: "icon",
|
|
18
|
+
className: cn("aui-composer-dictate size-8 rounded-full text-muted-foreground hover:text-foreground", className),
|
|
19
|
+
"aria-label": title,
|
|
20
|
+
onClick,
|
|
21
|
+
disabled,
|
|
22
|
+
children: /* @__PURE__ */ jsx(MicIcon, { className: "aui-composer-dictate-icon size-4" })
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
const DictationCancelButton = ({ onClick, disabled, className }) => /* @__PURE__ */ jsx(
|
|
26
|
+
TooltipIconButton,
|
|
27
|
+
{
|
|
28
|
+
tooltip: "Cancel dictation",
|
|
29
|
+
side: "bottom",
|
|
30
|
+
type: "button",
|
|
31
|
+
variant: "outline",
|
|
32
|
+
size: "icon",
|
|
33
|
+
className: cn("aui-composer-dictate-cancel size-8 rounded-full", className),
|
|
34
|
+
"aria-label": "Cancel dictation",
|
|
35
|
+
onClick,
|
|
36
|
+
disabled,
|
|
37
|
+
children: /* @__PURE__ */ jsx(XIcon, { className: "size-4" })
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
const DictationStopButton = ({ onClick, disabled, className }) => /* @__PURE__ */ jsx(
|
|
41
|
+
TooltipIconButton,
|
|
42
|
+
{
|
|
43
|
+
tooltip: "Stop and review",
|
|
44
|
+
side: "bottom",
|
|
45
|
+
type: "button",
|
|
46
|
+
variant: "outline",
|
|
47
|
+
size: "icon",
|
|
48
|
+
className: cn("aui-composer-dictate-stop size-8 rounded-full", className),
|
|
49
|
+
"aria-label": "Stop dictation",
|
|
50
|
+
onClick,
|
|
51
|
+
disabled,
|
|
52
|
+
children: /* @__PURE__ */ jsx(SquareIcon, { className: "size-3 fill-current" })
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
export {
|
|
56
|
+
DictationCancelButton,
|
|
57
|
+
DictationMicButton,
|
|
58
|
+
DictationStopButton
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=dictation-controls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dictation-controls.js","sources":["../../src/dictation/dictation-controls.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { MicIcon, SquareIcon, XIcon } from 'lucide-react';\nimport { TooltipIconButton } from '../chat/tooltip-icon-button';\nimport { cn } from '../lib/utils';\n\ninterface DictationControlProps {\n onClick: () => void;\n disabled?: boolean;\n className?: string;\n}\n\n/** Toolbar mic: starts a dictation session. */\nexport const DictationMicButton: FC<DictationControlProps & { title?: string }> = ({\n onClick,\n disabled,\n className,\n title = 'Dictate a message',\n}) => (\n <TooltipIconButton\n tooltip={title}\n side=\"bottom\"\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className={cn('aui-composer-dictate size-8 rounded-full text-muted-foreground hover:text-foreground', className)}\n aria-label={title}\n onClick={onClick}\n disabled={disabled}\n >\n <MicIcon className=\"aui-composer-dictate-icon size-4\" />\n </TooltipIconButton>\n);\n\n/** Recording mode, left side: discard the session. */\nexport const DictationCancelButton: FC<DictationControlProps> = ({ onClick, disabled, className }) => (\n <TooltipIconButton\n tooltip=\"Cancel dictation\"\n side=\"bottom\"\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n className={cn('aui-composer-dictate-cancel size-8 rounded-full', className)}\n aria-label=\"Cancel dictation\"\n onClick={onClick}\n disabled={disabled}\n >\n <XIcon className=\"size-4\" />\n </TooltipIconButton>\n);\n\n/** Recording mode, right side: stop and put the words in the composer. */\nexport const DictationStopButton: FC<DictationControlProps> = ({ onClick, disabled, className }) => (\n <TooltipIconButton\n tooltip=\"Stop and review\"\n side=\"bottom\"\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n className={cn('aui-composer-dictate-stop size-8 rounded-full', className)}\n aria-label=\"Stop dictation\"\n onClick={onClick}\n disabled={disabled}\n >\n <SquareIcon className=\"size-3 fill-current\" />\n </TooltipIconButton>\n);\n"],"names":[],"mappings":";;;;AAYO,MAAM,qBAAqE,CAAC;AAAA,EACjF;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AACV,MACE;AAAA,EAAC;AAAA,EAAA;AAAA,IACC,SAAS;AAAA,IACT,MAAK;AAAA,IACL,MAAK;AAAA,IACL,SAAQ;AAAA,IACR,MAAK;AAAA,IACL,WAAW,GAAG,wFAAwF,SAAS;AAAA,IAC/G,cAAY;AAAA,IACZ;AAAA,IACA;AAAA,IAEA,UAAA,oBAAC,SAAA,EAAQ,WAAU,mCAAA,CAAmC;AAAA,EAAA;AACxD;AAIK,MAAM,wBAAmD,CAAC,EAAE,SAAS,UAAU,gBACpF;AAAA,EAAC;AAAA,EAAA;AAAA,IACC,SAAQ;AAAA,IACR,MAAK;AAAA,IACL,MAAK;AAAA,IACL,SAAQ;AAAA,IACR,MAAK;AAAA,IACL,WAAW,GAAG,mDAAmD,SAAS;AAAA,IAC1E,cAAW;AAAA,IACX;AAAA,IACA;AAAA,IAEA,UAAA,oBAAC,OAAA,EAAM,WAAU,SAAA,CAAS;AAAA,EAAA;AAC5B;AAIK,MAAM,sBAAiD,CAAC,EAAE,SAAS,UAAU,gBAClF;AAAA,EAAC;AAAA,EAAA;AAAA,IACC,SAAQ;AAAA,IACR,MAAK;AAAA,IACL,MAAK;AAAA,IACL,SAAQ;AAAA,IACR,MAAK;AAAA,IACL,WAAW,GAAG,iDAAiD,SAAS;AAAA,IACxE,cAAW;AAAA,IACX;AAAA,IACA;AAAA,IAEA,UAAA,oBAAC,YAAA,EAAW,WAAU,sBAAA,CAAsB;AAAA,EAAA;AAC9C;"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const DICTATION_ENGINE_ERROR_KINDS = {
|
|
4
|
+
/** The user denied the microphone, or the browser blocked it. */
|
|
5
|
+
permission: "permission",
|
|
6
|
+
/** No speech engine is available in this browser. */
|
|
7
|
+
unsupported: "unsupported",
|
|
8
|
+
/** The engine could not reach its recognizer. */
|
|
9
|
+
connection: "connection",
|
|
10
|
+
/** The engine heard nothing for its silence window. */
|
|
11
|
+
noSpeech: "no-speech",
|
|
12
|
+
unknown: "unknown"
|
|
13
|
+
};
|
|
14
|
+
function resolveSpeechRecognitionCtor() {
|
|
15
|
+
if (typeof window === "undefined") return null;
|
|
16
|
+
const scope = window;
|
|
17
|
+
return scope.SpeechRecognition ?? scope.webkitSpeechRecognition ?? null;
|
|
18
|
+
}
|
|
19
|
+
function classifyWebSpeechError(code) {
|
|
20
|
+
switch (code) {
|
|
21
|
+
case "not-allowed":
|
|
22
|
+
case "service-not-allowed":
|
|
23
|
+
return {
|
|
24
|
+
kind: DICTATION_ENGINE_ERROR_KINDS.permission,
|
|
25
|
+
message: "Microphone access was denied. Allow the microphone for this site and try again."
|
|
26
|
+
};
|
|
27
|
+
case "network":
|
|
28
|
+
return {
|
|
29
|
+
kind: DICTATION_ENGINE_ERROR_KINDS.connection,
|
|
30
|
+
message: "Speech recognition could not connect. Check your connection and try again."
|
|
31
|
+
};
|
|
32
|
+
case "no-speech":
|
|
33
|
+
return {
|
|
34
|
+
kind: DICTATION_ENGINE_ERROR_KINDS.noSpeech,
|
|
35
|
+
message: "No speech was heard."
|
|
36
|
+
};
|
|
37
|
+
case "audio-capture":
|
|
38
|
+
return {
|
|
39
|
+
kind: DICTATION_ENGINE_ERROR_KINDS.permission,
|
|
40
|
+
message: "No microphone was found."
|
|
41
|
+
};
|
|
42
|
+
case "aborted":
|
|
43
|
+
return { kind: DICTATION_ENGINE_ERROR_KINDS.unknown, message: "Dictation was interrupted." };
|
|
44
|
+
default:
|
|
45
|
+
return { kind: DICTATION_ENGINE_ERROR_KINDS.unknown, message: "Dictation stopped unexpectedly." };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function foldWebSpeechResults(results, resultIndex) {
|
|
49
|
+
var _a;
|
|
50
|
+
let final = "";
|
|
51
|
+
let interim = "";
|
|
52
|
+
for (let i = resultIndex; i < results.length; i += 1) {
|
|
53
|
+
const result = results[i];
|
|
54
|
+
const transcript = ((_a = result == null ? void 0 : result[0]) == null ? void 0 : _a.transcript) ?? "";
|
|
55
|
+
if (!transcript) continue;
|
|
56
|
+
if (result.isFinal) final = final ? `${final} ${transcript.trim()}` : transcript.trim();
|
|
57
|
+
else interim = interim ? `${interim} ${transcript.trim()}` : transcript.trim();
|
|
58
|
+
}
|
|
59
|
+
return { final, interim };
|
|
60
|
+
}
|
|
61
|
+
function createWebSpeechDictationEngine() {
|
|
62
|
+
return {
|
|
63
|
+
id: "web-speech",
|
|
64
|
+
isSupported: () => resolveSpeechRecognitionCtor() !== null,
|
|
65
|
+
start: ({ lang, callbacks }) => {
|
|
66
|
+
const Ctor = resolveSpeechRecognitionCtor();
|
|
67
|
+
if (!Ctor) {
|
|
68
|
+
callbacks.onError({
|
|
69
|
+
kind: DICTATION_ENGINE_ERROR_KINDS.unsupported,
|
|
70
|
+
message: "This browser has no speech recognition."
|
|
71
|
+
});
|
|
72
|
+
callbacks.onEnd();
|
|
73
|
+
return { stop: () => void 0, abort: () => void 0 };
|
|
74
|
+
}
|
|
75
|
+
const recognition = new Ctor();
|
|
76
|
+
recognition.continuous = true;
|
|
77
|
+
recognition.interimResults = true;
|
|
78
|
+
recognition.lang = lang;
|
|
79
|
+
let ended = false;
|
|
80
|
+
const end = () => {
|
|
81
|
+
if (ended) return;
|
|
82
|
+
ended = true;
|
|
83
|
+
recognition.onresult = null;
|
|
84
|
+
recognition.onerror = null;
|
|
85
|
+
recognition.onend = null;
|
|
86
|
+
callbacks.onEnd();
|
|
87
|
+
};
|
|
88
|
+
recognition.onresult = (event) => {
|
|
89
|
+
callbacks.onTranscript(foldWebSpeechResults(event.results, event.resultIndex));
|
|
90
|
+
};
|
|
91
|
+
recognition.onerror = (event) => {
|
|
92
|
+
if (event.error === "aborted") return;
|
|
93
|
+
callbacks.onError(classifyWebSpeechError(event.error));
|
|
94
|
+
};
|
|
95
|
+
recognition.onend = end;
|
|
96
|
+
try {
|
|
97
|
+
recognition.start();
|
|
98
|
+
} catch (error) {
|
|
99
|
+
callbacks.onError({
|
|
100
|
+
kind: DICTATION_ENGINE_ERROR_KINDS.unknown,
|
|
101
|
+
message: error instanceof Error ? error.message : "Dictation could not start."
|
|
102
|
+
});
|
|
103
|
+
end();
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
stop: () => {
|
|
107
|
+
try {
|
|
108
|
+
recognition.stop();
|
|
109
|
+
} catch {
|
|
110
|
+
end();
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
abort: () => {
|
|
114
|
+
try {
|
|
115
|
+
recognition.abort();
|
|
116
|
+
} finally {
|
|
117
|
+
end();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
exports.DICTATION_ENGINE_ERROR_KINDS = DICTATION_ENGINE_ERROR_KINDS;
|
|
125
|
+
exports.classifyWebSpeechError = classifyWebSpeechError;
|
|
126
|
+
exports.createWebSpeechDictationEngine = createWebSpeechDictationEngine;
|
|
127
|
+
exports.foldWebSpeechResults = foldWebSpeechResults;
|
|
128
|
+
//# sourceMappingURL=dictation-engine.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dictation-engine.cjs","sources":["../../src/dictation/dictation-engine.ts"],"sourcesContent":["/**\n * Speech-to-text engines behind the composer's dictation mode.\n *\n * An engine turns microphone audio into a stream of transcript updates. The\n * SDK ships the browser's own `SpeechRecognition` engine (Chrome, Edge,\n * Safari), which needs no backend. A host can plug in another engine (for\n * example a relay to a server-side recognizer) through the `engine` option\n * of `useDictation` / `<AthenaChat dictation>`; the composer UI is engine\n * agnostic.\n */\n\nexport interface DictationTranscriptUpdate {\n /** Text the engine has committed. Appended to earlier committed text. */\n final: string;\n /** Text still being recognized; replaced on the next update. */\n interim: string;\n}\n\nexport interface DictationEngineCallbacks {\n onTranscript: (update: DictationTranscriptUpdate) => void;\n /** The engine stopped on its own (silence timeout, network, permission). */\n onEnd: () => void;\n onError: (error: DictationEngineError) => void;\n}\n\nexport interface DictationEngineSession {\n /** Ask the engine to finish. `onEnd` fires once it has flushed. */\n stop: () => void;\n /** Tear the engine down without waiting for a flush. */\n abort: () => void;\n}\n\nexport interface DictationEngine {\n /** Stable identifier surfaced in diagnostics (e.g. `web-speech`). */\n readonly id: string;\n /**\n * Whether this engine can run in the current browser. When false the\n * composer hides the mic instead of rendering a control that cannot work.\n */\n isSupported: () => boolean;\n /**\n * How long `stop()` may wait for the final transcript before the session\n * is forced closed. Engines that transcribe after recording need more than\n * the live default (1.5s).\n */\n readonly flushTimeoutMs?: number;\n /** Status shown while flushing (e.g. `Transcribing…`); defaults to `Finishing…`. */\n readonly stoppingLabel?: string;\n /**\n * Start recognizing. `stream` is the microphone stream the composer already\n * opened for the waveform; engines that read audio themselves (Web Speech)\n * ignore it, relay engines encode it.\n */\n start: (options: {\n stream: MediaStream | null;\n lang: string;\n callbacks: DictationEngineCallbacks;\n }) => DictationEngineSession;\n}\n\nexport const DICTATION_ENGINE_ERROR_KINDS = {\n /** The user denied the microphone, or the browser blocked it. */\n permission: 'permission',\n /** No speech engine is available in this browser. */\n unsupported: 'unsupported',\n /** The engine could not reach its recognizer. */\n connection: 'connection',\n /** The engine heard nothing for its silence window. */\n noSpeech: 'no-speech',\n unknown: 'unknown',\n} as const;\n\nexport type DictationEngineErrorKind =\n (typeof DICTATION_ENGINE_ERROR_KINDS)[keyof typeof DICTATION_ENGINE_ERROR_KINDS];\n\nexport interface DictationEngineError {\n kind: DictationEngineErrorKind;\n /** One line the composer can show the user. */\n message: string;\n}\n\n// ─── Web Speech API engine ──────────────────────────────────────────\n\n// The Web Speech API has no lib.dom entry in every TypeScript version the SDK\n// supports, and browsers still vendor-prefix the constructor.\ninterface SpeechRecognitionAlternativeLike {\n transcript: string;\n}\ninterface SpeechRecognitionResultLike {\n isFinal: boolean;\n readonly length: number;\n [index: number]: SpeechRecognitionAlternativeLike;\n}\ninterface SpeechRecognitionEventLike extends Event {\n resultIndex: number;\n results: ArrayLike<SpeechRecognitionResultLike>;\n}\ninterface SpeechRecognitionErrorEventLike extends Event {\n error: string;\n message?: string;\n}\ninterface SpeechRecognitionLike extends EventTarget {\n continuous: boolean;\n interimResults: boolean;\n lang: string;\n start: () => void;\n stop: () => void;\n abort: () => void;\n onresult: ((event: SpeechRecognitionEventLike) => void) | null;\n onerror: ((event: SpeechRecognitionErrorEventLike) => void) | null;\n onend: (() => void) | null;\n}\ntype SpeechRecognitionCtor = new () => SpeechRecognitionLike;\n\nfunction resolveSpeechRecognitionCtor(): SpeechRecognitionCtor | null {\n if (typeof window === 'undefined') return null;\n const scope = window as unknown as {\n SpeechRecognition?: SpeechRecognitionCtor;\n webkitSpeechRecognition?: SpeechRecognitionCtor;\n };\n return scope.SpeechRecognition ?? scope.webkitSpeechRecognition ?? null;\n}\n\n/** Map a Web Speech `error` code to the engine's error vocabulary. */\nexport function classifyWebSpeechError(code: string): DictationEngineError {\n switch (code) {\n case 'not-allowed':\n case 'service-not-allowed':\n return {\n kind: DICTATION_ENGINE_ERROR_KINDS.permission,\n message: 'Microphone access was denied. Allow the microphone for this site and try again.',\n };\n case 'network':\n return {\n kind: DICTATION_ENGINE_ERROR_KINDS.connection,\n message: 'Speech recognition could not connect. Check your connection and try again.',\n };\n case 'no-speech':\n return {\n kind: DICTATION_ENGINE_ERROR_KINDS.noSpeech,\n message: 'No speech was heard.',\n };\n case 'audio-capture':\n return {\n kind: DICTATION_ENGINE_ERROR_KINDS.permission,\n message: 'No microphone was found.',\n };\n case 'aborted':\n return { kind: DICTATION_ENGINE_ERROR_KINDS.unknown, message: 'Dictation was interrupted.' };\n default:\n return { kind: DICTATION_ENGINE_ERROR_KINDS.unknown, message: 'Dictation stopped unexpectedly.' };\n }\n}\n\n/**\n * Fold one Web Speech result event into `{ final, interim }`. Finalized\n * alternatives from `resultIndex` onward are joined with a space; the rest is\n * the interim tail. Pure so it can be tested without a browser.\n */\nexport function foldWebSpeechResults(\n results: ArrayLike<SpeechRecognitionResultLike>,\n resultIndex: number,\n): DictationTranscriptUpdate {\n let final = '';\n let interim = '';\n for (let i = resultIndex; i < results.length; i += 1) {\n const result = results[i];\n const transcript = result?.[0]?.transcript ?? '';\n if (!transcript) continue;\n if (result.isFinal) final = final ? `${final} ${transcript.trim()}` : transcript.trim();\n else interim = interim ? `${interim} ${transcript.trim()}` : transcript.trim();\n }\n return { final, interim };\n}\n\n/**\n * The browser's own recognizer. Zero dependencies and zero backend; Chrome\n * and Edge stream partial results, Safari returns results in chunks, Firefox\n * has no implementation (`isSupported()` is false there).\n */\nexport function createWebSpeechDictationEngine(): DictationEngine {\n return {\n id: 'web-speech',\n isSupported: () => resolveSpeechRecognitionCtor() !== null,\n start: ({ lang, callbacks }) => {\n const Ctor = resolveSpeechRecognitionCtor();\n if (!Ctor) {\n callbacks.onError({\n kind: DICTATION_ENGINE_ERROR_KINDS.unsupported,\n message: 'This browser has no speech recognition.',\n });\n callbacks.onEnd();\n return { stop: () => undefined, abort: () => undefined };\n }\n const recognition = new Ctor();\n recognition.continuous = true;\n recognition.interimResults = true;\n recognition.lang = lang;\n let ended = false;\n const end = () => {\n if (ended) return;\n ended = true;\n recognition.onresult = null;\n recognition.onerror = null;\n recognition.onend = null;\n callbacks.onEnd();\n };\n recognition.onresult = (event) => {\n callbacks.onTranscript(foldWebSpeechResults(event.results, event.resultIndex));\n };\n recognition.onerror = (event) => {\n // `aborted` follows our own abort(); the session already ended.\n if (event.error === 'aborted') return;\n callbacks.onError(classifyWebSpeechError(event.error));\n };\n recognition.onend = end;\n try {\n recognition.start();\n } catch (error) {\n callbacks.onError({\n kind: DICTATION_ENGINE_ERROR_KINDS.unknown,\n message: error instanceof Error ? error.message : 'Dictation could not start.',\n });\n end();\n }\n return {\n stop: () => {\n try {\n recognition.stop();\n } catch {\n end();\n }\n },\n abort: () => {\n try {\n recognition.abort();\n } finally {\n end();\n }\n },\n };\n },\n };\n}\n"],"names":[],"mappings":";;AA4DO,MAAM,+BAA+B;AAAA;AAAA,EAE1C,YAAY;AAAA;AAAA,EAEZ,aAAa;AAAA;AAAA,EAEb,YAAY;AAAA;AAAA,EAEZ,UAAU;AAAA,EACV,SAAS;AACX;AA4CA,SAAS,+BAA6D;AACpE,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,QAAM,QAAQ;AAId,SAAO,MAAM,qBAAqB,MAAM,2BAA2B;AACrE;AAGO,SAAS,uBAAuB,MAAoC;AACzE,UAAQ,MAAA;AAAA,IACN,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,QACL,MAAM,6BAA6B;AAAA,QACnC,SAAS;AAAA,MAAA;AAAA,IAEb,KAAK;AACH,aAAO;AAAA,QACL,MAAM,6BAA6B;AAAA,QACnC,SAAS;AAAA,MAAA;AAAA,IAEb,KAAK;AACH,aAAO;AAAA,QACL,MAAM,6BAA6B;AAAA,QACnC,SAAS;AAAA,MAAA;AAAA,IAEb,KAAK;AACH,aAAO;AAAA,QACL,MAAM,6BAA6B;AAAA,QACnC,SAAS;AAAA,MAAA;AAAA,IAEb,KAAK;AACH,aAAO,EAAE,MAAM,6BAA6B,SAAS,SAAS,6BAAA;AAAA,IAChE;AACE,aAAO,EAAE,MAAM,6BAA6B,SAAS,SAAS,kCAAA;AAAA,EAAkC;AAEtG;AAOO,SAAS,qBACd,SACA,aAC2B;;AAC3B,MAAI,QAAQ;AACZ,MAAI,UAAU;AACd,WAAS,IAAI,aAAa,IAAI,QAAQ,QAAQ,KAAK,GAAG;AACpD,UAAM,SAAS,QAAQ,CAAC;AACxB,UAAM,eAAa,sCAAS,OAAT,mBAAa,eAAc;AAC9C,QAAI,CAAC,WAAY;AACjB,QAAI,OAAO,QAAS,SAAQ,QAAQ,GAAG,KAAK,IAAI,WAAW,KAAA,CAAM,KAAK,WAAW,KAAA;AAAA,QAC5E,WAAU,UAAU,GAAG,OAAO,IAAI,WAAW,MAAM,KAAK,WAAW,KAAA;AAAA,EAC1E;AACA,SAAO,EAAE,OAAO,QAAA;AAClB;AAOO,SAAS,iCAAkD;AAChE,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,aAAa,MAAM,6BAAA,MAAmC;AAAA,IACtD,OAAO,CAAC,EAAE,MAAM,gBAAgB;AAC9B,YAAM,OAAO,6BAAA;AACb,UAAI,CAAC,MAAM;AACT,kBAAU,QAAQ;AAAA,UAChB,MAAM,6BAA6B;AAAA,UACnC,SAAS;AAAA,QAAA,CACV;AACD,kBAAU,MAAA;AACV,eAAO,EAAE,MAAM,MAAM,QAAW,OAAO,MAAM,OAAA;AAAA,MAC/C;AACA,YAAM,cAAc,IAAI,KAAA;AACxB,kBAAY,aAAa;AACzB,kBAAY,iBAAiB;AAC7B,kBAAY,OAAO;AACnB,UAAI,QAAQ;AACZ,YAAM,MAAM,MAAM;AAChB,YAAI,MAAO;AACX,gBAAQ;AACR,oBAAY,WAAW;AACvB,oBAAY,UAAU;AACtB,oBAAY,QAAQ;AACpB,kBAAU,MAAA;AAAA,MACZ;AACA,kBAAY,WAAW,CAAC,UAAU;AAChC,kBAAU,aAAa,qBAAqB,MAAM,SAAS,MAAM,WAAW,CAAC;AAAA,MAC/E;AACA,kBAAY,UAAU,CAAC,UAAU;AAE/B,YAAI,MAAM,UAAU,UAAW;AAC/B,kBAAU,QAAQ,uBAAuB,MAAM,KAAK,CAAC;AAAA,MACvD;AACA,kBAAY,QAAQ;AACpB,UAAI;AACF,oBAAY,MAAA;AAAA,MACd,SAAS,OAAO;AACd,kBAAU,QAAQ;AAAA,UAChB,MAAM,6BAA6B;AAAA,UACnC,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QAAA,CACnD;AACD,YAAA;AAAA,MACF;AACA,aAAO;AAAA,QACL,MAAM,MAAM;AACV,cAAI;AACF,wBAAY,KAAA;AAAA,UACd,QAAQ;AACN,gBAAA;AAAA,UACF;AAAA,QACF;AAAA,QACA,OAAO,MAAM;AACX,cAAI;AACF,wBAAY,MAAA;AAAA,UACd,UAAA;AACE,gBAAA;AAAA,UACF;AAAA,QACF;AAAA,MAAA;AAAA,IAEJ;AAAA,EAAA;AAEJ;;;;;"}
|