@dxos/plugin-transcription 0.8.4-main.28f8d3d → 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 (153) 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-UKUSJTQW.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-KJIQQHJB.mjs → chunk-COBQIZ6B.mjs} +78 -98
  8. package/dist/lib/browser/{chunk-KJIQQHJB.mjs.map → chunk-COBQIZ6B.mjs.map} +3 -3
  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-SREIAQSP.mjs → chunk-LCK3YRQF.mjs} +5 -5
  12. package/dist/lib/browser/chunk-LCK3YRQF.mjs.map +7 -0
  13. package/dist/lib/browser/{chunk-BJWYA2BU.mjs → chunk-ZS2FXKEF.mjs} +141 -168
  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-4MEJMKYQ.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-V77CN7XX.mjs → transcriber-NG5MRCP6.mjs} +7 -7
  23. package/dist/lib/browser/{transcriber-V77CN7XX.mjs.map → transcriber-NG5MRCP6.mjs.map} +1 -1
  24. package/dist/lib/browser/types/index.mjs +6 -8
  25. package/dist/lib/node-esm/{TranscriptContainer-GEUOOYHX.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-A33GNR44.mjs → chunk-A6SB234F.mjs} +78 -98
  29. package/dist/lib/node-esm/{chunk-A33GNR44.mjs.map → chunk-A6SB234F.mjs.map} +3 -3
  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-PHP7MCAY.mjs → chunk-N5Z4SKPH.mjs} +2 -2
  35. package/dist/lib/node-esm/{chunk-Y7PCJ34K.mjs → chunk-PEZRQYKO.mjs} +141 -168
  36. package/dist/lib/node-esm/chunk-PEZRQYKO.mjs.map +7 -0
  37. package/dist/lib/node-esm/{chunk-TM2ASOZA.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-EZUPNHUC.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-3SX3DNLV.mjs → transcriber-SMU7HD6F.mjs} +7 -7
  47. package/dist/lib/node-esm/{transcriber-3SX3DNLV.mjs.map → transcriber-SMU7HD6F.mjs.map} +1 -1
  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.map +1 -1
  54. package/dist/types/src/capabilities/index.d.ts +3 -2
  55. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  56. package/dist/types/src/capabilities/intent-resolver.d.ts +1 -1
  57. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  58. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  59. package/dist/types/src/capabilities/transcriber.d.ts +1 -1
  60. package/dist/types/src/components/Transcript/Transcript.d.ts +5 -5
  61. package/dist/types/src/components/Transcript/Transcript.d.ts.map +1 -1
  62. package/dist/types/src/components/Transcript/Transcript.stories.d.ts +31 -16
  63. package/dist/types/src/components/Transcript/Transcript.stories.d.ts.map +1 -1
  64. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts +3 -3
  65. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts.map +1 -1
  66. package/dist/types/src/components/Transcript/transcript-extension.d.ts +1 -5
  67. package/dist/types/src/components/Transcript/transcript-extension.d.ts.map +1 -1
  68. package/dist/types/src/components/TranscriptContainer.d.ts +2 -2
  69. package/dist/types/src/components/TranscriptContainer.d.ts.map +1 -1
  70. package/dist/types/src/components/stories/FileTranscription.stories.d.ts +8 -4
  71. package/dist/types/src/components/stories/FileTranscription.stories.d.ts.map +1 -1
  72. package/dist/types/src/components/stories/KeyWordRecognition.stories.d.ts +13 -6
  73. package/dist/types/src/components/stories/KeyWordRecognition.stories.d.ts.map +1 -1
  74. package/dist/types/src/components/stories/MicrophoneTranscription.stories.d.ts +10 -6
  75. package/dist/types/src/components/stories/MicrophoneTranscription.stories.d.ts.map +1 -1
  76. package/dist/types/src/components/stories/TranscriptionStory.d.ts +2 -1
  77. package/dist/types/src/components/stories/TranscriptionStory.d.ts.map +1 -1
  78. package/dist/types/src/functions/index.d.ts +3 -0
  79. package/dist/types/src/functions/index.d.ts.map +1 -0
  80. package/dist/types/src/functions/open.d.ts +7 -0
  81. package/dist/types/src/functions/open.d.ts.map +1 -0
  82. package/dist/types/src/functions/summarize.d.ts +11 -0
  83. package/dist/types/src/functions/summarize.d.ts.map +1 -0
  84. package/dist/types/src/hooks/useAudioFile.d.ts.map +1 -1
  85. package/dist/types/src/meta.d.ts +0 -1
  86. package/dist/types/src/meta.d.ts.map +1 -1
  87. package/dist/types/src/segments-normalization/normalization.d.ts +376 -331
  88. package/dist/types/src/segments-normalization/normalization.d.ts.map +1 -1
  89. package/dist/types/src/testing/testing.d.ts +1 -1
  90. package/dist/types/src/testing/testing.d.ts.map +1 -1
  91. package/dist/types/src/transcriber/transcription-manager.d.ts +3 -7
  92. package/dist/types/src/transcriber/transcription-manager.d.ts.map +1 -1
  93. package/dist/types/src/types/Transcript.d.ts +30 -0
  94. package/dist/types/src/types/Transcript.d.ts.map +1 -0
  95. package/dist/types/src/types/TranscriptAction.d.ts +23 -0
  96. package/dist/types/src/types/TranscriptAction.d.ts.map +1 -0
  97. package/dist/types/src/types/index.d.ts +2 -1
  98. package/dist/types/src/types/index.d.ts.map +1 -1
  99. package/dist/types/src/types/types.d.ts +0 -26
  100. package/dist/types/src/types/types.d.ts.map +1 -1
  101. package/dist/types/tsconfig.tsbuildinfo +1 -1
  102. package/package.json +61 -62
  103. package/src/TranscriptionPlugin.tsx +57 -53
  104. package/src/capabilities/blueprint-definition.ts +31 -0
  105. package/src/capabilities/capabilities.ts +3 -3
  106. package/src/capabilities/index.ts +1 -0
  107. package/src/capabilities/intent-resolver.ts +4 -10
  108. package/src/capabilities/react-surface.tsx +2 -2
  109. package/src/components/Transcript/Transcript.stories.tsx +32 -30
  110. package/src/components/Transcript/Transcript.tsx +17 -25
  111. package/src/components/Transcript/deprecated/OldTranscript.tsx +7 -7
  112. package/src/components/Transcript/transcript-extension.ts +2 -39
  113. package/src/components/TranscriptContainer.tsx +6 -6
  114. package/src/components/stories/FileTranscription.stories.tsx +42 -30
  115. package/src/components/stories/KeyWordRecognition.stories.tsx +16 -9
  116. package/src/components/stories/MicrophoneTranscription.stories.tsx +36 -32
  117. package/src/components/stories/TranscriptionStory.tsx +10 -6
  118. package/src/functions/index.ts +6 -0
  119. package/src/functions/open.ts +39 -0
  120. package/src/functions/summarize.ts +103 -0
  121. package/src/hooks/useAudioFile.ts +49 -53
  122. package/src/meta.ts +1 -3
  123. package/src/segments-normalization/normalization.test.ts +1 -1
  124. package/src/segments-normalization/normalization.ts +1 -1
  125. package/src/testing/testing.ts +5 -5
  126. package/src/transcriber/transcription-manager.ts +17 -38
  127. package/src/types/{schema.ts → Transcript.ts} +12 -9
  128. package/src/types/TranscriptAction.ts +24 -0
  129. package/src/types/index.ts +2 -1
  130. package/src/types/types.ts +0 -24
  131. package/dist/lib/browser/TranscriptContainer-FH7VAWYA.mjs +0 -11
  132. package/dist/lib/browser/chunk-5WH45KBY.mjs.map +0 -7
  133. package/dist/lib/browser/chunk-BJWYA2BU.mjs.map +0 -7
  134. package/dist/lib/browser/chunk-KWOOVS5G.mjs +0 -55
  135. package/dist/lib/browser/chunk-KWOOVS5G.mjs.map +0 -7
  136. package/dist/lib/browser/chunk-SREIAQSP.mjs.map +0 -7
  137. package/dist/lib/browser/intent-resolver-AOVQ7JI4.mjs +0 -30
  138. package/dist/lib/browser/intent-resolver-AOVQ7JI4.mjs.map +0 -7
  139. package/dist/lib/browser/react-surface-4MEJMKYQ.mjs.map +0 -7
  140. package/dist/lib/node-esm/chunk-IKQMDRY6.mjs +0 -56
  141. package/dist/lib/node-esm/chunk-IKQMDRY6.mjs.map +0 -7
  142. package/dist/lib/node-esm/chunk-TM2ASOZA.mjs.map +0 -7
  143. package/dist/lib/node-esm/chunk-XMUCZBP3.mjs.map +0 -7
  144. package/dist/lib/node-esm/chunk-Y7PCJ34K.mjs.map +0 -7
  145. package/dist/lib/node-esm/intent-resolver-P4XPIKQF.mjs +0 -31
  146. package/dist/lib/node-esm/intent-resolver-P4XPIKQF.mjs.map +0 -7
  147. package/dist/lib/node-esm/react-surface-EZUPNHUC.mjs.map +0 -7
  148. package/dist/types/src/types/schema.d.ts +0 -41
  149. package/dist/types/src/types/schema.d.ts.map +0 -1
  150. /package/dist/lib/browser/{TranscriptContainer-FH7VAWYA.mjs.map → TranscriptContainer-QN5WUMWJ.mjs.map} +0 -0
  151. /package/dist/lib/browser/{chunk-UKUSJTQW.mjs.map → chunk-77NWYLC6.mjs.map} +0 -0
  152. /package/dist/lib/node-esm/{TranscriptContainer-GEUOOYHX.mjs.map → TranscriptContainer-WCUWVVSD.mjs.map} +0 -0
  153. /package/dist/lib/node-esm/{chunk-PHP7MCAY.mjs.map → chunk-N5Z4SKPH.mjs.map} +0 -0
