@sdata/web-vue 3.22.3 → 3.23.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/es/index.d.ts CHANGED
@@ -128,8 +128,8 @@ export { default as Scrollbar } from './scrollbar';
128
128
  export type { ScrollbarElements, ScrollbarEventListener, ScrollbarEventListenerArgs, ScrollbarEventListeners, ScrollbarExpose, ScrollbarInstance, ScrollbarOptions, ScrollbarOptionsResolved, ScrollbarPlugin, ScrollbarProps, ScrollbarReadonlyOptions, ScrollbarState, ScrollbarType, ScrollbarUpdatedEvent, } from './scrollbar';
129
129
  export { default as Select } from './select';
130
130
  export type { FilterOption, SelectFallbackOption, SelectFieldNames, SelectInstance, SelectOption, SelectOptionData, SelectOptionGroup, SelectProps, } from './select';
131
- export { default as Sender, SenderHeader, SenderSwitch } from './sender';
132
- export type { SenderActionContext, SenderAllowSpeech, SenderComponents, SenderContentSlotConfig, SenderControlledSpeechConfig, SenderCustomSlotConfig, SenderCustomSlotRender, SenderCustomSlotState, SenderEmits, SenderFocusOptions, SenderHeaderInstance, SenderHeaderProps, SenderHeaderSemanticType, SenderInsertPosition, SenderInputSlotConfig, SenderInstance, SenderProps, SenderRef, SenderResolvedSlotConfig, SenderSelectSlotConfig, SenderSemanticType, SenderSkill, SenderSkillClosableConfig, SenderSpeechConfig, SenderSpeechDataEvent, SenderSpeechEndEvent, SenderSpeechEndReason, SenderSpeechErrorEvent, SenderSpeechErrorPhase, SenderSpeechStartEvent, SenderSpeechTransportCloseEvent, SenderSpeechTransportMessageEvent, SenderSpeechTransportOpenEvent, SenderSlotConfig, SenderSlotConfigBase, SenderSubmitType, SenderSuffixPlacement, SenderSwitchInstance, SenderSwitchProps, SenderTagSlotConfig, SenderTextSlotConfig, SenderValue, } from './sender';
131
+ export { default as Sender, RecorderCore, SenderHeader, SenderSwitch } from './sender';
132
+ export type { RecorderCoreConstructor, RecorderCoreInstance, RecorderCoreOptions, SenderActionContext, SenderAllowSpeech, SenderComponents, SenderContentSlotConfig, SenderCustomSlotConfig, SenderCustomSlotRender, SenderCustomSlotState, SenderEmits, SenderFocusOptions, SenderHeaderInstance, SenderHeaderProps, SenderHeaderSemanticType, SenderInsertPosition, SenderInputSlotConfig, SenderInstance, SenderProps, SenderRef, SenderResolvedSlotConfig, SenderSelectSlotConfig, SenderSemanticType, SenderSkill, SenderSkillClosableConfig, SenderSlotConfig, SenderSlotConfigBase, SenderSubmitType, SenderSuffixPlacement, SenderSwitchInstance, SenderSwitchProps, SenderTagSlotConfig, SenderTextSlotConfig, SenderValue, } from './sender';
133
133
  export { default as Secret } from './secret';
134
134
  export type { SecretInstance } from './secret';
135
135
  export { default as Skeleton, SkeletonLine, SkeletonShape } from './skeleton';
