@dxos/plugin-transcription 0.8.4-main.2e9d522 → 0.8.4-main.406dc2a

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 (170) hide show
  1. package/dist/lib/browser/TranscriptContainer-QN5WUMWJ.mjs +11 -0
  2. package/dist/lib/browser/blueprint-definition-GO6GTEBT.mjs +150 -0
  3. package/dist/lib/browser/blueprint-definition-GO6GTEBT.mjs.map +7 -0
  4. package/dist/lib/browser/{chunk-4FJIYH7T.mjs → chunk-77NWYLC6.mjs} +2 -2
  5. package/dist/lib/browser/{chunk-5WH45KBY.mjs → chunk-7LNCQDTT.mjs} +9 -4
  6. package/dist/lib/browser/chunk-7LNCQDTT.mjs.map +7 -0
  7. package/dist/lib/browser/{chunk-FFEPEPCU.mjs → chunk-COBQIZ6B.mjs} +85 -94
  8. package/dist/lib/browser/chunk-COBQIZ6B.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-GGHMOEKZ.mjs +63 -0
  10. package/dist/lib/browser/chunk-GGHMOEKZ.mjs.map +7 -0
  11. package/dist/lib/browser/{chunk-U6QBOGTN.mjs → chunk-LCK3YRQF.mjs} +5 -5
  12. package/dist/lib/browser/chunk-LCK3YRQF.mjs.map +7 -0
  13. package/dist/lib/browser/{chunk-5KPYIC3N.mjs → chunk-ZS2FXKEF.mjs} +410 -418
  14. package/dist/lib/browser/chunk-ZS2FXKEF.mjs.map +7 -0
  15. package/dist/lib/browser/index.mjs +22 -18
  16. package/dist/lib/browser/index.mjs.map +3 -3
  17. package/dist/lib/browser/intent-resolver-52EPFRMR.mjs +25 -0
  18. package/dist/lib/browser/intent-resolver-52EPFRMR.mjs.map +7 -0
  19. package/dist/lib/browser/meta.json +1 -1
  20. package/dist/lib/browser/{react-surface-E5D5AE3F.mjs → react-surface-LCP2D2MF.mjs} +10 -10
  21. package/dist/lib/browser/react-surface-LCP2D2MF.mjs.map +7 -0
  22. package/dist/lib/browser/{transcriber-PXT4QBFY.mjs → transcriber-NG5MRCP6.mjs} +7 -7
  23. package/dist/lib/browser/transcriber-NG5MRCP6.mjs.map +7 -0
  24. package/dist/lib/browser/types/index.mjs +6 -8
  25. package/dist/lib/node-esm/{TranscriptContainer-VBAOM5DG.mjs → TranscriptContainer-WCUWVVSD.mjs} +4 -4
  26. package/dist/lib/node-esm/blueprint-definition-6SU7GLBR.mjs +151 -0
  27. package/dist/lib/node-esm/blueprint-definition-6SU7GLBR.mjs.map +7 -0
  28. package/dist/lib/node-esm/{chunk-HT3W7LDP.mjs → chunk-A6SB234F.mjs} +85 -94
  29. package/dist/lib/node-esm/chunk-A6SB234F.mjs.map +7 -0
  30. package/dist/lib/node-esm/chunk-FTTZ4YSX.mjs +64 -0
  31. package/dist/lib/node-esm/chunk-FTTZ4YSX.mjs.map +7 -0
  32. package/dist/lib/node-esm/{chunk-XMUCZBP3.mjs → chunk-KLTPFUKN.mjs} +8 -4
  33. package/dist/lib/node-esm/chunk-KLTPFUKN.mjs.map +7 -0
  34. package/dist/lib/node-esm/{chunk-Y2B4HWSS.mjs → chunk-N5Z4SKPH.mjs} +2 -2
  35. package/dist/lib/node-esm/{chunk-TJKAO7VR.mjs → chunk-PEZRQYKO.mjs} +410 -418
  36. package/dist/lib/node-esm/chunk-PEZRQYKO.mjs.map +7 -0
  37. package/dist/lib/node-esm/{chunk-RHF2ALML.mjs → chunk-RWRG3S2O.mjs} +5 -5
  38. package/dist/lib/node-esm/chunk-RWRG3S2O.mjs.map +7 -0
  39. package/dist/lib/node-esm/index.mjs +22 -18
  40. package/dist/lib/node-esm/index.mjs.map +3 -3
  41. package/dist/lib/node-esm/intent-resolver-DZBQQRHN.mjs +26 -0
  42. package/dist/lib/node-esm/intent-resolver-DZBQQRHN.mjs.map +7 -0
  43. package/dist/lib/node-esm/meta.json +1 -1
  44. package/dist/lib/node-esm/{react-surface-QRDJOHN5.mjs → react-surface-7UC2YXX2.mjs} +10 -10
  45. package/dist/lib/node-esm/react-surface-7UC2YXX2.mjs.map +7 -0
  46. package/dist/lib/node-esm/{transcriber-YUTEVANE.mjs → transcriber-SMU7HD6F.mjs} +7 -7
  47. package/dist/lib/node-esm/transcriber-SMU7HD6F.mjs.map +7 -0
  48. package/dist/lib/node-esm/types/index.mjs +6 -8
  49. package/dist/types/src/TranscriptionPlugin.d.ts +1 -1
  50. package/dist/types/src/TranscriptionPlugin.d.ts.map +1 -1
  51. package/dist/types/src/capabilities/blueprint-definition.d.ts +5 -0
  52. package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -0
  53. package/dist/types/src/capabilities/capabilities.d.ts +1 -1
  54. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  55. package/dist/types/src/capabilities/index.d.ts +3 -2
  56. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  57. package/dist/types/src/capabilities/intent-resolver.d.ts +1 -1
  58. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  59. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  60. package/dist/types/src/capabilities/transcriber.d.ts +1 -1
  61. package/dist/types/src/capabilities/transcriber.d.ts.map +1 -1
  62. package/dist/types/src/components/Transcript/Transcript.d.ts +5 -5
  63. package/dist/types/src/components/Transcript/Transcript.d.ts.map +1 -1
  64. package/dist/types/src/components/Transcript/Transcript.stories.d.ts +31 -16
  65. package/dist/types/src/components/Transcript/Transcript.stories.d.ts.map +1 -1
  66. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts +3 -3
  67. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts.map +1 -1
  68. package/dist/types/src/components/Transcript/transcript-extension.d.ts +1 -5
  69. package/dist/types/src/components/Transcript/transcript-extension.d.ts.map +1 -1
  70. package/dist/types/src/components/TranscriptContainer.d.ts +2 -2
  71. package/dist/types/src/components/TranscriptContainer.d.ts.map +1 -1
  72. package/dist/types/src/components/stories/FileTranscription.stories.d.ts +8 -4
  73. package/dist/types/src/components/stories/FileTranscription.stories.d.ts.map +1 -1
  74. package/dist/types/src/components/stories/KeyWordRecognition.stories.d.ts +13 -6
  75. package/dist/types/src/components/stories/KeyWordRecognition.stories.d.ts.map +1 -1
  76. package/dist/types/src/components/stories/MicrophoneTranscription.stories.d.ts +10 -6
  77. package/dist/types/src/components/stories/MicrophoneTranscription.stories.d.ts.map +1 -1
  78. package/dist/types/src/components/stories/TranscriptionStory.d.ts +2 -1
  79. package/dist/types/src/components/stories/TranscriptionStory.d.ts.map +1 -1
  80. package/dist/types/src/functions/index.d.ts +3 -0
  81. package/dist/types/src/functions/index.d.ts.map +1 -0
  82. package/dist/types/src/functions/open.d.ts +7 -0
  83. package/dist/types/src/functions/open.d.ts.map +1 -0
  84. package/dist/types/src/functions/summarize.d.ts +11 -0
  85. package/dist/types/src/functions/summarize.d.ts.map +1 -0
  86. package/dist/types/src/hooks/useAudioFile.d.ts.map +1 -1
  87. package/dist/types/src/hooks/useVoiceInput.d.ts.map +1 -1
  88. package/dist/types/src/meta.d.ts +0 -1
  89. package/dist/types/src/meta.d.ts.map +1 -1
  90. package/dist/types/src/segments-normalization/normalization.d.ts +376 -325
  91. package/dist/types/src/segments-normalization/normalization.d.ts.map +1 -1
  92. package/dist/types/src/testing/testing.d.ts +2 -2
  93. package/dist/types/src/testing/testing.d.ts.map +1 -1
  94. package/dist/types/src/transcriber/media-stream-recorder.d.ts +1 -1
  95. package/dist/types/src/transcriber/media-stream-recorder.d.ts.map +1 -1
  96. package/dist/types/src/transcriber/transcriber.d.ts.map +1 -1
  97. package/dist/types/src/transcriber/transcription-manager.d.ts +3 -7
  98. package/dist/types/src/transcriber/transcription-manager.d.ts.map +1 -1
  99. package/dist/types/src/types/Transcript.d.ts +30 -0
  100. package/dist/types/src/types/Transcript.d.ts.map +1 -0
  101. package/dist/types/src/types/TranscriptAction.d.ts +23 -0
  102. package/dist/types/src/types/TranscriptAction.d.ts.map +1 -0
  103. package/dist/types/src/types/index.d.ts +2 -1
  104. package/dist/types/src/types/index.d.ts.map +1 -1
  105. package/dist/types/src/types/types.d.ts +0 -26
  106. package/dist/types/src/types/types.d.ts.map +1 -1
  107. package/dist/types/tsconfig.tsbuildinfo +1 -1
  108. package/package.json +63 -62
  109. package/src/TranscriptionPlugin.tsx +57 -53
  110. package/src/capabilities/blueprint-definition.ts +31 -0
  111. package/src/capabilities/capabilities.ts +6 -6
  112. package/src/capabilities/index.ts +1 -0
  113. package/src/capabilities/intent-resolver.ts +5 -11
  114. package/src/capabilities/react-surface.tsx +2 -2
  115. package/src/capabilities/transcriber.ts +3 -2
  116. package/src/components/Transcript/Transcript.stories.tsx +34 -31
  117. package/src/components/Transcript/Transcript.tsx +20 -27
  118. package/src/components/Transcript/deprecated/OldTranscript.tsx +9 -9
  119. package/src/components/Transcript/transcript-extension.ts +2 -39
  120. package/src/components/TranscriptContainer.tsx +7 -6
  121. package/src/components/stories/FileTranscription.stories.tsx +49 -36
  122. package/src/components/stories/KeyWordRecognition.stories.tsx +17 -10
  123. package/src/components/stories/MicrophoneTranscription.stories.tsx +43 -38
  124. package/src/components/stories/TranscriptionStory.tsx +10 -6
  125. package/src/functions/index.ts +6 -0
  126. package/src/functions/open.ts +39 -0
  127. package/src/functions/summarize.ts +103 -0
  128. package/src/hooks/useAudioFile.ts +49 -53
  129. package/src/hooks/useVoiceInput.ts +3 -2
  130. package/src/meta.ts +1 -3
  131. package/src/model/model.test.ts +1 -1
  132. package/src/segments-normalization/message-normalizer.ts +1 -1
  133. package/src/segments-normalization/normalization.test.ts +5 -5
  134. package/src/segments-normalization/normalization.ts +38 -37
  135. package/src/testing/testing.ts +7 -7
  136. package/src/transcriber/media-stream-recorder.ts +1 -1
  137. package/src/transcriber/transcriber.browser.test.ts +2 -1
  138. package/src/transcriber/transcriber.ts +2 -1
  139. package/src/transcriber/transcription-manager.ts +17 -38
  140. package/src/types/{schema.ts → Transcript.ts} +12 -9
  141. package/src/types/TranscriptAction.ts +24 -0
  142. package/src/types/index.ts +2 -1
  143. package/src/types/types.ts +0 -23
  144. package/dist/lib/browser/TranscriptContainer-6WCKCXQ6.mjs +0 -11
  145. package/dist/lib/browser/chunk-5KPYIC3N.mjs.map +0 -7
  146. package/dist/lib/browser/chunk-5WH45KBY.mjs.map +0 -7
  147. package/dist/lib/browser/chunk-FFEPEPCU.mjs.map +0 -7
  148. package/dist/lib/browser/chunk-R7INNNUQ.mjs +0 -55
  149. package/dist/lib/browser/chunk-R7INNNUQ.mjs.map +0 -7
  150. package/dist/lib/browser/chunk-U6QBOGTN.mjs.map +0 -7
  151. package/dist/lib/browser/intent-resolver-3O33MMP5.mjs +0 -30
  152. package/dist/lib/browser/intent-resolver-3O33MMP5.mjs.map +0 -7
  153. package/dist/lib/browser/react-surface-E5D5AE3F.mjs.map +0 -7
  154. package/dist/lib/browser/transcriber-PXT4QBFY.mjs.map +0 -7
  155. package/dist/lib/node-esm/chunk-HT3W7LDP.mjs.map +0 -7
  156. package/dist/lib/node-esm/chunk-RHF2ALML.mjs.map +0 -7
  157. package/dist/lib/node-esm/chunk-TJKAO7VR.mjs.map +0 -7
  158. package/dist/lib/node-esm/chunk-U65ZWK3L.mjs +0 -56
  159. package/dist/lib/node-esm/chunk-U65ZWK3L.mjs.map +0 -7
  160. package/dist/lib/node-esm/chunk-XMUCZBP3.mjs.map +0 -7
  161. package/dist/lib/node-esm/intent-resolver-YMN5SXNC.mjs +0 -31
  162. package/dist/lib/node-esm/intent-resolver-YMN5SXNC.mjs.map +0 -7
  163. package/dist/lib/node-esm/react-surface-QRDJOHN5.mjs.map +0 -7
  164. package/dist/lib/node-esm/transcriber-YUTEVANE.mjs.map +0 -7
  165. package/dist/types/src/types/schema.d.ts +0 -41
  166. package/dist/types/src/types/schema.d.ts.map +0 -1
  167. /package/dist/lib/browser/{TranscriptContainer-6WCKCXQ6.mjs.map → TranscriptContainer-QN5WUMWJ.mjs.map} +0 -0
  168. /package/dist/lib/browser/{chunk-4FJIYH7T.mjs.map → chunk-77NWYLC6.mjs.map} +0 -0
  169. /package/dist/lib/node-esm/{TranscriptContainer-VBAOM5DG.mjs.map → TranscriptContainer-WCUWVVSD.mjs.map} +0 -0
  170. /package/dist/lib/node-esm/{chunk-Y2B4HWSS.mjs.map → chunk-N5Z4SKPH.mjs.map} +0 -0
