@dxos/plugin-transcription 0.8.2-main.12df754 → 0.8.2-main.2f9c567

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.
Files changed (193) hide show
  1. package/dist/lib/browser/{TranscriptContainer-4D4BHE7G.mjs → TranscriptContainer-3LU3XBEB.mjs} +3 -3
  2. package/dist/lib/browser/app-graph-builder-YXELPN6F.mjs +349 -0
  3. package/dist/lib/browser/app-graph-builder-YXELPN6F.mjs.map +7 -0
  4. package/dist/lib/browser/{chunk-DR3NBV6Q.mjs → chunk-GWY2Q2NE.mjs} +105 -78
  5. package/dist/lib/browser/chunk-GWY2Q2NE.mjs.map +7 -0
  6. package/dist/lib/browser/{chunk-RHHJZQPM.mjs → chunk-HNIKXVCG.mjs} +135 -117
  7. package/dist/lib/browser/chunk-HNIKXVCG.mjs.map +7 -0
  8. package/dist/lib/browser/chunk-HRDAILQX.mjs +27 -0
  9. package/dist/lib/browser/chunk-HRDAILQX.mjs.map +7 -0
  10. package/dist/lib/browser/{chunk-5BE6RTNC.mjs → chunk-LTIQFX4W.mjs} +1 -1
  11. package/dist/lib/browser/{chunk-5BE6RTNC.mjs.map → chunk-LTIQFX4W.mjs.map} +1 -1
  12. package/dist/lib/browser/{chunk-5Q3R53U3.mjs → chunk-TGOKX32G.mjs} +21 -31
  13. package/dist/lib/browser/chunk-TGOKX32G.mjs.map +7 -0
  14. package/dist/lib/browser/index.mjs +29 -17
  15. package/dist/lib/browser/index.mjs.map +3 -3
  16. package/dist/lib/browser/{intent-resolver-ZQQEH27F.mjs → intent-resolver-BBB3DWV4.mjs} +4 -4
  17. package/dist/lib/browser/{intent-resolver-ZQQEH27F.mjs.map → intent-resolver-BBB3DWV4.mjs.map} +2 -2
  18. package/dist/lib/browser/{meeting-transcription-state-5D52VTHU.mjs → meeting-transcription-state-MNA2XROE.mjs} +2 -2
  19. package/dist/lib/browser/meta.json +1 -1
  20. package/dist/lib/browser/{react-surface-HETJ5XY6.mjs → react-surface-R277DACJ.mjs} +18 -7
  21. package/dist/lib/browser/react-surface-R277DACJ.mjs.map +7 -0
  22. package/dist/lib/browser/settings-I226DZ27.mjs +24 -0
  23. package/dist/lib/browser/settings-I226DZ27.mjs.map +7 -0
  24. package/dist/lib/browser/{transcriber-SMRLU2KY.mjs → transcriber-QRSS6X6S.mjs} +4 -4
  25. package/dist/lib/browser/types/index.mjs +3 -3
  26. package/dist/lib/node/{TranscriptContainer-ZWP6UKY6.cjs → TranscriptContainer-DICOMZLM.cjs} +8 -8
  27. package/dist/lib/node/{TranscriptContainer-ZWP6UKY6.cjs.map → TranscriptContainer-DICOMZLM.cjs.map} +2 -2
  28. package/dist/lib/node/app-graph-builder-I6LYVIYY.cjs +350 -0
  29. package/dist/lib/node/app-graph-builder-I6LYVIYY.cjs.map +7 -0
  30. package/dist/lib/node/{chunk-3QEJ5EBO.cjs → chunk-34WFY7M7.cjs} +22 -32
  31. package/dist/lib/node/chunk-34WFY7M7.cjs.map +7 -0
  32. package/dist/lib/node/{chunk-KO6MKCED.cjs → chunk-4JOJCZPJ.cjs} +138 -119
  33. package/dist/lib/node/chunk-4JOJCZPJ.cjs.map +7 -0
  34. package/dist/lib/node/{chunk-HYGDHS6X.cjs → chunk-JUGOXWES.cjs} +4 -4
  35. package/dist/lib/node/{chunk-HYGDHS6X.cjs.map → chunk-JUGOXWES.cjs.map} +1 -1
  36. package/dist/lib/node/{chunk-WXG4Z2EZ.cjs → chunk-K553L7CZ.cjs} +23 -8
  37. package/dist/lib/node/chunk-K553L7CZ.cjs.map +7 -0
  38. package/dist/lib/node/{chunk-LA73IHCB.cjs → chunk-RK4BEN5E.cjs} +108 -80
  39. package/dist/lib/node/chunk-RK4BEN5E.cjs.map +7 -0
  40. package/dist/lib/node/index.cjs +46 -34
  41. package/dist/lib/node/index.cjs.map +3 -3
  42. package/dist/lib/node/{intent-resolver-VY3XWYAA.cjs → intent-resolver-7XY3WITN.cjs} +8 -8
  43. package/dist/lib/node/{intent-resolver-VY3XWYAA.cjs.map → intent-resolver-7XY3WITN.cjs.map} +2 -2
  44. package/dist/lib/node/{meeting-transcription-state-OYWCY5XE.cjs → meeting-transcription-state-5XLTMWPA.cjs} +6 -6
  45. package/dist/lib/node/meta.json +1 -1
  46. package/dist/lib/node/{react-surface-M2JQIIDY.cjs → react-surface-HUSWAORU.cjs} +20 -11
  47. package/dist/lib/node/react-surface-HUSWAORU.cjs.map +7 -0
  48. package/dist/lib/node/settings-NDMU3EWQ.cjs +38 -0
  49. package/dist/lib/node/settings-NDMU3EWQ.cjs.map +7 -0
  50. package/dist/lib/node/{transcriber-E6AAB4SU.cjs → transcriber-UZJMG44C.cjs} +10 -10
  51. package/dist/lib/node/types/index.cjs +8 -8
  52. package/dist/lib/node/types/index.cjs.map +1 -1
  53. package/dist/lib/node-esm/{TranscriptContainer-3HJ752CP.mjs → TranscriptContainer-K4E4P5T6.mjs} +3 -3
  54. package/dist/lib/node-esm/app-graph-builder-6FY2UQOH.mjs +350 -0
  55. package/dist/lib/node-esm/app-graph-builder-6FY2UQOH.mjs.map +7 -0
  56. package/dist/lib/node-esm/chunk-3I7AC3D3.mjs +28 -0
  57. package/dist/lib/node-esm/chunk-3I7AC3D3.mjs.map +7 -0
  58. package/dist/lib/node-esm/{chunk-YXZOLOFT.mjs → chunk-5E2MR5XK.mjs} +1 -1
  59. package/dist/lib/node-esm/{chunk-YXZOLOFT.mjs.map → chunk-5E2MR5XK.mjs.map} +1 -1
  60. package/dist/lib/node-esm/{chunk-SDMGJZLN.mjs → chunk-6C34FO4C.mjs} +135 -117
  61. package/dist/lib/node-esm/chunk-6C34FO4C.mjs.map +7 -0
  62. package/dist/lib/node-esm/{chunk-K3R3YK3S.mjs → chunk-CKANLRS4.mjs} +105 -78
  63. package/dist/lib/node-esm/chunk-CKANLRS4.mjs.map +7 -0
  64. package/dist/lib/node-esm/{chunk-54HDN33F.mjs → chunk-KUQ3ZTRX.mjs} +21 -31
  65. package/dist/lib/node-esm/chunk-KUQ3ZTRX.mjs.map +7 -0
  66. package/dist/lib/node-esm/index.mjs +29 -17
  67. package/dist/lib/node-esm/index.mjs.map +3 -3
  68. package/dist/lib/node-esm/{intent-resolver-FCTNJPWY.mjs → intent-resolver-ACGAPLDZ.mjs} +4 -4
  69. package/dist/lib/node-esm/{intent-resolver-FCTNJPWY.mjs.map → intent-resolver-ACGAPLDZ.mjs.map} +2 -2
  70. package/dist/lib/node-esm/{meeting-transcription-state-XMZJ45P4.mjs → meeting-transcription-state-3WOZ52BQ.mjs} +2 -2
  71. package/dist/lib/node-esm/meta.json +1 -1
  72. package/dist/lib/node-esm/{react-surface-PXFSIM6V.mjs → react-surface-G2OY4MWL.mjs} +18 -7
  73. package/dist/lib/node-esm/react-surface-G2OY4MWL.mjs.map +7 -0
  74. package/dist/lib/node-esm/settings-O7X2UQQG.mjs +25 -0
  75. package/dist/lib/node-esm/settings-O7X2UQQG.mjs.map +7 -0
  76. package/dist/lib/node-esm/{transcriber-REAGRCPY.mjs → transcriber-NVVFH7IF.mjs} +4 -4
  77. package/dist/lib/node-esm/types/index.mjs +3 -3
  78. package/dist/types/src/TranscriptionPlugin.d.ts.map +1 -1
  79. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  80. package/dist/types/src/capabilities/capabilities.d.ts +1 -1
  81. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  82. package/dist/types/src/capabilities/index.d.ts +1 -0
  83. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  84. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  85. package/dist/types/src/capabilities/settings.d.ts +4 -0
  86. package/dist/types/src/capabilities/settings.d.ts.map +1 -0
  87. package/dist/types/src/components/Transcript/Transcript.d.ts +6 -4
  88. package/dist/types/src/components/Transcript/Transcript.d.ts.map +1 -1
  89. package/dist/types/src/components/Transcript/Transcript.stories.d.ts +8 -4
  90. package/dist/types/src/components/Transcript/Transcript.stories.d.ts.map +1 -1
  91. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts +27 -0
  92. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts.map +1 -0
  93. package/dist/types/src/components/Transcript/transcript-extension.d.ts +4 -4
  94. package/dist/types/src/components/Transcript/transcript-extension.d.ts.map +1 -1
  95. package/dist/types/src/components/TranscriptContainer.d.ts.map +1 -1
  96. package/dist/types/src/components/TranscriptionSettings/TranscriptionSettings.d.ts +6 -0
  97. package/dist/types/src/components/TranscriptionSettings/TranscriptionSettings.d.ts.map +1 -0
  98. package/dist/types/src/components/TranscriptionSettings/index.d.ts +2 -0
  99. package/dist/types/src/components/TranscriptionSettings/index.d.ts.map +1 -0
  100. package/dist/types/src/components/index.d.ts +1 -0
  101. package/dist/types/src/components/index.d.ts.map +1 -1
  102. package/dist/types/src/components/transcription.stories.d.ts +6 -3
  103. package/dist/types/src/components/transcription.stories.d.ts.map +1 -1
  104. package/dist/types/src/entity-extraction/entity-extraction.d.ts +33 -7
  105. package/dist/types/src/entity-extraction/entity-extraction.d.ts.map +1 -1
  106. package/dist/types/src/hooks/useQueueModelAdapter.d.ts +2 -2
  107. package/dist/types/src/hooks/useQueueModelAdapter.d.ts.map +1 -1
  108. package/dist/types/src/model/model.d.ts +23 -23
  109. package/dist/types/src/model/model.d.ts.map +1 -1
  110. package/dist/types/src/testing/index.d.ts +2 -0
  111. package/dist/types/src/testing/index.d.ts.map +1 -0
  112. package/dist/types/src/testing/testing.d.ts +40 -0
  113. package/dist/types/src/testing/testing.d.ts.map +1 -0
  114. package/dist/types/src/transcriber/transcriber.d.ts +2 -2
  115. package/dist/types/src/transcriber/transcriber.d.ts.map +1 -1
  116. package/dist/types/src/transcriber/transcription-manager.d.ts +17 -15
  117. package/dist/types/src/transcriber/transcription-manager.d.ts.map +1 -1
  118. package/dist/types/src/translations.d.ts +3 -0
  119. package/dist/types/src/translations.d.ts.map +1 -1
  120. package/dist/types/src/types/schema.d.ts +26 -40
  121. package/dist/types/src/types/schema.d.ts.map +1 -1
  122. package/dist/types/src/types/types.d.ts +11 -7
  123. package/dist/types/src/types/types.d.ts.map +1 -1
  124. package/package.json +51 -46
  125. package/src/TranscriptionPlugin.tsx +29 -12
  126. package/src/capabilities/app-graph-builder.ts +99 -29
  127. package/src/capabilities/capabilities.ts +1 -1
  128. package/src/capabilities/index.ts +1 -0
  129. package/src/capabilities/intent-resolver.ts +2 -2
  130. package/src/capabilities/react-surface.tsx +12 -4
  131. package/src/capabilities/settings.ts +20 -0
  132. package/src/components/Transcript/Transcript.stories.tsx +71 -35
  133. package/src/components/Transcript/Transcript.tsx +37 -30
  134. package/src/components/Transcript/deprecated/{Transcript.tsx → OldTranscript.tsx} +30 -1
  135. package/src/components/Transcript/transcript-extension.ts +20 -17
  136. package/src/components/TranscriptContainer.tsx +6 -4
  137. package/src/components/TranscriptionSettings/TranscriptionSettings.tsx +26 -0
  138. package/src/components/TranscriptionSettings/index.ts +5 -0
  139. package/src/components/index.ts +1 -0
  140. package/src/components/transcription.stories.tsx +83 -29
  141. package/src/entity-extraction/entity-extraction.test.ts +75 -10
  142. package/src/entity-extraction/entity-extraction.ts +110 -55
  143. package/src/entity-extraction/system-prompt.tpl +38 -9
  144. package/src/hooks/useQueueModelAdapter.ts +44 -11
  145. package/src/model/model.test.ts +90 -44
  146. package/src/model/model.ts +59 -59
  147. package/src/testing/index.ts +5 -0
  148. package/src/testing/testing.ts +221 -0
  149. package/src/transcriber/transcriber.browser.test.ts +3 -3
  150. package/src/transcriber/transcriber.ts +19 -10
  151. package/src/transcriber/transcription-manager.ts +66 -57
  152. package/src/translations.ts +2 -0
  153. package/src/types/schema.ts +10 -31
  154. package/src/types/types.ts +17 -7
  155. package/dist/lib/browser/app-graph-builder-I4SSIQMW.mjs +0 -168
  156. package/dist/lib/browser/app-graph-builder-I4SSIQMW.mjs.map +0 -7
  157. package/dist/lib/browser/chunk-5Q3R53U3.mjs.map +0 -7
  158. package/dist/lib/browser/chunk-DR3NBV6Q.mjs.map +0 -7
  159. package/dist/lib/browser/chunk-GI5LZ4SV.mjs +0 -8
  160. package/dist/lib/browser/chunk-GI5LZ4SV.mjs.map +0 -7
  161. package/dist/lib/browser/chunk-RHHJZQPM.mjs.map +0 -7
  162. package/dist/lib/browser/react-surface-HETJ5XY6.mjs.map +0 -7
  163. package/dist/lib/node/app-graph-builder-WWC4WNTG.cjs +0 -177
  164. package/dist/lib/node/app-graph-builder-WWC4WNTG.cjs.map +0 -7
  165. package/dist/lib/node/chunk-3QEJ5EBO.cjs.map +0 -7
  166. package/dist/lib/node/chunk-KO6MKCED.cjs.map +0 -7
  167. package/dist/lib/node/chunk-LA73IHCB.cjs.map +0 -7
  168. package/dist/lib/node/chunk-WXG4Z2EZ.cjs.map +0 -7
  169. package/dist/lib/node/react-surface-M2JQIIDY.cjs.map +0 -7
  170. package/dist/lib/node-esm/app-graph-builder-PPJQAFSI.mjs +0 -169
  171. package/dist/lib/node-esm/app-graph-builder-PPJQAFSI.mjs.map +0 -7
  172. package/dist/lib/node-esm/chunk-54HDN33F.mjs.map +0 -7
  173. package/dist/lib/node-esm/chunk-K3R3YK3S.mjs.map +0 -7
  174. package/dist/lib/node-esm/chunk-SDMGJZLN.mjs.map +0 -7
  175. package/dist/lib/node-esm/chunk-XY22OP6H.mjs +0 -10
  176. package/dist/lib/node-esm/chunk-XY22OP6H.mjs.map +0 -7
  177. package/dist/lib/node-esm/react-surface-PXFSIM6V.mjs.map +0 -7
  178. package/dist/types/src/components/Transcript/deprecated/Transcript.d.ts +0 -13
  179. package/dist/types/src/components/Transcript/deprecated/Transcript.d.ts.map +0 -1
  180. package/dist/types/src/components/Transcript/testings.d.ts +0 -51
  181. package/dist/types/src/components/Transcript/testings.d.ts.map +0 -1
  182. package/dist/types/src/testing/test-data.d.ts +0 -73
  183. package/dist/types/src/testing/test-data.d.ts.map +0 -1
  184. package/src/components/Transcript/testings.ts +0 -182
  185. package/src/testing/test-data.ts +0 -314
  186. /package/dist/lib/browser/{TranscriptContainer-4D4BHE7G.mjs.map → TranscriptContainer-3LU3XBEB.mjs.map} +0 -0
  187. /package/dist/lib/browser/{meeting-transcription-state-5D52VTHU.mjs.map → meeting-transcription-state-MNA2XROE.mjs.map} +0 -0
  188. /package/dist/lib/browser/{transcriber-SMRLU2KY.mjs.map → transcriber-QRSS6X6S.mjs.map} +0 -0
  189. /package/dist/lib/node/{meeting-transcription-state-OYWCY5XE.cjs.map → meeting-transcription-state-5XLTMWPA.cjs.map} +0 -0
  190. /package/dist/lib/node/{transcriber-E6AAB4SU.cjs.map → transcriber-UZJMG44C.cjs.map} +0 -0
  191. /package/dist/lib/node-esm/{TranscriptContainer-3HJ752CP.mjs.map → TranscriptContainer-K4E4P5T6.mjs.map} +0 -0
  192. /package/dist/lib/node-esm/{meeting-transcription-state-XMZJ45P4.mjs.map → meeting-transcription-state-3WOZ52BQ.mjs.map} +0 -0
  193. /package/dist/lib/node-esm/{transcriber-REAGRCPY.mjs.map → transcriber-NVVFH7IF.mjs.map} +0 -0
