@dxos/plugin-transcription 0.8.4-main.abd8ff62ef → 0.8.4-main.bc2380dfbc

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/neutral/TranscriptionArticle-L6XANBH5.mjs +54 -0
  4. package/dist/lib/neutral/TranscriptionArticle-L6XANBH5.mjs.map +7 -0
  5. package/dist/lib/neutral/TranscriptionPlugin.mjs +34 -0
  6. package/dist/lib/neutral/TranscriptionPlugin.mjs.map +7 -0
  7. package/dist/lib/neutral/TranscriptionPlugin.node.mjs +25 -0
  8. package/dist/lib/neutral/TranscriptionPlugin.node.mjs.map +7 -0
  9. package/dist/lib/neutral/TranscriptionPlugin.workerd.mjs +21 -0
  10. package/dist/lib/neutral/TranscriptionPlugin.workerd.mjs.map +7 -0
  11. package/dist/lib/neutral/blueprint-definition-HOXDG5QP.mjs +15 -0
  12. package/dist/lib/neutral/blueprint-definition-HOXDG5QP.mjs.map +7 -0
  13. package/dist/lib/neutral/blueprints/index.mjs +8 -0
  14. package/dist/lib/neutral/capabilities/index.mjs +17 -0
  15. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  16. package/dist/lib/{browser/chunk-NQQK7UZQ.mjs → neutral/chunk-2O3ONPRZ.mjs} +94 -40
  17. package/dist/lib/neutral/chunk-2O3ONPRZ.mjs.map +7 -0
  18. package/dist/lib/{browser/chunk-L4HW35L6.mjs → neutral/chunk-5FXU36PN.mjs} +19 -10
  19. package/dist/lib/neutral/chunk-5FXU36PN.mjs.map +7 -0
  20. package/dist/lib/neutral/chunk-GL4RS533.mjs +22 -0
  21. package/dist/lib/neutral/chunk-GL4RS533.mjs.map +7 -0
  22. package/dist/lib/{browser/index.mjs → neutral/chunk-ILSA3DU7.mjs} +32 -70
  23. package/dist/lib/neutral/chunk-ILSA3DU7.mjs.map +7 -0
  24. package/dist/lib/neutral/chunk-Y5EQAFFR.mjs +8 -0
  25. package/dist/lib/neutral/chunk-Y5EQAFFR.mjs.map +7 -0
  26. package/dist/lib/{browser/blueprints/index.mjs → neutral/chunk-ZR4BH4QQ.mjs} +4 -5
  27. package/dist/lib/neutral/chunk-ZR4BH4QQ.mjs.map +7 -0
  28. package/dist/lib/{browser → neutral}/components/index.mjs +83 -18
  29. package/dist/lib/neutral/components/index.mjs.map +7 -0
  30. package/dist/lib/neutral/containers/index.mjs +9 -0
  31. package/dist/lib/neutral/containers/index.mjs.map +7 -0
  32. package/dist/lib/{browser/create-ULR5DPJZ.mjs → neutral/create-RRWRIUSR.mjs} +4 -4
  33. package/dist/lib/neutral/create-RRWRIUSR.mjs.map +7 -0
  34. package/dist/lib/{browser → neutral}/hooks/index.mjs +5 -3
  35. package/dist/lib/neutral/index.mjs +44 -0
  36. package/dist/lib/neutral/meta.json +1 -0
  37. package/dist/lib/neutral/meta.mjs +8 -0
  38. package/dist/lib/{browser/open-SFMPLZVW.mjs → neutral/open-CVMX5KXZ.mjs} +5 -5
  39. package/dist/lib/neutral/open-CVMX5KXZ.mjs.map +7 -0
  40. package/dist/lib/neutral/operation-handler-NXMOAXFP.mjs +13 -0
  41. package/dist/lib/neutral/operation-handler-NXMOAXFP.mjs.map +7 -0
  42. package/dist/lib/neutral/operations/index.mjs +8 -0
  43. package/dist/lib/neutral/plugin.mjs +16 -0
  44. package/dist/lib/neutral/plugin.mjs.map +7 -0
  45. package/dist/lib/neutral/react-surface-YMZRO242.mjs +26 -0
  46. package/dist/lib/neutral/react-surface-YMZRO242.mjs.map +7 -0
  47. package/dist/lib/{browser/summarize-BDFX4APN.mjs → neutral/summarize-LA7TRW7A.mjs} +5 -5
  48. package/dist/lib/neutral/summarize-LA7TRW7A.mjs.map +7 -0
  49. package/dist/lib/neutral/text-content-F4ASMITB.mjs +30 -0
  50. package/dist/lib/neutral/text-content-F4ASMITB.mjs.map +7 -0
  51. package/dist/lib/neutral/transcriber-ABHATBVH.mjs +59 -0
  52. package/dist/lib/neutral/transcriber-ABHATBVH.mjs.map +7 -0
  53. package/dist/lib/neutral/translations.mjs +24 -0
  54. package/dist/lib/neutral/translations.mjs.map +7 -0
  55. package/dist/lib/neutral/types/index.mjs +10 -0
  56. package/dist/lib/neutral/types/index.mjs.map +7 -0
  57. package/dist/types/src/TranscriptionPlugin.d.ts.map +1 -1
  58. package/dist/types/src/TranscriptionPlugin.node.d.ts.map +1 -1
  59. package/dist/types/src/TranscriptionPlugin.workerd.d.ts +4 -0
  60. package/dist/types/src/TranscriptionPlugin.workerd.d.ts.map +1 -0
  61. package/dist/types/src/capabilities/index.d.ts +5 -1
  62. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  63. package/dist/types/src/capabilities/text-content.d.ts +8 -0
  64. package/dist/types/src/capabilities/text-content.d.ts.map +1 -0
  65. package/dist/types/src/components/Oracle/Oracle.d.ts +24 -0
  66. package/dist/types/src/components/Oracle/Oracle.d.ts.map +1 -0
  67. package/dist/types/src/components/Oracle/Oracle.stories.d.ts +32 -0
  68. package/dist/types/src/components/Oracle/Oracle.stories.d.ts.map +1 -0
  69. package/dist/types/src/components/Oracle/index.d.ts +2 -0
  70. package/dist/types/src/components/Oracle/index.d.ts.map +1 -0
  71. package/dist/types/src/components/Transcription/Transcription.d.ts +0 -5
  72. package/dist/types/src/components/Transcription/Transcription.d.ts.map +1 -1
  73. package/dist/types/src/components/Transcription/Transcription.stories.d.ts +13 -16
  74. package/dist/types/src/components/Transcription/Transcription.stories.d.ts.map +1 -1
  75. package/dist/types/src/components/index.d.ts +1 -0
  76. package/dist/types/src/components/index.d.ts.map +1 -1
  77. package/dist/types/src/containers/TranscriptionArticle/TranscriptionArticle.d.ts +6 -0
  78. package/dist/types/src/containers/TranscriptionArticle/TranscriptionArticle.d.ts.map +1 -0
  79. package/dist/types/src/containers/TranscriptionArticle/TranscriptionArticle.stories.d.ts +28 -0
  80. package/dist/types/src/containers/TranscriptionArticle/TranscriptionArticle.stories.d.ts.map +1 -0
  81. package/dist/types/src/containers/TranscriptionArticle/index.d.ts +2 -0
  82. package/dist/types/src/containers/TranscriptionArticle/index.d.ts.map +1 -0
  83. package/dist/types/src/containers/index.d.ts +1 -1
  84. package/dist/types/src/containers/index.d.ts.map +1 -1
  85. package/dist/types/src/enrichment/conversation.test.d.ts +2 -0
  86. package/dist/types/src/enrichment/conversation.test.d.ts.map +1 -0
  87. package/dist/types/src/enrichment/operations.d.ts +200 -0
  88. package/dist/types/src/enrichment/operations.d.ts.map +1 -0
  89. package/dist/types/src/enrichment/parser.d.ts +26 -0
  90. package/dist/types/src/enrichment/parser.d.ts.map +1 -0
  91. package/dist/types/src/enrichment/stream-simulator.d.ts +38 -0
  92. package/dist/types/src/enrichment/stream-simulator.d.ts.map +1 -0
  93. package/dist/types/src/hooks/index.d.ts +2 -1
  94. package/dist/types/src/hooks/index.d.ts.map +1 -1
  95. package/dist/types/src/hooks/useFeedModelAdapter.d.ts +12 -0
  96. package/dist/types/src/hooks/useFeedModelAdapter.d.ts.map +1 -0
  97. package/dist/types/src/hooks/useTranscriptionRecording.d.ts +12 -0
  98. package/dist/types/src/hooks/useTranscriptionRecording.d.ts.map +1 -0
  99. package/dist/types/src/index.d.ts +3 -4
  100. package/dist/types/src/index.d.ts.map +1 -1
  101. package/dist/types/src/normalization/message-normalizer.d.ts +7 -5
  102. package/dist/types/src/normalization/message-normalizer.d.ts.map +1 -1
  103. package/dist/types/src/normalization/normalization.d.ts +180 -1
  104. package/dist/types/src/normalization/normalization.d.ts.map +1 -1
  105. package/dist/types/src/operations/create.d.ts +2 -2
  106. package/dist/types/src/operations/create.d.ts.map +1 -1
  107. package/dist/types/src/operations/index.d.ts +0 -1
  108. package/dist/types/src/operations/index.d.ts.map +1 -1
  109. package/dist/types/src/operations/open.d.ts +2 -2
  110. package/dist/types/src/operations/open.d.ts.map +1 -1
  111. package/dist/types/src/operations/summarize.d.ts +2 -2
  112. package/dist/types/src/operations/summarize.d.ts.map +1 -1
  113. package/dist/types/src/plugin.d.ts +4 -0
  114. package/dist/types/src/plugin.d.ts.map +1 -0
  115. package/dist/types/src/stories/FileTranscription.stories.d.ts +5 -3
  116. package/dist/types/src/stories/FileTranscription.stories.d.ts.map +1 -1
  117. package/dist/types/src/stories/KeyWordRecognition.stories.d.ts.map +1 -1
  118. package/dist/types/src/stories/{MicrophoneTranscription.stories.d.ts → LiveTranscription.stories.d.ts} +4 -4
  119. package/dist/types/src/stories/LiveTranscription.stories.d.ts.map +1 -0
  120. package/dist/types/src/stories/TranscriptionStory.d.ts +11 -13
  121. package/dist/types/src/stories/TranscriptionStory.d.ts.map +1 -1
  122. package/dist/types/src/stories/common.d.ts +46 -0
  123. package/dist/types/src/stories/common.d.ts.map +1 -0
  124. package/dist/types/src/testing/testing.d.ts +5 -6
  125. package/dist/types/src/testing/testing.d.ts.map +1 -1
  126. package/dist/types/src/transcriber/transcriber.d.ts +4 -4
  127. package/dist/types/src/transcriber/transcriber.d.ts.map +1 -1
  128. package/dist/types/src/transcriber/transcription-manager.d.ts +6 -4
  129. package/dist/types/src/transcriber/transcription-manager.d.ts.map +1 -1
  130. package/dist/types/src/translations.d.ts +2 -0
  131. package/dist/types/src/translations.d.ts.map +1 -1
  132. package/dist/types/src/{operations/definitions.d.ts → types/TranscriptOperation.d.ts} +1 -1
  133. package/dist/types/src/types/TranscriptOperation.d.ts.map +1 -0
  134. package/dist/types/src/types/TranscriptionCapabilities.d.ts +17 -0
  135. package/dist/types/src/types/TranscriptionCapabilities.d.ts.map +1 -0
  136. package/dist/types/src/types/index.d.ts +2 -2
  137. package/dist/types/src/types/index.d.ts.map +1 -1
  138. package/dist/types/tsconfig.tsbuildinfo +1 -1
  139. package/package.json +125 -94
  140. package/src/TranscriptionPlugin.node.ts +3 -31
  141. package/src/TranscriptionPlugin.test.ts +3 -3
  142. package/src/TranscriptionPlugin.tsx +3 -31
  143. package/src/TranscriptionPlugin.workerd.ts +18 -0
  144. package/src/blueprints/transcription-blueprint.ts +1 -1
  145. package/src/capabilities/index.ts +1 -0
  146. package/src/capabilities/react-surface.tsx +2 -2
  147. package/src/capabilities/text-content.ts +33 -0
  148. package/src/capabilities/transcriber.ts +1 -1
  149. package/src/components/Oracle/Oracle.stories.tsx +147 -0
  150. package/src/components/Oracle/Oracle.tsx +141 -0
  151. package/src/components/Oracle/index.ts +5 -0
  152. package/src/components/Transcription/Transcription.stories.tsx +116 -150
  153. package/src/components/Transcription/Transcription.tsx +7 -13
  154. package/src/components/index.ts +1 -0
  155. package/src/containers/TranscriptionArticle/TranscriptionArticle.stories.tsx +61 -0
  156. package/src/containers/TranscriptionArticle/TranscriptionArticle.tsx +62 -0
  157. package/src/containers/TranscriptionArticle/index.ts +5 -0
  158. package/src/containers/index.ts +1 -1
  159. package/src/enrichment/conversation.test.ts +162 -0
  160. package/src/enrichment/conversation.txt +23 -0
  161. package/src/enrichment/operations.ts +268 -0
  162. package/src/enrichment/parser.ts +101 -0
  163. package/src/enrichment/stream-simulator.ts +125 -0
  164. package/src/hooks/index.ts +2 -1
  165. package/src/hooks/useFeedModelAdapter.ts +46 -0
  166. package/src/hooks/useTranscriptionRecording.ts +87 -0
  167. package/src/index.ts +3 -9
  168. package/src/normalization/message-normalizer.ts +25 -13
  169. package/src/normalization/normalization.test.ts +3 -37
  170. package/src/normalization/normalization.ts +6 -7
  171. package/src/operations/create.ts +2 -2
  172. package/src/operations/index.ts +0 -2
  173. package/src/operations/open.ts +2 -2
  174. package/src/operations/summarize.ts +3 -3
  175. package/src/plugin.ts +11 -0
  176. package/src/stories/FileTranscription.stories.tsx +49 -175
  177. package/src/stories/KeyWordRecognition.stories.tsx +7 -9
  178. package/src/stories/LiveTranscription.stories.tsx +153 -0
  179. package/src/stories/TranscriptionStory.tsx +32 -24
  180. package/src/stories/common.ts +233 -0
  181. package/src/testing/testing.ts +20 -27
  182. package/src/transcriber/transcriber.test.ts +9 -9
  183. package/src/transcriber/transcriber.ts +18 -14
  184. package/src/transcriber/transcription-manager.ts +14 -9
  185. package/src/translations.ts +3 -0
  186. package/src/{operations/definitions.ts → types/TranscriptOperation.ts} +2 -0
  187. package/src/types/TranscriptionCapabilities.ts +39 -0
  188. package/src/types/index.ts +2 -2
  189. package/dist/lib/browser/blueprints/index.mjs.map +0 -7
  190. package/dist/lib/browser/chunk-3L623EVL.mjs +0 -17
  191. package/dist/lib/browser/chunk-3L623EVL.mjs.map +0 -7
  192. package/dist/lib/browser/chunk-L4HW35L6.mjs.map +0 -7
  193. package/dist/lib/browser/chunk-NQQK7UZQ.mjs.map +0 -7
  194. package/dist/lib/browser/components/index.mjs.map +0 -7
  195. package/dist/lib/browser/create-ULR5DPJZ.mjs.map +0 -7
  196. package/dist/lib/browser/index.mjs.map +0 -7
  197. package/dist/lib/browser/meta.json +0 -1
  198. package/dist/lib/browser/open-SFMPLZVW.mjs.map +0 -7
  199. package/dist/lib/browser/operations/index.mjs +0 -13
  200. package/dist/lib/browser/operations/index.mjs.map +0 -7
  201. package/dist/lib/browser/summarize-BDFX4APN.mjs.map +0 -7
  202. package/dist/lib/browser/types/index.mjs +0 -10
  203. package/dist/lib/node-esm/blueprints/index.mjs +0 -32
  204. package/dist/lib/node-esm/blueprints/index.mjs.map +0 -7
  205. package/dist/lib/node-esm/chunk-7UIZWCD4.mjs +0 -112
  206. package/dist/lib/node-esm/chunk-7UIZWCD4.mjs.map +0 -7
  207. package/dist/lib/node-esm/chunk-C3AE2NYJ.mjs +0 -181
  208. package/dist/lib/node-esm/chunk-C3AE2NYJ.mjs.map +0 -7
  209. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
  210. package/dist/lib/node-esm/chunk-KO3IOD2O.mjs +0 -19
  211. package/dist/lib/node-esm/chunk-KO3IOD2O.mjs.map +0 -7
  212. package/dist/lib/node-esm/chunk-MQK4R6GT.mjs +0 -245
  213. package/dist/lib/node-esm/chunk-MQK4R6GT.mjs.map +0 -7
  214. package/dist/lib/node-esm/chunk-O533NBMK.mjs +0 -37
  215. package/dist/lib/node-esm/chunk-O533NBMK.mjs.map +0 -7
  216. package/dist/lib/node-esm/components/index.mjs +0 -208
  217. package/dist/lib/node-esm/components/index.mjs.map +0 -7
  218. package/dist/lib/node-esm/create-A5QHNWRF.mjs +0 -22
  219. package/dist/lib/node-esm/create-A5QHNWRF.mjs.map +0 -7
  220. package/dist/lib/node-esm/hooks/index.mjs +0 -18
  221. package/dist/lib/node-esm/index.mjs +0 -474
  222. package/dist/lib/node-esm/index.mjs.map +0 -7
  223. package/dist/lib/node-esm/meta.json +0 -1
  224. package/dist/lib/node-esm/open-OI24QNTO.mjs +0 -28
  225. package/dist/lib/node-esm/open-OI24QNTO.mjs.map +0 -7
  226. package/dist/lib/node-esm/operations/index.mjs +0 -14
  227. package/dist/lib/node-esm/operations/index.mjs.map +0 -7
  228. package/dist/lib/node-esm/summarize-3UNN24I2.mjs +0 -79
  229. package/dist/lib/node-esm/summarize-3UNN24I2.mjs.map +0 -7
  230. package/dist/lib/node-esm/types/index.mjs +0 -11
  231. package/dist/types/src/containers/TranscriptionContainer/TranscriptionContainer.d.ts +0 -6
  232. package/dist/types/src/containers/TranscriptionContainer/TranscriptionContainer.d.ts.map +0 -1
  233. package/dist/types/src/containers/TranscriptionContainer/index.d.ts +0 -2
  234. package/dist/types/src/containers/TranscriptionContainer/index.d.ts.map +0 -1
  235. package/dist/types/src/hooks/useQueueModelAdapter.d.ts +0 -8
  236. package/dist/types/src/hooks/useQueueModelAdapter.d.ts.map +0 -1
  237. package/dist/types/src/operations/definitions.d.ts.map +0 -1
  238. package/dist/types/src/stories/MicrophoneTranscription.stories.d.ts.map +0 -1
  239. package/dist/types/src/stories/useIsSpeaking.d.ts +0 -3
  240. package/dist/types/src/stories/useIsSpeaking.d.ts.map +0 -1
  241. package/dist/types/src/types/capabilities.d.ts +0 -19
  242. package/dist/types/src/types/capabilities.d.ts.map +0 -1
  243. package/dist/types/src/types/defs.d.ts +0 -5
  244. package/dist/types/src/types/defs.d.ts.map +0 -1
  245. package/src/containers/TranscriptionContainer/TranscriptionContainer.tsx +0 -35
  246. package/src/containers/TranscriptionContainer/index.ts +0 -5
  247. package/src/hooks/useQueueModelAdapter.ts +0 -66
  248. package/src/stories/MicrophoneTranscription.stories.tsx +0 -264
  249. package/src/stories/useIsSpeaking.tsx +0 -40
  250. package/src/types/capabilities.ts +0 -39
  251. package/src/types/defs.ts +0 -9
  252. /package/dist/lib/{browser/hooks → neutral/blueprints}/index.mjs.map +0 -0
  253. /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs +0 -0
  254. /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs.map +0 -0
  255. /package/dist/lib/{browser → neutral}/chunk-KAEXRX3Y.mjs +0 -0
  256. /package/dist/lib/{browser → neutral}/chunk-KAEXRX3Y.mjs.map +0 -0
  257. /package/dist/lib/{browser → neutral}/chunk-MLRJWAQH.mjs +0 -0
  258. /package/dist/lib/{browser → neutral}/chunk-MLRJWAQH.mjs.map +0 -0
  259. /package/dist/lib/{browser/types → neutral/hooks}/index.mjs.map +0 -0
  260. /package/dist/lib/{node-esm/hooks → neutral}/index.mjs.map +0 -0
  261. /package/dist/lib/{node-esm/chunk-HSLMI22Q.mjs.map → neutral/meta.mjs.map} +0 -0
  262. /package/dist/lib/{node-esm/types → neutral/operations}/index.mjs.map +0 -0