package/es/index.js CHANGED
@@ -128,6 +128,7 @@ import { RICH_TEXT_EDITOR_BUILT_IN_NODE_NAMES, RICH_TEXT_EDITOR_JSON_FORM_NODE_N
128
128
  import RichTextEditor from "./rich-text-editor/index.js";
129
129
  import Secret from "./secret/index.js";
130
130
  import SelectableCard from "./selectable-card/index.js";
131
+ import recorder_core_default from "./sender/recorder-core.js";
131
132
  import Sender, { SenderHeader, SenderSwitch } from "./sender/index.js";
132
133
  import line_default from "./skeleton/line.js";
133
134
  import shape_default from "./skeleton/shape.js";
@@ -151,4 +152,4 @@ import Typography from "./typography/index.js";
151
152
  import Upload from "./upload/index.js";
152
153
  import Watermark from "./watermark/index.js";
153
154
  import SDVue from "./sd-vue.js";
154
- export { $createInlineComponentNode, $isInlineComponentNode, A2UI_0_9_1, Affix, Alert, Anchor, anchor_link_default as AnchorLink, AutoComplete, Avatar, AvatarGroup, BackTop, Badge, BasicCrudTable, BorderBeam, Breadcrumb, breadcrumb_item_default as BreadcrumbItem, Button, button_group_default as ButtonGroup, Calendar, Card, card_grid_default as CardGrid, card_meta_default as CardMeta, Carousel, carousel_item_default as CarouselItem, Cascader, cascader_panel_default as CascaderPanel, Checkbox, checkbox_group_default as CheckboxGroup, grid_col_default as Col, Collapse, collapse_item_default as CollapseItem, ColorPicker, Comment, ConfigProvider, Copy, countdown_default as Countdown, Cropper, DatePicker, Descriptions, descriptions_item_default as DescriptionsItem, dropdown_group_default as Dgroup, Divider, dropdown_option_default as Doption, Drawer, Dropdown, dropdown_button_default as DropdownButton, dropdown_submenu_default as Dsubmenu, Ellipsis, Empty, FilePreviewer, Form, form_item_default as FormItem, Grid, grid_item_default as GridItem, Icon, Image, preview_default as ImagePreview, preview_action_default as ImagePreviewAction, preview_group_default as ImagePreviewGroup, InlineComponentNode, Input, input_group_default as InputGroup, InputNumber, input_password_default as InputPassword, input_search_default as InputSearch, InputTag, JsonForm, KvList, Layout, content_default as LayoutContent, footer_default as LayoutFooter, header_default as LayoutHeader, sider_default as LayoutSider, Link, List, list_item_default as ListItem, list_item_meta_default as ListItemMeta, Mention, Menu, item_default as MenuItem, item_group_default as MenuItemGroup, Message, Modal, month_picker_default as MonthPicker, Notification, OverflowList, PageHeader, Pagination, PerformantEllipsis, Popconfirm, Popover, Progress, QrCode, quarter_picker_default as QuarterPicker, RICH_TEXT_EDITOR_BUILT_IN_NODE_NAMES, RICH_TEXT_EDITOR_JSON_FORM_NODE_NAMES, Radio, radio_group_default as RadioGroup, range_picker_default as RangePicker, Rate, ResizeBox, Result, RichTextEditor, grid_row_default as Row, Scrollbar, Secret, Select, SelectableCard, Sender, SenderHeader, SenderSwitch, Skeleton, line_default as SkeletonLine, shape_default as SkeletonShape, Slider, Space, Spin, Split, Statistic, step_default as Step, Steps, sub_menu_default as SubMenu, Switch, tab_pane_default as TabPane, Table, table_column_default as TableColumn, Tabs, Tag, TagGroup, table_tbody_default as Tbody, table_td_default as Td, Textarea, table_th_default as Th, table_thead_default as Thead, ThemeProvider, TimePicker, Timeline, item_default$1 as TimelineItem, Toolbar, Tooltip, Tour, table_tr_default as Tr, Transfer, Tree, TreeSelect, Trigger, Typography, paragraph_default as TypographyParagraph, text_default as TypographyText, title_default as TypographyTitle, Upload, VerificationCode, Watermark, week_picker_default as WeekPicker, year_picker_default as YearPicker, addI18nMessages, SDVue as default, defineJsonFormComponents, defineJsonFormSchemas, getCssVarToken, getLocale, useFormItem, useLocale };
155
+ export { $createInlineComponentNode, $isInlineComponentNode, A2UI_0_9_1, Affix, Alert, Anchor, anchor_link_default as AnchorLink, AutoComplete, Avatar, AvatarGroup, BackTop, Badge, BasicCrudTable, BorderBeam, Breadcrumb, breadcrumb_item_default as BreadcrumbItem, Button, button_group_default as ButtonGroup, Calendar, Card, card_grid_default as CardGrid, card_meta_default as CardMeta, Carousel, carousel_item_default as CarouselItem, Cascader, cascader_panel_default as CascaderPanel, Checkbox, checkbox_group_default as CheckboxGroup, grid_col_default as Col, Collapse, collapse_item_default as CollapseItem, ColorPicker, Comment, ConfigProvider, Copy, countdown_default as Countdown, Cropper, DatePicker, Descriptions, descriptions_item_default as DescriptionsItem, dropdown_group_default as Dgroup, Divider, dropdown_option_default as Doption, Drawer, Dropdown, dropdown_button_default as DropdownButton, dropdown_submenu_default as Dsubmenu, Ellipsis, Empty, FilePreviewer, Form, form_item_default as FormItem, Grid, grid_item_default as GridItem, Icon, Image, preview_default as ImagePreview, preview_action_default as ImagePreviewAction, preview_group_default as ImagePreviewGroup, InlineComponentNode, Input, input_group_default as InputGroup, InputNumber, input_password_default as InputPassword, input_search_default as InputSearch, InputTag, JsonForm, KvList, Layout, content_default as LayoutContent, footer_default as LayoutFooter, header_default as LayoutHeader, sider_default as LayoutSider, Link, List, list_item_default as ListItem, list_item_meta_default as ListItemMeta, Mention, Menu, item_default as MenuItem, item_group_default as MenuItemGroup, Message, Modal, month_picker_default as MonthPicker, Notification, OverflowList, PageHeader, Pagination, PerformantEllipsis, Popconfirm, Popover, Progress, QrCode, quarter_picker_default as QuarterPicker, RICH_TEXT_EDITOR_BUILT_IN_NODE_NAMES, RICH_TEXT_EDITOR_JSON_FORM_NODE_NAMES, Radio, radio_group_default as RadioGroup, range_picker_default as RangePicker, Rate, recorder_core_default as RecorderCore, ResizeBox, Result, RichTextEditor, grid_row_default as Row, Scrollbar, Secret, Select, SelectableCard, Sender, SenderHeader, SenderSwitch, Skeleton, line_default as SkeletonLine, shape_default as SkeletonShape, Slider, Space, Spin, Split, Statistic, step_default as Step, Steps, sub_menu_default as SubMenu, Switch, tab_pane_default as TabPane, Table, table_column_default as TableColumn, Tabs, Tag, TagGroup, table_tbody_default as Tbody, table_td_default as Td, Textarea, table_th_default as Th, table_thead_default as Thead, ThemeProvider, TimePicker, Timeline, item_default$1 as TimelineItem, Toolbar, Tooltip, Tour, table_tr_default as Tr, Transfer, Tree, TreeSelect, Trigger, Typography, paragraph_default as TypographyParagraph, text_default as TypographyText, title_default as TypographyTitle, Upload, VerificationCode, Watermark, week_picker_default as WeekPicker, year_picker_default as YearPicker, addI18nMessages, SDVue as default, defineJsonFormComponents, defineJsonFormSchemas, getCssVarToken, getLocale, useFormItem, useLocale };
@@ -112,8 +112,10 @@ export declare const SenderSwitch: {
112
112
  unchecked?: (props: {}) => any;
113
113
  };
114
114
  });
115
+ export { default as RecorderCore } from './recorder-core';
116
+ export type { RecorderCore as RecorderCoreConstructor, RecorderCoreInstance, RecorderCoreOptions, } from './recorder-core';
115
117
  export type SenderInstance = InstanceType<typeof _Sender>;
116
118
  export type SenderHeaderInstance = InstanceType<typeof _SenderHeader>;
117
119
  export type SenderSwitchInstance = InstanceType<typeof _SenderSwitch>;
118
- export type { SenderActionContext, SenderAllowSpeech, SenderComponents, SenderContentSlotConfig, SenderControlledSpeechConfig, SenderCustomSlotConfig, SenderCustomSlotRender, SenderCustomSlotState, SenderEmits, SenderFocusOptions, SenderHeaderSemanticType, SenderHeaderProps, SenderInsertPosition, SenderInputSlotConfig, SenderProps, SenderRef, SenderResolvedSlotConfig, SenderSelectSlotConfig, SenderSemanticType, SenderSkill, SenderSkillClosableConfig, SenderSpeechConfig, SenderSpeechDataEvent, SenderSpeechEndEvent, SenderSpeechEndReason, SenderSpeechErrorEvent, SenderSpeechErrorPhase, SenderSpeechStartEvent, SenderSpeechTransportCloseEvent, SenderSpeechTransportMessageEvent, SenderSpeechTransportOpenEvent, SenderSlotConfig, SenderSlotConfigBase, SenderSubmitType, SenderSuffixPlacement, SenderSwitchProps, SenderTagSlotConfig, SenderTextSlotConfig, SenderValue, } from './types';
120
+ export type { SenderActionContext, SenderAllowSpeech, SenderComponents, SenderContentSlotConfig, SenderCustomSlotConfig, SenderCustomSlotRender, SenderCustomSlotState, SenderEmits, SenderFocusOptions, SenderHeaderSemanticType, SenderHeaderProps, SenderInsertPosition, SenderInputSlotConfig, SenderProps, SenderRef, SenderResolvedSlotConfig, SenderSelectSlotConfig, SenderSemanticType, SenderSkill, SenderSkillClosableConfig, SenderSlotConfig, SenderSlotConfigBase, SenderSubmitType, SenderSuffixPlacement, SenderSwitchProps, SenderTagSlotConfig, SenderTextSlotConfig, SenderValue, } from './types';
119
121
  export default Sender;
@@ -1,6 +1,7 @@
1
1
  import { getComponentPrefix, setGlobalConfig } from "../_utils/global-config.js";
