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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. package/dist/lib/browser/{TranscriptContainer-JQ3HFHLQ.mjs → TranscriptContainer-3LU3XBEB.mjs} +3 -2
  2. package/dist/lib/browser/app-graph-builder-YXELPN6F.mjs +349 -0
  3. package/dist/lib/browser/app-graph-builder-YXELPN6F.mjs.map +7 -0
  4. package/dist/lib/browser/{chunk-YCZISX63.mjs → chunk-GWY2Q2NE.mjs} +108 -81
  5. package/dist/lib/browser/chunk-GWY2Q2NE.mjs.map +7 -0
  6. package/dist/lib/browser/chunk-HNIKXVCG.mjs +720 -0
  7. package/dist/lib/browser/chunk-HNIKXVCG.mjs.map +7 -0
  8. package/dist/lib/browser/chunk-HRDAILQX.mjs +27 -0
  9. package/dist/lib/browser/chunk-HRDAILQX.mjs.map +7 -0
  10. package/dist/lib/browser/{chunk-GU2BNYKZ.mjs → chunk-KLD7O5GJ.mjs} +2 -12
  11. package/dist/lib/browser/chunk-KLD7O5GJ.mjs.map +7 -0
  12. package/dist/lib/browser/{chunk-YEPT334A.mjs → chunk-LTIQFX4W.mjs} +1 -1
  13. package/dist/lib/browser/chunk-LTIQFX4W.mjs.map +7 -0
  14. package/dist/lib/browser/chunk-TGOKX32G.mjs +63 -0
  15. package/dist/lib/browser/chunk-TGOKX32G.mjs.map +7 -0
  16. package/dist/lib/browser/index.mjs +46 -209
  17. package/dist/lib/browser/index.mjs.map +4 -4
  18. package/dist/lib/browser/{intent-resolver-4R66ZCKT.mjs → intent-resolver-BBB3DWV4.mjs} +8 -8
  19. package/dist/lib/browser/intent-resolver-BBB3DWV4.mjs.map +7 -0
  20. package/dist/lib/browser/{meeting-transcription-state-DLFHQK72.mjs → meeting-transcription-state-MNA2XROE.mjs} +4 -4
  21. package/dist/lib/browser/meeting-transcription-state-MNA2XROE.mjs.map +7 -0
  22. package/dist/lib/browser/meta.json +1 -1
  23. package/dist/lib/browser/{react-surface-FQUHU5G7.mjs → react-surface-R277DACJ.mjs} +18 -6
  24. package/dist/lib/browser/react-surface-R277DACJ.mjs.map +7 -0
  25. package/dist/lib/browser/settings-I226DZ27.mjs +24 -0
  26. package/dist/lib/browser/settings-I226DZ27.mjs.map +7 -0
  27. package/dist/lib/browser/{transcriber-LGMIWDYI.mjs → transcriber-QRSS6X6S.mjs} +8 -7
  28. package/dist/lib/browser/{transcriber-LGMIWDYI.mjs.map → transcriber-QRSS6X6S.mjs.map} +3 -3
  29. package/dist/lib/browser/types/index.mjs +3 -3
  30. package/dist/lib/node/{TranscriptContainer-QEYTTT2V.cjs → TranscriptContainer-DICOMZLM.cjs} +8 -7
  31. package/dist/lib/node/TranscriptContainer-DICOMZLM.cjs.map +7 -0
  32. package/dist/lib/node/app-graph-builder-I6LYVIYY.cjs +350 -0
  33. package/dist/lib/node/app-graph-builder-I6LYVIYY.cjs.map +7 -0
  34. package/dist/lib/node/{chunk-P4VVD6ZO.cjs → chunk-34WFY7M7.cjs} +24 -34
  35. package/dist/lib/node/chunk-34WFY7M7.cjs.map +7 -0
  36. package/dist/lib/node/chunk-4JOJCZPJ.cjs +737 -0
  37. package/dist/lib/node/chunk-4JOJCZPJ.cjs.map +7 -0
  38. package/dist/lib/node/{chunk-3KHUSQNW.cjs → chunk-J72PUQ3C.cjs} +6 -15
  39. package/dist/lib/node/chunk-J72PUQ3C.cjs.map +7 -0
  40. package/dist/lib/node/{chunk-CZMLKKWT.cjs → chunk-JUGOXWES.cjs} +4 -4
  41. package/dist/lib/node/chunk-JUGOXWES.cjs.map +7 -0
  42. package/dist/lib/node/{chunk-LP4FG423.cjs → chunk-K553L7CZ.cjs} +23 -8
  43. package/dist/lib/node/chunk-K553L7CZ.cjs.map +7 -0
  44. package/dist/lib/node/{chunk-FCCJALOT.cjs → chunk-RK4BEN5E.cjs} +111 -83
  45. package/dist/lib/node/chunk-RK4BEN5E.cjs.map +7 -0
  46. package/dist/lib/node/index.cjs +65 -237
  47. package/dist/lib/node/index.cjs.map +4 -4
  48. package/dist/lib/node/{intent-resolver-WMMCOUEH.cjs → intent-resolver-7XY3WITN.cjs} +11 -10
  49. package/dist/lib/node/intent-resolver-7XY3WITN.cjs.map +7 -0
  50. package/dist/lib/node/{meeting-transcription-state-URIYILEQ.cjs → meeting-transcription-state-5XLTMWPA.cjs} +7 -7
  51. package/dist/lib/node/meeting-transcription-state-5XLTMWPA.cjs.map +7 -0
  52. package/dist/lib/node/meta.json +1 -1
  53. package/dist/lib/node/{react-surface-WI2MGWCC.cjs → react-surface-HUSWAORU.cjs} +20 -10
  54. package/dist/lib/node/react-surface-HUSWAORU.cjs.map +7 -0
  55. package/dist/lib/node/settings-NDMU3EWQ.cjs +38 -0
  56. package/dist/lib/node/settings-NDMU3EWQ.cjs.map +7 -0
  57. package/dist/lib/node/{transcriber-QG5WPKX4.cjs → transcriber-UZJMG44C.cjs} +14 -13
  58. package/dist/lib/node/{transcriber-QG5WPKX4.cjs.map → transcriber-UZJMG44C.cjs.map} +3 -3
  59. package/dist/lib/node/types/index.cjs +8 -8
  60. package/dist/lib/node/types/index.cjs.map +1 -1
  61. package/dist/lib/node-esm/{TranscriptContainer-ZXUCOWGY.mjs → TranscriptContainer-K4E4P5T6.mjs} +3 -2
  62. package/dist/lib/node-esm/app-graph-builder-6FY2UQOH.mjs +350 -0
  63. package/dist/lib/node-esm/app-graph-builder-6FY2UQOH.mjs.map +7 -0
  64. package/dist/lib/node-esm/chunk-3I7AC3D3.mjs +28 -0
  65. package/dist/lib/node-esm/chunk-3I7AC3D3.mjs.map +7 -0
  66. package/dist/lib/node-esm/{chunk-PTZHDZ36.mjs → chunk-5E2MR5XK.mjs} +1 -1
  67. package/dist/lib/node-esm/chunk-5E2MR5XK.mjs.map +7 -0
  68. package/dist/lib/node-esm/chunk-6C34FO4C.mjs +721 -0
  69. package/dist/lib/node-esm/chunk-6C34FO4C.mjs.map +7 -0
  70. package/dist/lib/node-esm/{chunk-YQYBLFNI.mjs → chunk-CKANLRS4.mjs} +108 -81
  71. package/dist/lib/node-esm/chunk-CKANLRS4.mjs.map +7 -0
  72. package/dist/lib/node-esm/chunk-KUQ3ZTRX.mjs +64 -0
  73. package/dist/lib/node-esm/chunk-KUQ3ZTRX.mjs.map +7 -0
  74. package/dist/lib/node-esm/{chunk-HKATMD34.mjs → chunk-QMHMEKB3.mjs} +2 -12
  75. package/dist/lib/node-esm/chunk-QMHMEKB3.mjs.map +7 -0
  76. package/dist/lib/node-esm/index.mjs +46 -209
  77. package/dist/lib/node-esm/index.mjs.map +4 -4
  78. package/dist/lib/node-esm/{intent-resolver-QN2LO5TX.mjs → intent-resolver-ACGAPLDZ.mjs} +8 -8
  79. package/dist/lib/node-esm/intent-resolver-ACGAPLDZ.mjs.map +7 -0
  80. package/dist/lib/node-esm/{meeting-transcription-state-ZYVRAW6Y.mjs → meeting-transcription-state-3WOZ52BQ.mjs} +4 -4
  81. package/dist/lib/node-esm/meeting-transcription-state-3WOZ52BQ.mjs.map +7 -0
  82. package/dist/lib/node-esm/meta.json +1 -1
  83. package/dist/lib/node-esm/{react-surface-PCH6NTVB.mjs → react-surface-G2OY4MWL.mjs} +18 -6
  84. package/dist/lib/node-esm/react-surface-G2OY4MWL.mjs.map +7 -0
  85. package/dist/lib/node-esm/settings-O7X2UQQG.mjs +25 -0
  86. package/dist/lib/node-esm/settings-O7X2UQQG.mjs.map +7 -0
  87. package/dist/lib/node-esm/{transcriber-TMPYZ5L4.mjs → transcriber-NVVFH7IF.mjs} +8 -7
  88. package/dist/lib/node-esm/{transcriber-TMPYZ5L4.mjs.map → transcriber-NVVFH7IF.mjs.map} +3 -3
  89. package/dist/lib/node-esm/types/index.mjs +3 -3
  90. package/dist/types/src/TranscriptionPlugin.d.ts.map +1 -1
  91. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  92. package/dist/types/src/capabilities/capabilities.d.ts +5 -7
  93. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  94. package/dist/types/src/capabilities/index.d.ts +2 -4
  95. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  96. package/dist/types/src/capabilities/meeting-transcription-state.d.ts +2 -4
  97. package/dist/types/src/capabilities/meeting-transcription-state.d.ts.map +1 -1
  98. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  99. package/dist/types/src/capabilities/settings.d.ts +4 -0
  100. package/dist/types/src/capabilities/settings.d.ts.map +1 -0
  101. package/dist/types/src/capabilities/transcriber.d.ts.map +1 -1
  102. package/dist/types/src/components/Transcript/Transcript.d.ts +14 -4
  103. package/dist/types/src/components/Transcript/Transcript.d.ts.map +1 -1
  104. package/dist/types/src/components/Transcript/Transcript.stories.d.ts +19 -4
  105. package/dist/types/src/components/Transcript/Transcript.stories.d.ts.map +1 -1
  106. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts +27 -0
  107. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts.map +1 -0
  108. package/dist/types/src/components/Transcript/transcript-extension.d.ts +19 -0
  109. package/dist/types/src/components/Transcript/transcript-extension.d.ts.map +1 -0
  110. package/dist/types/src/components/TranscriptContainer.d.ts.map +1 -1
  111. package/dist/types/src/components/TranscriptionSettings/TranscriptionSettings.d.ts +6 -0
  112. package/dist/types/src/components/TranscriptionSettings/TranscriptionSettings.d.ts.map +1 -0
  113. package/dist/types/src/components/TranscriptionSettings/index.d.ts +2 -0
  114. package/dist/types/src/components/TranscriptionSettings/index.d.ts.map +1 -0
  115. package/dist/types/src/components/index.d.ts +1 -0
  116. package/dist/types/src/components/index.d.ts.map +1 -1
  117. package/dist/types/src/components/transcription.stories.d.ts +6 -3
  118. package/dist/types/src/components/transcription.stories.d.ts.map +1 -1
  119. package/dist/types/src/entity-extraction/entity-extraction.d.ts +42 -0
  120. package/dist/types/src/entity-extraction/entity-extraction.d.ts.map +1 -0
  121. package/dist/types/src/entity-extraction/entity-extraction.test.d.ts +2 -0
  122. package/dist/types/src/entity-extraction/entity-extraction.test.d.ts.map +1 -0
  123. package/dist/types/src/entity-extraction/index.d.ts +2 -0
  124. package/dist/types/src/entity-extraction/index.d.ts.map +1 -0
  125. package/dist/types/src/hooks/index.d.ts +1 -0
  126. package/dist/types/src/hooks/index.d.ts.map +1 -1
  127. package/dist/types/src/hooks/useQueueModelAdapter.d.ts +7 -0
  128. package/dist/types/src/hooks/useQueueModelAdapter.d.ts.map +1 -0
  129. package/dist/types/src/hooks/useTranscriber.d.ts +1 -1
  130. package/dist/types/src/hooks/useTranscriber.d.ts.map +1 -1
  131. package/dist/types/src/model/index.d.ts +2 -0
  132. package/dist/types/src/model/index.d.ts.map +1 -0
  133. package/dist/types/src/model/model.d.ts +68 -0
  134. package/dist/types/src/model/model.d.ts.map +1 -0
  135. package/dist/types/src/model/model.test.d.ts +2 -0
  136. package/dist/types/src/model/model.test.d.ts.map +1 -0
  137. package/dist/types/src/testing/index.d.ts +2 -0
  138. package/dist/types/src/testing/index.d.ts.map +1 -0
  139. package/dist/types/src/testing/testing.d.ts +40 -0
  140. package/dist/types/src/testing/testing.d.ts.map +1 -0
  141. package/dist/types/src/transcriber/transcriber.browser.test.d.ts +2 -0
  142. package/dist/types/src/transcriber/transcriber.browser.test.d.ts.map +1 -0
  143. package/dist/types/src/transcriber/transcriber.d.ts +2 -2
  144. package/dist/types/src/transcriber/transcriber.d.ts.map +1 -1
  145. package/dist/types/src/transcriber/transcription-manager.d.ts +17 -15
  146. package/dist/types/src/transcriber/transcription-manager.d.ts.map +1 -1
  147. package/dist/types/src/translations.d.ts +3 -0
  148. package/dist/types/src/translations.d.ts.map +1 -1
  149. package/dist/types/src/types/schema.d.ts +26 -40
  150. package/dist/types/src/types/schema.d.ts.map +1 -1
  151. package/dist/types/src/types/types.d.ts +11 -7
  152. package/dist/types/src/types/types.d.ts.map +1 -1
  153. package/dist/types/src/util/index.d.ts +0 -1
  154. package/dist/types/src/util/index.d.ts.map +1 -1
  155. package/package.json +53 -40
  156. package/src/TranscriptionPlugin.tsx +29 -12
  157. package/src/capabilities/app-graph-builder.ts +111 -52
  158. package/src/capabilities/capabilities.ts +4 -3
  159. package/src/capabilities/index.ts +1 -0
  160. package/src/capabilities/intent-resolver.ts +5 -5
  161. package/src/capabilities/meeting-transcription-state.ts +2 -2
  162. package/src/capabilities/react-surface.tsx +12 -4
  163. package/src/capabilities/settings.ts +20 -0
  164. package/src/capabilities/transcriber.ts +2 -1
  165. package/src/components/Transcript/Transcript.stories.tsx +201 -43
  166. package/src/components/Transcript/Transcript.tsx +83 -208
  167. package/src/components/Transcript/deprecated/OldTranscript.tsx +260 -0
  168. package/src/components/Transcript/transcript-extension.ts +236 -0
  169. package/src/components/TranscriptContainer.tsx +11 -6
  170. package/src/components/TranscriptionSettings/TranscriptionSettings.tsx +26 -0
  171. package/src/components/TranscriptionSettings/index.ts +5 -0
  172. package/src/components/index.ts +1 -0
  173. package/src/components/transcription.stories.tsx +163 -60
  174. package/src/entity-extraction/entity-extraction.test.ts +97 -0
  175. package/src/entity-extraction/entity-extraction.ts +149 -0
  176. package/src/entity-extraction/index.ts +5 -0
  177. package/src/entity-extraction/system-prompt.tpl +38 -0
  178. package/src/entity-extraction/types.d.ts +8 -0
  179. package/src/hooks/index.ts +1 -0
  180. package/src/hooks/useQueueModelAdapter.ts +63 -0
  181. package/src/hooks/useTranscriber.ts +3 -2
  182. package/src/model/index.ts +5 -0
  183. package/src/model/model.test.ts +169 -0
  184. package/src/model/model.ts +229 -0
  185. package/src/testing/index.ts +5 -0
  186. package/src/testing/testing.ts +221 -0
  187. package/src/transcriber/{transcriber.test.ts → transcriber.browser.test.ts} +3 -3
  188. package/src/transcriber/transcriber.ts +20 -11
  189. package/src/transcriber/transcription-manager.ts +67 -58
  190. package/src/translations.ts +2 -0
  191. package/src/types/schema.ts +11 -32
  192. package/src/types/types.ts +19 -9
  193. package/src/util/index.ts +0 -1
  194. package/dist/lib/browser/app-graph-builder-VDQOVX7P.mjs +0 -191
  195. package/dist/lib/browser/app-graph-builder-VDQOVX7P.mjs.map +0 -7
  196. package/dist/lib/browser/chunk-G4B6CPC2.mjs +0 -253
  197. package/dist/lib/browser/chunk-G4B6CPC2.mjs.map +0 -7
  198. package/dist/lib/browser/chunk-GU2BNYKZ.mjs.map +0 -7
  199. package/dist/lib/browser/chunk-K7FOYAXO.mjs +0 -8
  200. package/dist/lib/browser/chunk-K7FOYAXO.mjs.map +0 -7
  201. package/dist/lib/browser/chunk-OGNXNGJB.mjs +0 -73
  202. package/dist/lib/browser/chunk-OGNXNGJB.mjs.map +0 -7
  203. package/dist/lib/browser/chunk-YCZISX63.mjs.map +0 -7
  204. package/dist/lib/browser/chunk-YEPT334A.mjs.map +0 -7
  205. package/dist/lib/browser/intent-resolver-4R66ZCKT.mjs.map +0 -7
  206. package/dist/lib/browser/meeting-transcription-state-DLFHQK72.mjs.map +0 -7
  207. package/dist/lib/browser/react-surface-FQUHU5G7.mjs.map +0 -7
  208. package/dist/lib/node/TranscriptContainer-QEYTTT2V.cjs.map +0 -7
  209. package/dist/lib/node/app-graph-builder-IFSIVNUR.cjs +0 -200
  210. package/dist/lib/node/app-graph-builder-IFSIVNUR.cjs.map +0 -7
  211. package/dist/lib/node/chunk-3KHUSQNW.cjs.map +0 -7
  212. package/dist/lib/node/chunk-CZMLKKWT.cjs.map +0 -7
  213. package/dist/lib/node/chunk-FCCJALOT.cjs.map +0 -7
  214. package/dist/lib/node/chunk-LP4FG423.cjs.map +0 -7
  215. package/dist/lib/node/chunk-P4VVD6ZO.cjs.map +0 -7
  216. package/dist/lib/node/chunk-UOFHT4GZ.cjs +0 -280
  217. package/dist/lib/node/chunk-UOFHT4GZ.cjs.map +0 -7
  218. package/dist/lib/node/intent-resolver-WMMCOUEH.cjs.map +0 -7
  219. package/dist/lib/node/meeting-transcription-state-URIYILEQ.cjs.map +0 -7
  220. package/dist/lib/node/react-surface-WI2MGWCC.cjs.map +0 -7
  221. package/dist/lib/node-esm/app-graph-builder-UL6XLIZY.mjs +0 -192
  222. package/dist/lib/node-esm/app-graph-builder-UL6XLIZY.mjs.map +0 -7
  223. package/dist/lib/node-esm/chunk-6X7X2UZF.mjs +0 -254
  224. package/dist/lib/node-esm/chunk-6X7X2UZF.mjs.map +0 -7
  225. package/dist/lib/node-esm/chunk-GIO6J6FE.mjs +0 -74
  226. package/dist/lib/node-esm/chunk-GIO6J6FE.mjs.map +0 -7
  227. package/dist/lib/node-esm/chunk-HKATMD34.mjs.map +0 -7
  228. package/dist/lib/node-esm/chunk-J4D2RBBH.mjs +0 -10
  229. package/dist/lib/node-esm/chunk-J4D2RBBH.mjs.map +0 -7
  230. package/dist/lib/node-esm/chunk-PTZHDZ36.mjs.map +0 -7
  231. package/dist/lib/node-esm/chunk-YQYBLFNI.mjs.map +0 -7
  232. package/dist/lib/node-esm/intent-resolver-QN2LO5TX.mjs.map +0 -7
  233. package/dist/lib/node-esm/meeting-transcription-state-ZYVRAW6Y.mjs.map +0 -7
  234. package/dist/lib/node-esm/react-surface-PCH6NTVB.mjs.map +0 -7
  235. package/dist/types/src/transcriber/transcriber.test.d.ts +0 -2
  236. package/dist/types/src/transcriber/transcriber.test.d.ts.map +0 -1
  237. package/dist/types/src/util/random-queue-dxn.d.ts +0 -3
  238. package/dist/types/src/util/random-queue-dxn.d.ts.map +0 -1
  239. package/src/util/random-queue-dxn.ts +0 -9
  240. /package/dist/lib/browser/{TranscriptContainer-JQ3HFHLQ.mjs.map → TranscriptContainer-3LU3XBEB.mjs.map} +0 -0
  241. /package/dist/lib/node-esm/{TranscriptContainer-ZXUCOWGY.mjs.map → TranscriptContainer-K4E4P5T6.mjs.map} +0 -0