@@ -6,27 +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
23
  import { type SerializationModel } from '../../model';
25
- import { type TranscriptType } from '../../types';
24
+ import { type Transcript } from '../../types';
26
25
 
27
26
  import { transcript } from './transcript-extension';
28
27
 
29
- export const renderMarkdown =
28
+ // TODO(thure): Move this?
29
+ export const renderByline =
30
30
  (identities: Identity[]) =>
31
31
  (message: DataType.Message, index: number, debug = false): string[] => {
32
32
  if (message.sender.role === 'assistant') {
@@ -45,17 +45,18 @@ export const renderMarkdown =
45
45
  message.sender.identityDid;
46
46
  const blocks = message.blocks.filter((block) => block._tag === 'transcript');
47
47
  return [
48
+ // TODO(thure): Use an XML tag with the bits needed here.
48
49
  `###### ${name}` + (debug ? ` [${index}]:${message.id}` : ''),
49
50
  blocks.map((block) => block.text.trim()).join(' '),
50
51
  '',
51
52
  ];
52
53
  };
53
54
 
54
- export type TranscriptProps = ThemedClassName<{
55
- transcript?: TranscriptType;
55
+ export type TranscriptViewProps = ThemedClassName<{
56
56
  space?: Space;
57
+ transcript?: Transcript.Transcript;
57
58
  model: SerializationModel<DataType.Message>;
58
- // TODO(wittjosiah): Move to container.
59
+ // TODO(burdon): Are these still in use?
59
60
  attendableId?: string;
60
61
  ignoreAttention?: boolean;
61
62
  }>;
@@ -63,39 +64,30 @@ export type TranscriptProps = ThemedClassName<{
63
64
  /**
64
65
  * Transcript component implemented using the editor.
65
66
  */
66
- export const Transcript = ({
67
- classNames,
68
- transcript: object,
69
- space,
70
- model,
71
- attendableId,
72
- ignoreAttention,
73
- }: TranscriptProps) => {
67
+ export const TranscriptView = ({ classNames, space, transcript: object, model }: TranscriptViewProps) => {
74
68
  const { themeMode } = useThemeContext();
75
69
  const { parentRef } = useTextEditor(() => {
76
70
  return {
77
71
  extensions: [
78
72
  createBasicExtensions({ readOnly: true, lineWrapping: true, search: true }),
79
- createMarkdownExtensions({ themeMode }),
73
+ createMarkdownExtensions(),
80
74
  createThemeExtensions({ themeMode }),
81
75
  decorateMarkdown(),
82
- space && preview(),
76
+ preview(),
83
77
  transcript({
84
78
  model,
85
79
  started: object?.started ? new Date(object.started) : undefined,
86
- renderButton: createRenderer(({ onClick }) => (
87
- <IconButton icon='ph--arrow-line-down--regular' iconOnly label='Scroll to bottom' onClick={onClick} />
88
- )),
89
80
  }),
90
- ].filter(isNotFalsy),
81
+ autoScroll(),
82
+ ].filter(isTruthy),
91
83
  };
92
84
  }, [space, model]);
93
85
 
94
86
  return (
95
87
  <div
96
88
  ref={parentRef}
97
- className={mx('flex grow overflow-hidden', editorWidth, classNames)}
98
- 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. */}
99
91
  />
100
92
  );
101
93
  };
@@ -3,7 +3,7 @@
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
8
  import React, { type FC, type WheelEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';
9
9
  import { type OnResizeCallback, useResizeDetector } from 'react-resize-detector';
@@ -21,7 +21,7 @@ import {
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',
@@ -10,13 +10,13 @@ import { StackItem } from '@dxos/react-ui-stack';
10
10
  import { type DataType } from '@dxos/schema';
11
11
 
12
12
  import { useQueueModelAdapter } from '../hooks';
13
- import { type TranscriptType } from '../types';
13
+ import { type Transcript } from '../types';
14
14
 
15
- import { Transcript, renderMarkdown } from './Transcript';
15
+ import { TranscriptView, renderByline } from './Transcript';
16
16
 
17
17
  export type TranscriptionContainerProps = {
18
18
  role: string;
19
- transcript: TranscriptType;
19
+ transcript: Transcript.Transcript;
20
20
  };
21
21
 
22
22
  export const TranscriptionContainer = ({ transcript }: TranscriptionContainerProps) => {
@@ -24,11 +24,11 @@ export const TranscriptionContainer = ({ transcript }: TranscriptionContainerPro
24
24
  const space = getSpace(transcript);
25
25
  const members = useMembers(space?.key).map((member) => member.identity);
26
26
  const queue = useQueue<DataType.Message>(transcript.queue.dxn, { pollInterval: 1_000 });
27
- const model = useQueueModelAdapter(renderMarkdown(members), queue);
27
+ const model = useQueueModelAdapter(renderByline(members), queue);
28
28
 
29
29
  return (
30
- <StackItem.Content classNames='container-max-width pli-2'>
31
- <Transcript attendableId={attendableId} space={space} model={model} transcript={transcript} />
30
+ <StackItem.Content>
31
+ <TranscriptView attendableId={attendableId} space={space} model={model} transcript={transcript} />
32
32
  </StackItem.Content>
33
33
  );
34
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,17 +19,17 @@ 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
25
  import { Testing, seedTestData } from '@dxos/schema/testing';
27
- import { withLayout, withTheme } from '@dxos/storybook-utils';
28
26
 
29
27
  import { useAudioFile, useQueueModelAdapter, useTranscriber } from '../../hooks';
30
28
  import { MessageNormalizer, getActorId } from '../../segments-normalization';
31
29
  import { TestItem } from '../../testing';
32
30
  import { type MediaStreamRecorderParams, type TranscriberParams } from '../../transcriber';
33
31
  import { TranscriptionPlugin } from '../../TranscriptionPlugin';
34
- import { renderMarkdown } from '../Transcript';
32
+ import { renderByline } from '../Transcript';
35
33
 
36
34
  import { TranscriptionStory } from './TranscriptionStory';
37
35
  import { useIsSpeaking } from './useIsSpeaking';
@@ -68,7 +66,6 @@ const AudioFile = ({
68
66
 
69
67
  useEffect(() => {
70
68
  if (!audio) {
71
- log.warn('no audio');
72
69
  return;
73
70
  }
74
71
 
@@ -84,7 +81,7 @@ const AudioFile = ({
84
81
  const queueDxn = useMemo(() => createQueueDXN(), []);
85
82
  const queue = useMemo(() => new MemoryQueue<DataType.Message>(queueDxn), [queueDxn]);
86
83
 
87
- const model = useQueueModelAdapter(renderMarkdown([]), queue);
84
+ const model = useQueueModelAdapter(renderByline([]), queue);
88
85
  const handleSegments = useCallback<TranscriberParams['onSegments']>(
89
86
  async (blocks) => {
90
87
  void queue?.append([
@@ -147,10 +144,10 @@ const AudioFile = ({
147
144
  }, [normalizer]);
148
145
 
149
146
  const manageChunkRecording = () => {
150
- if (running && isSpeaking) {
147
+ if (running && isSpeaking && transcriber) {
151
148
  log.info('starting transcription');
152
149
  transcriber?.startChunksRecording();
153
- } else if (!isSpeaking || !running) {
150
+ } else if ((!isSpeaking || !running) && transcriber) {
154
151
  log.info('stopping transcription');
155
152
  transcriber?.stopChunksRecording();
156
153
  }
@@ -176,16 +173,23 @@ const AudioFile = ({
176
173
  manageChunkRecording();
177
174
  }, [isSpeaking, stream]);
178
175
 
179
- 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
+ );
180
185
  };
181
186
 
182
- const meta: Meta<typeof AudioFile> = {
187
+ const meta = {
183
188
  title: 'plugins/plugin-transcription/FileTranscription',
184
189
  decorators: [
190
+ withTheme,
185
191
  withPluginManager({
186
192
  plugins: [
187
- ThemePlugin({ tx: defaultTx }),
188
- StorybookLayoutPlugin(),
189
193
  ClientPlugin({
190
194
  types: [TestItem, DataType.Person, DataType.Organization, Testing.DocumentType],
191
195
  onClientInitialized: async ({ client }) => {
@@ -195,21 +199,28 @@ const meta: Meta<typeof AudioFile> = {
195
199
  await seedTestData(client.spaces.default);
196
200
  },
197
201
  }),
198
- SpacePlugin(),
202
+ SpacePlugin({}),
203
+ IntentPlugin(),
204
+
205
+ // UI
206
+ ThemePlugin({ tx: defaultTx }),
199
207
  SettingsPlugin(),
200
208
  PreviewPlugin(),
201
- IntentPlugin(),
202
209
  TranscriptionPlugin(),
210
+ StorybookLayoutPlugin({}),
203
211
  ],
204
212
  fireEvents: [Events.SetupAppGraph],
205
213
  }),
206
- withTheme,
207
- withLayout({ fullscreen: true, classNames: 'justify-center' }),
208
214
  ],
209
- };
215
+ parameters: {
216
+ layout: 'column',
217
+ },
218
+ } satisfies Meta;
210
219
 
211
220
  export default meta;
212
221
 
222
+ type Story = StoryObj<typeof meta>;
223
+
213
224
  const TRANSCRIBER_CONFIG = {
214
225
  transcribeAfterChunksAmount: 100,
215
226
  prefixBufferChunksAmount: 50,
@@ -232,15 +243,16 @@ export const Default: StoryObj<typeof AudioFile> = {
232
243
  },
233
244
  };
234
245
 
235
- export const WithSentenceNormalization: StoryObj<typeof AudioFile> = {
236
- render: AudioFile,
237
- args: {
238
- detectSpeaking: true,
239
- normalizeSentences: true,
240
- // https://learnenglish.britishcouncil.org/general-english/audio-zone/living-london
241
- audioUrl: 'https://dxos.network/audio-london.m4a',
242
- // textUrl: 'https://dxos.network/audio-london.txt',
243
- transcriberConfig: TRANSCRIBER_CONFIG,
244
- recorderConfig: RECORDER_CONFIG,
245
- },
246
- };
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
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
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,16 +26,16 @@ 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
32
  import { Testing, seedTestData } from '@dxos/schema/testing';
34
- import { withLayout, withTheme } from '@dxos/storybook-utils';
35
33
 
36
34
  import { useAudioTrack, useQueueModelAdapter, useTranscriber } from '../../hooks';
37
35
  import { TestItem } from '../../testing';
38
36
  import { type MediaStreamRecorderParams, type TranscriberParams } from '../../transcriber';
39
37
  import { TranscriptionPlugin } from '../../TranscriptionPlugin';
40
- import { renderMarkdown } from '../Transcript';
38
+ import { renderByline } from '../Transcript';
41
39
 
42
40
  import { TranscriptionStory } from './TranscriptionStory';
43
41
  import { useIsSpeaking } from './useIsSpeaking';
@@ -78,7 +76,7 @@ const DefaultStory = ({
78
76
  // TODO(dmaretskyi): Use space.queues.create() instead.
79
77
  const queueDxn = useMemo(() => createQueueDXN(), []);
80
78
  const queue = useMemo(() => new MemoryQueue<DataType.Message>(queueDxn), [queueDxn]);
81
- const model = useQueueModelAdapter(renderMarkdown([]), queue);
79
+ const model = useQueueModelAdapter(renderByline([]), queue);
82
80
  const space = useSpace();
83
81
 
84
82
  useEffect(() => {
@@ -101,9 +99,9 @@ const DefaultStory = ({
101
99
  if (entityExtraction === 'ner') {
102
100
  // Init model loading. Takes time.
103
101
  void getNer();
104
- extractionFunction = extractionNerFn;
102
+ extractionFunction = extractionNerFunction;
105
103
  } else if (entityExtraction === 'llm') {
106
- extractionFunction = extractionAnthropicFn;
104
+ extractionFunction = extractionAnthropicFunction;
107
105
  objects = space.db
108
106
  .query(
109
107
  Filter.or(
@@ -193,14 +191,13 @@ const DefaultStory = ({
193
191
  return <TranscriptionStory model={model} running={running} onRunningChange={setRunning} />;
194
192
  };
195
193
 
196
- const meta: Meta<typeof DefaultStory> = {
194
+ const meta = {
197
195
  title: 'plugins/plugin-transcription/MicrophoneTranscription',
198
196
  render: DefaultStory,
199
197
  decorators: [
198
+ withTheme,
200
199
  withPluginManager({
201
200
  plugins: [
202
- ThemePlugin({ tx: defaultTx }),
203
- StorybookLayoutPlugin(),
204
201
  ClientPlugin({
205
202
  types: [TestItem, DataType.Person, DataType.Organization, Testing.DocumentType],
206
203
  onClientInitialized: async ({ client }) => {
@@ -223,22 +220,27 @@ const meta: Meta<typeof DefaultStory> = {
223
220
  await seedTestData(client.spaces.default);
224
221
  },
225
222
  }),
226
- SpacePlugin(),
223
+ SpacePlugin({}),
224
+ IntentPlugin(),
227
225
  SettingsPlugin(),
226
+
227
+ // UI
228
+ ThemePlugin({ tx: defaultTx }),
228
229
  PreviewPlugin(),
229
- IntentPlugin(),
230
230
  TranscriptionPlugin(),
231
+ StorybookLayoutPlugin({}),
231
232
  ],
232
233
  fireEvents: [Events.SetupAppGraph],
233
234
  }),
234
- withTheme,
235
- withLayout({ fullscreen: true, classNames: 'justify-center' }),
236
235
  ],
237
- };
236
+ parameters: {
237
+ layout: 'column',
238
+ },
239
+ } satisfies Meta<typeof DefaultStory>;
238
240
 
239
241
  export default meta;
240
242
 
241
- type Story = StoryObj<typeof DefaultStory>;
243
+ type Story = StoryObj<typeof meta>;
242
244
 
243
245
  export const Default: Story = {
244
246
  args: {
@@ -254,19 +256,21 @@ export const Default: Story = {
254
256
  },
255
257
  };
256
258
 
257
- export const EntityExtraction: Story = {
258
- args: {
259
- detectSpeaking: true,
260
- entityExtraction: 'ner',
261
- transcriberConfig: TRANSCRIBER_CONFIG,
262
- recorderConfig: RECORDER_CONFIG,
263
- audioConstraints: {
264
- echoCancellation: true,
265
- noiseSuppression: true,
266
- autoGainControl: true,
267
- },
268
- },
269
- };
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
+ // };
270
274
 
271
275
  export const SpeechDetection: Story = {
272
276
  args: {
@@ -12,14 +12,15 @@ import { ScrollContainer } from '@dxos/react-ui-components';
12
12
  import { type DataType } from '@dxos/schema';
13
13
 
14
14
  import { type SerializationModel } from '../../model';
15
- import { Transcript } from '../Transcript';
15
+ import { TranscriptView } from '../Transcript';
16
16
 
17
17
  export const TranscriptionStory: FC<{
18
18
  model: SerializationModel<DataType.Message>;
19
+ disabled?: boolean;
19
20
  running: boolean;
20
21
  onRunningChange: Dispatch<SetStateAction<boolean>>;
21
- audioRef?: RefObject<HTMLAudioElement>;
22
- }> = ({ model, running, onRunningChange, audioRef }) => {
22
+ audioRef?: RefObject<HTMLAudioElement | null>;
23
+ }> = ({ model, running, onRunningChange, audioRef, disabled }) => {
23
24
  const space = useSpace();
24
25
 
25
26
  return (
@@ -28,14 +29,17 @@ export const TranscriptionStory: FC<{
28
29
  <Toolbar.Root>
29
30
  <IconButton
30
31
  iconOnly
32
+ disabled={disabled}
31
33
  icon={running ? 'ph--pause--regular' : 'ph--play--regular'}
32
34
  label={running ? 'Pause' : 'Play'}
33
35
  onClick={() => onRunningChange((running) => !running)}
34
36
  />
35
37
  </Toolbar.Root>
36
- <ScrollContainer>
37
- <Transcript space={space} model={model} attendableId='story' />
38
- </ScrollContainer>
38
+ <ScrollContainer.Root pin>
39
+ <ScrollContainer.Content>
40
+ <TranscriptView space={space} model={model} />
41
+ </ScrollContainer.Content>
42
+ </ScrollContainer.Root>
39
43
  </div>
40
44
  );
41
45
  };