2
2
  import sender_header_default from "./sender-header.js";
3
3
  import sender_switch_default from "./sender-switch.js";
4
+ import recorder_core_default from "./recorder-core.js";
4
5
  import sender_default from "./sender.js";
5
6
  //#region components/sender/index.ts
6
7
  var Sender = Object.assign(sender_default, {
@@ -17,4 +18,4 @@ var Sender = Object.assign(sender_default, {
17
18
  var SenderHeader = sender_header_default;
18
19
  var SenderSwitch = sender_switch_default;
19
20
  //#endregion
20
- export { SenderHeader, SenderSwitch, Sender as default };
21
+ export { recorder_core_default as RecorderCore, SenderHeader, SenderSwitch, Sender as default };
@@ -0,0 +1,25 @@
1
+ import Recorder from 'recorder-core';
2
+ import 'recorder-core/src/engine/beta-amr';
3
+ import 'recorder-core/src/engine/beta-amr-engine';
4
+ import 'recorder-core/src/engine/beta-ogg';
5
+ import 'recorder-core/src/engine/beta-ogg-engine';
6
+ import 'recorder-core/src/engine/beta-webm';
7
+ import 'recorder-core/src/engine/g711x';
8
+ import 'recorder-core/src/engine/mp3';
9
+ import 'recorder-core/src/engine/mp3-engine';
10
+ import 'recorder-core/src/engine/pcm';
11
+ import 'recorder-core/src/engine/wav';
12
+ import 'recorder-core/src/extensions/asr.aliyun.short';
13
+ import 'recorder-core/src/extensions/buffer_stream.player';
14
+ import 'recorder-core/src/extensions/create-audio.nmn2pcm';
15
+ import 'recorder-core/src/extensions/lib.fft';
16
+ import 'recorder-core/src/extensions/dtmf.decode';
17
+ import 'recorder-core/src/extensions/dtmf.encode';
18
+ import 'recorder-core/src/extensions/frequency.histogram.view';
19
+ import 'recorder-core/src/extensions/sonic';
20
+ import 'recorder-core/src/extensions/wavesurfer.view';
21
+ import 'recorder-core/src/extensions/waveview';
22
+ export type RecorderCore = typeof Recorder;
23
+ export type RecorderCoreInstance = ReturnType<RecorderCore>;
24
+ export type RecorderCoreOptions = Parameters<RecorderCore>[0];
25
+ export default Recorder;
@@ -0,0 +1,26 @@
1
+ import Recorder from "recorder-core";
2
+ import "recorder-core/src/engine/beta-amr";
3
+ import "recorder-core/src/engine/beta-amr-engine";
4
+ import "recorder-core/src/engine/beta-ogg";
5
+ import "recorder-core/src/engine/beta-ogg-engine";
6
+ import "recorder-core/src/engine/beta-webm";
7
+ import "recorder-core/src/engine/g711x";
8
+ import "recorder-core/src/engine/mp3";
9
+ import "recorder-core/src/engine/mp3-engine";
10
+ import "recorder-core/src/engine/pcm";
11
+ import "recorder-core/src/engine/wav";
12
+ import "recorder-core/src/extensions/asr.aliyun.short";
13
+ import "recorder-core/src/extensions/buffer_stream.player";
14
+ import "recorder-core/src/extensions/create-audio.nmn2pcm";
15
+ import "recorder-core/src/extensions/lib.fft";
16
+ import "recorder-core/src/extensions/dtmf.decode";
17
+ import "recorder-core/src/extensions/dtmf.encode";
18
+ import "recorder-core/src/extensions/frequency.histogram.view";
19
+ import "recorder-core/src/extensions/sonic";
20
+ import "recorder-core/src/extensions/wavesurfer.view";
21
+ import "recorder-core/src/extensions/waveview";
22
+ //#region components/sender/recorder-core.ts
23
+ Recorder.TrafficImgUrl = "";
24
+ var recorder_core_default = Recorder;
25
+ //#endregion
26
+ export { recorder_core_default as default };
@@ -11,7 +11,7 @@ import { senderInjectionKey } from "./context.js";
11
11
  import IconArrowUp from "../icon/icon-arrow-up/index.js";
12
12
  import IconMute from "../icon/icon-mute/index.js";
13
13
  import IconVoice from "../icon/icon-voice/index.js";
14
- import { useSpeech } from "./use-speech.js";
14
+ import { useRecorder } from "./use-recorder.js";
15
15
  import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, getCurrentInstance, h, mergeProps, nextTick, normalizeClass, normalizeStyle, openBlock, provide, renderSlot, resolveDynamicComponent, shallowRef, toRef, unref, useAttrs, watch, withCtx } from "vue";
16
16
  import { createReusableTemplate } from "@vueuse/core";
17
17
  import { INSERT_LINE_BREAK_COMMAND } from "lexical";
@@ -29,7 +29,7 @@ var sender_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
29
29
  submitType: { default: "enter" },
30
30
  disabled: { type: Boolean },
31
31
  slotConfig: {},
32
- allowSpeech: { type: [Boolean, Object] },
32
+ allowSpeech: { type: Boolean },
33
33
  autoSize: {
34
34
  type: [Boolean, Object],
35
35
  default: () => ({ maxRows: 8 })
@@ -56,13 +56,8 @@ var sender_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
56
56
  "pasteFile",
57
57
  "focus",
58
58
  "blur",
59
- "speechStart",
60
- "speechData",
61
59
  "speechEnd",
62
- "speechError",
63
- "speechTransportOpen",
64
- "speechTransportMessage",
65
- "speechTransportClose"
60
+ "speechError"
66
61
  ],
67
62
  setup(__props, { expose: __expose, emit: __emit }) {
68
63
  const props = __props;
@@ -404,24 +399,19 @@ var sender_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
404
399
  if (cancelDisabled.value || props.disabled) return;
405
400
  emit("cancel");
406
401
  };
407
- const { available: speechAvailable, recording, requesting: speechRequesting, statusText: speechStatusText, trigger: triggerSpeech } = useSpeech(toRef(props, "allowSpeech"), {
408
- onStart: (event) => emit("speechStart", event),
409
- onData: (event) => emit("speechData", event),
410
- onEnd: (event) => emit("speechEnd", event),
411
- onError: (event) => emit("speechError", event),
412
- onTransportOpen: (event) => emit("speechTransportOpen", event),
413
- onTransportMessage: (event) => emit("speechTransportMessage", event),
414
- onTransportClose: (event) => emit("speechTransportClose", event)
402
+ const { Recorder, available: speechAvailable, recorder, recording, requesting: speechRequesting, stopping: speechStopping, statusText: speechStatusText, trigger: triggerSpeech } = useRecorder(toRef(props, "allowSpeech"), {
403
+ onStop: (blob, duration, mime) => emit("speechEnd", blob, duration, mime),
404
+ onError: (message, isUserNotAllow) => emit("speechError", message, isUserNotAllow)
415
405
  });
416
406
  const actionContext = computed(() => ({
417
407
  send: triggerSend,
418
408
  clear: triggerClear,
419
409
  cancel: triggerCancel,
420
- speech: () => triggerSpeech(false),
410
+ speech: triggerSpeech,
421
411
  submitDisabled: Boolean(props.disabled || submitDisabled.value),
422
412
  clearDisabled: Boolean(props.disabled || clearDisabled.value),
423
413
  cancelDisabled: Boolean(props.disabled || cancelDisabled.value),
424
- speechDisabled: Boolean(props.disabled || !speechAvailable.value || speechRequesting.value),
414
+ speechDisabled: Boolean(props.disabled || !speechAvailable.value || speechRequesting.value || speechStopping.value),
425
415
  recording: recording.value,
426
416
  loading: Boolean(props.loading)
427
417
  }));
@@ -657,6 +647,14 @@ var sender_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
657
647
  }
658
648
  });
659
649
  __expose({
650
+ Recorder,
651
+ get recorder() {
652
+ var _recorder$value;
653
+ return (_recorder$value = recorder.value) !== null && _recorder$value !== void 0 ? _recorder$value : null;
654
+ },
655
+ get recording() {
656
+ return recording.value;
657
+ },
660
658
  get inputElement() {
661
659
  var _slotInputRef$value$r, _slotInputRef$value9;
662
660
  return isSlotMode.value ? (_slotInputRef$value$r = (_slotInputRef$value9 = slotInputRef.value) === null || _slotInputRef$value9 === void 0 ? void 0 : _slotInputRef$value9.rootElement) !== null && _slotInputRef$value$r !== void 0 ? _slotInputRef$value$r : null : getNativeTextarea();
@@ -681,7 +679,7 @@ var sender_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
681
679
  }, [renderSlot(_ctx.$slots, "suffix", { actions: actionContext.value }, () => [createElementVNode("div", { class: normalizeClass(`${unref(prefixCls)}-actions-list-presets`) }, [__props.allowSpeech ? (openBlock(), createBlock(unref(Button), {
682
680
  key: 0,
683
681
  type: "text",
684
- loading: unref(speechRequesting),
682
+ loading: unref(speechRequesting) || unref(speechStopping),
685
683
  disabled: actionContext.value.speechDisabled,
686
684
  title: unref(speechStatusText),
687
685
  class: normalizeClass([`${unref(prefixCls)}-actions-btn`, { [`${unref(prefixCls)}-actions-btn-disabled`]: actionContext.value.speechDisabled }]),
@@ -1,5 +1,6 @@
1
1
  import type { CSSProperties, Component, VNodeChild } from 'vue';
2
2
  import type { TooltipProps } from '../tooltip';
3
+ import type { RecorderCore, RecorderCoreInstance, RecorderCoreOptions } from './recorder-core';
3
4
  export type SenderSubmitType = 'enter' | 'shiftEnter';
4
5
  export type SenderInsertPosition = 'start' | 'end' | 'cursor';
5
6
  export type SenderSuffixPlacement = 'content' | 'footer';
@@ -7,59 +8,7 @@ export type SenderSemanticType = 'root' | 'prefix' | 'input' | 'suffix' | 'foote
7
8
  export interface SenderComponents {
8
9
  input?: Component;
9
10
  }
10
- export interface SenderSpeechConfig {
11
- recording?: boolean;
12
- onRecordingChange?: (recording: boolean) => void;
13
- audioConstraints?: MediaTrackConstraints;
14
- bufferSize?: number;
15
- workletUrl?: string;
16
- processorName?: string;
17
- processorOptions?: Record<string, unknown>;
18
- url?: string;
19
- protocols?: string | string[];
20
- sendMetadata?: boolean;
21
- }
22
- export type SenderControlledSpeechConfig = SenderSpeechConfig;
23
- export type SenderAllowSpeech = boolean | SenderSpeechConfig;
24
- export interface SenderSpeechStartEvent {
25
- source: 'capture' | 'controlled';
26
- startedAt: number;
27
- stream?: MediaStream;
28
- audioContext?: AudioContext;
29
- sampleRate?: number;
30
- }
31
- export interface SenderSpeechDataEvent {
32
- buffer: ArrayBuffer;
33
- sampleRate: number;
34
- sequence: number;
35
- timestamp: number;
36
- }
37
- export type SenderSpeechEndReason = 'manual' | 'controlled' | 'error' | 'unmount';
38
- export interface SenderSpeechEndEvent {
39
- source: 'capture' | 'controlled';
40
- reason: SenderSpeechEndReason;
41
- startedAt: number;
42
- endedAt: number;
43
- duration: number;
44
- chunks: number;
45
- }
46
- export type SenderSpeechErrorPhase = 'permission' | 'audioContext' | 'audioWorklet' | 'transport';
47
- export interface SenderSpeechErrorEvent {
48
- error: Error;
49
- phase: SenderSpeechErrorPhase;
50
- }
51
- export interface SenderSpeechTransportOpenEvent {
52
- event: Event;
53
- socket: WebSocket;
54
- }
55
- export interface SenderSpeechTransportMessageEvent {
56
- event: MessageEvent;
57
- socket: WebSocket;
58
- }
59
- export interface SenderSpeechTransportCloseEvent {
60
- event: CloseEvent;
61
- socket: WebSocket;
62
- }
11
+ export type SenderAllowSpeech = boolean | RecorderCoreOptions;
63
12
  export interface SenderSkillClosableConfig {
64
13
  closeIcon?: VNodeChild;
65
14
  onClose?: (event: MouseEvent) => void;
@@ -144,6 +93,9 @@ export interface SenderFocusOptions {
144
93
  key?: string;
145
94
  }
146
95
  export interface SenderRef {
96
+ readonly Recorder: RecorderCore;
97
+ readonly recorder: RecorderCoreInstance | null;
98
+ readonly recording: boolean;
147
99
  readonly inputElement: HTMLTextAreaElement | HTMLDivElement | null;
148
100
  readonly nativeElement: HTMLDivElement | null;
149
101
  focus: (options?: SenderFocusOptions) => void;
@@ -201,13 +153,8 @@ export interface SenderEmits {
201
153
  'pasteFile': [files: FileList];
202
154
  'focus': [event: FocusEvent];
203
155
  'blur': [event: FocusEvent];
204
- 'speechStart': [event: SenderSpeechStartEvent];
205
- 'speechData': [event: SenderSpeechDataEvent];
206
- 'speechEnd': [event: SenderSpeechEndEvent];
207
- 'speechError': [event: SenderSpeechErrorEvent];
208
- 'speechTransportOpen': [event: SenderSpeechTransportOpenEvent];
209
- 'speechTransportMessage': [event: SenderSpeechTransportMessageEvent];
210
- 'speechTransportClose': [event: SenderSpeechTransportCloseEvent];
156
+ 'speechEnd': [blob: Blob, duration: number, mime: string];
157
+ 'speechError': [message: string, isUserNotAllow: boolean];
211
158
  }
212
159
  export type SenderHeaderSemanticType = 'header' | 'content';
213
160
  export interface SenderHeaderProps {
@@ -0,0 +1,16 @@
1
+ import { type MaybeRefOrGetter } from 'vue';
2
+ import type { SenderAllowSpeech } from './types';
3
+ export interface UseRecorderOptions {
4
+ onStop?: (blob: Blob, duration: number, mime: string) => void;
5
+ onError?: (message: string, isUserNotAllow: boolean) => void;
6
+ }
7
+ export declare function useRecorder(allowSpeech: MaybeRefOrGetter<SenderAllowSpeech | undefined>, options?: UseRecorderOptions): {
8
+ Recorder: any;
9
+ available: import("vue").ComputedRef<boolean>;
10
+ recorder: import("vue").ShallowRef<any, any>;
11
+ recording: import("vue").ShallowRef<boolean, boolean>;
12
+ requesting: import("vue").ShallowRef<boolean, boolean>;
13
+ stopping: import("vue").ShallowRef<boolean, boolean>;
14
+ statusText: import("vue").ComputedRef<string>;
15
+ trigger: () => void;
16
+ };
@@ -0,0 +1,117 @@
1
+ import { useI18n } from "../locale/index.js";
2
+ import recorder_core_default from "./recorder-core.js";
3
+ import { computed, onBeforeUnmount, onMounted, shallowRef, toValue, watch } from "vue";
4
+ //#region components/sender/use-recorder.ts
5
+ function useRecorder(allowSpeech, options = {}) {
6
+ const { t } = useI18n();
7
+ const config = computed(() => toValue(allowSpeech));
8
+ const supported = shallowRef(false);
9
+ const recording = shallowRef(false);
10
+ const requesting = shallowRef(false);
11
+ const stopping = shallowRef(false);
12
+ const permissionDenied = shallowRef(false);
13
+ const captureError = shallowRef();
14
+ const recorder = shallowRef();
15
+ let mounted = false;
16
+ let supportChecked = false;
17
+ let disposed = false;
18
+ const available = computed(() => Boolean(config.value) && supported.value && !permissionDenied.value);
19
+ const statusText = computed(() => {
20
+ if (requesting.value) return t("sender.speech.requestingPermission");
21
+ if (permissionDenied.value) return t("sender.speech.permissionDenied");
22
+ if (captureError.value) return t("sender.speech.microphoneUnavailable");
23
+ if (!supported.value) return t("sender.speech.unsupported");
24
+ if (recording.value) return t("sender.speech.stop");
25
+ return t("sender.speech.start");
26
+ });
27
+ const close = () => {
28
+ var _recorder$value;
29
+ (_recorder$value = recorder.value) === null || _recorder$value === void 0 || _recorder$value.close();
30
+ recorder.value = void 0;
31
+ recording.value = false;
32
+ requesting.value = false;
33
+ stopping.value = false;
34
+ };
35
+ const stop = () => {
36
+ const current = recorder.value;
37
+ if (!current || !recording.value || requesting.value || stopping.value) return;
38
+ stopping.value = true;
39
+ const finish = () => {
40
+ recording.value = false;
41
+ requesting.value = false;
42
+ stopping.value = false;
43
+ recorder.value = void 0;
44
+ };
45
+ current.stop((blob, duration, mime) => {
46
+ var _options$onStop;
47
+ if (disposed) return;
48
+ (_options$onStop = options.onStop) === null || _options$onStop === void 0 || _options$onStop.call(options, blob, duration, mime);
49
+ finish();
50
+ }, (message) => {
51
+ var _options$onError;
52
+ if (disposed) return;
53
+ (_options$onError = options.onError) === null || _options$onError === void 0 || _options$onError.call(options, message, false);
54
+ finish();
55
+ }, true);
56
+ };
57
+ const start = () => {
58
+ if (!available.value || requesting.value || recording.value || stopping.value) return;
59
+ requesting.value = true;
60
+ permissionDenied.value = false;
61
+ captureError.value = void 0;
62
+ const current = recorder_core_default(typeof config.value === "object" ? config.value : {});
63
+ recorder.value = current;
64
+ current.open(() => {
65
+ if (disposed || recorder.value !== current) {
66
+ current.close();
67
+ return;
68
+ }
69
+ current.start();
70
+ recording.value = true;
71
+ requesting.value = false;
72
+ }, (message, isUserNotAllow) => {
73
+ var _options$onError2;
74
+ current.close();
75
+ if (disposed || recorder.value !== current) return;
76
+ recorder.value = void 0;
77
+ permissionDenied.value = isUserNotAllow;
78
+ if (!isUserNotAllow) captureError.value = message;
79
+ recording.value = false;
80
+ requesting.value = false;
81
+ (_options$onError2 = options.onError) === null || _options$onError2 === void 0 || _options$onError2.call(options, message, isUserNotAllow);
82
+ });
83
+ };
84
+ const trigger = () => {
85
+ if (recording.value) stop();
86
+ else start();
87
+ };
88
+ const checkSupport = () => {
89
+ if (supportChecked) return;
90
+ supportChecked = true;
91
+ supported.value = recorder_core_default.Support();
92
+ };
93
+ onMounted(() => {
94
+ mounted = true;
95
+ if (config.value) checkSupport();
96
+ });
97
+ watch(() => Boolean(config.value), (enabled) => {
98
+ if (enabled && mounted) checkSupport();
99
+ else if (!enabled) close();
100
+ });
101
+ onBeforeUnmount(() => {
102
+ disposed = true;
103
+ close();
104
+ });
105
+ return {
106
+ Recorder: recorder_core_default,
107
+ available,
108
+ recorder,
109
+ recording,
110
+ requesting,
111
+ stopping,
112
+ statusText,
113
+ trigger
114
+ };
115
+ }
116
+ //#endregion
117
+ export { useRecorder };
@@ -3038,7 +3038,7 @@
3038
3038
  "description": "Structured slot configuration"
3039
3039
  },
3040
3040
  "sd-sender/allow-speech": {
3041
- "description": "AudioWorklet capture, streaming transport, or controlled recording configuration"
3041
+ "description": "Whether recording is enabled; an object value is passed to recorder-core as-is"
3042
3042
  },
3043
3043
  "sd-sender/auto-size": {
3044
3044
  "description": "Textarea auto-size configuration"
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json",
3
3
  "framework": "vue",
4
4
  "name": "@sdata/web-vue",
5
- "version": "3.22.2",
5
+ "version": "3.22.3",
6
6
  "contributions": {
7
7
  "html": {
8
8
  "types-syntax": "typescript",
@@ -7419,7 +7419,7 @@
7419
7419
  },
7420
7420
  {
7421
7421
  "name": "allow-speech",
7422
- "description": "AudioWorklet capture, streaming transport, or controlled recording configuration",
7422
+ "description": "Whether recording is enabled; an object value is passed to recorder-core as-is",
7423
7423
  "value": {
7424
7424
  "type": "SenderAllowSpeech",
7425
7425
  "kind": "expression"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdata/web-vue",
3
- "version": "3.22.3",
3
+ "version": "3.23.0",
4
4
  "description": "SD Design Vue: A Vue.js 3 UI Library",
5
5
  "keywords": [
6
6
  "sd",
@@ -76,6 +76,7 @@
76
76
  "overlayscrollbars": "^2.16.0",
77
77
  "pdfjs-dist": "^6.1.200",
78
78
  "qrcode": "^1.5.4",
79
+ "recorder-core": "^1.3.26070800",
79
80
  "resize-observer-polyfill": "^1.5.1",
80
81
  "scroll-into-view-if-needed": "^3.1.0",
81
82
  "virtua": "^0.49.3",
@@ -1,19 +0,0 @@
1
- import { type MaybeRefOrGetter } from 'vue';
2
- import type { SenderAllowSpeech, SenderSpeechDataEvent, SenderSpeechEndEvent, SenderSpeechErrorEvent, SenderSpeechStartEvent, SenderSpeechTransportCloseEvent, SenderSpeechTransportMessageEvent, SenderSpeechTransportOpenEvent } from './types';
3
- interface UseSpeechOptions {
4
- onStart: (event: SenderSpeechStartEvent) => void;
5
- onData: (event: SenderSpeechDataEvent) => void;
6
- onEnd: (event: SenderSpeechEndEvent) => void;
7
- onError: (event: SenderSpeechErrorEvent) => void;
8
- onTransportOpen: (event: SenderSpeechTransportOpenEvent) => void;
9
- onTransportMessage: (event: SenderSpeechTransportMessageEvent) => void;
10
- onTransportClose: (event: SenderSpeechTransportCloseEvent) => void;
11
- }
12
- export declare function useSpeech(allowSpeech: MaybeRefOrGetter<SenderAllowSpeech | undefined>, options: UseSpeechOptions): {
13
- available: import("vue").ComputedRef<boolean>;
14
- recording: import("vue").ComputedRef<boolean>;
15
- requesting: import("vue").ComputedRef<boolean>;
16
- statusText: import("vue").ComputedRef<string>;
17
- trigger: (breakRecording?: boolean) => Promise<void>;
18
- };
19
- export {};
@@ -1,381 +0,0 @@
1
- import _objectSpread2 from "../_virtual/_@oxc-project_runtime@0.139.0/helpers/esm/objectSpread2.js";
2
- import { useI18n } from "../locale/index.js";
3
- import _asyncToGenerator from "../_virtual/_@oxc-project_runtime@0.139.0/helpers/esm/asyncToGenerator.js";
4
- import { computed, onBeforeUnmount, onMounted, shallowRef, toValue, watch } from "vue";
5
- //#region components/sender/use-speech.ts
6
- var DEFAULT_PROCESSOR_NAME = "sd-sender-audio-processor";
7
- var DEFAULT_BUFFER_SIZE = 4096;
8
- var resolveBufferSize = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(128, Math.floor(value)) : DEFAULT_BUFFER_SIZE;
9
- var WORKLET_SOURCE = `
10
- class SdSenderAudioProcessor extends AudioWorkletProcessor {
11
- constructor(options) {
12
- super();
13
- this.bufferSize = Math.max(128, options.processorOptions?.bufferSize || ${DEFAULT_BUFFER_SIZE});
14
- this.buffer = new Float32Array(this.bufferSize);
15
- this.offset = 0;
16
- this.stopped = false;
17
- this.port.onmessage = (event) => {
18
- if (event.data?.type !== 'flush') return;
19
- if (this.offset > 0) {
20
- const chunk = this.buffer.slice(0, this.offset);
21
- this.port.postMessage(chunk.buffer, [chunk.buffer]);
22
- this.offset = 0;
23
- }
24
- this.stopped = true;
25
- this.port.postMessage({ type: 'flushed' });
26
- };
27
- }
28
-
29
- process(inputs) {
30
- if (this.stopped) return false;
31
- const channels = inputs[0];
32
- if (!channels?.length) return true;
33
- const frames = channels[0].length;
34
- for (let frame = 0; frame < frames; frame += 1) {
35
- let sample = 0;
36
- for (let channel = 0; channel < channels.length; channel += 1) {
37
- sample += channels[channel][frame] || 0;
38
- }
39
- this.buffer[this.offset] = sample / channels.length;
40
- this.offset += 1;
41
- if (this.offset === this.bufferSize) {
42
- const chunk = this.buffer;
43
- this.port.postMessage(chunk.buffer, [chunk.buffer]);
44
- this.buffer = new Float32Array(this.bufferSize);
45
- this.offset = 0;
46
- }
47
- }
48
- return true;
49
- }
50
- }
51
- registerProcessor('${DEFAULT_PROCESSOR_NAME}', SdSenderAudioProcessor);
52
- `;
53
- var isPermissionDeniedError = (error) => error instanceof DOMException ? error.name === "NotAllowedError" || error.name === "SecurityError" : error instanceof Error && (error.name === "NotAllowedError" || error.name === "SecurityError");
54
- function useSpeech(allowSpeech, options) {
55
- const { t } = useI18n();
56
- const config = computed(() => toValue(allowSpeech));
57
- const controlled = computed(() => typeof config.value === "object" && typeof config.value.recording === "boolean" && typeof config.value.onRecordingChange === "function");
58
- const supported = shallowRef(false);
59
- const internalRecording = shallowRef(false);
60
- const requesting = shallowRef(false);
61
- const stopping = shallowRef(false);
62
- const captureError = shallowRef();
63
- const stream = shallowRef();
64
- const audioContext = shallowRef();
65
- const sourceNode = shallowRef();
66
- const workletNode = shallowRef();
67
- const socket = shallowRef();
68
- const startedAt = shallowRef(0);
69
- const chunks = shallowRef(0);
70
- const sequence = shallowRef(0);
71
- let generatedWorkletUrl;
72
- let resolveWorkletStop;
73
- let workletStopRequested = false;
74
- let workletReady = false;
75
- let disposed = false;
76
- const recording = computed(() => controlled.value && typeof config.value === "object" ? Boolean(config.value.recording) : internalRecording.value);
77
- const available = computed(() => Boolean(config.value) && (controlled.value || supported.value) && !(captureError.value && isPermissionDeniedError(captureError.value.error)));
78
- const statusText = computed(() => {
79
- var _captureError$value;
80
- if (requesting.value) return t("sender.speech.requestingPermission");
81
- if (captureError.value && isPermissionDeniedError(captureError.value.error)) return t("sender.speech.permissionDenied");
82
- if (!controlled.value && !supported.value) return t("sender.speech.unsupported");
83
- if (recording.value) return t("sender.speech.stop");
84
- if (((_captureError$value = captureError.value) === null || _captureError$value === void 0 ? void 0 : _captureError$value.error.name) === "NotFoundError") return t("sender.speech.noMicrophone");
85
- if (captureError.value) return t("sender.speech.microphoneUnavailable");
86
- return t("sender.speech.start");
87
- });
88
- const resolveAudioContext = () => {
89
- var _window$AudioContext;
90
- const AudioContextConstructor = (_window$AudioContext = window.AudioContext) !== null && _window$AudioContext !== void 0 ? _window$AudioContext : window.webkitAudioContext;
91
- if (!AudioContextConstructor) throw new Error("AudioContext is not supported");
92
- return new AudioContextConstructor();
93
- };
94
- const resolveWorkletUrl = () => {
95
- var _generatedWorkletUrl;
96
- if (typeof config.value === "object" && config.value.workletUrl) return config.value.workletUrl;
97
- (_generatedWorkletUrl = generatedWorkletUrl) !== null && _generatedWorkletUrl !== void 0 || (generatedWorkletUrl = URL.createObjectURL(new Blob([WORKLET_SOURCE], { type: "text/javascript" })));
98
- return generatedWorkletUrl;
99
- };
100
- const connectTransport = (url, protocols) => new Promise((resolve, reject) => {
101
- const nextSocket = protocols ? new WebSocket(url, protocols) : new WebSocket(url);
102
- let opened = false;
103
- socket.value = nextSocket;
104
- nextSocket.binaryType = "arraybuffer";
105
- nextSocket.onopen = (event) => {
106
- opened = true;
107
- options.onTransportOpen({
108
- event,
109
- socket: nextSocket
110
- });
111
- resolve(nextSocket);
112
- };
113
- nextSocket.onmessage = (event) => options.onTransportMessage({
114
- event,
115
- socket: nextSocket
116
- });
117
- nextSocket.onclose = (event) => options.onTransportClose({
118
- event,
119
- socket: nextSocket
120
- });
121
- nextSocket.onerror = () => {
122
- const errorEvent = {
123
- error: /* @__PURE__ */ new Error(`Unable to connect to speech URL: ${url}`),
124
- phase: "transport"
125
- };
126
- if (opened) options.onError(errorEvent);
127
- else reject(errorEvent.error);
128
- };
129
- });
130
- const stopWorklet = () => new Promise((resolve) => {
131
- var _workletNode$value;
132
- const port = (_workletNode$value = workletNode.value) === null || _workletNode$value === void 0 ? void 0 : _workletNode$value.port;
133
- if (!port || workletStopRequested) {
134
- resolve();
135
- return;
136
- }
137
- workletStopRequested = true;
138
- let settled = false;
139
- const finish = () => {
140
- if (settled) return;
141
- settled = true;
142
- window.clearTimeout(timeout);
143
- resolveWorkletStop = void 0;
144
- resolve();
145
- };
146
- const timeout = window.setTimeout(finish, 100);
147
- resolveWorkletStop = finish;
148
- port.postMessage({ type: "flush" });
149
- });
150
- const releaseResources = function() {
151
- var _ref = _asyncToGenerator(function* () {
152
- var _sourceNode$value, _stream$value, _audioContext$value;
153
- const currentWorkletNode = workletNode.value;
154
- yield stopWorklet();
155
- if (currentWorkletNode) {
156
- currentWorkletNode.port.onmessage = null;
157
- currentWorkletNode.port.close();
158
- currentWorkletNode.disconnect();
159
- }
160
- (_sourceNode$value = sourceNode.value) === null || _sourceNode$value === void 0 || _sourceNode$value.disconnect();
161
- (_stream$value = stream.value) === null || _stream$value === void 0 || _stream$value.getTracks().forEach((track) => track.stop());
162
- if (((_audioContext$value = audioContext.value) === null || _audioContext$value === void 0 ? void 0 : _audioContext$value.state) === "running") yield audioContext.value.suspend();
163
- if (socket.value && (socket.value.readyState === WebSocket.OPEN || socket.value.readyState === WebSocket.CONNECTING)) socket.value.close(1e3, "speech-ended");
164
- workletNode.value = void 0;
165
- sourceNode.value = void 0;
166
- stream.value = void 0;
167
- socket.value = void 0;
168
- });
169
- return function releaseResources() {
170
- return _ref.apply(this, arguments);
171
- };
172
- }();
173
- const closeAudioContext = function() {
174
- var _ref2 = _asyncToGenerator(function* () {
175
- if (audioContext.value && audioContext.value.state !== "closed") yield audioContext.value.close();
176
- audioContext.value = void 0;
177
- workletReady = false;
178
- });
179
- return function closeAudioContext() {
180
- return _ref2.apply(this, arguments);
181
- };
182
- }();
183
- const stopCapture = function() {
184
- var _ref3 = _asyncToGenerator(function* (reason = "manual") {
185
- if (!internalRecording.value || stopping.value) return;
186
- stopping.value = true;
187
- try {
188
- var _socket$value, _config$value$onRecor, _config$value;
189
- yield stopWorklet();
190
- const endedAt = performance.now();
191
- internalRecording.value = false;
192
- if (((_socket$value = socket.value) === null || _socket$value === void 0 ? void 0 : _socket$value.readyState) === WebSocket.OPEN && typeof config.value === "object" && config.value.sendMetadata !== false) socket.value.send(JSON.stringify({
193
- type: "end",
194
- reason
195
- }));
196
- yield releaseResources();
197
- if (typeof config.value === "object") (_config$value$onRecor = (_config$value = config.value).onRecordingChange) === null || _config$value$onRecor === void 0 || _config$value$onRecor.call(_config$value, false);
198
- options.onEnd({
199
- source: "capture",
200
- reason,
201
- startedAt: startedAt.value,
202
- endedAt,
203
- duration: endedAt - startedAt.value,
204
- chunks: chunks.value
205
- });
206
- } finally {
207
- stopping.value = false;
208
- }
209
- });
210
- return function stopCapture() {
211
- return _ref3.apply(this, arguments);
212
- };
213
- }();
214
- const startCapture = function() {
215
- var _ref4 = _asyncToGenerator(function* () {
216
- if (requesting.value || stopping.value || internalRecording.value || !available.value) return;
217
- requesting.value = true;
218
- captureError.value = void 0;
219
- let phase = "permission";
220
- try {
221
- var _speechConfig$audioCo, _speechConfig$process, _socket$value3, _speechConfig$onRecor;
222
- const speechConfig = typeof config.value === "object" ? config.value : void 0;
223
- const bufferSize = resolveBufferSize(speechConfig === null || speechConfig === void 0 ? void 0 : speechConfig.bufferSize);
224
- stream.value = yield navigator.mediaDevices.getUserMedia({
225
- audio: (_speechConfig$audioCo = speechConfig === null || speechConfig === void 0 ? void 0 : speechConfig.audioConstraints) !== null && _speechConfig$audioCo !== void 0 ? _speechConfig$audioCo : true,
226
- video: false
227
- });
228
- if (disposed) {
229
- yield releaseResources();
230
- return;
231
- }
232
- if (speechConfig === null || speechConfig === void 0 ? void 0 : speechConfig.url) {
233
- phase = "transport";
234
- yield connectTransport(speechConfig.url, speechConfig.protocols);
235
- if (disposed) {
236
- yield releaseResources();
237
- return;
238
- }
239
- }
240
- phase = "audioContext";
241
- let nextAudioContext = audioContext.value;
242
- if (!nextAudioContext) {
243
- nextAudioContext = resolveAudioContext();
244
- audioContext.value = nextAudioContext;
245
- workletReady = false;
246
- }
247
- if (nextAudioContext.state === "suspended") yield nextAudioContext.resume();
248
- phase = "audioWorklet";
249
- if (!workletReady) {
250
- yield nextAudioContext.audioWorklet.addModule(resolveWorkletUrl());
251
- workletReady = true;
252
- }
253
- if (disposed) {
254
- yield releaseResources();
255
- return;
256
- }
257
- const processorName = (_speechConfig$process = speechConfig === null || speechConfig === void 0 ? void 0 : speechConfig.processorName) !== null && _speechConfig$process !== void 0 ? _speechConfig$process : DEFAULT_PROCESSOR_NAME;
258
- const nextWorkletNode = new AudioWorkletNode(nextAudioContext, processorName, {
259
- numberOfInputs: 1,
260
- numberOfOutputs: 0,
261
- channelCount: 1,
262
- processorOptions: _objectSpread2(_objectSpread2({}, speechConfig === null || speechConfig === void 0 ? void 0 : speechConfig.processorOptions), {}, { bufferSize })
263
- });
264
- workletStopRequested = false;
265
- workletNode.value = nextWorkletNode;
266
- sourceNode.value = nextAudioContext.createMediaStreamSource(stream.value);
267
- nextWorkletNode.port.onmessage = (event) => {
268
- var _socket$value2;
269
- if (!(event.data instanceof ArrayBuffer) && "type" in event.data) {
270
- if (event.data.type === "flushed") resolveWorkletStop === null || resolveWorkletStop === void 0 || resolveWorkletStop();
271
- return;
272
- }
273
- if (!internalRecording.value) return;
274
- const buffer = event.data;
275
- const dataEvent = {
276
- buffer,
277
- sampleRate: nextAudioContext.sampleRate,
278
- sequence: sequence.value,
279
- timestamp: performance.now()
280
- };
281
- sequence.value += 1;
282
- chunks.value += 1;
283
- options.onData(dataEvent);
284
- if (((_socket$value2 = socket.value) === null || _socket$value2 === void 0 ? void 0 : _socket$value2.readyState) === WebSocket.OPEN) socket.value.send(buffer);
285
- };
286
- sourceNode.value.connect(nextWorkletNode);
287
- startedAt.value = performance.now();
288
- chunks.value = 0;
289
- sequence.value = 0;
290
- internalRecording.value = true;
291
- if (((_socket$value3 = socket.value) === null || _socket$value3 === void 0 ? void 0 : _socket$value3.readyState) === WebSocket.OPEN && (speechConfig === null || speechConfig === void 0 ? void 0 : speechConfig.sendMetadata) !== false) socket.value.send(JSON.stringify({
292
- type: "start",
293
- format: "pcm-f32",
294
- channels: 1,
295
- sampleRate: nextAudioContext.sampleRate,
296
- bufferSize
297
- }));
298
- speechConfig === null || speechConfig === void 0 || (_speechConfig$onRecor = speechConfig.onRecordingChange) === null || _speechConfig$onRecor === void 0 || _speechConfig$onRecor.call(speechConfig, true);
299
- options.onStart({
300
- source: "capture",
301
- startedAt: startedAt.value,
302
- stream: stream.value,
303
- audioContext: nextAudioContext,
304
- sampleRate: nextAudioContext.sampleRate
305
- });
306
- } catch (error) {
307
- const errorEvent = {
308
- error: error instanceof Error ? error : new Error(String(error)),
309
- phase
310
- };
311
- captureError.value = errorEvent;
312
- options.onError(errorEvent);
313
- yield releaseResources();
314
- yield closeAudioContext();
315
- } finally {
316
- requesting.value = false;
317
- }
318
- });
319
- return function startCapture() {
320
- return _ref4.apply(this, arguments);
321
- };
322
- }();
323
- const trigger = function() {
324
- var _ref5 = _asyncToGenerator(function* (breakRecording = false) {
325
- if (requesting.value || stopping.value) return;
326
- if (breakRecording && !recording.value) return;
327
- if (controlled.value && typeof config.value === "object") {
328
- var _config$value$onRecor2, _config$value2;
329
- const nextRecording = !recording.value;
330
- (_config$value$onRecor2 = (_config$value2 = config.value).onRecordingChange) === null || _config$value$onRecor2 === void 0 || _config$value$onRecor2.call(_config$value2, nextRecording);
331
- const now = performance.now();
332
- if (nextRecording) {
333
- startedAt.value = now;
334
- options.onStart({
335
- source: "controlled",
336
- startedAt: now
337
- });
338
- } else options.onEnd({
339
- source: "controlled",
340
- reason: "controlled",
341
- startedAt: startedAt.value,
342
- endedAt: now,
343
- duration: now - startedAt.value,
344
- chunks: 0
345
- });
346
- return;
347
- }
348
- if (internalRecording.value) yield stopCapture();
349
- else yield startCapture();
350
- });
351
- return function trigger() {
352
- return _ref5.apply(this, arguments);
353
- };
354
- }();
355
- onMounted(() => {
356
- var _navigator$mediaDevic;
357
- supported.value = Boolean(typeof ((_navigator$mediaDevic = navigator.mediaDevices) === null || _navigator$mediaDevic === void 0 ? void 0 : _navigator$mediaDevic.getUserMedia) === "function" && typeof AudioContext !== "undefined" && typeof AudioWorkletNode !== "undefined");
358
- });
359
- watch(() => Boolean(config.value), (enabled) => {
360
- if (!enabled) stopCapture();
361
- });
362
- watch(controlled, (nextControlled) => {
363
- if (nextControlled) stopCapture();
364
- });
365
- onBeforeUnmount(_asyncToGenerator(function* () {
366
- disposed = true;
367
- if (internalRecording.value) yield stopCapture("unmount");
368
- else yield releaseResources();
369
- yield closeAudioContext();
370
- if (generatedWorkletUrl) URL.revokeObjectURL(generatedWorkletUrl);
371
- }));
372
- return {
373
- available,
374
- recording,
375
- requesting: computed(() => requesting.value || stopping.value),
376
- statusText,
377
- trigger
378
- };
379
- }
380
- //#endregion
381
- export { useSpeech };