@@ -3,12 +3,12 @@
3
3
  //
4
4
 
5
5
  import { contributes } from '@dxos/app-framework';
6
- import { create } from '@dxos/live-object';
6
+ import { live } from '@dxos/live-object';
7
7
 
8
8
  import { TranscriptionCapabilities } from './capabilities';
9
9
 
10
10
  export default () => {
11
- const state = create<TranscriptionCapabilities.MeetingTranscriptionState>({
11
+ const state = live<TranscriptionCapabilities.MeetingTranscriptionState>({
12
12
  enabled: false,
13
13
  });
14
14
 
@@ -5,15 +5,23 @@
5
5
  import React from 'react';
6
6
 
7
7
  import { Capabilities, contributes, createSurface } from '@dxos/app-framework';
8
+ import { SettingsStore } from '@dxos/local-storage';
8
9
 
9
- import { TranscriptionContainer } from '../components';
10
- import { TRANSCRIPTION_PLUGIN } from '../meta';
11
- import { isTranscript, type TranscriptType } from '../types';
10
+ import { TranscriptionContainer, TranscriptionSettings } from '../components';
11
+ import { meta } from '../meta';
12
+ import { isTranscript, type TranscriptionSettingsProps, type TranscriptType } from '../types';
12
13
 
13
14
  export default () =>
14
15
  contributes(Capabilities.ReactSurface, [
15
16
  createSurface({
16
- id: `${TRANSCRIPTION_PLUGIN}/article/transcript`,
17
+ id: `${meta.id}/plugin-settings`,
18
+ role: 'article',
19
+ filter: (data): data is { subject: SettingsStore<TranscriptionSettingsProps> } =>
20
+ data.subject instanceof SettingsStore && data.subject.prefix === meta.id,
21
+ component: ({ data: { subject } }) => <TranscriptionSettings settings={subject.value} />,
22
+ }),
23
+ createSurface({
24
+ id: `${meta.id}/article/transcript`,
17
25
  role: ['article', 'section'],
18
26
  filter: (data): data is { subject: TranscriptType } => isTranscript(data.subject),
19
27
  component: ({ data, role }) => <TranscriptionContainer transcript={data.subject} role={role} />,
@@ -0,0 +1,20 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Capabilities, contributes } from '@dxos/app-framework';
6
+ import { live } from '@dxos/live-object';
7
+
8
+ import { TRANSCRIPTION_PLUGIN } from '../meta';
9
+ import { TranscriptionSettingsSchema } from '../types';
10
+
11
+ export default () => {
12
+ // TODO(wittjosiah): `live` currently doesn't handle schema default values.
13
+ const settings = live(TranscriptionSettingsSchema, { entityExtraction: true });
14
+
15
+ return contributes(Capabilities.Settings, {
16
+ schema: TranscriptionSettingsSchema,
17
+ prefix: TRANSCRIPTION_PLUGIN,
18
+ value: settings,
19
+ });
20
+ };
@@ -29,12 +29,13 @@ const TRANSCRIBE_AFTER_CHUNKS_AMOUNT = 50;
29
29
  * Records audio while user is speaking and transcribes it after user is done speaking.
30
30
  */
31
31
  export default () => {
32
- const getTranscriber: TranscriptionCapabilities.GetTranscriber = ({ audioStreamTrack, onSegments }) => {
32
+ const getTranscriber: TranscriptionCapabilities.GetTranscriber = ({ audioStreamTrack, onSegments, config }) => {
33
33
  // Initialize audio transcription.
34
34
  return new Transcriber({
35
35
  config: {
36
36
  transcribeAfterChunksAmount: TRANSCRIBE_AFTER_CHUNKS_AMOUNT,
37
37
  prefixBufferChunksAmount: PREFIXED_CHUNKS_AMOUNT,
38
+ ...config,
38
39
  },
39
40
  recorder: new MediaStreamRecorder({
40
41
  mediaStreamTrack: audioStreamTrack,
@@ -4,60 +4,200 @@
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
- import { type StoryObj, type Meta } from '@storybook/react';
8
- import React, { useEffect, useState } from 'react';
7
+ import { type Meta, type StoryObj } from '@storybook/react';
8
+ import React, { type FC, useEffect, useMemo, useState } from 'react';
9
9
 
10
+ import { IntentPlugin, SettingsPlugin } from '@dxos/app-framework';
11
+ import { withPluginManager } from '@dxos/app-framework/testing';
12
+ import { ObjectId } from '@dxos/echo-schema';
13
+ import { ClientPlugin } from '@dxos/plugin-client';
14
+ import { PreviewPlugin } from '@dxos/plugin-preview';
15
+ import { SpacePlugin } from '@dxos/plugin-space';
16
+ import { StorybookLayoutPlugin } from '@dxos/plugin-storybook-layout';
17
+ import { ThemePlugin } from '@dxos/plugin-theme';
10
18
  import { faker } from '@dxos/random';
11
- import { hues } from '@dxos/react-ui-theme';
12
- import { withTheme, withLayout } from '@dxos/storybook-utils';
19
+ import { useMembers, useSpace } from '@dxos/react-client/echo';
20
+ import { IconButton, Toolbar } from '@dxos/react-ui';
21
+ import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
22
+ import { defaultTx } from '@dxos/react-ui-theme';
23
+ import { DataType } from '@dxos/schema';
24
+ import { Testing } from '@dxos/schema/testing';
25
+ import { withLayout } from '@dxos/storybook-utils';
13
26
 
14
- import { Transcript } from './Transcript';
27
+ import { renderMarkdown, Transcript, type TranscriptProps } from './Transcript';
28
+ import { useQueueModelAdapter } from '../../hooks';
29
+ import { SerializationModel } from '../../model';
30
+ import {
31
+ MessageBuilder,
32
+ TestItem,
33
+ useTestTranscriptionQueue,
34
+ useTestTranscriptionQueueWithEntityExtraction,
35
+ } from '../../testing';
15
36
  import translations from '../../translations';
16
37
 
17
- let start = new Date(Date.now() - 24 * 60 * 60 * 10_000);
18
- const next = () => {
19
- start = new Date(start.getTime() + Math.random() * 10_000);
20
- return start;
38
+ faker.seed(1);
39
+
40
+ /**
41
+ * Story wrapper with test controls.
42
+ */
43
+ const TranscriptContainer: FC<
44
+ TranscriptProps & {
45
+ running: boolean;
46
+ onRunningChange: (running: boolean) => void;
47
+ onReset?: () => void;
48
+ }
49
+ > = ({ space, model, running, onRunningChange, onReset }) => {
50
+ return (
51
+ <div className='grid grid-rows-[1fr_40px] grow divide-y divide-separator'>
52
+ <Transcript space={space} model={model} />
53
+ <div className='grid grid-cols-[1fr_16rem] overflow-hidden'>
54
+ <div className='flex items-center'>
55
+ <SyntaxHighlighter language='json' className='text-sm'>
56
+ {JSON.stringify(model.toJSON())}
57
+ </SyntaxHighlighter>
58
+ </div>
59
+ <Toolbar.Root classNames='justify-end'>
60
+ <IconButton
61
+ icon={running ? 'ph--pause--regular' : 'ph--play--regular'}
62
+ label={running ? 'Pause' : 'Start'}
63
+ onClick={() => onRunningChange(!running)}
64
+ />
65
+ {onReset && <IconButton icon='ph--x--regular' label='Reset' onClick={onReset} />}
66
+ </Toolbar.Root>
67
+ </div>
68
+ </div>
69
+ );
21
70
  };
22
71
 
23
- const users = Array.from({ length: 5 }, () => ({
24
- authorName: faker.person.fullName(),
25
- authorHue: faker.helpers.arrayElement(hues),
26
- }));
27
-
28
- const createBlock = () => {
29
- const author = faker.helpers.arrayElement(users);
30
- return {
31
- id: faker.string.uuid(),
32
- ...author,
33
- segments: Array.from({ length: 1 + Math.floor(Math.random() * 2) }, () => ({
34
- started: next(),
35
- text: faker.lorem.paragraph(),
36
- })),
72
+ type StoryProps = { messages?: DataType.Message[] } & Pick<TranscriptProps, 'ignoreAttention' | 'attendableId'>;
73
+
74
+ /**
75
+ * Basic story mutates array of messages.
76
+ */
77
+ const BasicStory = ({ messages: initialMessages = [], ...props }: StoryProps) => {
78
+ const [reset, setReset] = useState({});
79
+ const builder = useMemo(() => new MessageBuilder(), []);
80
+ const model = useMemo(
81
+ () => new SerializationModel<DataType.Message>(renderMarkdown([]), initialMessages),
82
+ [initialMessages, reset],
83
+ );
84
+ const [running, setRunning] = useState(true);
85
+ const [currentMessage, setCurrentMessage] = useState<DataType.Message | null>(null);
86
+ useEffect(() => {
87
+ if (!running) {
88
+ return;
89
+ }
90
+
91
+ if (!currentMessage) {
92
+ void builder.createMessage().then((message) => {
93
+ model.appendChunk(message);
94
+ setCurrentMessage(message);
95
+ });
96
+ return;
97
+ }
98
+
99
+ const i = setInterval(() => {
100
+ if (currentMessage?.blocks.length && currentMessage.blocks.length >= 3) {
101
+ setCurrentMessage(null);
102
+ clearInterval(i);
103
+ return;
104
+ }
105
+
106
+ currentMessage.blocks.push(builder.createBlock());
107
+ model.updateChunk(currentMessage);
108
+ }, 3_000);
109
+
110
+ return () => clearInterval(i);
111
+ }, [model, currentMessage, running]);
112
+
113
+ const handleReset = () => {
114
+ setCurrentMessage(null);
115
+ setRunning(false);
116
+ setReset({});
37
117
  };
118
+
119
+ return (
120
+ <TranscriptContainer
121
+ model={model}
122
+ running={running}
123
+ onRunningChange={setRunning}
124
+ onReset={handleReset}
125
+ {...props}
126
+ />
127
+ );
38
128
  };
39
129
 
40
- const meta: Meta<typeof Transcript> = {
130
+ /**
131
+ * Queue story mutates queue with model adapter.
132
+ */
133
+ const QueueStory = ({
134
+ messages: initialMessages = [],
135
+ queueId,
136
+ onReset,
137
+ ...props
138
+ }: StoryProps & { queueId: ObjectId; onReset: () => void }) => {
139
+ const [running, setRunning] = useState(true);
140
+ const space = useSpace();
141
+ const members = useMembers(space?.key).map((member) => member.identity);
142
+ const queue = useTestTranscriptionQueue(space, queueId, running, 2_000);
143
+ const model = useQueueModelAdapter(renderMarkdown(members), queue, initialMessages);
144
+
145
+ return (
146
+ <TranscriptContainer
147
+ space={space}
148
+ model={model}
149
+ running={running}
150
+ onRunningChange={setRunning}
151
+ onReset={onReset}
152
+ {...props}
153
+ />
154
+ );
155
+ };
156
+
157
+ // TODO(burdon): Reconcile with QueueStory.
158
+ const EntityExtractionQueueStory = () => {
159
+ const [running, setRunning] = useState(true);
160
+ const space = useSpace();
161
+ const members = useMembers(space?.key).map((member) => member.identity);
162
+ const queue = useTestTranscriptionQueueWithEntityExtraction(space, undefined, running, 2_000);
163
+ const model = useQueueModelAdapter(renderMarkdown(members), queue, []);
164
+
165
+ return <TranscriptContainer space={space} model={model} running={running} onRunningChange={setRunning} />;
166
+ };
167
+
168
+ /**
169
+ * Wrapper remounts on refresh to reload queue.
170
+ */
171
+ const QueueStoryWrapper = () => {
172
+ const [queueId] = useState(ObjectId.random());
173
+ const [key, setKey] = useState(ObjectId.random().toString());
174
+ const handleReset = () => {
175
+ setKey(ObjectId.random().toString());
176
+ };
177
+
178
+ return <QueueStory key={key} queueId={queueId} onReset={handleReset} />;
179
+ };
180
+
181
+ const meta: Meta<typeof QueueStory> = {
41
182
  title: 'plugins/plugin-transcription/Transcript',
42
- component: Transcript,
43
- render: ({ blocks: initialBlocks = [], ...args }) => {
44
- const [blocks, setBlocks] = useState(initialBlocks);
45
- useEffect(() => {
46
- const i = setInterval(() => {
47
- setBlocks((blocks) => [...blocks, createBlock()]);
48
- }, 1_000);
49
-
50
- return () => clearInterval(i);
51
- }, []);
52
-
53
- return <Transcript {...args} blocks={blocks} />;
54
- },
55
183
  decorators: [
56
- withTheme,
57
- withLayout({
58
- tooltips: true,
59
- fullscreen: true,
184
+ withPluginManager({
185
+ plugins: [
186
+ ThemePlugin({ tx: defaultTx }),
187
+ StorybookLayoutPlugin(),
188
+ ClientPlugin({
189
+ types: [TestItem, Testing.DocumentType, DataType.Person, DataType.Organization],
190
+ onClientInitialized: async (_, client) => {
191
+ await client.halo.createIdentity();
192
+ },
193
+ }),
194
+ PreviewPlugin(),
195
+ SpacePlugin(),
196
+ SettingsPlugin(),
197
+ IntentPlugin(),
198
+ ],
60
199
  }),
200
+ withLayout({ tooltips: true, fullscreen: true }),
61
201
  ],
62
202
  parameters: {
63
203
  translations,
@@ -66,17 +206,35 @@ const meta: Meta<typeof Transcript> = {
66
206
 
67
207
  export default meta;
68
208
 
69
- type Story = StoryObj<typeof Transcript>;
209
+ type Story = StoryObj<typeof QueueStory>;
70
210
 
71
211
  export const Default: Story = {
212
+ render: BasicStory,
72
213
  args: {
73
214
  ignoreAttention: true,
74
215
  attendableId: 'story',
75
- blocks: Array.from({ length: 10 }, createBlock),
216
+ messages: await Promise.all(Array.from({ length: 10 }, () => MessageBuilder.singleton.createMessage())),
76
217
  },
77
218
  };
78
219
 
79
220
  export const Empty: Story = {
221
+ render: BasicStory,
222
+ args: {
223
+ ignoreAttention: true,
224
+ attendableId: 'story',
225
+ },
226
+ };
227
+
228
+ export const WithQueue: Story = {
229
+ render: QueueStoryWrapper,
230
+ args: {
231
+ ignoreAttention: true,
232
+ attendableId: 'story',
233
+ },
234
+ };
235
+
236
+ export const WithEntityExtractionQueue: Story = {
237
+ render: EntityExtractionQueueStory,
80
238
  args: {
81
239
  ignoreAttention: true,
82
240
  attendableId: 'story',
@@ -1,225 +1,100 @@
1
1
  //
2
- // Copyright 2023 DXOS.org
2
+ // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { intervalToDuration } from 'date-fns/intervalToDuration';
6
- import { yieldOrContinue } from 'main-thread-scheduling';
7
- import React, { type FC, useCallback, useEffect, useMemo, useRef, useState, type WheelEvent } from 'react';
8
- import { type OnResizeCallback, useResizeDetector } from 'react-resize-detector';
5
+ import React from 'react';
9
6
 
10
- import { IconButton, type ThemedClassName, useTranslation } from '@dxos/react-ui';
11
- import { useAttention } from '@dxos/react-ui-attention';
7
+ import { type Space } from '@dxos/react-client/echo';
8
+ import { type Identity } from '@dxos/react-client/halo';
9
+ import { IconButton, type ThemedClassName, useThemeContext } from '@dxos/react-ui';
12
10
  import {
13
- type DxGridCellValue,
14
- type DxGridElement,
15
- type DxGridAxisMeta,
16
- type DxGridPlaneCells,
17
- Grid,
18
- toPlaneCellIndex,
19
- } from '@dxos/react-ui-grid';
11
+ createBasicExtensions,
12
+ createMarkdownExtensions,
13
+ createRenderer,
14
+ createThemeExtensions,
15
+ decorateMarkdown,
16
+ editorWidth,
17
+ preview,
18
+ useTextEditor,
19
+ } from '@dxos/react-ui-editor';
20
20
  import { mx } from '@dxos/react-ui-theme';
21
-
22
- import { TRANSCRIPTION_PLUGIN } from '../../meta';
23
- import { type TranscriptBlock } from '../../types';
24
-
25
- // TODO(burdon): Actions (e.g., mark, summarize, translate, label, delete).
26
-
27
- const lineHeight = 20;
28
- const cellSpacing = 8 + 2;
29
- const timestampColumnWidth = 68;
30
-
31
- const authorClasses = 'font-medium text-base leading-[20px]';
32
- const timestampClasses = 'text-xs leading-[20px] text-description pie-0 tabular-nums';
33
- const segmentTextClasses = 'text-sm whitespace-normal hyphens-auto';
34
- const measureClasses = mx(
35
- // NOTE(thure): The `inline-start` value must equal `timestampColumnWidth` plus grid’s gap (1px)
36
- 'absolute inline-start-[69px] inline-end-0 invisible z-[-1] border',
37
- 'pli-[--dx-grid-cell-padding-inline] plb-[--dx-grid-cell-padding-block] leading-[20px]',
38
- segmentTextClasses,
39
- );
40
-
41
- const rowDefault = {
42
- grid: { size: lineHeight + cellSpacing },
43
- };
44
-
45
- type QueueRows = [number, number][];
46
-
47
- const mapTranscriptQueue = (blocks?: TranscriptBlock[]): QueueRows => {
48
- if (!blocks || !Array.isArray(blocks) || blocks.length === 0) {
49
- return [];
50
- } else {
51
- return blocks.flatMap((block, blockIndex) => {
52
- return [
53
- [blockIndex, -1],
54
- ...block.segments.map((segment, segmentIndex) => {
55
- return [blockIndex, segmentIndex] as [number, number];
56
- }),
57
- ];
58
- });
59
- }
60
- };
61
-
62
- const pad = (n = 0) => String(n).padStart(2, '0');
63
-
64
- const formatTimestamp = (start: Date, end: Date) => {
65
- const { hours, minutes, seconds } = intervalToDuration({ start, end });
66
- return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
67
- };
68
-
69
- const measureRows = async (
70
- host: HTMLDivElement,
71
- blocks: TranscriptBlock[],
72
- queueMap: QueueRows,
73
- signal: AbortSignal,
74
- ) => {
75
- const result: DxGridAxisMeta = { grid: {} };
76
- for (let row = 0; row < queueMap.length; row++) {
77
- await yieldOrContinue('smooth', signal);
78
- const [blockIndex, segmentIndex] = queueMap[row];
79
- if (segmentIndex < 0) {
80
- result.grid[row] = { size: lineHeight + cellSpacing };
81
- } else {
82
- host.textContent = blocks[blockIndex]!.segments[segmentIndex]!.text;
83
- result.grid[row] = { size: host.offsetHeight };
21
+ import { type DataType } from '@dxos/schema';
22
+ import { isNotFalsy } from '@dxos/util';
23
+
24
+ import { transcript } from './transcript-extension';
25
+ import { type SerializationModel } from '../../model';
26
+ import { type TranscriptType } from '../../types';
27
+
28
+ export const renderMarkdown =
29
+ (identities: Identity[]) =>
30
+ (message: DataType.Message, index: number, debug = false): string[] => {
31
+ if (message.sender.role === 'assistant') {
32
+ // Start/stop block.
33
+ return [message.blocks.find((block) => block.type === 'transcription')?.text ?? '', ''];
84
34
  }
85
- }
86
- return result;
87
- };
35
+
36
+ // TODO(burdon): Use link/reference markup for users (with popover).
37
+ // TODO(burdon): Color and avatar.
38
+ const identity = identities.find((identity) => identity.did === message.sender.identityDid);
39
+ const name =
40
+ identity?.profile?.displayName ??
41
+ message.sender.contact?.target?.fullName ??
42
+ message.sender.name ??
43
+ message.sender.email ??
44
+ message.sender.identityDid;
45
+ const blocks = message.blocks.filter((block) => block.type === 'transcription');
46
+ return [
47
+ `###### ${name}` + (debug ? ` [${index}]:${message.id}` : ''),
48
+ blocks.map((block) => block.text.trim()).join(' '),
49
+ '',
50
+ ];
51
+ };
88
52
 
89
53
  export type TranscriptProps = ThemedClassName<{
90
- blocks?: TranscriptBlock[];
54
+ transcript?: TranscriptType;
55
+ space?: Space;
56
+ model: SerializationModel<DataType.Message>;
57
+ // TODO(wittjosiah): Move to container.
91
58
  attendableId?: string;
92
59
  ignoreAttention?: boolean;
93
60
  }>;
94
61
 
95
- export const Transcript: FC<TranscriptProps> = ({ classNames, blocks, attendableId, ignoreAttention }) => {
96
- const { t } = useTranslation(TRANSCRIPTION_PLUGIN);
97
- const { hasAttention } = useAttention(attendableId);
98
- const [dxGrid, setDxGrid] = useState<DxGridElement | null>(null);
99
- const [rows, setRows] = useState<DxGridAxisMeta | undefined>(undefined);
100
- const [columns, setColumns] = useState<DxGridAxisMeta | undefined>(undefined);
101
- const [autoScroll, setAutoScroll] = useState(true);
102
-
103
- const queueMap = useMemo(() => mapTranscriptQueue(blocks), [blocks]);
104
-
105
- const handleWheel = useCallback(
106
- (event: WheelEvent) => {
107
- setAutoScroll(false);
108
- if (!ignoreAttention && !hasAttention) {
109
- event.stopPropagation();
110
- }
111
- },
112
- [hasAttention, ignoreAttention],
113
- );
114
-
115
- const abortControllerRef = useRef<AbortController>();
116
-
117
- const handleResize = useCallback(
118
- async ({ entry }: { entry: { target: HTMLDivElement } | null }) => {
119
- if (entry?.target && Array.isArray(blocks) && blocks[0] && queueMap) {
120
- abortControllerRef.current?.abort();
121
- abortControllerRef.current = new AbortController();
122
- setColumns({ grid: { 0: { size: timestampColumnWidth }, 1: { size: entry.target.offsetWidth } } });
123
- return measureRows(entry.target, blocks, queueMap, abortControllerRef.current.signal)
124
- .then(setRows)
125
- .catch(() => {
126
- // Aborted mid-measurement by new size.
127
- });
128
- }
129
- },
130
- [blocks, queueMap],
131
- );
132
-
133
- const { width, ref: measureRef } = useResizeDetector({
134
- onResize: handleResize as OnResizeCallback,
135
- refreshOptions: { leading: true },
136
- });
137
-
138
- useEffect(() => {
139
- if (queueMap.length !== Object.keys(rows?.grid ?? {}).length) {
140
- void handleResize({ entry: { target: measureRef.current } }).then(() => {
141
- if (autoScroll) {
142
- // TODO(thure): Implement a deterministic way to do this when `rows` has fully settled and grid has a new `maxPosBlock`.
143
- setTimeout(() => dxGrid?.scrollToEndRow(), 50);
144
- }
145
- });
146
- }
147
- }, [blocks, queueMap, width, autoScroll]);
148
-
149
- useEffect(() => {
150
- if (dxGrid && Array.isArray(blocks) && blocks[0]) {
151
- const transcriptStart = blocks[0]!.segments[0]!.started;
152
- dxGrid.getCells = (range, plane) => {
153
- switch (plane) {
154
- case 'grid': {
155
- const cells: DxGridPlaneCells = {};
156
- for (let row = range.start.row; row <= range.end.row && row < queueMap.length; row++) {
157
- const [blockIndex, segmentIndex] = queueMap[row];
158
-
159
- cells[toPlaneCellIndex({ col: 0, row })] = {
160
- readonly: true,
161
- value:
162
- segmentIndex < 0
163
- ? formatTimestamp(transcriptStart, blocks[blockIndex]!.segments[Math.max(0, segmentIndex)]!.started)
164
- : '',
165
- className: timestampClasses,
166
- } satisfies DxGridCellValue;
167
-
168
- cells[toPlaneCellIndex({ col: 1, row })] = (
169
- segmentIndex < 0
170
- ? {
171
- readonly: true,
172
- accessoryHtml: `<span data-hue="${blocks[blockIndex]!.authorHue}" class="dx-text-hue">${blocks[blockIndex]!.authorName}</span>`,
173
- className: authorClasses,
174
- }
175
- : {
176
- readonly: true,
177
- value: blocks[blockIndex]!.segments[segmentIndex]!.text,
178
- className: segmentTextClasses,
179
- }
180
- ) satisfies DxGridCellValue;
181
- }
182
- return cells;
183
- }
184
- default:
185
- return {};
186
- }
187
- };
188
- }
189
- }, [dxGrid, blocks]);
190
-
191
- const handleScrollToEnd = useCallback(() => {
192
- setAutoScroll(true);
193
- dxGrid?.scrollToEndRow();
194
- }, [dxGrid, autoScroll]);
62
+ /**
63
+ * Transcript component implemented using the editor.
64
+ */
65
+ export const Transcript = ({
66
+ classNames,
67
+ transcript: object,
68
+ space,
69
+ model,
70
+ attendableId,
71
+ ignoreAttention,
72
+ }: TranscriptProps) => {
73
+ const { themeMode } = useThemeContext();
74
+ const { parentRef } = useTextEditor(() => {
75
+ return {
76
+ extensions: [
77
+ createBasicExtensions({ readOnly: true, lineWrapping: true }),
78
+ createMarkdownExtensions({ themeMode }),
79
+ createThemeExtensions({ themeMode }),
80
+ decorateMarkdown(),
81
+ space && preview(),
82
+ transcript({
83
+ model,
84
+ 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
+ }),
89
+ ].filter(isNotFalsy),
90
+ };
91
+ }, [space, model]);
195
92
 
196
93
  return (
197
- <div role='none' className={mx('relative min-bs-0', classNames)}>
198
- <Grid.Root id={`${attendableId}--transcript`}>
199
- <Grid.Content
200
- limitColumns={2}
201
- limitRows={queueMap.length}
202
- columns={columns}
203
- rows={rows}
204
- rowDefault={rowDefault}
205
- onWheel={handleWheel}
206
- className='[--dx-grid-base:var(--dx-baseSurface)] [--dx-grid-lines:var(--dx-baseSurface)] [&_.dx-grid]:min-bs-0 [&_.dx-grid]:min-is-0 [&_.dx-grid]:select-auto'
207
- ref={setDxGrid}
208
- />
209
- </Grid.Root>
210
- <div role='none' {...{ inert: '' }} aria-hidden className={measureClasses} ref={measureRef} />
211
- <IconButton
212
- icon='ph--arrow-line-down--regular'
213
- iconOnly
214
- label={t('scroll to end label')}
215
- tooltipSide='left'
216
- data-state={autoScroll ? 'invisible' : 'visible'}
217
- classNames={[
218
- 'absolute inline-end-2 block-end-2 opacity-0 pointer-events-none',
219
- 'data-[state="visible"]:pointer-events-auto data-[state="visible"]:opacity-100 transition-opacity',
220
- ]}
221
- onClick={handleScrollToEnd}
222
- />
223
- </div>
94
+ <div
95
+ ref={parentRef}
96
+ className={mx('flex grow overflow-hidden', editorWidth, classNames)}
97
+ data-popover-collision-boundary={true}
98
+ />
224
99
  );
225
100
  };