@@ -6,26 +6,27 @@ import React from 'react';
6
6
 
7
7
  import { type Space } from '@dxos/react-client/echo';
8
8
  import { type Identity } from '@dxos/react-client/halo';
9
- import { IconButton, type ThemedClassName, useThemeContext } from '@dxos/react-ui';
9
+ import { type ThemedClassName, useThemeContext } from '@dxos/react-ui';
10
10
  import {
11
+ autoScroll,
11
12
  createBasicExtensions,
12
13
  createMarkdownExtensions,
13
- createRenderer,
14
14
  createThemeExtensions,
15
15
  decorateMarkdown,
16
- editorWidth,
17
16
  preview,
18
17
  useTextEditor,
19
18
  } from '@dxos/react-ui-editor';
20
19
  import { mx } from '@dxos/react-ui-theme';
21
20
  import { type DataType } from '@dxos/schema';
22
- import { isNotFalsy } from '@dxos/util';
21
+ import { isTruthy } from '@dxos/util';
23
22
 
24
- import { transcript } from './transcript-extension';
25
23
  import { type SerializationModel } from '../../model';
26
- import { type TranscriptType } from '../../types';
24
+ import { type Transcript } from '../../types';
25
+
26
+ import { transcript } from './transcript-extension';
27
27
 
