@dxos/plugin-transcription 0.8.2-main.12df754 → 0.8.2-main.30e4dbb
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/dist/lib/browser/{TranscriptContainer-4D4BHE7G.mjs → TranscriptContainer-3LU3XBEB.mjs} +3 -3
- package/dist/lib/browser/app-graph-builder-YXELPN6F.mjs +349 -0
- package/dist/lib/browser/app-graph-builder-YXELPN6F.mjs.map +7 -0
- package/dist/lib/browser/{chunk-DR3NBV6Q.mjs → chunk-GWY2Q2NE.mjs} +105 -78
- package/dist/lib/browser/chunk-GWY2Q2NE.mjs.map +7 -0
- package/dist/lib/browser/{chunk-RHHJZQPM.mjs → chunk-HNIKXVCG.mjs} +135 -117
- package/dist/lib/browser/chunk-HNIKXVCG.mjs.map +7 -0
- package/dist/lib/browser/chunk-HRDAILQX.mjs +27 -0
- package/dist/lib/browser/chunk-HRDAILQX.mjs.map +7 -0
- package/dist/lib/browser/{chunk-5BE6RTNC.mjs → chunk-LTIQFX4W.mjs} +1 -1
- package/dist/lib/browser/{chunk-5BE6RTNC.mjs.map → chunk-LTIQFX4W.mjs.map} +1 -1
- package/dist/lib/browser/{chunk-5Q3R53U3.mjs → chunk-TGOKX32G.mjs} +21 -31
- package/dist/lib/browser/chunk-TGOKX32G.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +29 -17
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/{intent-resolver-ZQQEH27F.mjs → intent-resolver-BBB3DWV4.mjs} +4 -4
- package/dist/lib/browser/{intent-resolver-ZQQEH27F.mjs.map → intent-resolver-BBB3DWV4.mjs.map} +2 -2
- package/dist/lib/browser/{meeting-transcription-state-5D52VTHU.mjs → meeting-transcription-state-MNA2XROE.mjs} +2 -2
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{react-surface-HETJ5XY6.mjs → react-surface-R277DACJ.mjs} +18 -7
- package/dist/lib/browser/react-surface-R277DACJ.mjs.map +7 -0
- package/dist/lib/browser/settings-I226DZ27.mjs +24 -0
- package/dist/lib/browser/settings-I226DZ27.mjs.map +7 -0
- package/dist/lib/browser/{transcriber-SMRLU2KY.mjs → transcriber-QRSS6X6S.mjs} +4 -4
- package/dist/lib/browser/types/index.mjs +3 -3
- package/dist/lib/node/{TranscriptContainer-ZWP6UKY6.cjs → TranscriptContainer-DICOMZLM.cjs} +8 -8
- package/dist/lib/node/{TranscriptContainer-ZWP6UKY6.cjs.map → TranscriptContainer-DICOMZLM.cjs.map} +2 -2
- package/dist/lib/node/app-graph-builder-I6LYVIYY.cjs +350 -0
- package/dist/lib/node/app-graph-builder-I6LYVIYY.cjs.map +7 -0
- package/dist/lib/node/{chunk-3QEJ5EBO.cjs → chunk-34WFY7M7.cjs} +22 -32
- package/dist/lib/node/chunk-34WFY7M7.cjs.map +7 -0
- package/dist/lib/node/{chunk-KO6MKCED.cjs → chunk-4JOJCZPJ.cjs} +138 -119
- package/dist/lib/node/chunk-4JOJCZPJ.cjs.map +7 -0
- package/dist/lib/node/{chunk-HYGDHS6X.cjs → chunk-JUGOXWES.cjs} +4 -4
- package/dist/lib/node/{chunk-HYGDHS6X.cjs.map → chunk-JUGOXWES.cjs.map} +1 -1
- package/dist/lib/node/{chunk-WXG4Z2EZ.cjs → chunk-K553L7CZ.cjs} +23 -8
- package/dist/lib/node/chunk-K553L7CZ.cjs.map +7 -0
- package/dist/lib/node/{chunk-LA73IHCB.cjs → chunk-RK4BEN5E.cjs} +108 -80
- package/dist/lib/node/chunk-RK4BEN5E.cjs.map +7 -0
- package/dist/lib/node/index.cjs +46 -34
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/{intent-resolver-VY3XWYAA.cjs → intent-resolver-7XY3WITN.cjs} +8 -8
- package/dist/lib/node/{intent-resolver-VY3XWYAA.cjs.map → intent-resolver-7XY3WITN.cjs.map} +2 -2
- package/dist/lib/node/{meeting-transcription-state-OYWCY5XE.cjs → meeting-transcription-state-5XLTMWPA.cjs} +6 -6
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/{react-surface-M2JQIIDY.cjs → react-surface-HUSWAORU.cjs} +20 -11
- package/dist/lib/node/react-surface-HUSWAORU.cjs.map +7 -0
- package/dist/lib/node/settings-NDMU3EWQ.cjs +38 -0
- package/dist/lib/node/settings-NDMU3EWQ.cjs.map +7 -0
- package/dist/lib/node/{transcriber-E6AAB4SU.cjs → transcriber-UZJMG44C.cjs} +10 -10
- package/dist/lib/node/types/index.cjs +8 -8
- package/dist/lib/node/types/index.cjs.map +1 -1
- package/dist/lib/node-esm/{TranscriptContainer-3HJ752CP.mjs → TranscriptContainer-K4E4P5T6.mjs} +3 -3
- package/dist/lib/node-esm/app-graph-builder-6FY2UQOH.mjs +350 -0
- package/dist/lib/node-esm/app-graph-builder-6FY2UQOH.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-3I7AC3D3.mjs +28 -0
- package/dist/lib/node-esm/chunk-3I7AC3D3.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-YXZOLOFT.mjs → chunk-5E2MR5XK.mjs} +1 -1
- package/dist/lib/node-esm/{chunk-YXZOLOFT.mjs.map → chunk-5E2MR5XK.mjs.map} +1 -1
- package/dist/lib/node-esm/{chunk-SDMGJZLN.mjs → chunk-6C34FO4C.mjs} +135 -117
- package/dist/lib/node-esm/chunk-6C34FO4C.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-K3R3YK3S.mjs → chunk-CKANLRS4.mjs} +105 -78
- package/dist/lib/node-esm/chunk-CKANLRS4.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-54HDN33F.mjs → chunk-KUQ3ZTRX.mjs} +21 -31
- package/dist/lib/node-esm/chunk-KUQ3ZTRX.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +29 -17
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/{intent-resolver-FCTNJPWY.mjs → intent-resolver-ACGAPLDZ.mjs} +4 -4
- package/dist/lib/node-esm/{intent-resolver-FCTNJPWY.mjs.map → intent-resolver-ACGAPLDZ.mjs.map} +2 -2
- package/dist/lib/node-esm/{meeting-transcription-state-XMZJ45P4.mjs → meeting-transcription-state-3WOZ52BQ.mjs} +2 -2
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/{react-surface-PXFSIM6V.mjs → react-surface-G2OY4MWL.mjs} +18 -7
- package/dist/lib/node-esm/react-surface-G2OY4MWL.mjs.map +7 -0
- package/dist/lib/node-esm/settings-O7X2UQQG.mjs +25 -0
- package/dist/lib/node-esm/settings-O7X2UQQG.mjs.map +7 -0
- package/dist/lib/node-esm/{transcriber-REAGRCPY.mjs → transcriber-NVVFH7IF.mjs} +4 -4
- package/dist/lib/node-esm/types/index.mjs +3 -3
- package/dist/types/src/TranscriptionPlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/capabilities.d.ts +1 -1
- package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +1 -0
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/capabilities/settings.d.ts +4 -0
- package/dist/types/src/capabilities/settings.d.ts.map +1 -0
- package/dist/types/src/components/Transcript/Transcript.d.ts +6 -4
- package/dist/types/src/components/Transcript/Transcript.d.ts.map +1 -1
- package/dist/types/src/components/Transcript/Transcript.stories.d.ts +8 -4
- package/dist/types/src/components/Transcript/Transcript.stories.d.ts.map +1 -1
- package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts +27 -0
- package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts.map +1 -0
- package/dist/types/src/components/Transcript/transcript-extension.d.ts +4 -4
- package/dist/types/src/components/Transcript/transcript-extension.d.ts.map +1 -1
- package/dist/types/src/components/TranscriptContainer.d.ts.map +1 -1
- package/dist/types/src/components/TranscriptionSettings/TranscriptionSettings.d.ts +6 -0
- package/dist/types/src/components/TranscriptionSettings/TranscriptionSettings.d.ts.map +1 -0
- package/dist/types/src/components/TranscriptionSettings/index.d.ts +2 -0
- package/dist/types/src/components/TranscriptionSettings/index.d.ts.map +1 -0
- 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/components/transcription.stories.d.ts +6 -3
- package/dist/types/src/components/transcription.stories.d.ts.map +1 -1
- package/dist/types/src/entity-extraction/entity-extraction.d.ts +33 -7
- package/dist/types/src/entity-extraction/entity-extraction.d.ts.map +1 -1
- package/dist/types/src/hooks/useAudioFile.d.ts.map +1 -1
- package/dist/types/src/hooks/useAudioTrack.d.ts.map +1 -1
- package/dist/types/src/hooks/useQueueModelAdapter.d.ts +2 -2
- package/dist/types/src/hooks/useQueueModelAdapter.d.ts.map +1 -1
- package/dist/types/src/hooks/useTranscriber.d.ts.map +1 -1
- package/dist/types/src/hooks/useVoiceInput.d.ts.map +1 -1
- package/dist/types/src/model/model.d.ts +23 -23
- package/dist/types/src/model/model.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/testing.d.ts +40 -0
- package/dist/types/src/testing/testing.d.ts.map +1 -0
- package/dist/types/src/transcriber/transcriber.d.ts +2 -2
- package/dist/types/src/transcriber/transcriber.d.ts.map +1 -1
- package/dist/types/src/transcriber/transcription-manager.d.ts +17 -15
- package/dist/types/src/transcriber/transcription-manager.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +3 -18
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/schema.d.ts +26 -40
- package/dist/types/src/types/schema.d.ts.map +1 -1
- package/dist/types/src/types/types.d.ts +11 -7
- package/dist/types/src/types/types.d.ts.map +1 -1
- package/dist/types/src/util/array.d.ts.map +1 -1
- package/dist/types/src/util/get-time-str.d.ts.map +1 -1
- package/dist/types/src/util/monitor-audio-level.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +51 -46
- package/src/TranscriptionPlugin.tsx +29 -12
- package/src/capabilities/app-graph-builder.ts +99 -29
- package/src/capabilities/capabilities.ts +1 -1
- package/src/capabilities/index.ts +1 -0
- package/src/capabilities/intent-resolver.ts +2 -2
- package/src/capabilities/react-surface.tsx +12 -4
- package/src/capabilities/settings.ts +20 -0
- package/src/components/Transcript/Transcript.stories.tsx +72 -36
- package/src/components/Transcript/Transcript.tsx +37 -30
- package/src/components/Transcript/deprecated/{Transcript.tsx → OldTranscript.tsx} +30 -1
- package/src/components/Transcript/transcript-extension.ts +20 -17
- package/src/components/TranscriptContainer.tsx +6 -4
- package/src/components/TranscriptionSettings/TranscriptionSettings.tsx +26 -0
- package/src/components/TranscriptionSettings/index.ts +5 -0
- package/src/components/index.ts +1 -0
- package/src/components/transcription.stories.tsx +84 -34
- package/src/entity-extraction/entity-extraction.test.ts +75 -10
- package/src/entity-extraction/entity-extraction.ts +110 -55
- package/src/entity-extraction/system-prompt.tpl +38 -9
- package/src/hooks/useQueueModelAdapter.ts +44 -11
- package/src/model/model.test.ts +90 -44
- package/src/model/model.ts +59 -59
- package/src/testing/index.ts +5 -0
- package/src/testing/testing.ts +221 -0
- package/src/transcriber/transcriber.browser.test.ts +3 -3
- package/src/transcriber/transcriber.ts +19 -10
- package/src/transcriber/transcription-manager.ts +66 -57
- package/src/translations.ts +2 -0
- package/src/types/schema.ts +10 -31
- package/src/types/types.ts +17 -7
- package/dist/lib/browser/app-graph-builder-I4SSIQMW.mjs +0 -168
- package/dist/lib/browser/app-graph-builder-I4SSIQMW.mjs.map +0 -7
- package/dist/lib/browser/chunk-5Q3R53U3.mjs.map +0 -7
- package/dist/lib/browser/chunk-DR3NBV6Q.mjs.map +0 -7
- package/dist/lib/browser/chunk-GI5LZ4SV.mjs +0 -8
- package/dist/lib/browser/chunk-GI5LZ4SV.mjs.map +0 -7
- package/dist/lib/browser/chunk-RHHJZQPM.mjs.map +0 -7
- package/dist/lib/browser/react-surface-HETJ5XY6.mjs.map +0 -7
- package/dist/lib/node/app-graph-builder-WWC4WNTG.cjs +0 -177
- package/dist/lib/node/app-graph-builder-WWC4WNTG.cjs.map +0 -7
- package/dist/lib/node/chunk-3QEJ5EBO.cjs.map +0 -7
- package/dist/lib/node/chunk-KO6MKCED.cjs.map +0 -7
- package/dist/lib/node/chunk-LA73IHCB.cjs.map +0 -7
- package/dist/lib/node/chunk-WXG4Z2EZ.cjs.map +0 -7
- package/dist/lib/node/react-surface-M2JQIIDY.cjs.map +0 -7
- package/dist/lib/node-esm/app-graph-builder-PPJQAFSI.mjs +0 -169
- package/dist/lib/node-esm/app-graph-builder-PPJQAFSI.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-54HDN33F.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-K3R3YK3S.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-SDMGJZLN.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-XY22OP6H.mjs +0 -10
- package/dist/lib/node-esm/chunk-XY22OP6H.mjs.map +0 -7
- package/dist/lib/node-esm/react-surface-PXFSIM6V.mjs.map +0 -7
- package/dist/types/src/components/Transcript/deprecated/Transcript.d.ts +0 -13
- package/dist/types/src/components/Transcript/deprecated/Transcript.d.ts.map +0 -1
- package/dist/types/src/components/Transcript/testings.d.ts +0 -51
- package/dist/types/src/components/Transcript/testings.d.ts.map +0 -1
- package/dist/types/src/testing/test-data.d.ts +0 -73
- package/dist/types/src/testing/test-data.d.ts.map +0 -1
- package/src/components/Transcript/testings.ts +0 -182
- package/src/testing/test-data.ts +0 -314
- /package/dist/lib/browser/{TranscriptContainer-4D4BHE7G.mjs.map → TranscriptContainer-3LU3XBEB.mjs.map} +0 -0
- /package/dist/lib/browser/{meeting-transcription-state-5D52VTHU.mjs.map → meeting-transcription-state-MNA2XROE.mjs.map} +0 -0
- /package/dist/lib/browser/{transcriber-SMRLU2KY.mjs.map → transcriber-QRSS6X6S.mjs.map} +0 -0
- /package/dist/lib/node/{meeting-transcription-state-OYWCY5XE.cjs.map → meeting-transcription-state-5XLTMWPA.cjs.map} +0 -0
- /package/dist/lib/node/{transcriber-E6AAB4SU.cjs.map → transcriber-UZJMG44C.cjs.map} +0 -0
- /package/dist/lib/node-esm/{TranscriptContainer-3HJ752CP.mjs.map → TranscriptContainer-K4E4P5T6.mjs.map} +0 -0
- /package/dist/lib/node-esm/{meeting-transcription-state-XMZJ45P4.mjs.map → meeting-transcription-state-3WOZ52BQ.mjs.map} +0 -0
- /package/dist/lib/node-esm/{transcriber-REAGRCPY.mjs.map → transcriber-NVVFH7IF.mjs.map} +0 -0
|
@@ -2,24 +2,31 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import type { Schema } from 'effect';
|
|
6
|
+
|
|
5
7
|
import { Capabilities, contributes, createIntent, type PluginsContext } from '@dxos/app-framework';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
8
|
+
import { AIServiceEdgeClient, type AIServiceClient } from '@dxos/assistant';
|
|
9
|
+
import { AI_SERVICE_ENDPOINT } from '@dxos/assistant/testing';
|
|
10
|
+
import { Filter, fullyQualifiedId, getSpace, makeRef, type Space } from '@dxos/client/echo';
|
|
11
|
+
import { getSchemaTypename, isInstanceOf, type BaseEchoObject } from '@dxos/echo-schema';
|
|
9
12
|
import { invariant } from '@dxos/invariant';
|
|
13
|
+
import { DXN } from '@dxos/keys';
|
|
14
|
+
import { log } from '@dxos/log';
|
|
10
15
|
import { ClientCapabilities } from '@dxos/plugin-client';
|
|
11
16
|
import { PLANK_COMPANION_TYPE, ATTENDABLE_PATH_SEPARATOR } from '@dxos/plugin-deck/types';
|
|
12
17
|
import { createExtension, type Node } from '@dxos/plugin-graph';
|
|
18
|
+
import { DocumentType } from '@dxos/plugin-markdown/types';
|
|
13
19
|
import { MeetingCapabilities, type CallState, type MediaState } from '@dxos/plugin-meeting';
|
|
14
|
-
import { MeetingType } from '@dxos/plugin-meeting/types';
|
|
20
|
+
import { MeetingType, type MeetingCallProperties } from '@dxos/plugin-meeting/types';
|
|
15
21
|
import { type buf } from '@dxos/protocols/buf';
|
|
16
22
|
import { type TranscriptionPayloadSchema } from '@dxos/protocols/buf/dxos/edge/calls_pb';
|
|
17
|
-
import {
|
|
23
|
+
import { DataType } from '@dxos/schema';
|
|
18
24
|
|
|
19
25
|
import { TranscriptionCapabilities } from './capabilities';
|
|
26
|
+
import { processTranscriptMessage } from '../entity-extraction';
|
|
20
27
|
import { TRANSCRIPTION_PLUGIN } from '../meta';
|
|
21
28
|
import { TranscriptionManager } from '../transcriber';
|
|
22
|
-
import { TranscriptionAction, TranscriptType } from '../types';
|
|
29
|
+
import { TranscriptionAction, type TranscriptionSettingsProps, TranscriptType } from '../types';
|
|
23
30
|
|
|
24
31
|
// TODO(wittjosiah): Factor out.
|
|
25
32
|
// TODO(wittjosiah): Can we stop using protobuf for this?
|
|
@@ -49,8 +56,7 @@ export default (context: PluginsContext) =>
|
|
|
49
56
|
contributes(Capabilities.AppGraphBuilder, [
|
|
50
57
|
createExtension({
|
|
51
58
|
id: `${TRANSCRIPTION_PLUGIN}/meeting-transcript`,
|
|
52
|
-
filter: (node): node is Node<MeetingType> =>
|
|
53
|
-
isInstanceOf(MeetingType, node.data) && node.type !== PLANK_COMPANION_TYPE,
|
|
59
|
+
filter: (node): node is Node<MeetingType> => isInstanceOf(MeetingType, node.data),
|
|
54
60
|
actions: ({ node }) => {
|
|
55
61
|
const meeting = node.data;
|
|
56
62
|
const state = context.requestCapability(TranscriptionCapabilities.MeetingTranscriptionState);
|
|
@@ -61,14 +67,13 @@ export default (context: PluginsContext) =>
|
|
|
61
67
|
// NOTE: We are not saving the state of the transcription manager here.
|
|
62
68
|
// We expect the state to be updated through `onCallStateUpdated` once it is propagated through Swarm.
|
|
63
69
|
// This is done to avoid race conditions and to not handle optimistic updates.
|
|
64
|
-
|
|
65
70
|
const transcript = await getMeetingTranscript(context, meeting);
|
|
66
71
|
invariant(transcript, 'Failed to create transcript');
|
|
67
72
|
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
enabled: !state.enabled,
|
|
73
|
+
const callManager = context.requestCapability(MeetingCapabilities.CallManager);
|
|
74
|
+
callManager.setActivity(getSchemaTypename(TranscriptType)!, {
|
|
71
75
|
queueDxn: transcript.queue.dxn.toString(),
|
|
76
|
+
enabled: !state.enabled,
|
|
72
77
|
});
|
|
73
78
|
},
|
|
74
79
|
properties: {
|
|
@@ -86,6 +91,14 @@ export default (context: PluginsContext) =>
|
|
|
86
91
|
const meeting = node.data;
|
|
87
92
|
const client = context.requestCapability(ClientCapabilities.Client);
|
|
88
93
|
const state = context.requestCapability(TranscriptionCapabilities.MeetingTranscriptionState);
|
|
94
|
+
const settings = context
|
|
95
|
+
.requestCapability(Capabilities.SettingsStore)
|
|
96
|
+
.getStore<TranscriptionSettingsProps>(TRANSCRIPTION_PLUGIN)!.value;
|
|
97
|
+
|
|
98
|
+
// TODO(dmaretskyi): Request via capability.
|
|
99
|
+
const aiClient: AIServiceClient | undefined = new AIServiceEdgeClient({
|
|
100
|
+
endpoint: AI_SERVICE_ENDPOINT.REMOTE,
|
|
101
|
+
});
|
|
89
102
|
|
|
90
103
|
return [
|
|
91
104
|
{
|
|
@@ -95,29 +108,45 @@ export default (context: PluginsContext) =>
|
|
|
95
108
|
properties: {
|
|
96
109
|
label: ['transcript companion label', { ns: TRANSCRIPTION_PLUGIN }],
|
|
97
110
|
icon: 'ph--subtitles--regular',
|
|
111
|
+
position: 'hoist',
|
|
98
112
|
disposition: 'hidden',
|
|
99
113
|
schema: TranscriptType,
|
|
100
114
|
getIntent: ({ space }: { space: Space }) =>
|
|
101
115
|
createIntent(TranscriptionAction.Create, { spaceId: space.id }),
|
|
102
|
-
|
|
103
|
-
|
|
116
|
+
|
|
117
|
+
// TODO(burdon): Is this the right place to inisitalize the state? Change to a capability?
|
|
118
|
+
onJoin: async ({ meeting }: { meeting: MeetingType; roomId: string }) => {
|
|
104
119
|
const identity = client.halo.identity.get();
|
|
105
120
|
invariant(identity);
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
121
|
+
const space = getSpace(meeting);
|
|
122
|
+
invariant(space);
|
|
123
|
+
|
|
124
|
+
let messageEnricher;
|
|
125
|
+
if (aiClient && settings.entityExtraction) {
|
|
126
|
+
messageEnricher = createEntityExtractionEnricher({
|
|
127
|
+
aiClient,
|
|
128
|
+
// TODO(dmaretskyi): Have those be discovered from the schema graph or contributed by capabilities?
|
|
129
|
+
// This forced me to add a dependency on markdown plugin.
|
|
130
|
+
// This will be replaced with a vector search index anyway, so its not a big deal.
|
|
131
|
+
contextTypes: [DocumentType, DataType.Person, DataType.Organization],
|
|
132
|
+
space,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// TODO(burdon): The TranscriptionManager singleton is part of the state and should just be updated here.
|
|
137
|
+
state.transcriptionManager = await new TranscriptionManager({
|
|
138
|
+
edgeClient: client.edge,
|
|
139
|
+
messageEnricher,
|
|
140
|
+
})
|
|
141
|
+
.setIdentityDid(identity.did)
|
|
142
|
+
.open();
|
|
114
143
|
},
|
|
115
|
-
onLeave: () => {
|
|
116
|
-
|
|
144
|
+
onLeave: async () => {
|
|
145
|
+
await state.transcriptionManager?.close();
|
|
117
146
|
state.transcriptionManager = undefined;
|
|
118
147
|
state.enabled = false;
|
|
119
148
|
},
|
|
120
|
-
onCallStateUpdated: (callState: CallState) => {
|
|
149
|
+
onCallStateUpdated: async (callState: CallState) => {
|
|
121
150
|
const typename = getSchemaTypename(TranscriptType);
|
|
122
151
|
const transcription = typename ? callState.activities?.[typename] : undefined;
|
|
123
152
|
if (!transcription?.payload) {
|
|
@@ -126,16 +155,57 @@ export default (context: PluginsContext) =>
|
|
|
126
155
|
|
|
127
156
|
const payload: TranscriptionPayload = transcription.payload;
|
|
128
157
|
state.enabled = !!payload.enabled;
|
|
129
|
-
|
|
130
|
-
|
|
158
|
+
if (payload.queueDxn) {
|
|
159
|
+
// NOTE: Must set queue before enabling transcription.
|
|
160
|
+
state.transcriptionManager?.setQueue(DXN.parse(payload.queueDxn));
|
|
161
|
+
}
|
|
162
|
+
await state.transcriptionManager?.setEnabled(payload.enabled);
|
|
131
163
|
},
|
|
132
|
-
onMediaStateUpdated: ([mediaState, isSpeaking]: [MediaState, boolean]) => {
|
|
164
|
+
onMediaStateUpdated: async ([mediaState, isSpeaking]: [MediaState, boolean]) => {
|
|
133
165
|
void state.transcriptionManager?.setAudioTrack(mediaState.audioTrack);
|
|
134
166
|
void state.transcriptionManager?.setRecording(isSpeaking);
|
|
135
167
|
},
|
|
136
|
-
},
|
|
168
|
+
} satisfies MeetingCallProperties,
|
|
137
169
|
},
|
|
138
170
|
];
|
|
139
171
|
},
|
|
140
172
|
}),
|
|
141
173
|
]);
|
|
174
|
+
|
|
175
|
+
type EntityExtractionEnricherFactoryOptions = {
|
|
176
|
+
aiClient: AIServiceClient;
|
|
177
|
+
contextTypes: Schema.Schema.AnyNoContext[];
|
|
178
|
+
space: Space;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const createEntityExtractionEnricher = ({ aiClient, contextTypes, space }: EntityExtractionEnricherFactoryOptions) => {
|
|
182
|
+
return async (message: DataType.Message) => {
|
|
183
|
+
const { objects } = await space.db.query(Filter.or(...contextTypes.map((s) => Filter.schema(s)))).run();
|
|
184
|
+
log.info('context', { objects });
|
|
185
|
+
|
|
186
|
+
const { message: enhancedMessage, timeElapsed } = await processTranscriptMessage({
|
|
187
|
+
aiService: aiClient,
|
|
188
|
+
message,
|
|
189
|
+
context: { objects: await Promise.all(objects.map(processContextObject)) },
|
|
190
|
+
options: { timeout: ENTITY_EXTRACTOR_TIMEOUT, fallbackToRaw: true },
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
log.info('entity extraction time', { timeElapsed });
|
|
194
|
+
return enhancedMessage;
|
|
195
|
+
};
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const processContextObject = async (object: BaseEchoObject): Promise<any> => {
|
|
199
|
+
// TODO(dmaretskyi): Documents need special processing is the content is behind a ref.
|
|
200
|
+
// TODO(dmaretskyi): Think about a way to handle this serialization with a decorator.
|
|
201
|
+
// if (isInstanceOf(DocumentType, object)) {
|
|
202
|
+
// return {
|
|
203
|
+
// ...object,
|
|
204
|
+
// content: await object.content.load(),
|
|
205
|
+
// };
|
|
206
|
+
// }
|
|
207
|
+
|
|
208
|
+
return object;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const ENTITY_EXTRACTOR_TIMEOUT = 25_000;
|
|
@@ -18,8 +18,8 @@ export namespace TranscriptionCapabilities {
|
|
|
18
18
|
export const Transcriber = defineCapability<GetTranscriber>(`${TRANSCRIPTION_PLUGIN}/capability/transcriber`);
|
|
19
19
|
|
|
20
20
|
export type MeetingTranscriptionState = Live<{
|
|
21
|
-
enabled: boolean;
|
|
22
21
|
transcriptionManager?: TranscriptionManager;
|
|
22
|
+
enabled: boolean; // TODO(burdon): Move state into TranscriptionManager.
|
|
23
23
|
}>;
|
|
24
24
|
export const MeetingTranscriptionState = defineCapability<MeetingTranscriptionState>(
|
|
25
25
|
`${TRANSCRIPTION_PLUGIN}/capability/meeting-transcription-state`,
|
|
@@ -9,5 +9,6 @@ export const IntentResolver = lazy(() => import('./intent-resolver'));
|
|
|
9
9
|
export const MeetingTranscriptionState = lazy(() => import('./meeting-transcription-state'));
|
|
10
10
|
export const ReactSurface = lazy(() => import('./react-surface'));
|
|
11
11
|
export const Transcriber = lazy(() => import('./transcriber'));
|
|
12
|
+
export const Settings = lazy(() => import('./settings'));
|
|
12
13
|
|
|
13
14
|
export * from './capabilities';
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { Capabilities, contributes, createResolver, type PluginsContext } from '@dxos/app-framework';
|
|
6
6
|
import { live, refFromDXN } from '@dxos/live-object';
|
|
7
|
-
import { type SpaceId,
|
|
7
|
+
import { type SpaceId, createQueueDxn } from '@dxos/react-client/echo';
|
|
8
8
|
|
|
9
9
|
import { TranscriptionAction, TranscriptType } from '../types';
|
|
10
10
|
import { getTimeStr } from '../util';
|
|
@@ -15,7 +15,7 @@ export default (context: PluginsContext) =>
|
|
|
15
15
|
intent: TranscriptionAction.Create,
|
|
16
16
|
resolve: ({ name, spaceId }) => {
|
|
17
17
|
const transcript = live(TranscriptType, {
|
|
18
|
-
queue: refFromDXN(
|
|
18
|
+
queue: refFromDXN(createQueueDxn(spaceId as SpaceId)),
|
|
19
19
|
name: name ?? `Transcript ${getTimeStr(Date.now())}`,
|
|
20
20
|
});
|
|
21
21
|
|
|
@@ -5,15 +5,23 @@
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
7
|
import { Capabilities, contributes, createSurface } from '@dxos/app-framework';
|
|
8
|
+
import { SettingsStore } from '@dxos/local-storage';
|
|
8
9
|
|
|
9
|
-
import { TranscriptionContainer } from '../components';
|
|
10
|
-
import {
|
|
11
|
-
import { isTranscript, type TranscriptType } from '../types';
|
|
10
|
+
import { TranscriptionContainer, TranscriptionSettings } from '../components';
|
|
11
|
+
import { meta } from '../meta';
|
|
12
|
+
import { isTranscript, type TranscriptionSettingsProps, type TranscriptType } from '../types';
|
|
12
13
|
|
|
13
14
|
export default () =>
|
|
14
15
|
contributes(Capabilities.ReactSurface, [
|
|
15
16
|
createSurface({
|
|
16
|
-
id: `${
|
|
17
|
+
id: `${meta.id}/plugin-settings`,
|
|
18
|
+
role: 'article',
|
|
19
|
+
filter: (data): data is { subject: SettingsStore<TranscriptionSettingsProps> } =>
|
|
20
|
+
data.subject instanceof SettingsStore && data.subject.prefix === meta.id,
|
|
21
|
+
component: ({ data: { subject } }) => <TranscriptionSettings settings={subject.value} />,
|
|
22
|
+
}),
|
|
23
|
+
createSurface({
|
|
24
|
+
id: `${meta.id}/article/transcript`,
|
|
17
25
|
role: ['article', 'section'],
|
|
18
26
|
filter: (data): data is { subject: TranscriptType } => isTranscript(data.subject),
|
|
19
27
|
component: ({ data, role }) => <TranscriptionContainer transcript={data.subject} role={role} />,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Capabilities, contributes } from '@dxos/app-framework';
|
|
6
|
+
import { live } from '@dxos/live-object';
|
|
7
|
+
|
|
8
|
+
import { TRANSCRIPTION_PLUGIN } from '../meta';
|
|
9
|
+
import { TranscriptionSettingsSchema } from '../types';
|
|
10
|
+
|
|
11
|
+
export default () => {
|
|
12
|
+
// TODO(wittjosiah): `live` currently doesn't handle schema default values.
|
|
13
|
+
const settings = live(TranscriptionSettingsSchema, { entityExtraction: true });
|
|
14
|
+
|
|
15
|
+
return contributes(Capabilities.Settings, {
|
|
16
|
+
schema: TranscriptionSettingsSchema,
|
|
17
|
+
prefix: TRANSCRIPTION_PLUGIN,
|
|
18
|
+
value: settings,
|
|
19
|
+
});
|
|
20
|
+
};
|
|
@@ -5,33 +5,35 @@
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
7
|
import { type Meta, type StoryObj } from '@storybook/react';
|
|
8
|
-
import React, { useEffect, useMemo, useState
|
|
8
|
+
import React, { type FC, useEffect, useMemo, useState } from 'react';
|
|
9
9
|
|
|
10
10
|
import { IntentPlugin, SettingsPlugin } from '@dxos/app-framework';
|
|
11
11
|
import { withPluginManager } from '@dxos/app-framework/testing';
|
|
12
|
+
import { ObjectId } from '@dxos/echo-schema';
|
|
12
13
|
import { ClientPlugin } from '@dxos/plugin-client';
|
|
13
14
|
import { PreviewPlugin } from '@dxos/plugin-preview';
|
|
14
15
|
import { SpacePlugin } from '@dxos/plugin-space';
|
|
15
16
|
import { StorybookLayoutPlugin } from '@dxos/plugin-storybook-layout';
|
|
16
17
|
import { ThemePlugin } from '@dxos/plugin-theme';
|
|
17
18
|
import { faker } from '@dxos/random';
|
|
18
|
-
import { useSpace } from '@dxos/react-client/echo';
|
|
19
|
+
import { useMembers, useSpace } from '@dxos/react-client/echo';
|
|
19
20
|
import { IconButton, Toolbar } from '@dxos/react-ui';
|
|
20
21
|
import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
21
22
|
import { defaultTx } from '@dxos/react-ui-theme';
|
|
23
|
+
import { DataType } from '@dxos/schema';
|
|
24
|
+
import { Testing } from '@dxos/schema/testing';
|
|
22
25
|
import { withLayout } from '@dxos/storybook-utils';
|
|
23
26
|
|
|
24
27
|
import { renderMarkdown, Transcript, type TranscriptProps } from './Transcript';
|
|
28
|
+
import { useQueueModelAdapter } from '../../hooks';
|
|
29
|
+
import { SerializationModel } from '../../model';
|
|
25
30
|
import {
|
|
26
|
-
|
|
31
|
+
MessageBuilder,
|
|
27
32
|
TestItem,
|
|
28
33
|
useTestTranscriptionQueue,
|
|
29
34
|
useTestTranscriptionQueueWithEntityExtraction,
|
|
30
|
-
} from '
|
|
31
|
-
import { useQueueModelAdapter } from '../../hooks';
|
|
32
|
-
import { BlockModel } from '../../model';
|
|
35
|
+
} from '../../testing';
|
|
33
36
|
import translations from '../../translations';
|
|
34
|
-
import { type TranscriptBlock } from '../../types';
|
|
35
37
|
|
|
36
38
|
faker.seed(1);
|
|
37
39
|
|
|
@@ -67,45 +69,49 @@ const TranscriptContainer: FC<
|
|
|
67
69
|
);
|
|
68
70
|
};
|
|
69
71
|
|
|
70
|
-
type StoryProps = {
|
|
72
|
+
type StoryProps = { messages?: DataType.Message[] } & Pick<TranscriptProps, 'ignoreAttention' | 'attendableId'>;
|
|
71
73
|
|
|
72
74
|
/**
|
|
73
|
-
* Basic story mutates array of
|
|
75
|
+
* Basic story mutates array of messages.
|
|
74
76
|
*/
|
|
75
|
-
const BasicStory = ({
|
|
77
|
+
const BasicStory = ({ messages: initialMessages = [], ...props }: StoryProps) => {
|
|
76
78
|
const [reset, setReset] = useState({});
|
|
77
|
-
const builder = useMemo(() => new
|
|
78
|
-
const model = useMemo(
|
|
79
|
+
const builder = useMemo(() => new MessageBuilder(), []);
|
|
80
|
+
const model = useMemo(
|
|
81
|
+
() => new SerializationModel<DataType.Message>(renderMarkdown([]), initialMessages),
|
|
82
|
+
[initialMessages, reset],
|
|
83
|
+
);
|
|
79
84
|
const [running, setRunning] = useState(true);
|
|
80
|
-
const [
|
|
85
|
+
const [currentMessage, setCurrentMessage] = useState<DataType.Message | null>(null);
|
|
81
86
|
useEffect(() => {
|
|
82
87
|
if (!running) {
|
|
83
88
|
return;
|
|
84
89
|
}
|
|
85
90
|
|
|
86
|
-
if (!
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
91
|
+
if (!currentMessage) {
|
|
92
|
+
void builder.createMessage().then((message) => {
|
|
93
|
+
model.appendChunk(message);
|
|
94
|
+
setCurrentMessage(message);
|
|
95
|
+
});
|
|
90
96
|
return;
|
|
91
97
|
}
|
|
92
98
|
|
|
93
99
|
const i = setInterval(() => {
|
|
94
|
-
if (
|
|
95
|
-
|
|
100
|
+
if (currentMessage?.blocks.length && currentMessage.blocks.length >= 3) {
|
|
101
|
+
setCurrentMessage(null);
|
|
96
102
|
clearInterval(i);
|
|
97
103
|
return;
|
|
98
104
|
}
|
|
99
105
|
|
|
100
|
-
|
|
101
|
-
model.
|
|
106
|
+
currentMessage.blocks.push(builder.createBlock());
|
|
107
|
+
model.updateChunk(currentMessage);
|
|
102
108
|
}, 3_000);
|
|
103
109
|
|
|
104
110
|
return () => clearInterval(i);
|
|
105
|
-
}, [model,
|
|
111
|
+
}, [model, currentMessage, running]);
|
|
106
112
|
|
|
107
113
|
const handleReset = () => {
|
|
108
|
-
|
|
114
|
+
setCurrentMessage(null);
|
|
109
115
|
setRunning(false);
|
|
110
116
|
setReset({});
|
|
111
117
|
};
|
|
@@ -124,24 +130,54 @@ const BasicStory = ({ blocks: initialBlocks = [], ...props }: StoryProps) => {
|
|
|
124
130
|
/**
|
|
125
131
|
* Queue story mutates queue with model adapter.
|
|
126
132
|
*/
|
|
127
|
-
const QueueStory = ({
|
|
133
|
+
const QueueStory = ({
|
|
134
|
+
messages: initialMessages = [],
|
|
135
|
+
queueId,
|
|
136
|
+
onReset,
|
|
137
|
+
...props
|
|
138
|
+
}: StoryProps & { queueId: ObjectId; onReset: () => void }) => {
|
|
128
139
|
const [running, setRunning] = useState(true);
|
|
129
140
|
const space = useSpace();
|
|
130
|
-
const
|
|
131
|
-
const
|
|
141
|
+
const members = useMembers(space?.key).map((member) => member.identity);
|
|
142
|
+
const queue = useTestTranscriptionQueue(space, queueId, running, 2_000);
|
|
143
|
+
const model = useQueueModelAdapter(renderMarkdown(members), queue, initialMessages);
|
|
132
144
|
|
|
133
|
-
return
|
|
145
|
+
return (
|
|
146
|
+
<TranscriptContainer
|
|
147
|
+
space={space}
|
|
148
|
+
model={model}
|
|
149
|
+
running={running}
|
|
150
|
+
onRunningChange={setRunning}
|
|
151
|
+
onReset={onReset}
|
|
152
|
+
{...props}
|
|
153
|
+
/>
|
|
154
|
+
);
|
|
134
155
|
};
|
|
135
156
|
|
|
157
|
+
// TODO(burdon): Reconcile with QueueStory.
|
|
136
158
|
const EntityExtractionQueueStory = () => {
|
|
137
159
|
const [running, setRunning] = useState(true);
|
|
138
160
|
const space = useSpace();
|
|
139
|
-
const
|
|
140
|
-
const
|
|
161
|
+
const members = useMembers(space?.key).map((member) => member.identity);
|
|
162
|
+
const queue = useTestTranscriptionQueueWithEntityExtraction(space, undefined, running, 2_000);
|
|
163
|
+
const model = useQueueModelAdapter(renderMarkdown(members), queue, []);
|
|
141
164
|
|
|
142
165
|
return <TranscriptContainer space={space} model={model} running={running} onRunningChange={setRunning} />;
|
|
143
166
|
};
|
|
144
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Wrapper remounts on refresh to reload queue.
|
|
170
|
+
*/
|
|
171
|
+
const QueueStoryWrapper = () => {
|
|
172
|
+
const [queueId] = useState(ObjectId.random());
|
|
173
|
+
const [key, setKey] = useState(ObjectId.random().toString());
|
|
174
|
+
const handleReset = () => {
|
|
175
|
+
setKey(ObjectId.random().toString());
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
return <QueueStory key={key} queueId={queueId} onReset={handleReset} />;
|
|
179
|
+
};
|
|
180
|
+
|
|
145
181
|
const meta: Meta<typeof QueueStory> = {
|
|
146
182
|
title: 'plugins/plugin-transcription/Transcript',
|
|
147
183
|
decorators: [
|
|
@@ -150,7 +186,7 @@ const meta: Meta<typeof QueueStory> = {
|
|
|
150
186
|
ThemePlugin({ tx: defaultTx }),
|
|
151
187
|
StorybookLayoutPlugin(),
|
|
152
188
|
ClientPlugin({
|
|
153
|
-
types: [TestItem],
|
|
189
|
+
types: [TestItem, Testing.DocumentType, DataType.Person, DataType.Organization],
|
|
154
190
|
onClientInitialized: async (_, client) => {
|
|
155
191
|
await client.halo.createIdentity();
|
|
156
192
|
},
|
|
@@ -161,7 +197,7 @@ const meta: Meta<typeof QueueStory> = {
|
|
|
161
197
|
IntentPlugin(),
|
|
162
198
|
],
|
|
163
199
|
}),
|
|
164
|
-
withLayout({
|
|
200
|
+
withLayout({ fullscreen: true }),
|
|
165
201
|
],
|
|
166
202
|
parameters: {
|
|
167
203
|
translations,
|
|
@@ -170,14 +206,14 @@ const meta: Meta<typeof QueueStory> = {
|
|
|
170
206
|
|
|
171
207
|
export default meta;
|
|
172
208
|
|
|
173
|
-
type Story = StoryObj<
|
|
209
|
+
type Story = StoryObj<typeof QueueStory>;
|
|
174
210
|
|
|
175
211
|
export const Default: Story = {
|
|
176
212
|
render: BasicStory,
|
|
177
213
|
args: {
|
|
178
214
|
ignoreAttention: true,
|
|
179
215
|
attendableId: 'story',
|
|
180
|
-
|
|
216
|
+
messages: await Promise.all(Array.from({ length: 10 }, () => MessageBuilder.singleton.createMessage())),
|
|
181
217
|
},
|
|
182
218
|
};
|
|
183
219
|
|
|
@@ -190,7 +226,7 @@ export const Empty: Story = {
|
|
|
190
226
|
};
|
|
191
227
|
|
|
192
228
|
export const WithQueue: Story = {
|
|
193
|
-
render:
|
|
229
|
+
render: QueueStoryWrapper,
|
|
194
230
|
args: {
|
|
195
231
|
ignoreAttention: true,
|
|
196
232
|
attendableId: 'story',
|
|
@@ -200,7 +236,7 @@ export const WithQueue: Story = {
|
|
|
200
236
|
export const WithEntityExtractionQueue: Story = {
|
|
201
237
|
render: EntityExtractionQueueStory,
|
|
202
238
|
args: {
|
|
203
|
-
|
|
204
|
-
|
|
239
|
+
ignoreAttention: true,
|
|
240
|
+
attendableId: 'story',
|
|
205
241
|
},
|
|
206
242
|
};
|
|
@@ -4,10 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
|
-
import { DXN } from '@dxos/keys';
|
|
8
|
-
import { log } from '@dxos/log';
|
|
9
|
-
import { resolveRef, useClient } from '@dxos/react-client';
|
|
10
7
|
import { type Space } from '@dxos/react-client/echo';
|
|
8
|
+
import { type Identity } from '@dxos/react-client/halo';
|
|
11
9
|
import { IconButton, type ThemedClassName, useThemeContext } from '@dxos/react-ui';
|
|
12
10
|
import {
|
|
13
11
|
createBasicExtensions,
|
|
@@ -20,26 +18,42 @@ import {
|
|
|
20
18
|
useTextEditor,
|
|
21
19
|
} from '@dxos/react-ui-editor';
|
|
22
20
|
import { mx } from '@dxos/react-ui-theme';
|
|
21
|
+
import { type DataType } from '@dxos/schema';
|
|
23
22
|
import { isNotFalsy } from '@dxos/util';
|
|
24
23
|
|
|
25
24
|
import { transcript } from './transcript-extension';
|
|
26
|
-
import { type
|
|
27
|
-
import { type TranscriptType
|
|
25
|
+
import { type SerializationModel } from '../../model';
|
|
26
|
+
import { type TranscriptType } from '../../types';
|
|
28
27
|
|
|
29
|
-
export const renderMarkdown =
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
export const renderMarkdown =
|
|
29
|
+
(identities: Identity[]) =>
|
|
30
|
+
(message: DataType.Message, index: number, debug = false): string[] => {
|
|
31
|
+
if (message.sender.role === 'assistant') {
|
|
32
|
+
// Start/stop block.
|
|
33
|
+
return [message.blocks.find((block) => block.type === 'transcription')?.text ?? '', ''];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// TODO(burdon): Use link/reference markup for users (with popover).
|
|
37
|
+
// TODO(burdon): Color and avatar.
|
|
38
|
+
const identity = identities.find((identity) => identity.did === message.sender.identityDid);
|
|
39
|
+
const name =
|
|
40
|
+
identity?.profile?.displayName ??
|
|
41
|
+
message.sender.contact?.target?.fullName ??
|
|
42
|
+
message.sender.name ??
|
|
43
|
+
message.sender.email ??
|
|
44
|
+
message.sender.identityDid;
|
|
45
|
+
const blocks = message.blocks.filter((block) => block.type === 'transcription');
|
|
46
|
+
return [
|
|
47
|
+
`###### ${name}` + (debug ? ` [${index}]:${message.id}` : ''),
|
|
48
|
+
blocks.map((block) => block.text.trim()).join(' '),
|
|
49
|
+
'',
|
|
50
|
+
];
|
|
51
|
+
};
|
|
38
52
|
|
|
39
53
|
export type TranscriptProps = ThemedClassName<{
|
|
40
54
|
transcript?: TranscriptType;
|
|
41
55
|
space?: Space;
|
|
42
|
-
model:
|
|
56
|
+
model: SerializationModel<DataType.Message>;
|
|
43
57
|
// TODO(wittjosiah): Move to container.
|
|
44
58
|
attendableId?: string;
|
|
45
59
|
ignoreAttention?: boolean;
|
|
@@ -56,7 +70,6 @@ export const Transcript = ({
|
|
|
56
70
|
attendableId,
|
|
57
71
|
ignoreAttention,
|
|
58
72
|
}: TranscriptProps) => {
|
|
59
|
-
const client = useClient();
|
|
60
73
|
const { themeMode } = useThemeContext();
|
|
61
74
|
const { parentRef } = useTextEditor(() => {
|
|
62
75
|
return {
|
|
@@ -65,19 +78,7 @@ export const Transcript = ({
|
|
|
65
78
|
createMarkdownExtensions({ themeMode }),
|
|
66
79
|
createThemeExtensions({ themeMode }),
|
|
67
80
|
decorateMarkdown(),
|
|
68
|
-
space &&
|
|
69
|
-
preview({
|
|
70
|
-
onLookup: async ({ label, ref }) => {
|
|
71
|
-
log.info('onLookup', { label, ref });
|
|
72
|
-
const dxn = DXN.parse(ref);
|
|
73
|
-
if (!dxn) {
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const object = await resolveRef(client, dxn, space);
|
|
78
|
-
return { label, object };
|
|
79
|
-
},
|
|
80
|
-
}),
|
|
81
|
+
space && preview(),
|
|
81
82
|
transcript({
|
|
82
83
|
model,
|
|
83
84
|
started: object?.started ? new Date(object.started) : undefined,
|
|
@@ -89,5 +90,11 @@ export const Transcript = ({
|
|
|
89
90
|
};
|
|
90
91
|
}, [space, model]);
|
|
91
92
|
|
|
92
|
-
return
|
|
93
|
+
return (
|
|
94
|
+
<div
|
|
95
|
+
ref={parentRef}
|
|
96
|
+
className={mx('flex grow overflow-hidden', editorWidth, classNames)}
|
|
97
|
+
data-popover-collision-boundary={true}
|
|
98
|
+
/>
|
|
99
|
+
);
|
|
93
100
|
};
|
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { intervalToDuration } from 'date-fns/intervalToDuration';
|
|
6
|
+
import { Schema } from 'effect';
|
|
6
7
|
import { yieldOrContinue } from 'main-thread-scheduling';
|
|
7
8
|
import React, { type FC, useCallback, useEffect, useMemo, useRef, useState, type WheelEvent } from 'react';
|
|
8
9
|
import { type OnResizeCallback, useResizeDetector } from 'react-resize-detector';
|
|
9
10
|
|
|
11
|
+
import { Type } from '@dxos/echo';
|
|
10
12
|
import { IconButton, type ThemedClassName, useTranslation } from '@dxos/react-ui';
|
|
11
13
|
import { useAttention } from '@dxos/react-ui-attention';
|
|
12
14
|
import {
|
|
@@ -20,7 +22,34 @@ import {
|
|
|
20
22
|
import { mx } from '@dxos/react-ui-theme';
|
|
21
23
|
|
|
22
24
|
import { TRANSCRIPTION_PLUGIN } from '../../../meta';
|
|
23
|
-
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Transcription fragment.
|
|
28
|
+
*/
|
|
29
|
+
const TranscriptSegment = Schema.Struct({
|
|
30
|
+
// TODO(burdon): TS from service is not Unix TS (x1000).
|
|
31
|
+
started: Schema.String,
|
|
32
|
+
text: Schema.String,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
type TranscriptSegment = Schema.Schema.Type<typeof TranscriptSegment>;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Transcription block (from single speaker).
|
|
39
|
+
*/
|
|
40
|
+
const TranscriptBlock = Schema.Struct({
|
|
41
|
+
id: Schema.String,
|
|
42
|
+
authorName: Schema.optional(Schema.String), // TODO(burdon): Replace with identityDid.
|
|
43
|
+
authorHue: Schema.optional(Schema.String), // TOOD(burdon): Remove.
|
|
44
|
+
segments: Schema.Array(TranscriptSegment),
|
|
45
|
+
}).pipe(
|
|
46
|
+
Type.def({
|
|
47
|
+
typename: 'dxos.org/type/TranscriptBlock',
|
|
48
|
+
version: '0.1.0',
|
|
49
|
+
}),
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
type TranscriptBlock = Schema.Schema.Type<typeof TranscriptBlock>;
|
|
24
53
|
|
|
25
54
|
// TODO(burdon): Actions (e.g., mark, summarize, translate, label, delete).
|
|
26
55
|
|