@@ -0,0 +1,141 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { Primitive } from '@radix-ui/react-primitive';
6
+ import { Slot } from '@radix-ui/react-slot';
7
+ import React, { type PropsWithChildren, createContext, useContext, useMemo } from 'react';
8
+
9
+ import '@dxos/react-ui-graph/styles/graph.css';
10
+ import { type ThemedClassName } from '@dxos/react-ui';
11
+ import { TextBlock } from '@dxos/react-ui-components';
12
+ import { SVG } from '@dxos/react-ui-graph';
13
+ import { composableProps, mx, slottable } from '@dxos/ui-theme';
14
+ import { type SlottableProps } from '@dxos/ui-types';
15
+
16
+ //
17
+ // Context
18
+ //
19
+
20
+ type OracleContextValue = {
21
+ /** Latest utterance / phrase rendered by `Oracle.Text`. */
22
+ text: string;
23
+ /** Per-character reveal delay in ms for the streaming TextBlock. */
24
+ delay: number;
25
+ };
26
+
27
+ const OracleContext = createContext<OracleContextValue | null>(null);
28
+
29
+ const useOracleContext = (consumer: string): OracleContextValue => {
30
+ const ctx = useContext(OracleContext);
31
+ if (!ctx) {
32
+ throw new Error(`<Oracle.${consumer}> must be rendered inside <Oracle.Root>`);
33
+ }
34
+ return ctx;
35
+ };
36
+
37
+ //
38
+ // Root (headless — provides context only, renders no DOM)
39
+ //
40
+
41
+ export type OracleRootProps = PropsWithChildren<{
42
+ /** Latest utterance / phrase rendered by `Oracle.Text`. */
43
+ text?: string;
44
+ /** Per-character reveal delay in ms for the streaming TextBlock. @default 12 */
45
+ delay?: number;
46
+ }>;
47
+
48
+ /**
49
+ * Oracle surface root. Headless — provides text/delay context to subcomponents and renders
50
+ * no DOM of its own. Wrap subcomponents in `<Oracle.Content>` for the standard relative
51
+ * stacking layout, or place `<Oracle.Canvas>` / `<Oracle.Text>` inside any positioned
52
+ * container of your choice.
53
+ */
54
+ const Root = ({ children, text = '', delay = 12 }: OracleRootProps) => {
55
+ const value = useMemo(() => ({ text, delay }), [text, delay]);
56
+ return <OracleContext.Provider value={value}>{children}</OracleContext.Provider>;
57
+ };
58
+
59
+ //
60
+ // Content
61
+ //
62
+
63
+ export type OracleContentProps = SlottableProps;
64
+
65
+ /**
66
+ * Positioned container that stacks `Oracle.Canvas` (the SVG graph viewport, absolutely
67
+ * positioned to fill) under `Oracle.Text` (the centered overlay). Use this for the standard
68
+ * Oracle layout; skip it if you need custom positioning.
69
+ *
70
+ * Pass `asChild` to merge the positioning + sizing onto a parent slot (e.g. `Panel.Content`).
71
+ */
72
+ const Content = slottable<HTMLDivElement>(({ children, asChild, ...props }, forwardedRef) => {
73
+ const Comp = asChild ? Slot : Primitive.div;
74
+ return (
75
+ <Comp {...composableProps(props, { classNames: 'relative dx-expander' })} ref={forwardedRef}>
76
+ {children}
77
+ </Comp>
78
+ );
79
+ });
80
+
81
+ Content.displayName = 'Oracle.Content';
82
+
83
+ //
84
+ // Canvas
85
+ //
86
+
87
+ export type OracleCanvasProps = ThemedClassName<PropsWithChildren<{}>>;
88
+
89
+ /**
90
+ * SVG graph viewport. Renders sensible defaults (markers, grid, pan/zoom, empty graph)
91
+ * when no children are supplied; consumers can replace those by passing their own
92
+ * SVG.* children (e.g. a populated `<SVG.Graph model={…} />`).
93
+ */
94
+ const Canvas = ({ classNames, children }: OracleCanvasProps) => (
95
+ <SVG.Root centered classNames={mx('absolute inset-0', classNames)}>
96
+ {children ?? (
97
+ <>
98
+ <SVG.Markers />
99
+ <SVG.Grid />
100
+ <SVG.Zoom extent={[1 / 4, 4]}>
101
+ <SVG.Graph />
102
+ </SVG.Zoom>
103
+ </>
104
+ )}
105
+ </SVG.Root>
106
+ );
107
+
108
+ //
109
+ // Text
110
+ //
111
+
112
+ export type OracleTextProps = ThemedClassName<{}>;
113
+
114
+ /**
115
+ * Centered text overlay. Reads `text` and `delay` from `Oracle.Root` context and renders
116
+ * a streaming `TextBlock` floating above the canvas. Pointer-events are disabled so the
117
+ * underlying graph remains interactive.
118
+ */
119
+ const Text = ({ classNames }: OracleTextProps) => {
120
+ const { text, delay } = useOracleContext('Text');
121
+ return (
122
+ <div className='pointer-events-none absolute inset-0 flex items-center justify-center'>
123
+ <TextBlock
124
+ classNames={mx('max-w-xl px-6 py-4 text-center text-2xl font-medium text-balance text-subdued', classNames)}
125
+ text={text}
126
+ delay={delay}
127
+ />
128
+ </div>
129
+ );
130
+ };
131
+
132
+ //
133
+ // Namespace
134
+ //
135
+
136
+ export const Oracle = {
137
+ Root,
138
+ Content,
139
+ Canvas,
140
+ Text,
141
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './Oracle';
@@ -5,29 +5,22 @@
5
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import * as Effect from 'effect/Effect';
7
7
  import type * as Types from 'effect/Types';
8
- import React, { type FC, useEffect, useMemo, useState } from 'react';
8
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
9
9
 
10
10
  import { withPluginManager } from '@dxos/app-framework/testing';
11
- import { Key } from '@dxos/echo';
12
- import { ClientPlugin } from '@dxos/plugin-client';
13
- import { initializeIdentity } from '@dxos/plugin-client/testing';
14
- import { PreviewPlugin } from '@dxos/plugin-preview';
11
+ import { Feed, Filter, Obj, Query } from '@dxos/echo';
12
+ import { ClientPlugin, initializeIdentity } from '@dxos/plugin-client/testing';
13
+ import { PreviewPlugin } from '@dxos/plugin-preview/testing';
15
14
  import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
16
15
  import { random } from '@dxos/random';
17
- import { useMembers, useSpaces } from '@dxos/react-client/echo';
18
- import { IconButton, Toolbar } from '@dxos/react-ui';
16
+ import { useMembers, useQuery, useSpaces } from '@dxos/react-client/echo';
17
+ import { IconButton, Panel, Toolbar } from '@dxos/react-ui';
19
18
  import { JsonHighlighter } from '@dxos/react-ui-syntax-highlighter';
20
19
  import { withLayout } from '@dxos/react-ui/testing';
21
- import { TestSchema } from '@dxos/schema/testing';
22
- import { type ContentBlock, type Message, Organization, Person } from '@dxos/types';
23
-
24
- import { useQueueModelAdapter } from '#hooks';
25
- import {
26
- MessageBuilder,
27
- TestItem,
28
- useTestTranscriptionQueue,
29
- useTestTranscriptionQueueWithEntityExtraction,
30
- } from '#testing';
20
+ import { type ContentBlock, Message, Organization, Person } from '@dxos/types';
21
+
22
+ import { useFeedModelAdapter } from '#hooks';
23
+ import { MessageBuilder, TestItem, useTestTranscriptionQueue } from '#testing';
31
24
  import { translations } from '#translations';
32
25
 
33
26
  import { SerializationModel } from '../../model';
@@ -36,56 +29,69 @@ import { Transcription, type TranscriptionProps } from './Transcription';
36
29
 
37
30
  random.seed(1);
38
31
 
39
- /**
40
- * Story wrapper with test controls.
41
- */
42
- const TranscriptContainer: FC<
43
- TranscriptionProps & {
44
- running: boolean;
45
- onRunningChange: (running: boolean) => void;
46
- onReset?: () => void;
47
- }
48
- > = ({ model, running, onRunningChange, onReset }) => {
49
- return (
50
- <div className='grid grid-rows-[1fr_40px] grow divide-y divide-separator'>
51
- <Transcription model={model} />
52
- <div className='grid grid-cols-[1fr_16rem] overflow-hidden'>
53
- <div className='flex items-center'>
54
- <JsonHighlighter data={model.toJSON()} indent={0} classNames='text-sm' />
55
- </div>
56
- <Toolbar.Root classNames='justify-end'>
57
- <IconButton
58
- icon={running ? 'ph--pause--regular' : 'ph--play--regular'}
59
- label={running ? 'Pause' : 'Start'}
60
- onClick={() => onRunningChange(!running)}
61
- />
62
- {onReset && <IconButton icon='ph--x--regular' label='Reset' onClick={onReset} />}
63
- </Toolbar.Root>
64
- </div>
65
- </div>
32
+ type Source = 'local' | 'feed';
33
+
34
+ type DefaultStoryProps = {
35
+ /** Where transcript messages come from. */
36
+ source?: Source;
37
+ /** Number of synthetic messages to pre-populate (local source only). */
38
+ seed?: number;
39
+ };
40
+
41
+ const DefaultStory = ({ source = 'local', seed = 0, ...props }: DefaultStoryProps) => {
42
+ const [resetKey, setResetKey] = useState(0);
43
+ const handleReset = useCallback(() => setResetKey((value) => value + 1), []);
44
+ return source === 'feed' ? (
45
+ <FeedStory key={resetKey} onReset={handleReset} {...props} />
46
+ ) : (
47
+ <LocalStory key={resetKey} seed={seed} onReset={handleReset} {...props} />
66
48
  );
67
49
  };
68
50
 
69
- type DefaultStoryProps = { messages?: Message.Message[] } & Pick<
70
- TranscriptionProps,
71
- 'ignoreAttention' | 'attendableId'
72
- >;
51
+ type InnerProps = {
52
+ onReset: () => void;
53
+ };
73
54
 
74
- /**
75
- * Basic story mutates array of messages.
76
- */
77
- const BasicStory = ({ messages: initialMessages = [], ...props }: DefaultStoryProps) => {
78
- const [reset, setReset] = useState({});
55
+ const LocalStory = ({ seed = 0, onReset, ...props }: InnerProps & { seed?: number }) => {
79
56
  const builder = useMemo(() => new MessageBuilder(), []);
57
+ const [initialMessages, setInitialMessages] = useState<Message.Message[]>([]);
58
+ const [ready, setReady] = useState(seed === 0);
59
+
60
+ useEffect(() => {
61
+ // Reset on every seed change so Storybook controls don't leave stale messages from a prior run.
62
+ let cancelled = false;
63
+ setInitialMessages([]);
64
+ setReady(seed === 0);
65
+
66
+ if (seed === 0) {
67
+ return () => {
68
+ cancelled = true;
69
+ };
70
+ }
71
+
72
+ void Promise.all(Array.from({ length: seed }, () => builder.createMessage())).then((messages) => {
73
+ if (cancelled) {
74
+ return;
75
+ }
76
+ setInitialMessages(messages);
77
+ setReady(true);
78
+ });
79
+
80
+ return () => {
81
+ cancelled = true;
82
+ };
83
+ }, [seed, builder]);
84
+
80
85
  const model = useMemo(
81
86
  () => new SerializationModel<Message.Message>(renderByline([]), initialMessages),
82
- [initialMessages, reset],
87
+ [initialMessages],
83
88
  );
89
+
84
90
  const [running, setRunning] = useState(true);
85
- // Use mutable type for local message building (not ECHO-managed).
86
91
  const [currentMessage, setCurrentMessage] = useState<Types.Mutable<Message.Message> | null>(null);
92
+
87
93
  useEffect(() => {
88
- if (!running) {
94
+ if (!ready || !running) {
89
95
  return;
90
96
  }
91
97
 
@@ -97,83 +103,68 @@ const BasicStory = ({ messages: initialMessages = [], ...props }: DefaultStoryPr
97
103
  return;
98
104
  }
99
105
 
100
- const i = setInterval(() => {
101
- if (currentMessage?.blocks.length && currentMessage.blocks.length >= 3) {
106
+ const id = setInterval(() => {
107
+ if (currentMessage.blocks.length >= 3) {
102
108
  setCurrentMessage(null);
103
- clearInterval(i);
109
+ clearInterval(id);
104
110
  return;
105
111
  }
106
-
107
- (currentMessage.blocks as ContentBlock.Any[]).push(builder.createBlock());
112
+ Obj.update(currentMessage, (currentMessage) => {
113
+ (currentMessage.blocks as ContentBlock.Any[]).push(builder.createBlock());
114
+ });
108
115
  model.updateChunk(currentMessage);
109
116
  }, 3_000);
110
117
 
111
- return () => clearInterval(i);
112
- }, [model, currentMessage, running]);
113
-
114
- const handleReset = () => {
115
- setCurrentMessage(null);
116
- setRunning(false);
117
- setReset({});
118
- };
119
-
120
- return (
121
- <TranscriptContainer
122
- model={model}
123
- running={running}
124
- onRunningChange={setRunning}
125
- onReset={handleReset}
126
- {...props}
127
- />
128
- );
118
+ return () => clearInterval(id);
119
+ }, [ready, running, currentMessage, model, builder]);
120
+
121
+ return <Shell model={model} running={running} onRunningChange={setRunning} onReset={onReset} {...props} />;
129
122
  };
130
123
 
131
- /**
132
- * Queue story mutates queue with model adapter.
133
- */
134
- const QueueStory = ({
135
- messages: initialMessages = [],
136
- queueId,
137
- onReset,
138
- ...props
139
- }: DefaultStoryProps & { queueId: Key.ObjectId; onReset: () => void }) => {
124
+ const FeedStory = ({ onReset, ...props }: InnerProps) => {
140
125
  const [running, setRunning] = useState(true);
141
126
  const [space] = useSpaces();
142
127
  const members = useMembers(space?.id).map((member) => member.identity);
143
- const queue = useTestTranscriptionQueue(space, queueId, running, 2_000);
144
- const model = useQueueModelAdapter(renderByline(members), queue, initialMessages);
145
-
146
- return (
147
- <TranscriptContainer model={model} running={running} onRunningChange={setRunning} onReset={onReset} {...props} />
128
+ const feed = useTestTranscriptionQueue(space, running, 2_000);
129
+ const messages = useQuery(
130
+ space?.db,
131
+ feed ? Query.select(Filter.type(Message.Message)).from(feed) : Query.select(Filter.nothing()),
148
132
  );
149
- };
150
-
151
- // TODO(burdon): Reconcile with QueueStory.
152
- const EntityExtractionQueueStory = () => {
153
- const [running, setRunning] = useState(true);
154
- const [space] = useSpaces();
155
- const members = useMembers(space?.key).map((member) => member.identity);
156
- const queue = useTestTranscriptionQueueWithEntityExtraction(space, undefined, running, 2_000);
157
- const model = useQueueModelAdapter(renderByline(members), queue, []);
133
+ const model = useFeedModelAdapter(renderByline(members), messages, []);
158
134
 
159
- return <TranscriptContainer model={model} running={running} onRunningChange={setRunning} />;
135
+ return <Shell model={model} running={running} onRunningChange={setRunning} onReset={onReset} {...props} />;
160
136
  };
161
137
 
162
- /**
163
- * Wrapper remounts on refresh to reload queue.
164
- */
165
- const QueueStoryWrapper = () => {
166
- const [queueId] = useState(Key.ObjectId.random());
167
- const [key, setKey] = useState(Key.ObjectId.random().toString());
168
- const handleReset = () => {
169
- setKey(Key.ObjectId.random().toString());
170
- };
171
-
172
- return <QueueStory key={key} queueId={queueId} onReset={handleReset} />;
138
+ type ShellProps = TranscriptionProps & {
139
+ running: boolean;
140
+ onRunningChange: (running: boolean) => void;
141
+ onReset: () => void;
173
142
  };
174
143
 
144
+ const Shell = ({ model, running, onRunningChange, onReset, ...props }: ShellProps) => (
145
+ <Panel.Root>
146
+ <Panel.Toolbar asChild>
147
+ <Toolbar.Root classNames='justify-end'>
148
+ <IconButton
149
+ icon={running ? 'ph--pause--regular' : 'ph--play--regular'}
150
+ label={running ? 'Pause' : 'Start'}
151
+ onClick={() => onRunningChange(!running)}
152
+ />
153
+ <IconButton icon='ph--x--regular' label='Reset' onClick={onReset} />
154
+ </Toolbar.Root>
155
+ </Panel.Toolbar>
156
+ <Panel.Content className='dx-document'>
157
+ <Transcription model={model} {...props} />
158
+ </Panel.Content>
159
+ <Panel.Statusbar className='flex items-center justify-center'>
160
+ <JsonHighlighter data={model.toJSON()} indent={0} classNames='text-sm' />
161
+ </Panel.Statusbar>
162
+ </Panel.Root>
163
+ );
164
+
175
165
  const meta = {
176
166
  title: 'plugins/plugin-transcription/components/Transcription',
167
+ render: DefaultStory,
177
168
  decorators: [
178
169
  withLayout({ layout: 'fullscreen' }),
179
170
  withPluginManager({
@@ -181,13 +172,12 @@ const meta = {
181
172
  ...corePlugins(),
182
173
  StorybookPlugin({}),
183
174
  ClientPlugin({
184
- types: [TestItem, TestSchema.DocumentType, Person.Person, Organization.Organization],
175
+ types: [TestItem, Person.Person, Organization.Organization, Feed.Feed, Message.Message],
185
176
  onClientInitialized: ({ client }) =>
186
177
  Effect.gen(function* () {
187
178
  yield* initializeIdentity(client);
188
179
  }),
189
180
  }),
190
-
191
181
  PreviewPlugin(),
192
182
  ],
193
183
  }),
@@ -196,52 +186,28 @@ const meta = {
196
186
  layout: 'fullscreen',
197
187
  translations,
198
188
  },
199
- } satisfies Meta;
189
+ } satisfies Meta<typeof DefaultStory>;
200
190
 
201
191
  export default meta;
202
192
 
203
- const DefaultStory = (props: DefaultStoryProps) => {
204
- const [messages, setMessages] = useState<Message.Message[]>([]);
205
- useEffect(() => {
206
- void Promise.all(Array.from({ length: 10 }, () => MessageBuilder.singleton.createMessage())).then(setMessages);
207
- }, []);
208
- if (messages.length === 0) {
209
- return <div>Loading messages...</div>;
210
- }
211
- return <BasicStory {...props} messages={messages} />;
212
- };
193
+ type Story = StoryObj<typeof meta>;
213
194
 
214
- export const Default: StoryObj<typeof DefaultStory> = {
215
- render: DefaultStory,
195
+ export const Default: Story = {
216
196
  args: {
217
- ignoreAttention: true,
218
- attendableId: 'story',
197
+ source: 'local',
198
+ seed: 10,
219
199
  },
220
200
  };
221
201
 
222
- export const Empty: StoryObj<typeof BasicStory> = {
223
- render: BasicStory,
202
+ export const Empty: Story = {
224
203
  args: {
225
- ignoreAttention: true,
226
- attendableId: 'story',
204
+ source: 'local',
205
+ seed: 0,
227
206
  },
228
207
  };
229
208
 
230
- export const WithQueue: StoryObj<typeof QueueStoryWrapper> = {
231
- render: QueueStoryWrapper,
209
+ export const WithQueue: Story = {
232
210
  args: {
233
- ignoreAttention: true,
234
- attendableId: 'story',
211
+ source: 'feed',
235
212
  },
236
213
  };
237
-
238
- // NOTE: We are running out of free quota on hugging face entity extraction.
239
- // TODO(mykola): Fix AI service in entity extraction function.
240
- // TODO(mykola): Fix hugging face quota issues.
241
- // export const WithEntityExtractionQueue: StoryObj<typeof EntityExtractionQueueStory> = {
242
- // render: EntityExtractionQueueStory,
243
- // args: {
244
- // ignoreAttention: true,
245
- // attendableId: 'story',
246
- // },
247
- // };
@@ -8,16 +8,15 @@ import { useThemeContext } from '@dxos/react-ui';
8
8
  import { useTextEditor } from '@dxos/react-ui-editor';
9
9
  import { type Message, type Transcript } from '@dxos/types';
10
10
  import {
11
- autoScroll,
12
11
  createBasicExtensions,
13
12
  createMarkdownExtensions,
14
13
  createThemeExtensions,
15
14
  decorateMarkdown,
16
15
  documentSlots,
17
16
  preview,
17
+ scroller,
18
18
  } from '@dxos/ui-editor';
19
19
  import { composable, composableProps } from '@dxos/ui-theme';
20
- import { isTruthy } from '@dxos/util';
21
20
 
22
21
  import { type SerializationModel } from '../../model';
23
22
  import { renderByline } from '../../util';
@@ -28,14 +27,9 @@ export { renderByline };
28
27
  export type TranscriptionProps = {
29
28
  transcript?: Transcript.Transcript;
30
29
  model: SerializationModel<Message.Message>;
31
- // TODO(burdon): Are these still in use?
32
- attendableId?: string;
33
- ignoreAttention?: boolean;
34
30
  };
35
31
 
36
- /**
37
- * Transcript component implemented using the editor.
38
- */
32
+ // TODO(burdon): Remove forwardedRef/use react-ui-editor.
39
33
  export const Transcription = composable<HTMLDivElement, TranscriptionProps>(
40
34
  ({ transcript: object, model, children, ...props }, forwardedRef) => {
41
35
  const { themeMode } = useThemeContext();
@@ -43,21 +37,21 @@ export const Transcription = composable<HTMLDivElement, TranscriptionProps>(
43
37
  return {
44
38
  extensions: [
45
39
  createBasicExtensions({ readOnly: true, lineWrapping: true, search: true }),
46
- createMarkdownExtensions(),
47
40
  createThemeExtensions({ themeMode, slots: documentSlots }),
41
+ createMarkdownExtensions(),
48
42
  decorateMarkdown(),
49
43
  preview(),
50
44
  transcription({ model, started: object?.started ? new Date(object.started) : undefined }),
51
- autoScroll(),
52
- ].filter(isTruthy),
45
+ scroller(),
46
+ ],
53
47
  };
54
- }, [model]);
48
+ }, [model, themeMode]);
55
49
 
56
50
  return (
57
51
  <div
58
52
  {...composableProps(props, { classNames: 'dx-container' })}
53
+ data-popover-collision-boundary={true}
59
54
  ref={parentRef}
60
- data-popover-collision-boundary={true /* TODO(thure): Make this a constant and document it. */}
61
55
  />
62
56
  );
63
57
  },
@@ -2,4 +2,5 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ export * from './Oracle';
5
6
  export * from './Transcription';
@@ -0,0 +1,61 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import * as Effect from 'effect/Effect';
7
+ import React from 'react';
8
+
9
+ import { withPluginManager } from '@dxos/app-framework/testing';
10
+ import { Feed, Query, Ref } from '@dxos/echo';
11
+ import { ClientPlugin, initializeIdentity } from '@dxos/plugin-client/testing';
12
+ import { corePlugins } from '@dxos/plugin-testing';
13
+ import { useQuery, useSpaces } from '@dxos/react-client/echo';
14
+ import { Loading, withLayout } from '@dxos/react-ui/testing';
15
+ import { Message, Transcript } from '@dxos/types';
16
+
17
+ import { translations } from '#translations';
18
+
19
+ import { TestItem } from '../../testing';
20
+ import { TranscriptionPlugin } from '../../TranscriptionPlugin';
21
+ import { TranscriptionArticle } from './TranscriptionArticle';
22
+
23
+ const DefaultStory = () => {
24
+ const [space] = useSpaces();
25
+ const [transcript] = useQuery(space?.db, Query.type(Transcript.Transcript));
26
+ if (!space || !transcript) {
27
+ return <Loading />;
28
+ }
29
+
30
+ return <TranscriptionArticle role='article' attendableId='story' subject={transcript} />;
31
+ };
32
+
33
+ const meta = {
34
+ title: 'plugins/plugin-transcription/containers/TranscriptionArticle',
35
+ render: DefaultStory,
36
+ decorators: [
37
+ withLayout({ layout: 'column' }),
38
+ withPluginManager({
39
+ plugins: [
40
+ ...corePlugins(),
41
+ ClientPlugin({
42
+ types: [Transcript.Transcript, Feed.Feed, Message.Message, TestItem],
43
+ onClientInitialized: ({ client }) =>
44
+ Effect.gen(function* () {
45
+ const { personalSpace } = yield* initializeIdentity(client);
46
+ const feed = personalSpace.db.add(Feed.make({ name: 'transcription' }));
47
+ personalSpace.db.add(Transcript.make(Ref.make(feed)));
48
+ }),
49
+ }),
50
+ TranscriptionPlugin(),
51
+ ],
52
+ }),
53
+ ],
54
+ parameters: { layout: 'fullscreen', translations },
55
+ } satisfies Meta<typeof DefaultStory>;
56
+
57
+ export default meta;
58
+
59
+ type Story = StoryObj<typeof meta>;
60
+
61
+ export const Default: Story = {};