@@ -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, randomQueueDxn } from '@dxos/react-client/echo';
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(randomQueueDxn(spaceId as SpaceId)),
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 { TRANSCRIPTION_PLUGIN } from '../meta';
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: `${TRANSCRIPTION_PLUGIN}/article/transcript`,
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, type FC } from 'react';
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
- BlockBuilder,
31
+ MessageBuilder,
27
32
  TestItem,
28
33
  useTestTranscriptionQueue,
29
34
  useTestTranscriptionQueueWithEntityExtraction,
30
- } from './testings';
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 = { blocks?: TranscriptBlock[] } & Pick<TranscriptProps, 'ignoreAttention' | 'attendableId'>;
72
+ type StoryProps = { messages?: DataType.Message[] } & Pick<TranscriptProps, 'ignoreAttention' | 'attendableId'>;
71
73
 
72
74
  /**
73
- * Basic story mutates array of blocks.
75
+ * Basic story mutates array of messages.
74
76
  */
75
- const BasicStory = ({ blocks: initialBlocks = [], ...props }: StoryProps) => {
77
+ const BasicStory = ({ messages: initialMessages = [], ...props }: StoryProps) => {
76
78
  const [reset, setReset] = useState({});
77
- const builder = useMemo(() => new BlockBuilder(), []);
78
- const model = useMemo(() => new BlockModel<TranscriptBlock>(renderMarkdown, initialBlocks), [initialBlocks, reset]);
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 [currentBlock, setCurrentBlock] = useState<TranscriptBlock | null>(null);
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 (!currentBlock) {
87
- const block = builder.createBlock();
88
- model.appendBlock(block);
89
- setCurrentBlock(block);
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 (currentBlock?.segments.length && currentBlock.segments.length >= 3) {
95
- setCurrentBlock(null);
100
+ if (currentMessage?.blocks.length && currentMessage.blocks.length >= 3) {
101
+ setCurrentMessage(null);
96
102
  clearInterval(i);
97
103
  return;
98
104
  }
99
105
 
100
- currentBlock.segments.push(builder.createSegment());
101
- model.updateBlock(currentBlock);
106
+ currentMessage.blocks.push(builder.createBlock());
107
+ model.updateChunk(currentMessage);
102
108
  }, 3_000);
103
109
 
104
110
  return () => clearInterval(i);
105
- }, [model, currentBlock, running]);
111
+ }, [model, currentMessage, running]);
106
112
 
