@dxos/plugin-transcription 0.8.4-main.abd8ff62ef → 0.8.4-main.bc2380dfbc
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/LICENSE +102 -5
- package/README.md +1 -1
- package/dist/lib/neutral/TranscriptionArticle-L6XANBH5.mjs +54 -0
- package/dist/lib/neutral/TranscriptionArticle-L6XANBH5.mjs.map +7 -0
- package/dist/lib/neutral/TranscriptionPlugin.mjs +34 -0
- package/dist/lib/neutral/TranscriptionPlugin.mjs.map +7 -0
- package/dist/lib/neutral/TranscriptionPlugin.node.mjs +25 -0
- package/dist/lib/neutral/TranscriptionPlugin.node.mjs.map +7 -0
- package/dist/lib/neutral/TranscriptionPlugin.workerd.mjs +21 -0
- package/dist/lib/neutral/TranscriptionPlugin.workerd.mjs.map +7 -0
- package/dist/lib/neutral/blueprint-definition-HOXDG5QP.mjs +15 -0
- package/dist/lib/neutral/blueprint-definition-HOXDG5QP.mjs.map +7 -0
- package/dist/lib/neutral/blueprints/index.mjs +8 -0
- package/dist/lib/neutral/capabilities/index.mjs +17 -0
- package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
- package/dist/lib/{browser/chunk-NQQK7UZQ.mjs → neutral/chunk-2O3ONPRZ.mjs} +94 -40
- package/dist/lib/neutral/chunk-2O3ONPRZ.mjs.map +7 -0
- package/dist/lib/{browser/chunk-L4HW35L6.mjs → neutral/chunk-5FXU36PN.mjs} +19 -10
- package/dist/lib/neutral/chunk-5FXU36PN.mjs.map +7 -0
- package/dist/lib/neutral/chunk-GL4RS533.mjs +22 -0
- package/dist/lib/neutral/chunk-GL4RS533.mjs.map +7 -0
- package/dist/lib/{browser/index.mjs → neutral/chunk-ILSA3DU7.mjs} +32 -70
- package/dist/lib/neutral/chunk-ILSA3DU7.mjs.map +7 -0
- package/dist/lib/neutral/chunk-Y5EQAFFR.mjs +8 -0
- package/dist/lib/neutral/chunk-Y5EQAFFR.mjs.map +7 -0
- package/dist/lib/{browser/blueprints/index.mjs → neutral/chunk-ZR4BH4QQ.mjs} +4 -5
- package/dist/lib/neutral/chunk-ZR4BH4QQ.mjs.map +7 -0
- package/dist/lib/{browser → neutral}/components/index.mjs +83 -18
- package/dist/lib/neutral/components/index.mjs.map +7 -0
- package/dist/lib/neutral/containers/index.mjs +9 -0
- package/dist/lib/neutral/containers/index.mjs.map +7 -0
- package/dist/lib/{browser/create-ULR5DPJZ.mjs → neutral/create-RRWRIUSR.mjs} +4 -4
- package/dist/lib/neutral/create-RRWRIUSR.mjs.map +7 -0
- package/dist/lib/{browser → neutral}/hooks/index.mjs +5 -3
- package/dist/lib/neutral/index.mjs +44 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/neutral/meta.mjs +8 -0
- package/dist/lib/{browser/open-SFMPLZVW.mjs → neutral/open-CVMX5KXZ.mjs} +5 -5
- package/dist/lib/neutral/open-CVMX5KXZ.mjs.map +7 -0
- package/dist/lib/neutral/operation-handler-NXMOAXFP.mjs +13 -0
- package/dist/lib/neutral/operation-handler-NXMOAXFP.mjs.map +7 -0
- package/dist/lib/neutral/operations/index.mjs +8 -0
- package/dist/lib/neutral/plugin.mjs +16 -0
- package/dist/lib/neutral/plugin.mjs.map +7 -0
- package/dist/lib/neutral/react-surface-YMZRO242.mjs +26 -0
- package/dist/lib/neutral/react-surface-YMZRO242.mjs.map +7 -0
- package/dist/lib/{browser/summarize-BDFX4APN.mjs → neutral/summarize-LA7TRW7A.mjs} +5 -5
- package/dist/lib/neutral/summarize-LA7TRW7A.mjs.map +7 -0
- package/dist/lib/neutral/text-content-F4ASMITB.mjs +30 -0
- package/dist/lib/neutral/text-content-F4ASMITB.mjs.map +7 -0
- package/dist/lib/neutral/transcriber-ABHATBVH.mjs +59 -0
- package/dist/lib/neutral/transcriber-ABHATBVH.mjs.map +7 -0
- package/dist/lib/neutral/translations.mjs +24 -0
- package/dist/lib/neutral/translations.mjs.map +7 -0
- package/dist/lib/neutral/types/index.mjs +10 -0
- package/dist/lib/neutral/types/index.mjs.map +7 -0
- package/dist/types/src/TranscriptionPlugin.d.ts.map +1 -1
- package/dist/types/src/TranscriptionPlugin.node.d.ts.map +1 -1
- package/dist/types/src/TranscriptionPlugin.workerd.d.ts +4 -0
- package/dist/types/src/TranscriptionPlugin.workerd.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +5 -1
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/text-content.d.ts +8 -0
- package/dist/types/src/capabilities/text-content.d.ts.map +1 -0
- package/dist/types/src/components/Oracle/Oracle.d.ts +24 -0
- package/dist/types/src/components/Oracle/Oracle.d.ts.map +1 -0
- package/dist/types/src/components/Oracle/Oracle.stories.d.ts +32 -0
- package/dist/types/src/components/Oracle/Oracle.stories.d.ts.map +1 -0
- package/dist/types/src/components/Oracle/index.d.ts +2 -0
- package/dist/types/src/components/Oracle/index.d.ts.map +1 -0
- package/dist/types/src/components/Transcription/Transcription.d.ts +0 -5
- package/dist/types/src/components/Transcription/Transcription.d.ts.map +1 -1
- package/dist/types/src/components/Transcription/Transcription.stories.d.ts +13 -16
- package/dist/types/src/components/Transcription/Transcription.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/containers/TranscriptionArticle/TranscriptionArticle.d.ts +6 -0
- package/dist/types/src/containers/TranscriptionArticle/TranscriptionArticle.d.ts.map +1 -0
- package/dist/types/src/containers/TranscriptionArticle/TranscriptionArticle.stories.d.ts +28 -0
- package/dist/types/src/containers/TranscriptionArticle/TranscriptionArticle.stories.d.ts.map +1 -0
- package/dist/types/src/containers/TranscriptionArticle/index.d.ts +2 -0
- package/dist/types/src/containers/TranscriptionArticle/index.d.ts.map +1 -0
- package/dist/types/src/containers/index.d.ts +1 -1
- package/dist/types/src/containers/index.d.ts.map +1 -1
- package/dist/types/src/enrichment/conversation.test.d.ts +2 -0
- package/dist/types/src/enrichment/conversation.test.d.ts.map +1 -0
- package/dist/types/src/enrichment/operations.d.ts +200 -0
- package/dist/types/src/enrichment/operations.d.ts.map +1 -0
- package/dist/types/src/enrichment/parser.d.ts +26 -0
- package/dist/types/src/enrichment/parser.d.ts.map +1 -0
- package/dist/types/src/enrichment/stream-simulator.d.ts +38 -0
- package/dist/types/src/enrichment/stream-simulator.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +2 -1
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useFeedModelAdapter.d.ts +12 -0
- package/dist/types/src/hooks/useFeedModelAdapter.d.ts.map +1 -0
- package/dist/types/src/hooks/useTranscriptionRecording.d.ts +12 -0
- package/dist/types/src/hooks/useTranscriptionRecording.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +3 -4
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/normalization/message-normalizer.d.ts +7 -5
- package/dist/types/src/normalization/message-normalizer.d.ts.map +1 -1
- package/dist/types/src/normalization/normalization.d.ts +180 -1
- package/dist/types/src/normalization/normalization.d.ts.map +1 -1
- package/dist/types/src/operations/create.d.ts +2 -2
- package/dist/types/src/operations/create.d.ts.map +1 -1
- package/dist/types/src/operations/index.d.ts +0 -1
- package/dist/types/src/operations/index.d.ts.map +1 -1
- package/dist/types/src/operations/open.d.ts +2 -2
- package/dist/types/src/operations/open.d.ts.map +1 -1
- package/dist/types/src/operations/summarize.d.ts +2 -2
- package/dist/types/src/operations/summarize.d.ts.map +1 -1
- package/dist/types/src/plugin.d.ts +4 -0
- package/dist/types/src/plugin.d.ts.map +1 -0
- package/dist/types/src/stories/FileTranscription.stories.d.ts +5 -3
- package/dist/types/src/stories/FileTranscription.stories.d.ts.map +1 -1
- package/dist/types/src/stories/KeyWordRecognition.stories.d.ts.map +1 -1
- package/dist/types/src/stories/{MicrophoneTranscription.stories.d.ts → LiveTranscription.stories.d.ts} +4 -4
- package/dist/types/src/stories/LiveTranscription.stories.d.ts.map +1 -0
- package/dist/types/src/stories/TranscriptionStory.d.ts +11 -13
- package/dist/types/src/stories/TranscriptionStory.d.ts.map +1 -1
- package/dist/types/src/stories/common.d.ts +46 -0
- package/dist/types/src/stories/common.d.ts.map +1 -0
- package/dist/types/src/testing/testing.d.ts +5 -6
- package/dist/types/src/testing/testing.d.ts.map +1 -1
- package/dist/types/src/transcriber/transcriber.d.ts +4 -4
- package/dist/types/src/transcriber/transcriber.d.ts.map +1 -1
- package/dist/types/src/transcriber/transcription-manager.d.ts +6 -4
- package/dist/types/src/transcriber/transcription-manager.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +2 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/{operations/definitions.d.ts → types/TranscriptOperation.d.ts} +1 -1
- package/dist/types/src/types/TranscriptOperation.d.ts.map +1 -0
- package/dist/types/src/types/TranscriptionCapabilities.d.ts +17 -0
- package/dist/types/src/types/TranscriptionCapabilities.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +2 -2
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +125 -94
- package/src/TranscriptionPlugin.node.ts +3 -31
- package/src/TranscriptionPlugin.test.ts +3 -3
- package/src/TranscriptionPlugin.tsx +3 -31
- package/src/TranscriptionPlugin.workerd.ts +18 -0
- package/src/blueprints/transcription-blueprint.ts +1 -1
- package/src/capabilities/index.ts +1 -0
- package/src/capabilities/react-surface.tsx +2 -2
- package/src/capabilities/text-content.ts +33 -0
- package/src/capabilities/transcriber.ts +1 -1
- package/src/components/Oracle/Oracle.stories.tsx +147 -0
- package/src/components/Oracle/Oracle.tsx +141 -0
- package/src/components/Oracle/index.ts +5 -0
- package/src/components/Transcription/Transcription.stories.tsx +116 -150
- package/src/components/Transcription/Transcription.tsx +7 -13
- package/src/components/index.ts +1 -0
- package/src/containers/TranscriptionArticle/TranscriptionArticle.stories.tsx +61 -0
- package/src/containers/TranscriptionArticle/TranscriptionArticle.tsx +62 -0
- package/src/containers/TranscriptionArticle/index.ts +5 -0
- package/src/containers/index.ts +1 -1
- package/src/enrichment/conversation.test.ts +162 -0
- package/src/enrichment/conversation.txt +23 -0
- package/src/enrichment/operations.ts +268 -0
- package/src/enrichment/parser.ts +101 -0
- package/src/enrichment/stream-simulator.ts +125 -0
- package/src/hooks/index.ts +2 -1
- package/src/hooks/useFeedModelAdapter.ts +46 -0
- package/src/hooks/useTranscriptionRecording.ts +87 -0
- package/src/index.ts +3 -9
- package/src/normalization/message-normalizer.ts +25 -13
- package/src/normalization/normalization.test.ts +3 -37
- package/src/normalization/normalization.ts +6 -7
- package/src/operations/create.ts +2 -2
- package/src/operations/index.ts +0 -2
- package/src/operations/open.ts +2 -2
- package/src/operations/summarize.ts +3 -3
- package/src/plugin.ts +11 -0
- package/src/stories/FileTranscription.stories.tsx +49 -175
- package/src/stories/KeyWordRecognition.stories.tsx +7 -9
- package/src/stories/LiveTranscription.stories.tsx +153 -0
- package/src/stories/TranscriptionStory.tsx +32 -24
- package/src/stories/common.ts +233 -0
- package/src/testing/testing.ts +20 -27
- package/src/transcriber/transcriber.test.ts +9 -9
- package/src/transcriber/transcriber.ts +18 -14
- package/src/transcriber/transcription-manager.ts +14 -9
- package/src/translations.ts +3 -0
- package/src/{operations/definitions.ts → types/TranscriptOperation.ts} +2 -0
- package/src/types/TranscriptionCapabilities.ts +39 -0
- package/src/types/index.ts +2 -2
- package/dist/lib/browser/blueprints/index.mjs.map +0 -7
- package/dist/lib/browser/chunk-3L623EVL.mjs +0 -17
- package/dist/lib/browser/chunk-3L623EVL.mjs.map +0 -7
- package/dist/lib/browser/chunk-L4HW35L6.mjs.map +0 -7
- package/dist/lib/browser/chunk-NQQK7UZQ.mjs.map +0 -7
- package/dist/lib/browser/components/index.mjs.map +0 -7
- package/dist/lib/browser/create-ULR5DPJZ.mjs.map +0 -7
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/open-SFMPLZVW.mjs.map +0 -7
- package/dist/lib/browser/operations/index.mjs +0 -13
- package/dist/lib/browser/operations/index.mjs.map +0 -7
- package/dist/lib/browser/summarize-BDFX4APN.mjs.map +0 -7
- package/dist/lib/browser/types/index.mjs +0 -10
- package/dist/lib/node-esm/blueprints/index.mjs +0 -32
- package/dist/lib/node-esm/blueprints/index.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-7UIZWCD4.mjs +0 -112
- package/dist/lib/node-esm/chunk-7UIZWCD4.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-C3AE2NYJ.mjs +0 -181
- package/dist/lib/node-esm/chunk-C3AE2NYJ.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
- package/dist/lib/node-esm/chunk-KO3IOD2O.mjs +0 -19
- package/dist/lib/node-esm/chunk-KO3IOD2O.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-MQK4R6GT.mjs +0 -245
- package/dist/lib/node-esm/chunk-MQK4R6GT.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-O533NBMK.mjs +0 -37
- package/dist/lib/node-esm/chunk-O533NBMK.mjs.map +0 -7
- package/dist/lib/node-esm/components/index.mjs +0 -208
- package/dist/lib/node-esm/components/index.mjs.map +0 -7
- package/dist/lib/node-esm/create-A5QHNWRF.mjs +0 -22
- package/dist/lib/node-esm/create-A5QHNWRF.mjs.map +0 -7
- package/dist/lib/node-esm/hooks/index.mjs +0 -18
- package/dist/lib/node-esm/index.mjs +0 -474
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/open-OI24QNTO.mjs +0 -28
- package/dist/lib/node-esm/open-OI24QNTO.mjs.map +0 -7
- package/dist/lib/node-esm/operations/index.mjs +0 -14
- package/dist/lib/node-esm/operations/index.mjs.map +0 -7
- package/dist/lib/node-esm/summarize-3UNN24I2.mjs +0 -79
- package/dist/lib/node-esm/summarize-3UNN24I2.mjs.map +0 -7
- package/dist/lib/node-esm/types/index.mjs +0 -11
- package/dist/types/src/containers/TranscriptionContainer/TranscriptionContainer.d.ts +0 -6
- package/dist/types/src/containers/TranscriptionContainer/TranscriptionContainer.d.ts.map +0 -1
- package/dist/types/src/containers/TranscriptionContainer/index.d.ts +0 -2
- package/dist/types/src/containers/TranscriptionContainer/index.d.ts.map +0 -1
- package/dist/types/src/hooks/useQueueModelAdapter.d.ts +0 -8
- package/dist/types/src/hooks/useQueueModelAdapter.d.ts.map +0 -1
- package/dist/types/src/operations/definitions.d.ts.map +0 -1
- package/dist/types/src/stories/MicrophoneTranscription.stories.d.ts.map +0 -1
- package/dist/types/src/stories/useIsSpeaking.d.ts +0 -3
- package/dist/types/src/stories/useIsSpeaking.d.ts.map +0 -1
- package/dist/types/src/types/capabilities.d.ts +0 -19
- package/dist/types/src/types/capabilities.d.ts.map +0 -1
- package/dist/types/src/types/defs.d.ts +0 -5
- package/dist/types/src/types/defs.d.ts.map +0 -1
- package/src/containers/TranscriptionContainer/TranscriptionContainer.tsx +0 -35
- package/src/containers/TranscriptionContainer/index.ts +0 -5
- package/src/hooks/useQueueModelAdapter.ts +0 -66
- package/src/stories/MicrophoneTranscription.stories.tsx +0 -264
- package/src/stories/useIsSpeaking.tsx +0 -40
- package/src/types/capabilities.ts +0 -39
- package/src/types/defs.ts +0 -9
- /package/dist/lib/{browser/hooks → neutral/blueprints}/index.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs +0 -0
- /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/chunk-KAEXRX3Y.mjs +0 -0
- /package/dist/lib/{browser → neutral}/chunk-KAEXRX3Y.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/chunk-MLRJWAQH.mjs +0 -0
- /package/dist/lib/{browser → neutral}/chunk-MLRJWAQH.mjs.map +0 -0
- /package/dist/lib/{browser/types → neutral/hooks}/index.mjs.map +0 -0
- /package/dist/lib/{node-esm/hooks → neutral}/index.mjs.map +0 -0
- /package/dist/lib/{node-esm/chunk-HSLMI22Q.mjs.map → neutral/meta.mjs.map} +0 -0
- /package/dist/lib/{node-esm/types → neutral/operations}/index.mjs.map +0 -0
|
@@ -3,57 +3,50 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
-
import
|
|
7
|
-
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
6
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
8
7
|
|
|
9
|
-
import {
|
|
10
|
-
import { AppActivationEvents } from '@dxos/app-toolkit';
|
|
11
|
-
import { scheduleTask } from '@dxos/async';
|
|
12
|
-
import { Context } from '@dxos/context';
|
|
13
|
-
import { Obj } from '@dxos/echo';
|
|
14
|
-
import { MemoryQueue } from '@dxos/echo-db';
|
|
15
|
-
import { createQueueDXN } from '@dxos/echo/internal';
|
|
16
|
-
import { FunctionExecutor, ServiceContainer } from '@dxos/functions-runtime';
|
|
17
|
-
import { log } from '@dxos/log';
|
|
18
|
-
import { ClientPlugin } from '@dxos/plugin-client';
|
|
19
|
-
import { initializeIdentity } from '@dxos/plugin-client/testing';
|
|
20
|
-
import { PreviewPlugin } from '@dxos/plugin-preview';
|
|
21
|
-
import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
|
|
22
|
-
import { withLayout } from '@dxos/react-ui/testing';
|
|
23
|
-
import { TestSchema } from '@dxos/schema/testing';
|
|
24
|
-
import { type Actor, Message, Organization, Person } from '@dxos/types';
|
|
25
|
-
import { seedTestData } from '@dxos/types/testing';
|
|
8
|
+
import { useAudioFile } from '#hooks';
|
|
26
9
|
|
|
27
|
-
import { useAudioFile, useQueueModelAdapter, useTranscriber } from '#hooks';
|
|
28
|
-
import { TestItem } from '#testing';
|
|
29
|
-
|
|
30
|
-
import { MessageNormalizer, getActorId } from '../normalization';
|
|
31
10
|
import { type MediaStreamRecorderProps, type TranscriberProps } from '../transcriber';
|
|
32
|
-
import {
|
|
33
|
-
|
|
11
|
+
import {
|
|
12
|
+
useIsSpeaking,
|
|
13
|
+
createStoryDecorators,
|
|
14
|
+
useStoryAppendSegments,
|
|
15
|
+
useStoryMessageModel,
|
|
16
|
+
useStoryTranscriber,
|
|
17
|
+
} from './common';
|
|
34
18
|
import { TranscriptionStory } from './TranscriptionStory';
|
|
35
|
-
import { useIsSpeaking } from './useIsSpeaking';
|
|
36
19
|
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
normalizeSentences,
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
20
|
+
const DEFAULT_TRANSCRIBER_CONFIG = {
|
|
21
|
+
transcribeAfterChunksAmount: 100,
|
|
22
|
+
prefixBufferChunksAmount: 50,
|
|
23
|
+
normalizeSentences: true,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const DEFAULT_RECORDER_CONFIG = {
|
|
27
|
+
interval: 200,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type DefaultStoryProps = {
|
|
45
31
|
detectSpeaking?: boolean;
|
|
46
32
|
normalizeSentences?: boolean;
|
|
47
33
|
audioUrl: string;
|
|
48
34
|
transcriberConfig?: TranscriberProps['config'];
|
|
49
35
|
recorderConfig?: MediaStreamRecorderProps['config'];
|
|
50
36
|
audioConstraints?: MediaTrackConstraints;
|
|
51
|
-
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const DefaultStory = ({
|
|
40
|
+
detectSpeaking,
|
|
41
|
+
audioUrl,
|
|
42
|
+
normalizeSentences,
|
|
43
|
+
transcriberConfig = DEFAULT_TRANSCRIBER_CONFIG,
|
|
44
|
+
recorderConfig = DEFAULT_RECORDER_CONFIG,
|
|
45
|
+
audioConstraints,
|
|
46
|
+
}: DefaultStoryProps) => {
|
|
52
47
|
const [running, setRunning] = useState(false);
|
|
53
|
-
const actor: Actor.Actor = useMemo(() => ({ name: 'You' }), []);
|
|
54
48
|
|
|
55
|
-
// Optional uploaded file overrides the default URL.
|
|
56
|
-
// The useEffect below revokes the previous URL whenever it changes (and on unmount).
|
|
49
|
+
// Optional uploaded file overrides the default URL; revoke the previous URL on change/unmount.
|
|
57
50
|
const [uploadedUrl, setUploadedUrl] = useState<string>();
|
|
58
51
|
useEffect(() => {
|
|
59
52
|
return () => {
|
|
@@ -69,12 +62,10 @@ const AudioFile = ({
|
|
|
69
62
|
}, []);
|
|
70
63
|
|
|
71
64
|
const sourceUrl = uploadedUrl ?? audioUrl;
|
|
72
|
-
|
|
73
|
-
// Audio.
|
|
74
65
|
const { audio, track, stream } = useAudioFile(sourceUrl, audioConstraints);
|
|
75
|
-
|
|
76
|
-
// Speaking.
|
|
77
66
|
const isSpeaking = detectSpeaking ? useIsSpeaking(track) : true;
|
|
67
|
+
|
|
68
|
+
// Pipe the decoded stream into the <audio> element so the user hears playback.
|
|
78
69
|
const ref = useRef<HTMLAudioElement>(null);
|
|
79
70
|
useEffect(() => {
|
|
80
71
|
if (ref.current && stream) {
|
|
@@ -82,6 +73,7 @@ const AudioFile = ({
|
|
|
82
73
|
}
|
|
83
74
|
}, [stream]);
|
|
84
75
|
|
|
76
|
+
// Mirror the toolbar's running flag to the underlying HTMLAudioElement.
|
|
85
77
|
useEffect(() => {
|
|
86
78
|
if (!audio) {
|
|
87
79
|
return;
|
|
@@ -94,176 +86,58 @@ const AudioFile = ({
|
|
|
94
86
|
}
|
|
95
87
|
}, [audio, running]);
|
|
96
88
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const queueDxn = useMemo(() => createQueueDXN(), []);
|
|
100
|
-
const queue = useMemo(() => new MemoryQueue<Message.Message>(queueDxn), [queueDxn]);
|
|
101
|
-
|
|
102
|
-
const model = useQueueModelAdapter(renderByline([]), queue);
|
|
103
|
-
const handleSegments = useCallback<TranscriberProps['onSegments']>(
|
|
104
|
-
async (blocks) => {
|
|
105
|
-
void queue?.append([
|
|
106
|
-
Obj.make(Message.Message, {
|
|
107
|
-
created: new Date().toISOString(),
|
|
108
|
-
sender: actor,
|
|
109
|
-
blocks,
|
|
110
|
-
}),
|
|
111
|
-
]);
|
|
112
|
-
},
|
|
113
|
-
[queue],
|
|
114
|
-
);
|
|
89
|
+
const { model, appendMessage } = useStoryMessageModel();
|
|
90
|
+
const handleSegments = useStoryAppendSegments(appendMessage);
|
|
115
91
|
|
|
116
|
-
|
|
92
|
+
useStoryTranscriber({
|
|
117
93
|
audioStreamTrack: track,
|
|
118
|
-
|
|
94
|
+
running,
|
|
95
|
+
isSpeaking,
|
|
119
96
|
transcriberConfig,
|
|
120
97
|
recorderConfig,
|
|
98
|
+
onSegments: handleSegments,
|
|
121
99
|
});
|
|
122
100
|
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
const executor = new FunctionExecutor(
|
|
129
|
-
new ServiceContainer().setServices({
|
|
130
|
-
// ai: {
|
|
131
|
-
// client: new Edge AiServiceClient({
|
|
132
|
-
// endpoint: AI_SERVICE_ENDPOINT.REMOTE,
|
|
133
|
-
// defaultGenerationOptions: {
|
|
134
|
-
// model: '@anthropic/claude-3-5-sonnet-20241022',
|
|
135
|
-
// },
|
|
136
|
-
// }),
|
|
137
|
-
// },
|
|
138
|
-
}),
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
return new MessageNormalizer({
|
|
142
|
-
functionExecutor: executor,
|
|
143
|
-
queue,
|
|
144
|
-
startingCursor: { actorId: getActorId(actor), timestamp: new Date().toISOString() },
|
|
145
|
-
});
|
|
146
|
-
}, [normalizeSentences, queue, actor]);
|
|
147
|
-
|
|
148
|
-
useEffect(() => {
|
|
149
|
-
if (!normalizer) {
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
const ctx = new Context();
|
|
153
|
-
scheduleTask(ctx, async () => {
|
|
154
|
-
await normalizer.open();
|
|
155
|
-
ctx.onDispose(async () => {
|
|
156
|
-
await normalizer.close();
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
return () => {
|
|
160
|
-
void ctx.dispose();
|
|
161
|
-
};
|
|
162
|
-
}, [normalizer]);
|
|
163
|
-
|
|
164
|
-
const manageChunkRecording = () => {
|
|
165
|
-
if (running && isSpeaking && transcriber) {
|
|
166
|
-
log.info('starting transcription');
|
|
167
|
-
transcriber?.startChunksRecording();
|
|
168
|
-
} else if ((!isSpeaking || !running) && transcriber) {
|
|
169
|
-
log.info('stopping transcription');
|
|
170
|
-
transcriber?.stopChunksRecording();
|
|
171
|
-
}
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
useEffect(() => {
|
|
175
|
-
const ctx = new Context();
|
|
176
|
-
scheduleTask(ctx, async () => {
|
|
177
|
-
if (transcriber && running) {
|
|
178
|
-
await transcriber.open();
|
|
179
|
-
} else if (!running) {
|
|
180
|
-
await transcriber?.close();
|
|
181
|
-
}
|
|
182
|
-
manageChunkRecording();
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
return () => {
|
|
186
|
-
void ctx.dispose();
|
|
187
|
-
};
|
|
188
|
-
}, [transcriber, running]);
|
|
189
|
-
|
|
190
|
-
useEffect(() => {
|
|
191
|
-
manageChunkRecording();
|
|
192
|
-
}, [isSpeaking, stream]);
|
|
101
|
+
// TODO(burdon): Sentence normalization moved to require a real space-backed Feed + FeedService runtime.
|
|
102
|
+
// Re-enable here once the story has access to one.
|
|
103
|
+
void normalizeSentences;
|
|
193
104
|
|
|
194
105
|
return (
|
|
195
106
|
<TranscriptionStory
|
|
107
|
+
audioRef={ref}
|
|
196
108
|
disabled={!stream}
|
|
197
109
|
model={model}
|
|
198
110
|
running={running}
|
|
199
111
|
onRunningChange={setRunning}
|
|
200
|
-
audioRef={ref}
|
|
201
112
|
onUpload={handleUpload}
|
|
202
113
|
/>
|
|
203
114
|
);
|
|
204
115
|
};
|
|
205
116
|
|
|
206
117
|
const meta = {
|
|
207
|
-
title: 'plugins/plugin-transcription/
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
plugins: [
|
|
212
|
-
...corePlugins(),
|
|
213
|
-
StorybookPlugin({}),
|
|
214
|
-
ClientPlugin({
|
|
215
|
-
types: [TestItem, Person.Person, Organization.Organization, TestSchema.DocumentType],
|
|
216
|
-
onClientInitialized: ({ client }) =>
|
|
217
|
-
Effect.gen(function* () {
|
|
218
|
-
const { personalSpace } = yield* initializeIdentity(client);
|
|
219
|
-
yield* Effect.promise(() => seedTestData(personalSpace));
|
|
220
|
-
}),
|
|
221
|
-
}),
|
|
222
|
-
|
|
223
|
-
PreviewPlugin(),
|
|
224
|
-
TranscriptionPlugin(),
|
|
225
|
-
],
|
|
226
|
-
fireEvents: [AppActivationEvents.SetupAppGraph],
|
|
227
|
-
}),
|
|
228
|
-
],
|
|
229
|
-
} satisfies Meta;
|
|
118
|
+
title: 'plugins/plugin-transcription/stories/FileTranscription',
|
|
119
|
+
render: DefaultStory,
|
|
120
|
+
decorators: createStoryDecorators(),
|
|
121
|
+
} satisfies Meta<DefaultStoryProps>;
|
|
230
122
|
|
|
231
123
|
export default meta;
|
|
232
124
|
|
|
233
125
|
type Story = StoryObj<typeof meta>;
|
|
234
126
|
|
|
235
|
-
const
|
|
236
|
-
transcribeAfterChunksAmount: 100,
|
|
237
|
-
prefixBufferChunksAmount: 50,
|
|
238
|
-
normalizeSentences: true,
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
const RECORDER_CONFIG = {
|
|
242
|
-
interval: 200,
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
export const Default: StoryObj<typeof AudioFile> = {
|
|
246
|
-
render: AudioFile,
|
|
127
|
+
export const Default: Story = {
|
|
247
128
|
args: {
|
|
248
129
|
detectSpeaking: true,
|
|
249
130
|
// https://learnenglish.britishcouncil.org/general-english/audio-zone/living-london
|
|
250
131
|
audioUrl: 'https://dxos.network/audio-london.m4a',
|
|
251
132
|
// textUrl: 'https://dxos.network/audio-london.txt',
|
|
252
|
-
transcriberConfig: TRANSCRIBER_CONFIG,
|
|
253
|
-
recorderConfig: RECORDER_CONFIG,
|
|
254
133
|
},
|
|
255
134
|
};
|
|
256
135
|
|
|
257
136
|
// TODO(mykola): Fix sentence normalization.
|
|
258
137
|
// export const WithSentenceNormalization: StoryObj<typeof AudioFile> = {
|
|
259
|
-
// render: AudioFile,
|
|
260
138
|
// args: {
|
|
261
139
|
// detectSpeaking: true,
|
|
262
140
|
// normalizeSentences: true,
|
|
263
|
-
// // https://learnenglish.britishcouncil.org/general-english/audio-zone/living-london
|
|
264
141
|
// audioUrl: 'https://dxos.network/audio-london.m4a',
|
|
265
|
-
// // textUrl: 'https://dxos.network/audio-london.txt',
|
|
266
|
-
// transcriberConfig: TRANSCRIBER_CONFIG,
|
|
267
|
-
// recorderConfig: RECORDER_CONFIG,
|
|
268
142
|
// },
|
|
269
143
|
// };
|
|
@@ -23,7 +23,6 @@ const DefaultStory = ({ keywords }: DefaultStoryProps) => {
|
|
|
23
23
|
const [running, setRunning] = useState(false);
|
|
24
24
|
const [matchingWords, setMatchingWords] = useState<Word[]>(keywords.map((word) => ({ text: word, matched: false })));
|
|
25
25
|
|
|
26
|
-
const [transcript, setTranscript] = useState('');
|
|
27
26
|
const recognition = useMemo(() => {
|
|
28
27
|
// TODO(mykola): Fix types
|
|
29
28
|
const SpeechRecognition = (window as any).SpeechRecognition || (window as any).webkitSpeechRecognition;
|
|
@@ -38,16 +37,16 @@ const DefaultStory = ({ keywords }: DefaultStoryProps) => {
|
|
|
38
37
|
recognition.interimResults = true;
|
|
39
38
|
recognition.lang = 'en-US';
|
|
40
39
|
recognition.maxAlternatives = 1;
|
|
41
|
-
// Add this line to enable punctuation
|
|
40
|
+
// Add this line to enable punctuation.
|
|
42
41
|
recognition.grammars = new SpeechGrammarList();
|
|
43
42
|
|
|
44
|
-
// TODO(mykola): Fix types
|
|
43
|
+
// TODO(mykola): Fix types.
|
|
45
44
|
recognition.onresult = (event: any) => {
|
|
46
45
|
log.info('recognition result', { event });
|
|
47
46
|
const current = event.resultIndex;
|
|
48
47
|
const transcript = event.results[current][0].transcript;
|
|
49
48
|
|
|
50
|
-
// Remove punctuation and normalize whitespace for comparison
|
|
49
|
+
// Remove punctuation and normalize whitespace for comparison.
|
|
51
50
|
const normalizeText = (text: string) =>
|
|
52
51
|
text
|
|
53
52
|
.toLowerCase()
|
|
@@ -57,7 +56,7 @@ const DefaultStory = ({ keywords }: DefaultStoryProps) => {
|
|
|
57
56
|
|
|
58
57
|
const normalizedTranscript = normalizeText(transcript);
|
|
59
58
|
|
|
60
|
-
// Check each keyword and update matching words
|
|
59
|
+
// Check each keyword and update matching words.
|
|
61
60
|
const updatedWords = matchingWords.map((word) => {
|
|
62
61
|
const normalizedKeyword = normalizeText(word.text);
|
|
63
62
|
const matched = normalizedTranscript.includes(normalizedKeyword);
|
|
@@ -65,10 +64,9 @@ const DefaultStory = ({ keywords }: DefaultStoryProps) => {
|
|
|
65
64
|
});
|
|
66
65
|
|
|
67
66
|
setMatchingWords(updatedWords);
|
|
68
|
-
setTranscript(transcript);
|
|
69
67
|
};
|
|
70
68
|
|
|
71
|
-
// TODO(mykola): Fix types
|
|
69
|
+
// TODO(mykola): Fix types.
|
|
72
70
|
recognition.onerror = (event: any) => {
|
|
73
71
|
log.error('Speech recognition error:', { error: event.error });
|
|
74
72
|
setRunning(false);
|
|
@@ -99,7 +97,7 @@ const DefaultStory = ({ keywords }: DefaultStoryProps) => {
|
|
|
99
97
|
</Panel.Toolbar>
|
|
100
98
|
|
|
101
99
|
<Panel.Content>
|
|
102
|
-
<div className='p-
|
|
100
|
+
<div className='p-2 grid grid-cols-[repeat(auto-fill,minmax(6rem,1fr))] gap-2'>
|
|
103
101
|
{matchingWords.map((word, index) => (
|
|
104
102
|
<span
|
|
105
103
|
key={index}
|
|
@@ -118,7 +116,7 @@ const DefaultStory = ({ keywords }: DefaultStoryProps) => {
|
|
|
118
116
|
};
|
|
119
117
|
|
|
120
118
|
const meta = {
|
|
121
|
-
title: 'plugins/plugin-transcription/
|
|
119
|
+
title: 'plugins/plugin-transcription/stories/KeyWordDetection',
|
|
122
120
|
render: DefaultStory,
|
|
123
121
|
decorators: [withTheme(), withLayout({ layout: 'column' })],
|
|
124
122
|
} satisfies Meta<typeof DefaultStory>;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
type ExtractionFunction,
|
|
10
|
+
extractionAnthropicFunction,
|
|
11
|
+
extractionNerFunction,
|
|
12
|
+
getNer,
|
|
13
|
+
processTranscriptMessage,
|
|
14
|
+
} from '@dxos/assistant/extraction';
|
|
15
|
+
import { Filter, type Obj } from '@dxos/echo';
|
|
16
|
+
import { invariant } from '@dxos/invariant';
|
|
17
|
+
import { useSpaces } from '@dxos/react-client/echo';
|
|
18
|
+
import { Message, Organization, Person } from '@dxos/types';
|
|
19
|
+
|
|
20
|
+
import { useAudioTrack } from '#hooks';
|
|
21
|
+
|
|
22
|
+
import { type MediaStreamRecorderProps, type TranscriberProps } from '../transcriber';
|
|
23
|
+
import { useIsSpeaking, createStoryDecorators, useStoryMessageModel, useStoryTranscriber } from './common';
|
|
24
|
+
import { TranscriptionStory } from './TranscriptionStory';
|
|
25
|
+
|
|
26
|
+
const DEFAULT_TRANSCRIBER_CONFIG = {
|
|
27
|
+
transcribeAfterChunksAmount: 50,
|
|
28
|
+
prefixBufferChunksAmount: 10,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const DEFAULT_RECORDER_CONFIG = {
|
|
32
|
+
interval: 200,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type DefaultStoryProps = {
|
|
36
|
+
detectSpeaking?: boolean;
|
|
37
|
+
transcriberConfig?: TranscriberProps['config'];
|
|
38
|
+
recorderConfig?: MediaStreamRecorderProps['config'];
|
|
39
|
+
audioConstraints?: MediaTrackConstraints;
|
|
40
|
+
entityExtraction?: 'ner' | 'llm';
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const DefaultStory = ({
|
|
44
|
+
detectSpeaking,
|
|
45
|
+
entityExtraction,
|
|
46
|
+
transcriberConfig = DEFAULT_TRANSCRIBER_CONFIG,
|
|
47
|
+
recorderConfig = DEFAULT_RECORDER_CONFIG,
|
|
48
|
+
audioConstraints,
|
|
49
|
+
}: DefaultStoryProps) => {
|
|
50
|
+
const [running, setRunning] = useState(false);
|
|
51
|
+
const track = useAudioTrack(running, audioConstraints);
|
|
52
|
+
const isSpeaking = detectSpeaking ? useIsSpeaking(track) : true;
|
|
53
|
+
const { model, appendMessage } = useStoryMessageModel();
|
|
54
|
+
const [space] = useSpaces();
|
|
55
|
+
|
|
56
|
+
// Resolve the entity-extraction function + nearby objects once per `entityExtraction` toggle.
|
|
57
|
+
const { extractionFunction, objects } = useMemo(() => {
|
|
58
|
+
if (!space || !entityExtraction) {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let extractionFunction: ExtractionFunction | undefined;
|
|
63
|
+
let objects: Promise<Obj.Unknown[]> | undefined;
|
|
64
|
+
if (entityExtraction === 'ner') {
|
|
65
|
+
void getNer(); // Init model loading. Takes time.
|
|
66
|
+
extractionFunction = extractionNerFunction;
|
|
67
|
+
} else if (entityExtraction === 'llm') {
|
|
68
|
+
extractionFunction = extractionAnthropicFunction;
|
|
69
|
+
objects = space.db.query(Filter.or(Filter.type(Person.Person), Filter.type(Organization.Organization))).run();
|
|
70
|
+
}
|
|
71
|
+
return { extractionFunction, objects };
|
|
72
|
+
}, [entityExtraction, space]);
|
|
73
|
+
|
|
74
|
+
// Build a Message from each transcribed batch; optionally enrich via entity extraction.
|
|
75
|
+
const handleSegments = useCallback<TranscriberProps['onSegments']>(
|
|
76
|
+
async (blocks) => {
|
|
77
|
+
const message = Message.make({
|
|
78
|
+
sender: { name: 'You' },
|
|
79
|
+
created: new Date().toISOString(),
|
|
80
|
+
blocks,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
if (entityExtraction && space) {
|
|
84
|
+
invariant(extractionFunction, 'extractionFunction is required');
|
|
85
|
+
const result = await processTranscriptMessage({
|
|
86
|
+
function: extractionFunction,
|
|
87
|
+
input: { message, objects: await objects },
|
|
88
|
+
options: { fallbackToRaw: true, timeout: 30_000 },
|
|
89
|
+
});
|
|
90
|
+
appendMessage(result.message);
|
|
91
|
+
} else {
|
|
92
|
+
appendMessage(message);
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
[appendMessage, space, entityExtraction, extractionFunction, objects],
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
useStoryTranscriber({
|
|
99
|
+
audioStreamTrack: track,
|
|
100
|
+
running,
|
|
101
|
+
isSpeaking,
|
|
102
|
+
transcriberConfig,
|
|
103
|
+
recorderConfig,
|
|
104
|
+
onSegments: handleSegments,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
return <TranscriptionStory model={model} running={running} onRunningChange={setRunning} />;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const meta = {
|
|
111
|
+
title: 'plugins/plugin-transcription/stories/LiveTranscription',
|
|
112
|
+
render: DefaultStory,
|
|
113
|
+
decorators: createStoryDecorators({ enableVectorIndex: true }),
|
|
114
|
+
} satisfies Meta<typeof DefaultStory>;
|
|
115
|
+
|
|
116
|
+
export default meta;
|
|
117
|
+
|
|
118
|
+
type Story = StoryObj<typeof meta>;
|
|
119
|
+
|
|
120
|
+
export const Default: Story = {
|
|
121
|
+
args: {
|
|
122
|
+
detectSpeaking: false,
|
|
123
|
+
audioConstraints: {
|
|
124
|
+
echoCancellation: false,
|
|
125
|
+
noiseSuppression: false,
|
|
126
|
+
autoGainControl: false,
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export const SpeechDetection: Story = {
|
|
132
|
+
args: {
|
|
133
|
+
detectSpeaking: true,
|
|
134
|
+
audioConstraints: {
|
|
135
|
+
echoCancellation: true,
|
|
136
|
+
noiseSuppression: true,
|
|
137
|
+
autoGainControl: true,
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// TODO(mykola): Fix hugging face quota issues.
|
|
143
|
+
// export const EntityExtraction: Story = {
|
|
144
|
+
// args: {
|
|
145
|
+
// detectSpeaking: true,
|
|
146
|
+
// entityExtraction: 'ner',
|
|
147
|
+
// audioConstraints: {
|
|
148
|
+
// echoCancellation: true,
|
|
149
|
+
// noiseSuppression: true,
|
|
150
|
+
// autoGainControl: true,
|
|
151
|
+
// },
|
|
152
|
+
// },
|
|
153
|
+
// };
|
|
@@ -2,15 +2,12 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
5
|
import React, {
|
|
8
6
|
type ChangeEvent,
|
|
9
7
|
type Dispatch,
|
|
10
|
-
type FC,
|
|
11
|
-
type ReactNode,
|
|
12
8
|
type RefObject,
|
|
13
9
|
type SetStateAction,
|
|
10
|
+
useCallback,
|
|
14
11
|
useRef,
|
|
15
12
|
} from 'react';
|
|
16
13
|
|
|
@@ -20,34 +17,46 @@ import { type Message } from '@dxos/types';
|
|
|
20
17
|
import { Transcription } from '../components';
|
|
21
18
|
import { type SerializationModel } from '../model';
|
|
22
19
|
|
|
20
|
+
export type TranscriptionStoryProps = {
|
|
21
|
+
audioRef?: RefObject<HTMLAudioElement | null>;
|
|
22
|
+
model: SerializationModel<Message.Message>;
|
|
23
|
+
running: boolean;
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
uploadAccept?: string;
|
|
26
|
+
onRunningChange: Dispatch<SetStateAction<boolean>>;
|
|
27
|
+
onUpload?: (file: File) => void;
|
|
28
|
+
};
|
|
29
|
+
|
|
23
30
|
/**
|
|
24
31
|
* Story wrapper that renders a transcript with playback controls, an optional audio file
|
|
25
32
|
* upload affordance, and a toolbar slot for additional controls.
|
|
26
33
|
*
|
|
27
34
|
* @param onUpload - Callback fired when the user picks a local audio file via the upload button.
|
|
28
35
|
* @param uploadAccept - HTML `accept` filter for the hidden file input. Defaults to `audio/*`.
|
|
29
|
-
* @param toolbarSlot - Extra content rendered after the upload button inside the toolbar.
|
|
30
36
|
*/
|
|
31
|
-
export const TranscriptionStory
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
running
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}> = ({ model, running, onRunningChange, audioRef, disabled, onUpload, uploadAccept = 'audio/*', toolbarSlot }) => {
|
|
37
|
+
export const TranscriptionStory = ({
|
|
38
|
+
audioRef,
|
|
39
|
+
model,
|
|
40
|
+
running,
|
|
41
|
+
disabled,
|
|
42
|
+
uploadAccept = 'audio/*',
|
|
43
|
+
onRunningChange,
|
|
44
|
+
onUpload,
|
|
45
|
+
}: TranscriptionStoryProps) => {
|
|
41
46
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
42
47
|
|
|
43
|
-
const handleFileChange = (
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
const handleFileChange = useCallback(
|
|
49
|
+
(event: ChangeEvent<HTMLInputElement>) => {
|
|
50
|
+
const file = event.target.files?.[0];
|
|
51
|
+
if (file && onUpload) {
|
|
52
|
+
onUpload(file);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Reset so the same file can be re-selected.
|
|
56
|
+
event.target.value = '';
|
|
57
|
+
},
|
|
58
|
+
[onUpload],
|
|
59
|
+
);
|
|
51
60
|
|
|
52
61
|
return (
|
|
53
62
|
<>
|
|
@@ -79,7 +88,6 @@ export const TranscriptionStory: FC<{
|
|
|
79
88
|
/>
|
|
80
89
|
</>
|
|
81
90
|
)}
|
|
82
|
-
{toolbarSlot}
|
|
83
91
|
</Toolbar.Root>
|
|
84
92
|
<ScrollContainer.Root pin>
|
|
85
93
|
<ScrollContainer.Content>
|