28
- export const renderMarkdown =
28
+ // TODO(thure): Move this?
29
+ export const renderByline =
29
30
  (identities: Identity[]) =>
30
31
  (message: DataType.Message, index: number, debug = false): string[] => {
31
32
  if (message.sender.role === 'assistant') {
@@ -44,17 +45,18 @@ export const renderMarkdown =
44
45
  message.sender.identityDid;
45
46
  const blocks = message.blocks.filter((block) => block._tag === 'transcript');
46
47
  return [
48
+ // TODO(thure): Use an XML tag with the bits needed here.
47
49
  `###### ${name}` + (debug ? ` [${index}]:${message.id}` : ''),
48
50
  blocks.map((block) => block.text.trim()).join(' '),
49
51
  '',
50
52
  ];
51
53
  };
52
54
 
53
- export type TranscriptProps = ThemedClassName<{
54
- transcript?: TranscriptType;
55
+ export type TranscriptViewProps = ThemedClassName<{
55
56
  space?: Space;
57
+ transcript?: Transcript.Transcript;
56
58
  model: SerializationModel<DataType.Message>;
57
- // TODO(wittjosiah): Move to container.
59
+ // TODO(burdon): Are these still in use?
58
60
  attendableId?: string;
59
61
  ignoreAttention?: boolean;
60
62
  }>;
@@ -62,39 +64,30 @@ export type TranscriptProps = ThemedClassName<{
62
64
  /**
63
65
  * Transcript component implemented using the editor.
64
66
  */
65
- export const Transcript = ({
66
- classNames,
67
- transcript: object,
68
- space,
69
- model,
70
- attendableId,
71
- ignoreAttention,
72
- }: TranscriptProps) => {
67
+ export const TranscriptView = ({ classNames, space, transcript: object, model }: TranscriptViewProps) => {
73
68
  const { themeMode } = useThemeContext();
74
69
  const { parentRef } = useTextEditor(() => {
75
70
  return {
76
71
  extensions: [
77
- createBasicExtensions({ readOnly: true, lineWrapping: true }),
78
- createMarkdownExtensions({ themeMode }),
72
+ createBasicExtensions({ readOnly: true, lineWrapping: true, search: true }),
73
+ createMarkdownExtensions(),
79
74
  createThemeExtensions({ themeMode }),
80
75
  decorateMarkdown(),
81
- space && preview(),
76
+ preview(),
82
77
  transcript({
83
78
  model,
84
79
  started: object?.started ? new Date(object.started) : undefined,
85
- renderButton: createRenderer(({ onClick }) => (
86
- <IconButton icon='ph--arrow-line-down--regular' iconOnly label='Scroll to bottom' onClick={onClick} />
87
- )),
88
80
  }),
89
- ].filter(isNotFalsy),
81
+ autoScroll(),
82
+ ].filter(isTruthy),
90
83
  };
91
84
  }, [space, model]);
92
85
 
93
86
  return (
94
87
  <div
95
88
  ref={parentRef}
96
- className={mx('flex grow overflow-hidden', editorWidth, classNames)}
97
- data-popover-collision-boundary={true}
89
+ className={mx('flex grow overflow-hidden container-max-width', classNames)}
90
+ data-popover-collision-boundary={true /* TODO(thure): Make this a constant and document it. */}
98
91
  />
99
92
  );
100
93
  };
@@ -3,25 +3,25 @@
3
3
  //
4
4
 
5
5
  import { intervalToDuration } from 'date-fns/intervalToDuration';
6
- import { Schema } from 'effect';
6
+ import * as Schema from 'effect/Schema';
7
7
  import { yieldOrContinue } from 'main-thread-scheduling';
8
- import React, { type FC, useCallback, useEffect, useMemo, useRef, useState, type WheelEvent } from 'react';
8
+ import React, { type FC, type WheelEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';
9
9
  import { type OnResizeCallback, useResizeDetector } from 'react-resize-detector';
10
10
 
11
11
  import { Type } from '@dxos/echo';
12
12
  import { IconButton, type ThemedClassName, useTranslation } from '@dxos/react-ui';
13
13
  import { useAttention } from '@dxos/react-ui-attention';
14
14
  import {
15
+ type DxGridAxisMeta,
15
16
  type DxGridCellValue,
16
17
  type DxGridElement,
17
- type DxGridAxisMeta,
18
18
  type DxGridPlaneCells,
19
19
  Grid,
20
20
  toPlaneCellIndex,
21
21
  } from '@dxos/react-ui-grid';
22
22
  import { mx } from '@dxos/react-ui-theme';
23
23
 
24
- import { TRANSCRIPTION_PLUGIN } from '../../../meta';
24
+ import { meta } from '../../../meta';
25
25
 
26
26
  /**
27
27
  * Transcription fragment.
@@ -115,7 +115,7 @@ const measureRows = async (
115
115
  return result;
116
116
  };
117
117
 
118
- export type TranscriptProps = ThemedClassName<{
118
+ export type TranscriptViewProps = ThemedClassName<{
119
119
  blocks?: TranscriptBlock[];
120
120
  attendableId?: string;
121
121
  ignoreAttention?: boolean;
@@ -124,8 +124,8 @@ export type TranscriptProps = ThemedClassName<{
124
124
  /**
125
125
  * @@deprecated
126
126
  */
127
- export const Transcript: FC<TranscriptProps> = ({ classNames, blocks, attendableId, ignoreAttention }) => {
128
- const { t } = useTranslation(TRANSCRIPTION_PLUGIN);
127
+ export const Transcript: FC<TranscriptViewProps> = ({ classNames, blocks, attendableId, ignoreAttention }) => {
128
+ const { t } = useTranslation(meta.id);
129
129
  const [dxGrid, setDxGrid] = useState<DxGridElement | null>(null);
130
130
  const [rows, setRows] = useState<DxGridAxisMeta | undefined>(undefined);
131
131
  const [columns, setColumns] = useState<DxGridAxisMeta | undefined>(undefined);
@@ -144,7 +144,7 @@ export const Transcript: FC<TranscriptProps> = ({ classNames, blocks, attendable
144
144
  [hasAttention, ignoreAttention],
145
145
  );
146
146
 
147
- const abortControllerRef = useRef<AbortController>();
147
+ const abortControllerRef = useRef<AbortController>(null);
148
148
 
149
149
  const handleResize = useCallback(
150
150
  async ({ entry }: { entry: { target: HTMLDivElement } | null }) => {
@@ -242,7 +242,7 @@ export const Transcript: FC<TranscriptProps> = ({ classNames, blocks, attendable
242
242
  ref={setDxGrid}
243
243
  />
244
244
  </Grid.Root>
245
- <div role='none' {...{ inert: '' }} aria-hidden className={measureClasses} ref={measureRef} />
245
+ <div role='none' {...{ inert: true }} aria-hidden className={measureClasses} ref={measureRef} />
246
246
  <IconButton
247
247
  icon='ph--arrow-line-down--regular'
248
248
  iconOnly
@@ -8,7 +8,6 @@ import { format } from 'date-fns/format';
8
8
  import { intervalToDuration } from 'date-fns/intervalToDuration';
9
9
 
10
10
  import { type CleanupFn, addEventListener, combine } from '@dxos/async';
11
- import { type RenderCallback } from '@dxos/react-ui-editor';
12
11
  import { type DataType } from '@dxos/schema';
13
12
 
14
13
  import { DocumentAdapter, type SerializationModel } from '../../model';
@@ -19,13 +18,12 @@ import { DocumentAdapter, type SerializationModel } from '../../model';
19
18
  export type TranscriptOptions = {
20
19
  model: SerializationModel<DataType.Message>;
21
20
  started?: Date;
22
- renderButton?: RenderCallback<{ onClick: () => void }>;
23
21
  };
24
22
 
25
23
  /**
26
24
  * Scrolling transcript with timestamps.
27
25
  */
28
- export const transcript = ({ model, started, renderButton }: TranscriptOptions): Extension => {
26
+ export const transcript = ({ model, started }: TranscriptOptions): Extension => {
29
27
  return [
30
28
  // Show timestamps in the gutter.
31
29
  gutter({
@@ -90,22 +88,6 @@ export const transcript = ({ model, started, renderButton }: TranscriptOptions):
90
88
  });
91
89
  };
92
90
 
93
- // Scroll button.
94
- if (renderButton) {
95
- this._controls = document.createElement('div');
96
- this._controls.classList.add('cm-controls', 'transition-opacity', 'duration-300', 'opacity-0');
97
- view.dom.appendChild(this._controls);
98
- renderButton(
99
- this._controls,
100
- {
101
- onClick: () => {
102
- scrollToBottom(false);
103
- },
104
- },
105
- view,
106
- );
107
- }
108
-
109
91
  // Event listeners.
110
92
  this._cleanup = combine(
111
93
  addEventListener(view.scrollDOM, 'scroll', () => {
@@ -131,7 +113,7 @@ export const transcript = ({ model, started, renderButton }: TranscriptOptions):
131
113
  );
132
114
  }
133
115
 
134
- update(update: ViewUpdate) {
116
+ update(_update: ViewUpdate) {
135
117
  // Initial sync.
136
118
  if (!this._initialized) {
137
119
  this._initialized = true;
@@ -149,25 +131,6 @@ export const transcript = ({ model, started, renderButton }: TranscriptOptions):
149
131
  ),
150
132
 
151
133
  EditorView.theme({
152
- '.cm-scroller': {
153
- overflowY: 'scroll',
154
- },
155
- '.cm-hide-scrollbar': {
156
- scrollbarWidth: 'none',
157
- '-ms-overflow-style': 'none',
158
- },
159
- '.cm-hide-scrollbar::-webkit-scrollbar': {
160
- display: 'none',
161
- },
162
- '.cm-controls': {
163
- position: 'absolute',
164
- bottom: '0.5rem',
165
- right: '0.5rem',
166
- zIndex: 1000,
167
- },
168
- '.cm-line': {
169
- paddingRight: '1rem',
170
- },
171
134
  '.cm-timestamp-gutter': {
172
135
  width: '6rem',
173
136
  paddingRight: '1rem',
@@ -9,13 +9,14 @@ import { useMembers, useQueue } from '@dxos/react-client/echo';
9
9
  import { StackItem } from '@dxos/react-ui-stack';
10
10
  import { type DataType } from '@dxos/schema';
11
11
 
12
- import { Transcript, renderMarkdown } from './Transcript';
13
12
  import { useQueueModelAdapter } from '../hooks';
14
- import { type TranscriptType } from '../types';
13
+ import { type Transcript } from '../types';
14
+
15
+ import { TranscriptView, renderByline } from './Transcript';
15
16
 
16
17
  export type TranscriptionContainerProps = {
17
18
  role: string;
18
- transcript: TranscriptType;
19
+ transcript: Transcript.Transcript;
19
20
  };
20
21
 
21
22
  export const TranscriptionContainer = ({ transcript }: TranscriptionContainerProps) => {
@@ -23,11 +24,11 @@ export const TranscriptionContainer = ({ transcript }: TranscriptionContainerPro
23
24
  const space = getSpace(transcript);
24
25
  const members = useMembers(space?.key).map((member) => member.identity);
25
26
  const queue = useQueue<DataType.Message>(transcript.queue.dxn, { pollInterval: 1_000 });
26
- const model = useQueueModelAdapter(renderMarkdown(members), queue);
27
+ const model = useQueueModelAdapter(renderByline(members), queue);
27
28
 
28
29
  return (
29
- <StackItem.Content classNames='container-max-width pli-2'>
30
- <Transcript attendableId={attendableId} space={space} model={model} transcript={transcript} />
30
+ <StackItem.Content>
31
+ <TranscriptView attendableId={attendableId} space={space} model={model} transcript={transcript} />
31
32
  </StackItem.Content>
32
33
  );
33
34
  };
@@ -2,8 +2,6 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
9
7
 
@@ -12,8 +10,8 @@ import { withPluginManager } from '@dxos/app-framework/testing';
12
10
  import { scheduleTask } from '@dxos/async';
13
11
  import { Context } from '@dxos/context';
14
12
  import { Obj } from '@dxos/echo';
13
+ import { createQueueDXN } from '@dxos/echo/internal';
15
14
  import { MemoryQueue } from '@dxos/echo-db';
16
- import { createQueueDXN } from '@dxos/echo-schema';
17
15
  import { FunctionExecutor, ServiceContainer } from '@dxos/functions';
18
16
  import { log } from '@dxos/log';
19
17
  import { ClientPlugin } from '@dxos/plugin-client';
@@ -21,19 +19,20 @@ import { PreviewPlugin } from '@dxos/plugin-preview';
21
19
  import { SpacePlugin } from '@dxos/plugin-space';
22
20
  import { StorybookLayoutPlugin } from '@dxos/plugin-storybook-layout';
23
21
  import { ThemePlugin } from '@dxos/plugin-theme';
22
+ import { withTheme } from '@dxos/react-ui/testing';
24
23
  import { defaultTx } from '@dxos/react-ui-theme';
25
24
  import { DataType } from '@dxos/schema';
26
- import { seedTestData, Testing } from '@dxos/schema/testing';
27
- import { withLayout, withTheme } from '@dxos/storybook-utils';
25
+ import { Testing, seedTestData } from '@dxos/schema/testing';
28
26
 
29
- import { TranscriptionStory } from './TranscriptionStory';
30
- import { useIsSpeaking } from './useIsSpeaking';
31
- import { TranscriptionPlugin } from '../../TranscriptionPlugin';
32
27
  import { useAudioFile, useQueueModelAdapter, useTranscriber } from '../../hooks';
33
- import { getActorId, MessageNormalizer } from '../../segments-normalization';
28
+ import { MessageNormalizer, getActorId } from '../../segments-normalization';
34
29
  import { TestItem } from '../../testing';
35
30
  import { type MediaStreamRecorderParams, type TranscriberParams } from '../../transcriber';
36
- import { renderMarkdown } from '../Transcript';
31
+ import { TranscriptionPlugin } from '../../TranscriptionPlugin';
32
+ import { renderByline } from '../Transcript';
33
+
34
+ import { TranscriptionStory } from './TranscriptionStory';
35
+ import { useIsSpeaking } from './useIsSpeaking';
37
36
 
38
37
  const AudioFile = ({
39
38
  detectSpeaking,
@@ -67,7 +66,6 @@ const AudioFile = ({
67
66
 
68
67
  useEffect(() => {
69
68
  if (!audio) {
70
- log.warn('no audio');
71
69
  return;
72
70
  }
73
71
 
@@ -83,7 +81,7 @@ const AudioFile = ({
83
81
  const queueDxn = useMemo(() => createQueueDXN(), []);
84
82
  const queue = useMemo(() => new MemoryQueue<DataType.Message>(queueDxn), [queueDxn]);
85
83
 
86
- const model = useQueueModelAdapter(renderMarkdown([]), queue);
84
+ const model = useQueueModelAdapter(renderByline([]), queue);
87
85
  const handleSegments = useCallback<TranscriberParams['onSegments']>(
88
86
  async (blocks) => {
89
87
  void queue?.append([
@@ -146,10 +144,10 @@ const AudioFile = ({
146
144
  }, [normalizer]);
147
145
 
148
146
  const manageChunkRecording = () => {
149
- if (running && isSpeaking) {
147
+ if (running && isSpeaking && transcriber) {
150
148
  log.info('starting transcription');
151
149
  transcriber?.startChunksRecording();
152
- } else if (!isSpeaking || !running) {
150
+ } else if ((!isSpeaking || !running) && transcriber) {
153
151
  log.info('stopping transcription');
154
152
  transcriber?.stopChunksRecording();
155
153
  }
@@ -175,40 +173,54 @@ const AudioFile = ({
175
173
  manageChunkRecording();
176
174
  }, [isSpeaking, stream]);
177
175
 
178
- return <TranscriptionStory model={model} running={running} onRunningChange={setRunning} audioRef={ref} />;
176
+ return (
177
+ <TranscriptionStory
178
+ disabled={!stream}
179
+ model={model}
180
+ running={running}
181
+ onRunningChange={setRunning}
182
+ audioRef={ref}
183
+ />
184
+ );
179
185
  };
180
186
 
181
- const meta: Meta<typeof AudioFile> = {
187
+ const meta = {
182
188
  title: 'plugins/plugin-transcription/FileTranscription',
183
189
  decorators: [
190
+ withTheme,
184
191
  withPluginManager({
185
192
  plugins: [
186
- ThemePlugin({ tx: defaultTx }),
187
- StorybookLayoutPlugin(),
188
193
  ClientPlugin({
189
194
  types: [TestItem, DataType.Person, DataType.Organization, Testing.DocumentType],
190
- onClientInitialized: async (_, client) => {
195
+ onClientInitialized: async ({ client }) => {
191
196
  await client.halo.createIdentity();
192
197
  await client.spaces.waitUntilReady();
193
198
  await client.spaces.default.waitUntilReady();
194
199
  await seedTestData(client.spaces.default);
195
200
  },
196
201
  }),
197
- SpacePlugin(),
202
+ SpacePlugin({}),
203
+ IntentPlugin(),
204
+
205
+ // UI
206
+ ThemePlugin({ tx: defaultTx }),
198
207
  SettingsPlugin(),
199
208
  PreviewPlugin(),
200
- IntentPlugin(),
201
209
  TranscriptionPlugin(),
210
+ StorybookLayoutPlugin({}),
202
211
  ],
203
212
  fireEvents: [Events.SetupAppGraph],
204
213
  }),
205
- withTheme,
206
- withLayout({ fullscreen: true, classNames: 'justify-center' }),
207
214
  ],
208
- };
215
+ parameters: {
216
+ layout: 'column',
217
+ },
218
+ } satisfies Meta;
209
219
 
210
220
  export default meta;
211
221
 
222
+ type Story = StoryObj<typeof meta>;
223
+
212
224
  const TRANSCRIBER_CONFIG = {
213
225
  transcribeAfterChunksAmount: 100,
214
226
  prefixBufferChunksAmount: 50,
@@ -231,15 +243,16 @@ export const Default: StoryObj<typeof AudioFile> = {
231
243
  },
232
244
  };
233
245
 
234
- export const WithSentenceNormalization: StoryObj<typeof AudioFile> = {
235
- render: AudioFile,
236
- args: {
237
- detectSpeaking: true,
238
- normalizeSentences: true,
239
- // https://learnenglish.britishcouncil.org/general-english/audio-zone/living-london
240
- audioUrl: 'https://dxos.network/audio-london.m4a',
241
- // textUrl: 'https://dxos.network/audio-london.txt',
242
- transcriberConfig: TRANSCRIBER_CONFIG,
243
- recorderConfig: RECORDER_CONFIG,
244
- },
245
- };
246
+ // TODO(mykola): Fix sentence normalization.
247
+ // export const WithSentenceNormalization: StoryObj<typeof AudioFile> = {
248
+ // render: AudioFile,
249
+ // args: {
250
+ // detectSpeaking: true,
251
+ // normalizeSentences: true,
252
+ // // https://learnenglish.britishcouncil.org/general-english/audio-zone/living-london
253
+ // audioUrl: 'https://dxos.network/audio-london.m4a',
254
+ // // textUrl: 'https://dxos.network/audio-london.txt',
255
+ // transcriberConfig: TRANSCRIBER_CONFIG,
256
+ // recorderConfig: RECORDER_CONFIG,
257
+ // },
258
+ // };
@@ -2,22 +2,24 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
- import { type StoryObj, type Meta } from '@storybook/react-vite';
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { useEffect, useMemo, useState } from 'react';
9
7
 
10
8
  import { log } from '@dxos/log';
11
9
  import { Toolbar } from '@dxos/react-ui';
10
+ import { withTheme } from '@dxos/react-ui/testing';
12
11
  import { mx } from '@dxos/react-ui-theme';
13
- import { withLayout, withTheme } from '@dxos/storybook-utils';
14
12
 
15
13
  type Word = {
16
14
  text: string;
17
15
  matched: boolean;
18
16
  };
19
17
 
20
- const SpeechRecognition = ({ keywords }: { keywords: string[] }) => {
18
+ type StoryProps = {
19
+ keywords: string[];
20
+ };
21
+
22
+ const DefaultStory = ({ keywords }: StoryProps) => {
21
23
  const [running, setRunning] = useState(false);
22
24
  const [matchingWords, setMatchingWords] = useState<Word[]>(keywords.map((word) => ({ text: word, matched: false })));
23
25
 
@@ -113,15 +115,20 @@ const SpeechRecognition = ({ keywords }: { keywords: string[] }) => {
113
115
  );
114
116
  };
115
117
 
116
- const meta: Meta<typeof SpeechRecognition> = {
118
+ const meta = {
117
119
  title: 'plugins/plugin-transcription/KeyWordDetection',
118
- decorators: [withTheme, withLayout({ fullscreen: true, classNames: 'justify-center' })],
119
- };
120
+ render: DefaultStory,
121
+ decorators: [withTheme],
122
+ parameters: {
123
+ layout: 'column',
124
+ },
125
+ } satisfies Meta<typeof DefaultStory>;
120
126
 
121
127
  export default meta;
122
128
 
123
- export const SpeechRecognitionAPI: StoryObj<typeof SpeechRecognition> = {
124
- render: SpeechRecognition,
129
+ type Story = StoryObj<typeof meta>;
130
+
131
+ export const SpeechRecognitionAPI: Story = {
125
132
  args: {
126
133
  keywords: ['kai', 'computer', 'hello'],
127
134
  },
@@ -2,8 +2,6 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
9
7
 
@@ -11,14 +9,14 @@ import { Events, IntentPlugin, SettingsPlugin } from '@dxos/app-framework';
11
9
  import { withPluginManager } from '@dxos/app-framework/testing';
12
10
  import {
13
11
  type ExtractionFunction,
14
- extractionAnthropicFn,
15
- extractionNerFn,
12
+ extractionAnthropicFunction,
13
+ extractionNerFunction,
16
14
  getNer,
17
15
  processTranscriptMessage,
18
- } from '@dxos/assistant';
16
+ } from '@dxos/assistant/extraction';
19
17
  import { Filter, Obj, type Type } from '@dxos/echo';
18
+ import { createQueueDXN } from '@dxos/echo/internal';
20
19
  import { MemoryQueue } from '@dxos/echo-db';
21
- import { createQueueDXN } from '@dxos/echo-schema';
22
20
  import { FunctionExecutor, ServiceContainer } from '@dxos/functions';
23
21
  import { invariant } from '@dxos/invariant';
24
22
  import { log } from '@dxos/log';
@@ -28,18 +26,19 @@ import { SpacePlugin } from '@dxos/plugin-space';
28
26
  import { StorybookLayoutPlugin } from '@dxos/plugin-storybook-layout';
29
27
  import { ThemePlugin } from '@dxos/plugin-theme';
30
28
  import { IndexKind, useSpace } from '@dxos/react-client/echo';
29
+ import { withTheme } from '@dxos/react-ui/testing';
31
30
  import { defaultTx } from '@dxos/react-ui-theme';
32
31
  import { DataType } from '@dxos/schema';
33
- import { seedTestData, Testing } from '@dxos/schema/testing';
34
- import { withLayout, withTheme } from '@dxos/storybook-utils';
32
+ import { Testing, seedTestData } from '@dxos/schema/testing';
35
33
 
36
- import { TranscriptionStory } from './TranscriptionStory';
37
- import { useIsSpeaking } from './useIsSpeaking';
38
- import { TranscriptionPlugin } from '../../TranscriptionPlugin';
39
34
  import { useAudioTrack, useQueueModelAdapter, useTranscriber } from '../../hooks';
40
35
  import { TestItem } from '../../testing';
41
36
  import { type MediaStreamRecorderParams, type TranscriberParams } from '../../transcriber';
42
- import { renderMarkdown } from '../Transcript';
37
+ import { TranscriptionPlugin } from '../../TranscriptionPlugin';
38
+ import { renderByline } from '../Transcript';
39
+
40
+ import { TranscriptionStory } from './TranscriptionStory';
41
+ import { useIsSpeaking } from './useIsSpeaking';
43
42
 
44
43
  const TRANSCRIBER_CONFIG = {
45
44
  transcribeAfterChunksAmount: 50,
@@ -77,7 +76,7 @@ const DefaultStory = ({
77
76
  // TODO(dmaretskyi): Use space.queues.create() instead.
78
77
  const queueDxn = useMemo(() => createQueueDXN(), []);
79
78
  const queue = useMemo(() => new MemoryQueue<DataType.Message>(queueDxn), [queueDxn]);
80
- const model = useQueueModelAdapter(renderMarkdown([]), queue);
79
+ const model = useQueueModelAdapter(renderByline([]), queue);
81
80
  const space = useSpace();
82
81
 
83
82
  useEffect(() => {
@@ -100,9 +99,9 @@ const DefaultStory = ({
100
99
  if (entityExtraction === 'ner') {
101
100
  // Init model loading. Takes time.
102
101
  void getNer();
103
- extractionFunction = extractionNerFn;
102
+ extractionFunction = extractionNerFunction;
104
103
  } else if (entityExtraction === 'llm') {
105
- extractionFunction = extractionAnthropicFn;
104
+ extractionFunction = extractionAnthropicFunction;
106
105
  objects = space.db
107
106
  .query(
108
107
  Filter.or(
@@ -192,17 +191,16 @@ const DefaultStory = ({
192
191
  return <TranscriptionStory model={model} running={running} onRunningChange={setRunning} />;
193
192
  };
194
193
 
195
- const meta: Meta<typeof DefaultStory> = {
194
+ const meta = {
196
195
  title: 'plugins/plugin-transcription/MicrophoneTranscription',
197
196
  render: DefaultStory,
198
197
  decorators: [
198
+ withTheme,
199
199
  withPluginManager({
200
200
  plugins: [
201
- ThemePlugin({ tx: defaultTx }),
202
- StorybookLayoutPlugin(),
203
201
  ClientPlugin({
204
202
  types: [TestItem, DataType.Person, DataType.Organization, Testing.DocumentType],
205
- onClientInitialized: async (_, client) => {
203
+ onClientInitialized: async ({ client }) => {
206
204
  await client.halo.createIdentity();
207
205
  await client.spaces.waitUntilReady();
208
206
  await client.spaces.default.waitUntilReady();
@@ -222,22 +220,27 @@ const meta: Meta<typeof DefaultStory> = {
222
220
  await seedTestData(client.spaces.default);
223
221
  },
224
222
  }),
225
- SpacePlugin(),
223
+ SpacePlugin({}),
224
+ IntentPlugin(),
226
225
  SettingsPlugin(),
226
+
227
+ // UI
228
+ ThemePlugin({ tx: defaultTx }),
227
229
  PreviewPlugin(),
228
- IntentPlugin(),
229
230
  TranscriptionPlugin(),
231
+ StorybookLayoutPlugin({}),
230
232
  ],
231
233
  fireEvents: [Events.SetupAppGraph],
232
234
  }),
233
- withTheme,
234
- withLayout({ fullscreen: true, classNames: 'justify-center' }),
235
235
  ],
236
- };
236
+ parameters: {
237
+ layout: 'column',
238
+ },
239
+ } satisfies Meta<typeof DefaultStory>;
237
240
 
238
241
  export default meta;
239
242
 
240
- type Story = StoryObj<typeof DefaultStory>;
243
+ type Story = StoryObj<typeof meta>;
241
244
 
242
245
  export const Default: Story = {
243
246
  args: {
@@ -253,19 +256,21 @@ export const Default: Story = {
253
256
  },
254
257
  };
255
258
 
256
- export const EntityExtraction: Story = {
257
- args: {
258
- detectSpeaking: true,
259
- entityExtraction: 'ner',
260
- transcriberConfig: TRANSCRIBER_CONFIG,
261
- recorderConfig: RECORDER_CONFIG,
262
- audioConstraints: {
263
- echoCancellation: true,
264
- noiseSuppression: true,
265
- autoGainControl: true,
266
- },
267
- },
268
- };
259
+ // NOTE: We are running out of free quota on hugging face entity extraction.
260
+ // TODO(mykola): Fix hugging face quota issues.
261
+ // export const EntityExtraction: Story = {
262
+ // args: {
263
+ // detectSpeaking: true,
264
+ // entityExtraction: 'ner',
265
+ // transcriberConfig: TRANSCRIBER_CONFIG,
266
+ // recorderConfig: RECORDER_CONFIG,
267
+ // audioConstraints: {
268
+ // echoCancellation: true,
269
+ // noiseSuppression: true,
270
+ // autoGainControl: true,
271
+ // },
272
+ // },
273
+ // };
269
274
 
270
275
  export const SpeechDetection: Story = {
271
276
  args: {