107
113
  const handleReset = () => {
108
- setCurrentBlock(null);
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 = ({ blocks: initialBlocks = [], ...props }: StoryProps) => {
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 queue = useTestTranscriptionQueue(space, running, 2_000);
131
- const model = useQueueModelAdapter(renderMarkdown, queue, initialBlocks);
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 <TranscriptContainer space={space} model={model} running={running} onRunningChange={setRunning} {...props} />;
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 queue = useTestTranscriptionQueueWithEntityExtraction(space, running, 2_000);
140
- const model = useQueueModelAdapter(renderMarkdown, queue, []);
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
  },
@@ -170,14 +206,14 @@ const meta: Meta<typeof QueueStory> = {
170
206
 
171
207
  export default meta;
172
208
 
173
- type Story = StoryObj<StoryProps>;
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
- blocks: Array.from({ length: 10 }, () => BlockBuilder.singleton.createBlock()),
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: QueueStory,
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
- // ignoreAttention: true,
204
- // attendableId: 'story',
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 BlockModel } from '../../model';
27
- import { type TranscriptType, type TranscriptBlock } from '../../types';
25
+ import { type SerializationModel } from '../../model';
26
+ import { type TranscriptType } from '../../types';
28
27
 
29
- export const renderMarkdown = (block: TranscriptBlock, debug = false): string[] => {
30
- // TODO(burdon): Use link/reference markup for users (with popover).
31
- // TODO(burdon): Color and avatar.
32
- return [
33
- `###### ${block.authorName}` + (debug ? ` (${block.id})` : ''),
34
- block.segments.map((segment) => segment.text.trim()).join(' '),
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: BlockModel<TranscriptBlock>;
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 <div ref={parentRef} className={mx('flex grow overflow-hidden', editorWidth, classNames)} />;
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
- import { type TranscriptBlock } from '../../../types';
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
 
@@ -9,15 +9,15 @@ import { intervalToDuration } from 'date-fns/intervalToDuration';
9
9
 
10
10
  import { type CleanupFn, addEventListener, combine } from '@dxos/async';
11
11
  import { type RenderCallback } from '@dxos/react-ui-editor';
12
+ import { type DataType } from '@dxos/schema';
12
13
 
13
- import { DocumentAdapter, type BlockModel } from '../../model';
14
- import { type TranscriptBlock } from '../../types';
14
+ import { DocumentAdapter, type SerializationModel } from '../../model';
15
15
 
16
16
  /**
17
- * Data structure that maps Blocks queue to lines with transcript state.
17
+ * Data structure that maps Chunks queue to lines with transcript state.
18
18
  */
19
19
  export type TranscriptOptions = {
20
- model: BlockModel<TranscriptBlock>;
20
+ model: SerializationModel<DataType.Message>;
21
21
  started?: Date;
22
22
  renderButton?: RenderCallback<{ onClick: () => void }>;
23
23
  };
@@ -32,12 +32,13 @@ export const transcript = ({ model, started, renderButton }: TranscriptOptions):
32
32
  class: 'cm-timestamp-gutter',
33
33
  lineMarkerChange: (update) => update.docChanged || update.viewportChanged,
34
34
  markers: (view) => {
35
- const start = getStartTime(started, model.blocks[0]);
35
+ const start = getStartTime(started, model.chunks[0]);
36
36
  const builder = new RangeSetBuilder<GutterMarker>();
37
37
  for (const { from, to } of view.visibleRanges) {
38
38
  let line = view.state.doc.lineAt(from);
39
39
  while (line.from <= to) {
40
- const timestamp = model.getBlockAtLine(line.number)?.segments[0]?.started;
40
+ const block = model.getChunkAtLine(line.number)?.blocks[0];
41
+ const timestamp = block?.type === 'transcription' && block.started;
41
42
  if (timestamp) {
42
43
  builder.add(line.from, line.from, new TimestampMarker(line.number, new Date(timestamp), start));
43
44
  }
@@ -69,13 +70,15 @@ export const transcript = ({ model, started, renderButton }: TranscriptOptions):
69
70
  let isAutoScrolling = false;
70
71
  let hasScrolled = false;
71
72
 
72
- const scrollToBottom = () => {
73
- scroller.style.scrollBehavior = 'smooth';
73
+ let timeout: NodeJS.Timeout | undefined;
74
+ const scrollToBottom = (smooth = false) => {
75
+ scroller.style.scrollBehavior = smooth ? 'smooth' : '';
74
76
 
75
77
  // Temporarily hide scrollbar to prevent flicker.
76
78
  scroller.classList.add('cm-hide-scrollbar');
77
79
  isAutoScrolling = true;
78
- setTimeout(() => {
80
+ clearTimeout(timeout);
81
+ timeout = setTimeout(() => {
79
82
  this._controls?.classList.add('opacity-0');
80
83
  scroller.classList.remove('cm-hide-scrollbar');
81
84
  isAutoScrolling = false;
@@ -96,7 +99,7 @@ export const transcript = ({ model, started, renderButton }: TranscriptOptions):
96
99
  this._controls,
97
100
  {
98
101
  onClick: () => {
99
- scrollToBottom();
102
+ scrollToBottom(false);
100
103
  },
101
104
  },
102
105
  view,
@@ -122,7 +125,7 @@ export const transcript = ({ model, started, renderButton }: TranscriptOptions):
122
125
 
123
126
  // Scroll.
124
127
  if (autoScroll) {
125
- scrollToBottom();
128
+ scrollToBottom(true);
126
129
  }
127
130
  }),
128
131
  );
@@ -210,24 +213,24 @@ class TimestampMarker extends GutterMarker {
210
213
  }
211
214
  }
212
215
 
213
- const getStartTime = (started?: Date, block?: TranscriptBlock): Date | undefined => {
216
+ const getStartTime = (started?: Date, message?: DataType.Message): Date | undefined => {
214
217
  if (started) {
215
218
  return started;
216
219
  }
217
220
 
218
- if (block?.segments[0]?.started) {
219
- return new Date(block.segments[0].started);
221
+ if (message?.blocks[0]?.type === 'transcription' && message.blocks[0].started) {
222
+ return new Date(message.blocks[0].started);
220
223
  }
221
224
 
222
225
  return undefined;
223
226
  };
224
227
 
225
228
  const formatTimestamp = (timestamp: Date, relative?: Date) => {
226
- if (!relative) {
227
- return format(timestamp, 'HH:mm:ss');
228
- } else {
229
+ if (relative) {
229
230
  const pad = (n = 0) => String(n).padStart(2, '0');
230
231
  const { hours, minutes, seconds } = intervalToDuration({ start: relative, end: timestamp });
231
232
  return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
233
+ } else {
234
+ return format(timestamp, 'HH:mm:ss');
232
235
  }
233
236
  };
@@ -5,18 +5,20 @@
5
5
  import React, { type FC } from 'react';
6
6
 
7
7
  import { fullyQualifiedId, getSpace } from '@dxos/client/echo';
8
- import { useQueue } from '@dxos/react-client/echo';
8
+ import { useMembers, useQueue } from '@dxos/react-client/echo';
9
9
  import { StackItem } from '@dxos/react-ui-stack';
10
+ import { type DataType } from '@dxos/schema';
10
11
 
11
12
  import { Transcript, renderMarkdown } from './Transcript';
12
13
  import { useQueueModelAdapter } from '../hooks';
13
- import { type TranscriptBlock, type TranscriptType } from '../types';
14
+ import { type TranscriptType } from '../types';
14
15
 
15
16
  export const TranscriptionContainer: FC<{ role: string; transcript: TranscriptType }> = ({ role, transcript }) => {
16
17
  const attendableId = fullyQualifiedId(transcript);
17
18
  const space = getSpace(transcript);
18
- const queue = useQueue<TranscriptBlock>(transcript.queue.dxn, { pollInterval: 1_000 });
19
- const model = useQueueModelAdapter(renderMarkdown, queue);
19
+ const members = useMembers(space?.key).map((member) => member.identity);
20
+ const queue = useQueue<DataType.Message>(transcript.queue.dxn, { pollInterval: 1_000 });
21
+ const model = useQueueModelAdapter(renderMarkdown(members), queue);
20
22
 
21
23
  return (
22
24
  <StackItem.Content role={role} classNames='container-max-width pli-2'>
@@ -0,0 +1,26 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+
7
+ import { Input, useTranslation } from '@dxos/react-ui';
8
+ import { DeprecatedFormContainer, DeprecatedFormInput } from '@dxos/react-ui-form';
9
+
10
+ import { TRANSCRIPTION_PLUGIN } from '../../meta';
11
+ import { type TranscriptionSettingsProps } from '../../types';
12
+
13
+ export const TranscriptionSettings = ({ settings }: { settings: TranscriptionSettingsProps }) => {
14
+ const { t } = useTranslation(TRANSCRIPTION_PLUGIN);
15
+
16
+ return (
17
+ <DeprecatedFormContainer>
18
+ <DeprecatedFormInput label={t('settings entity extraction label')}>
19
+ <Input.Switch
20
+ checked={!!settings.entityExtraction}
21
+ onCheckedChange={(checked) => (settings.entityExtraction = checked)}
22
+ />
23
+ </DeprecatedFormInput>
24
+ </DeprecatedFormContainer>
25
+ );
26
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './TranscriptionSettings';
@@ -6,5 +6,6 @@ import { lazy } from 'react';
6
6
 
7
7
  export * from './Transcript';
8
8
  export * from './TranscriptContainer';
9
+ export * from './TranscriptionSettings';
9
10
 
10
11
  export const TranscriptContainer = lazy(() => import('./TranscriptContainer'));