@schlessera/brain-ui-react 0.4.0

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 (311) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -0
  3. package/dist/components/chat/ask-user-card.d.ts +12 -0
  4. package/dist/components/chat/ask-user-card.d.ts.map +1 -0
  5. package/dist/components/chat/ask-user-card.js +185 -0
  6. package/dist/components/chat/ask-user-card.js.map +1 -0
  7. package/dist/components/chat/brain-markdown.d.ts +35 -0
  8. package/dist/components/chat/brain-markdown.d.ts.map +1 -0
  9. package/dist/components/chat/brain-markdown.js +363 -0
  10. package/dist/components/chat/brain-markdown.js.map +1 -0
  11. package/dist/components/chat/chat-page.d.ts +2 -0
  12. package/dist/components/chat/chat-page.d.ts.map +1 -0
  13. package/dist/components/chat/chat-page.js +442 -0
  14. package/dist/components/chat/chat-page.js.map +1 -0
  15. package/dist/components/chat/command-palette.d.ts +5 -0
  16. package/dist/components/chat/command-palette.d.ts.map +1 -0
  17. package/dist/components/chat/command-palette.js +18 -0
  18. package/dist/components/chat/command-palette.js.map +1 -0
  19. package/dist/components/chat/markdown-content.d.ts +4 -0
  20. package/dist/components/chat/markdown-content.d.ts.map +1 -0
  21. package/dist/components/chat/markdown-content.js +6 -0
  22. package/dist/components/chat/markdown-content.js.map +1 -0
  23. package/dist/components/chat/message-bubble.d.ts +9 -0
  24. package/dist/components/chat/message-bubble.d.ts.map +1 -0
  25. package/dist/components/chat/message-bubble.js +129 -0
  26. package/dist/components/chat/message-bubble.js.map +1 -0
  27. package/dist/components/chat/message-share.d.ts +10 -0
  28. package/dist/components/chat/message-share.d.ts.map +1 -0
  29. package/dist/components/chat/message-share.js +65 -0
  30. package/dist/components/chat/message-share.js.map +1 -0
  31. package/dist/components/chat/renderers/claude-tools.d.ts +3 -0
  32. package/dist/components/chat/renderers/claude-tools.d.ts.map +1 -0
  33. package/dist/components/chat/renderers/claude-tools.js +38 -0
  34. package/dist/components/chat/renderers/claude-tools.js.map +1 -0
  35. package/dist/components/chat/renderers/generic.d.ts +9 -0
  36. package/dist/components/chat/renderers/generic.d.ts.map +1 -0
  37. package/dist/components/chat/renderers/generic.js +68 -0
  38. package/dist/components/chat/renderers/generic.js.map +1 -0
  39. package/dist/components/chat/renderers/index.d.ts +5 -0
  40. package/dist/components/chat/renderers/index.d.ts.map +1 -0
  41. package/dist/components/chat/renderers/index.js +17 -0
  42. package/dist/components/chat/renderers/index.js.map +1 -0
  43. package/dist/components/chat/risk-hints.d.ts +20 -0
  44. package/dist/components/chat/risk-hints.d.ts.map +1 -0
  45. package/dist/components/chat/risk-hints.js +81 -0
  46. package/dist/components/chat/risk-hints.js.map +1 -0
  47. package/dist/components/chat/session-drawer.d.ts +6 -0
  48. package/dist/components/chat/session-drawer.d.ts.map +1 -0
  49. package/dist/components/chat/session-drawer.js +96 -0
  50. package/dist/components/chat/session-drawer.js.map +1 -0
  51. package/dist/components/chat/share-block.d.ts +9 -0
  52. package/dist/components/chat/share-block.d.ts.map +1 -0
  53. package/dist/components/chat/share-block.js +70 -0
  54. package/dist/components/chat/share-block.js.map +1 -0
  55. package/dist/components/chat/tool-call-timeline.d.ts +8 -0
  56. package/dist/components/chat/tool-call-timeline.d.ts.map +1 -0
  57. package/dist/components/chat/tool-call-timeline.js +100 -0
  58. package/dist/components/chat/tool-call-timeline.js.map +1 -0
  59. package/dist/components/chat/tool-views.d.ts +116 -0
  60. package/dist/components/chat/tool-views.d.ts.map +1 -0
  61. package/dist/components/chat/tool-views.js +628 -0
  62. package/dist/components/chat/tool-views.js.map +1 -0
  63. package/dist/components/chat/welcome-state.d.ts +4 -0
  64. package/dist/components/chat/welcome-state.d.ts.map +1 -0
  65. package/dist/components/chat/welcome-state.js +10 -0
  66. package/dist/components/chat/welcome-state.js.map +1 -0
  67. package/dist/components/connectivity/connection-gate.d.ts +5 -0
  68. package/dist/components/connectivity/connection-gate.d.ts.map +1 -0
  69. package/dist/components/connectivity/connection-gate.js +42 -0
  70. package/dist/components/connectivity/connection-gate.js.map +1 -0
  71. package/dist/components/connectivity/login-screen.d.ts +9 -0
  72. package/dist/components/connectivity/login-screen.d.ts.map +1 -0
  73. package/dist/components/connectivity/login-screen.js +99 -0
  74. package/dist/components/connectivity/login-screen.js.map +1 -0
  75. package/dist/components/files/file-panel.d.ts +5 -0
  76. package/dist/components/files/file-panel.d.ts.map +1 -0
  77. package/dist/components/files/file-panel.js +46 -0
  78. package/dist/components/files/file-panel.js.map +1 -0
  79. package/dist/components/files/file-tree.d.ts +2 -0
  80. package/dist/components/files/file-tree.d.ts.map +1 -0
  81. package/dist/components/files/file-tree.js +63 -0
  82. package/dist/components/files/file-tree.js.map +1 -0
  83. package/dist/components/files/file-viewer-binary.d.ts +5 -0
  84. package/dist/components/files/file-viewer-binary.d.ts.map +1 -0
  85. package/dist/components/files/file-viewer-binary.js +20 -0
  86. package/dist/components/files/file-viewer-binary.js.map +1 -0
  87. package/dist/components/files/file-viewer-html.d.ts +4 -0
  88. package/dist/components/files/file-viewer-html.d.ts.map +1 -0
  89. package/dist/components/files/file-viewer-html.js +5 -0
  90. package/dist/components/files/file-viewer-html.js.map +1 -0
  91. package/dist/components/files/file-viewer-markdown.d.ts +4 -0
  92. package/dist/components/files/file-viewer-markdown.d.ts.map +1 -0
  93. package/dist/components/files/file-viewer-markdown.js +10 -0
  94. package/dist/components/files/file-viewer-markdown.js.map +1 -0
  95. package/dist/components/files/file-viewer-raw.d.ts +5 -0
  96. package/dist/components/files/file-viewer-raw.d.ts.map +1 -0
  97. package/dist/components/files/file-viewer-raw.js +15 -0
  98. package/dist/components/files/file-viewer-raw.js.map +1 -0
  99. package/dist/components/files/file-viewer.d.ts +2 -0
  100. package/dist/components/files/file-viewer.d.ts.map +1 -0
  101. package/dist/components/files/file-viewer.js +175 -0
  102. package/dist/components/files/file-viewer.js.map +1 -0
  103. package/dist/components/files/frontmatter-panel.d.ts +5 -0
  104. package/dist/components/files/frontmatter-panel.d.ts.map +1 -0
  105. package/dist/components/files/frontmatter-panel.js +12 -0
  106. package/dist/components/files/frontmatter-panel.js.map +1 -0
  107. package/dist/components/layout/app-shell.d.ts +5 -0
  108. package/dist/components/layout/app-shell.d.ts.map +1 -0
  109. package/dist/components/layout/app-shell.js +10 -0
  110. package/dist/components/layout/app-shell.js.map +1 -0
  111. package/dist/components/layout/mobile-tab-bar.d.ts +2 -0
  112. package/dist/components/layout/mobile-tab-bar.d.ts.map +1 -0
  113. package/dist/components/layout/mobile-tab-bar.js +16 -0
  114. package/dist/components/layout/mobile-tab-bar.js.map +1 -0
  115. package/dist/components/layout/side-rail.d.ts +2 -0
  116. package/dist/components/layout/side-rail.d.ts.map +1 -0
  117. package/dist/components/layout/side-rail.js +26 -0
  118. package/dist/components/layout/side-rail.js.map +1 -0
  119. package/dist/components/layout/slide-panel.d.ts +9 -0
  120. package/dist/components/layout/slide-panel.d.ts.map +1 -0
  121. package/dist/components/layout/slide-panel.js +18 -0
  122. package/dist/components/layout/slide-panel.js.map +1 -0
  123. package/dist/components/quick-actions/streaming-modal.d.ts +8 -0
  124. package/dist/components/quick-actions/streaming-modal.d.ts.map +1 -0
  125. package/dist/components/quick-actions/streaming-modal.js +92 -0
  126. package/dist/components/quick-actions/streaming-modal.js.map +1 -0
  127. package/dist/components/quick-actions/whatsup-modal.d.ts +5 -0
  128. package/dist/components/quick-actions/whatsup-modal.d.ts.map +1 -0
  129. package/dist/components/quick-actions/whatsup-modal.js +82 -0
  130. package/dist/components/quick-actions/whatsup-modal.js.map +1 -0
  131. package/dist/components/settings/passkey-panel.d.ts +10 -0
  132. package/dist/components/settings/passkey-panel.d.ts.map +1 -0
  133. package/dist/components/settings/passkey-panel.js +118 -0
  134. package/dist/components/settings/passkey-panel.js.map +1 -0
  135. package/dist/components/share/share-menu.d.ts +23 -0
  136. package/dist/components/share/share-menu.d.ts.map +1 -0
  137. package/dist/components/share/share-menu.js +58 -0
  138. package/dist/components/share/share-menu.js.map +1 -0
  139. package/dist/components/voice/dictation-sheet.d.ts +6 -0
  140. package/dist/components/voice/dictation-sheet.d.ts.map +1 -0
  141. package/dist/components/voice/dictation-sheet.js +91 -0
  142. package/dist/components/voice/dictation-sheet.js.map +1 -0
  143. package/dist/components/voice/mic-button.d.ts +6 -0
  144. package/dist/components/voice/mic-button.d.ts.map +1 -0
  145. package/dist/components/voice/mic-button.js +9 -0
  146. package/dist/components/voice/mic-button.js.map +1 -0
  147. package/dist/components/voice/review-card.d.ts +8 -0
  148. package/dist/components/voice/review-card.d.ts.map +1 -0
  149. package/dist/components/voice/review-card.js +8 -0
  150. package/dist/components/voice/review-card.js.map +1 -0
  151. package/dist/config.d.ts +19 -0
  152. package/dist/config.d.ts.map +1 -0
  153. package/dist/config.js +10 -0
  154. package/dist/config.js.map +1 -0
  155. package/dist/hooks/use-vpn-status.d.ts +2 -0
  156. package/dist/hooks/use-vpn-status.d.ts.map +1 -0
  157. package/dist/hooks/use-vpn-status.js +71 -0
  158. package/dist/hooks/use-vpn-status.js.map +1 -0
  159. package/dist/hooks/use-websocket.d.ts +8 -0
  160. package/dist/hooks/use-websocket.d.ts.map +1 -0
  161. package/dist/hooks/use-websocket.js +366 -0
  162. package/dist/hooks/use-websocket.js.map +1 -0
  163. package/dist/index.d.ts +25 -0
  164. package/dist/index.d.ts.map +1 -0
  165. package/dist/index.js +31 -0
  166. package/dist/index.js.map +1 -0
  167. package/dist/lib/api-client.d.ts +115 -0
  168. package/dist/lib/api-client.d.ts.map +1 -0
  169. package/dist/lib/api-client.js +70 -0
  170. package/dist/lib/api-client.js.map +1 -0
  171. package/dist/lib/backend.d.ts +7 -0
  172. package/dist/lib/backend.d.ts.map +1 -0
  173. package/dist/lib/backend.js +33 -0
  174. package/dist/lib/backend.js.map +1 -0
  175. package/dist/lib/frontmatter.d.ts +21 -0
  176. package/dist/lib/frontmatter.d.ts.map +1 -0
  177. package/dist/lib/frontmatter.js +88 -0
  178. package/dist/lib/frontmatter.js.map +1 -0
  179. package/dist/lib/image-attachments.d.ts +36 -0
  180. package/dist/lib/image-attachments.d.ts.map +1 -0
  181. package/dist/lib/image-attachments.js +175 -0
  182. package/dist/lib/image-attachments.js.map +1 -0
  183. package/dist/lib/passkeys.d.ts +18 -0
  184. package/dist/lib/passkeys.d.ts.map +1 -0
  185. package/dist/lib/passkeys.js +36 -0
  186. package/dist/lib/passkeys.js.map +1 -0
  187. package/dist/lib/share.d.ts +53 -0
  188. package/dist/lib/share.d.ts.map +1 -0
  189. package/dist/lib/share.js +144 -0
  190. package/dist/lib/share.js.map +1 -0
  191. package/dist/lib/strip-markdown.d.ts +10 -0
  192. package/dist/lib/strip-markdown.d.ts.map +1 -0
  193. package/dist/lib/strip-markdown.js +37 -0
  194. package/dist/lib/strip-markdown.js.map +1 -0
  195. package/dist/lib/tool-names.d.ts +17 -0
  196. package/dist/lib/tool-names.d.ts.map +1 -0
  197. package/dist/lib/tool-names.js +23 -0
  198. package/dist/lib/tool-names.js.map +1 -0
  199. package/dist/lib/utils.d.ts +3 -0
  200. package/dist/lib/utils.d.ts.map +1 -0
  201. package/dist/lib/utils.js +6 -0
  202. package/dist/lib/utils.js.map +1 -0
  203. package/dist/lib/ws-client.d.ts +26 -0
  204. package/dist/lib/ws-client.d.ts.map +1 -0
  205. package/dist/lib/ws-client.js +93 -0
  206. package/dist/lib/ws-client.js.map +1 -0
  207. package/dist/stores/chat-store.d.ts +131 -0
  208. package/dist/stores/chat-store.d.ts.map +1 -0
  209. package/dist/stores/chat-store.js +376 -0
  210. package/dist/stores/chat-store.js.map +1 -0
  211. package/dist/stores/connection-store.d.ts +11 -0
  212. package/dist/stores/connection-store.d.ts.map +1 -0
  213. package/dist/stores/connection-store.js +8 -0
  214. package/dist/stores/connection-store.js.map +1 -0
  215. package/dist/stores/file-store.d.ts +53 -0
  216. package/dist/stores/file-store.d.ts.map +1 -0
  217. package/dist/stores/file-store.js +284 -0
  218. package/dist/stores/file-store.js.map +1 -0
  219. package/dist/stores/provider-store.d.ts +24 -0
  220. package/dist/stores/provider-store.d.ts.map +1 -0
  221. package/dist/stores/provider-store.js +56 -0
  222. package/dist/stores/provider-store.js.map +1 -0
  223. package/dist/stores/ui-store.d.ts +21 -0
  224. package/dist/stores/ui-store.d.ts.map +1 -0
  225. package/dist/stores/ui-store.js +23 -0
  226. package/dist/stores/ui-store.js.map +1 -0
  227. package/dist/styles.css +2 -0
  228. package/dist/voice/asr-clients.d.ts +2 -0
  229. package/dist/voice/asr-clients.d.ts.map +1 -0
  230. package/dist/voice/asr-clients.js +33 -0
  231. package/dist/voice/asr-clients.js.map +1 -0
  232. package/dist/voice/asr-deepgram.d.ts +38 -0
  233. package/dist/voice/asr-deepgram.d.ts.map +1 -0
  234. package/dist/voice/asr-deepgram.js +307 -0
  235. package/dist/voice/asr-deepgram.js.map +1 -0
  236. package/dist/voice/asr-webspeech.d.ts +23 -0
  237. package/dist/voice/asr-webspeech.d.ts.map +1 -0
  238. package/dist/voice/asr-webspeech.js +93 -0
  239. package/dist/voice/asr-webspeech.js.map +1 -0
  240. package/dist/voice/use-dictation.d.ts +6 -0
  241. package/dist/voice/use-dictation.d.ts.map +1 -0
  242. package/dist/voice/use-dictation.js +174 -0
  243. package/dist/voice/use-dictation.js.map +1 -0
  244. package/dist/voice/voice-store.d.ts +26 -0
  245. package/dist/voice/voice-store.d.ts.map +1 -0
  246. package/dist/voice/voice-store.js +27 -0
  247. package/dist/voice/voice-store.js.map +1 -0
  248. package/package.json +71 -0
  249. package/src/components/chat/ask-user-card.tsx +451 -0
  250. package/src/components/chat/brain-markdown.tsx +547 -0
  251. package/src/components/chat/chat-page.tsx +820 -0
  252. package/src/components/chat/command-palette.tsx +68 -0
  253. package/src/components/chat/markdown-content.tsx +5 -0
  254. package/src/components/chat/message-bubble.tsx +362 -0
  255. package/src/components/chat/message-share.ts +78 -0
  256. package/src/components/chat/renderers/claude-tools.tsx +53 -0
  257. package/src/components/chat/renderers/generic.tsx +85 -0
  258. package/src/components/chat/renderers/index.ts +19 -0
  259. package/src/components/chat/risk-hints.ts +95 -0
  260. package/src/components/chat/session-drawer.tsx +213 -0
  261. package/src/components/chat/share-block.tsx +128 -0
  262. package/src/components/chat/tool-call-timeline.tsx +322 -0
  263. package/src/components/chat/tool-views.tsx +898 -0
  264. package/src/components/chat/welcome-state.tsx +105 -0
  265. package/src/components/connectivity/connection-gate.tsx +137 -0
  266. package/src/components/connectivity/login-screen.tsx +169 -0
  267. package/src/components/files/file-panel.tsx +116 -0
  268. package/src/components/files/file-tree.tsx +143 -0
  269. package/src/components/files/file-viewer-binary.tsx +63 -0
  270. package/src/components/files/file-viewer-html.tsx +10 -0
  271. package/src/components/files/file-viewer-markdown.tsx +14 -0
  272. package/src/components/files/file-viewer-raw.tsx +44 -0
  273. package/src/components/files/file-viewer.tsx +290 -0
  274. package/src/components/files/frontmatter-panel.tsx +78 -0
  275. package/src/components/layout/app-shell.tsx +25 -0
  276. package/src/components/layout/mobile-tab-bar.tsx +54 -0
  277. package/src/components/layout/side-rail.tsx +128 -0
  278. package/src/components/layout/slide-panel.tsx +64 -0
  279. package/src/components/quick-actions/streaming-modal.tsx +168 -0
  280. package/src/components/quick-actions/whatsup-modal.tsx +149 -0
  281. package/src/components/settings/passkey-panel.tsx +281 -0
  282. package/src/components/share/share-menu.tsx +121 -0
  283. package/src/components/voice/dictation-sheet.tsx +217 -0
  284. package/src/components/voice/mic-button.tsx +31 -0
  285. package/src/components/voice/review-card.tsx +66 -0
  286. package/src/config.ts +27 -0
  287. package/src/hooks/use-vpn-status.ts +75 -0
  288. package/src/hooks/use-websocket.ts +403 -0
  289. package/src/index.ts +46 -0
  290. package/src/lib/api-client.ts +173 -0
  291. package/src/lib/backend.ts +35 -0
  292. package/src/lib/frontmatter.ts +99 -0
  293. package/src/lib/image-attachments.ts +221 -0
  294. package/src/lib/passkeys.ts +50 -0
  295. package/src/lib/share.ts +167 -0
  296. package/src/lib/strip-markdown.ts +38 -0
  297. package/src/lib/tool-names.ts +27 -0
  298. package/src/lib/utils.ts +6 -0
  299. package/src/lib/ws-client.ts +109 -0
  300. package/src/stores/chat-store.ts +619 -0
  301. package/src/stores/connection-store.ts +23 -0
  302. package/src/stores/file-store.ts +344 -0
  303. package/src/stores/provider-store.ts +85 -0
  304. package/src/stores/ui-store.ts +48 -0
  305. package/src/styles.css +4 -0
  306. package/src/theme.css +425 -0
  307. package/src/voice/asr-clients.ts +45 -0
  308. package/src/voice/asr-deepgram.ts +325 -0
  309. package/src/voice/asr-webspeech.ts +135 -0
  310. package/src/voice/use-dictation.ts +187 -0
  311. package/src/voice/voice-store.ts +55 -0
@@ -0,0 +1,38 @@
1
+ import type { AsrClient } from "@schlessera/brain-ui-sdk/client";
2
+ import type { AsrEvent } from "@schlessera/brain-ui-sdk/protocol";
3
+ export interface DeepgramClientOptions {
4
+ /** Full Deepgram streaming WS URL (base params + keyterms), from the server. */
5
+ url: string;
6
+ /** Short-lived Deepgram token, from the server. */
7
+ token: string;
8
+ onEvent: (evt: AsrEvent) => void;
9
+ onAudioLevel?: (level: number) => void;
10
+ onError?: (err: Error) => void;
11
+ onClose?: () => void;
12
+ }
13
+ export declare class DeepgramClient implements AsrClient {
14
+ private ws;
15
+ private recorder;
16
+ private stream;
17
+ private audioContext;
18
+ private analyser;
19
+ private levelRaf;
20
+ private opts;
21
+ private pending;
22
+ private closed;
23
+ private drainResolve;
24
+ private wakeLock;
25
+ private wakeLockHandler;
26
+ constructor(opts: DeepgramClientOptions);
27
+ start(): Promise<void>;
28
+ private startRecorder;
29
+ private flushPending;
30
+ private handleDeepgramMessage;
31
+ private acquireWakeLock;
32
+ private releaseWakeLock;
33
+ private startAudioLevelMeter;
34
+ stop(): void;
35
+ drainAndStop(maxWaitMs?: number): Promise<void>;
36
+ private cleanup;
37
+ }
38
+ //# sourceMappingURL=asr-deepgram.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asr-deepgram.d.ts","sourceRoot":"","sources":["../../src/voice/asr-deepgram.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAElE,MAAM,WAAW,qBAAqB;IACpC,gFAAgF;IAChF,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,IAAI,CAAC;IACjC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,qBAAa,cAAe,YAAW,SAAS;IAC9C,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,IAAI,CAAwB;IAIpC,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,eAAe,CAA6B;gBAExC,IAAI,EAAE,qBAAqB;IAIjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsD5B,OAAO,CAAC,aAAa;IAmBrB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,qBAAqB;YAkCf,eAAe;IA0B7B,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,oBAAoB;IA6B5B,IAAI;IAQE,YAAY,CAAC,SAAS,SAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAgDnD,OAAO,CAAC,OAAO;CAiChB"}
@@ -0,0 +1,307 @@
1
+ // Deepgram streaming ASR client.
2
+ // Captures mic audio with MediaRecorder (Opus) and pipes it to Deepgram's
3
+ // streaming endpoint via WebSocket. The full connect URL (base params + domain
4
+ // keyterms) and a short-lived token are minted server-side and injected here;
5
+ // this client just opens the socket and emits partial + final transcript
6
+ // events. Implements the @schlessera/brain-ui-sdk AsrClient surface.
7
+ export class DeepgramClient {
8
+ ws = null;
9
+ recorder = null;
10
+ stream = null;
11
+ audioContext = null;
12
+ analyser = null;
13
+ levelRaf = null;
14
+ opts;
15
+ // Audio chunks captured before the Deepgram socket opens. The recorder starts
16
+ // as soon as the mic is granted; this holds the head of the utterance until
17
+ // it can be flushed on ws.onopen.
18
+ pending = [];
19
+ closed = false;
20
+ drainResolve = null;
21
+ wakeLock = null;
22
+ wakeLockHandler = null;
23
+ constructor(opts) {
24
+ this.opts = opts;
25
+ }
26
+ async start() {
27
+ const stream = await navigator.mediaDevices.getUserMedia({
28
+ audio: {
29
+ echoCancellation: true,
30
+ noiseSuppression: true,
31
+ channelCount: 1,
32
+ },
33
+ });
34
+ // Stopped while the mic permission was resolving? Release the freshly
35
+ // granted stream now — otherwise it stays live with no reference left to
36
+ // stop it (cleanup() already ran before the stream existed).
37
+ if (this.closed) {
38
+ for (const track of stream.getTracks())
39
+ track.stop();
40
+ return;
41
+ }
42
+ this.stream = stream;
43
+ void this.acquireWakeLock();
44
+ this.startAudioLevelMeter(this.stream);
45
+ // Record from the moment the mic is live — chunks buffer in `pending` until
46
+ // the socket opens, so connect latency doesn't eat the first words.
47
+ this.startRecorder();
48
+ if (this.closed)
49
+ return;
50
+ // Token is passed as a sec-websocket-protocol header per Deepgram spec.
51
+ this.ws = new WebSocket(this.opts.url, ["token", this.opts.token]);
52
+ this.ws.onopen = () => {
53
+ this.flushPending();
54
+ };
55
+ this.ws.onmessage = (evt) => {
56
+ try {
57
+ const data = JSON.parse(evt.data);
58
+ this.handleDeepgramMessage(data);
59
+ }
60
+ catch (err) {
61
+ this.opts.onError?.(err instanceof Error ? err : new Error("Bad Deepgram message"));
62
+ }
63
+ };
64
+ this.ws.onerror = () => {
65
+ this.opts.onError?.(new Error("Deepgram socket error"));
66
+ };
67
+ this.ws.onclose = () => {
68
+ this.cleanup();
69
+ if (!this.closed)
70
+ this.opts.onClose?.();
71
+ };
72
+ }
73
+ startRecorder() {
74
+ if (!this.stream)
75
+ return;
76
+ const mimeType = pickOpusMime();
77
+ this.recorder = new MediaRecorder(this.stream, {
78
+ mimeType,
79
+ audioBitsPerSecond: 32000,
80
+ });
81
+ this.recorder.ondataavailable = (evt) => {
82
+ if (evt.data.size === 0)
83
+ return;
84
+ if (this.ws?.readyState === WebSocket.OPEN) {
85
+ this.ws.send(evt.data);
86
+ }
87
+ else {
88
+ this.pending.push(evt.data);
89
+ }
90
+ };
91
+ // 250ms chunks — small enough to feel live, big enough to be Opus-frame-aligned
92
+ this.recorder.start(250);
93
+ }
94
+ flushPending() {
95
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
96
+ return;
97
+ for (const chunk of this.pending)
98
+ this.ws.send(chunk);
99
+ this.pending = [];
100
+ }
101
+ handleDeepgramMessage(data) {
102
+ if (data.type === "Results") {
103
+ const alt = data.channel?.alternatives?.[0];
104
+ const transcript = alt?.transcript ?? "";
105
+ if (data.is_final) {
106
+ if (transcript) {
107
+ this.opts.onEvent({
108
+ type: "final",
109
+ text: transcript,
110
+ endsTurn: !!data.speech_final,
111
+ });
112
+ }
113
+ // If we are draining, this final settles the wait — but allow more
114
+ // arrivals up to the deadline. We resolve on Metadata or timeout.
115
+ }
116
+ else if (transcript) {
117
+ this.opts.onEvent({ type: "partial", text: transcript });
118
+ }
119
+ return;
120
+ }
121
+ if (data.type === "UtteranceEnd") {
122
+ this.opts.onEvent({ type: "final", text: "", endsTurn: true });
123
+ return;
124
+ }
125
+ if (data.type === "Metadata") {
126
+ // Final summary — Deepgram sends this when the stream is closing.
127
+ this.drainResolve?.();
128
+ this.drainResolve = null;
129
+ return;
130
+ }
131
+ }
132
+ // Keep the screen on for the duration of capture so the phone doesn't sleep
133
+ // mid-sentence and clip the audio. The lock is released when the page becomes
134
+ // hidden, so re-acquire on visibility return.
135
+ async acquireWakeLock() {
136
+ if (typeof navigator === "undefined" || !("wakeLock" in navigator)) {
137
+ return;
138
+ }
139
+ try {
140
+ this.wakeLock = await navigator.wakeLock.request("screen");
141
+ this.wakeLock.addEventListener("release", () => {
142
+ this.wakeLock = null;
143
+ });
144
+ if (!this.wakeLockHandler) {
145
+ this.wakeLockHandler = () => {
146
+ if (document.visibilityState === "visible" &&
147
+ !this.closed &&
148
+ !this.wakeLock) {
149
+ void this.acquireWakeLock();
150
+ }
151
+ };
152
+ document.addEventListener("visibilitychange", this.wakeLockHandler);
153
+ }
154
+ }
155
+ catch (err) {
156
+ console.warn("[voice] Wake lock request failed:", err);
157
+ }
158
+ }
159
+ releaseWakeLock() {
160
+ if (this.wakeLockHandler) {
161
+ document.removeEventListener("visibilitychange", this.wakeLockHandler);
162
+ this.wakeLockHandler = null;
163
+ }
164
+ if (this.wakeLock) {
165
+ this.wakeLock.release().catch(() => { });
166
+ this.wakeLock = null;
167
+ }
168
+ }
169
+ startAudioLevelMeter(stream) {
170
+ if (!this.opts.onAudioLevel)
171
+ return;
172
+ try {
173
+ const ctx = new AudioContext();
174
+ const src = ctx.createMediaStreamSource(stream);
175
+ const analyser = ctx.createAnalyser();
176
+ analyser.fftSize = 512;
177
+ src.connect(analyser);
178
+ this.audioContext = ctx;
179
+ this.analyser = analyser;
180
+ const buf = new Uint8Array(analyser.frequencyBinCount);
181
+ const tick = () => {
182
+ if (!this.analyser)
183
+ return;
184
+ this.analyser.getByteTimeDomainData(buf);
185
+ let sum = 0;
186
+ for (let i = 0; i < buf.length; i++) {
187
+ const v = (buf[i] - 128) / 128;
188
+ sum += v * v;
189
+ }
190
+ const rms = Math.sqrt(sum / buf.length);
191
+ this.opts.onAudioLevel?.(Math.min(1, rms * 4));
192
+ this.levelRaf = requestAnimationFrame(tick);
193
+ };
194
+ tick();
195
+ }
196
+ catch (err) {
197
+ console.warn("[voice] Audio level meter failed:", err);
198
+ }
199
+ }
200
+ stop() {
201
+ this.closed = true;
202
+ this.cleanup();
203
+ }
204
+ // Graceful stop: tells Deepgram to flush any buffered audio, waits for the
205
+ // remaining final results, then closes. Use this for "Done" — it preserves
206
+ // the tail of an utterance that hasn't been finalized yet.
207
+ async drainAndStop(maxWaitMs = 2000) {
208
+ this.closed = true;
209
+ if (this.recorder && this.recorder.state === "recording") {
210
+ try {
211
+ this.recorder.stop();
212
+ }
213
+ catch { }
214
+ }
215
+ // If the socket is still connecting, give it a moment to open so the
216
+ // buffered head of the utterance can still be delivered and transcribed.
217
+ if (this.ws && this.ws.readyState === WebSocket.CONNECTING) {
218
+ await new Promise((resolve) => {
219
+ const timer = setTimeout(resolve, maxWaitMs);
220
+ const settle = () => {
221
+ clearTimeout(timer);
222
+ resolve();
223
+ };
224
+ this.ws.addEventListener("open", settle, { once: true });
225
+ this.ws.addEventListener("close", settle, { once: true });
226
+ });
227
+ }
228
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
229
+ this.cleanup();
230
+ return;
231
+ }
232
+ this.flushPending();
233
+ await new Promise((resolve) => {
234
+ let done = false;
235
+ const finish = () => {
236
+ if (done)
237
+ return;
238
+ done = true;
239
+ this.drainResolve = null;
240
+ clearTimeout(timer);
241
+ resolve();
242
+ };
243
+ const timer = setTimeout(finish, maxWaitMs);
244
+ this.drainResolve = finish;
245
+ try {
246
+ // Finalize emits final results for buffered audio without closing.
247
+ this.ws.send(JSON.stringify({ type: "Finalize" }));
248
+ // CloseStream then triggers a final Metadata message and closes.
249
+ this.ws.send(JSON.stringify({ type: "CloseStream" }));
250
+ }
251
+ catch {
252
+ finish();
253
+ }
254
+ });
255
+ this.cleanup();
256
+ }
257
+ cleanup() {
258
+ this.releaseWakeLock();
259
+ if (this.levelRaf !== null) {
260
+ cancelAnimationFrame(this.levelRaf);
261
+ this.levelRaf = null;
262
+ }
263
+ if (this.recorder && this.recorder.state !== "inactive") {
264
+ try {
265
+ this.recorder.stop();
266
+ }
267
+ catch { }
268
+ }
269
+ this.recorder = null;
270
+ this.pending = [];
271
+ if (this.stream) {
272
+ for (const track of this.stream.getTracks())
273
+ track.stop();
274
+ this.stream = null;
275
+ }
276
+ if (this.audioContext) {
277
+ this.audioContext.close().catch(() => { });
278
+ this.audioContext = null;
279
+ }
280
+ this.analyser = null;
281
+ if (this.ws && this.ws.readyState <= WebSocket.OPEN) {
282
+ try {
283
+ // Send Deepgram's CloseStream control message before closing
284
+ if (this.ws.readyState === WebSocket.OPEN) {
285
+ this.ws.send(JSON.stringify({ type: "CloseStream" }));
286
+ }
287
+ this.ws.close();
288
+ }
289
+ catch { }
290
+ }
291
+ this.ws = null;
292
+ }
293
+ }
294
+ function pickOpusMime() {
295
+ const candidates = [
296
+ "audio/webm;codecs=opus",
297
+ "audio/ogg;codecs=opus",
298
+ "audio/webm",
299
+ ];
300
+ for (const c of candidates) {
301
+ if (typeof MediaRecorder !== "undefined" && MediaRecorder.isTypeSupported(c)) {
302
+ return c;
303
+ }
304
+ }
305
+ return "audio/webm";
306
+ }
307
+ //# sourceMappingURL=asr-deepgram.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asr-deepgram.js","sourceRoot":"","sources":["../../src/voice/asr-deepgram.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { AsrClient } from "@schlessera/brain-ui-sdk/client";
2
+ import type { AsrEvent } from "@schlessera/brain-ui-sdk/protocol";
3
+ /** True when this browser supports the Web Speech API. */
4
+ export declare function isWebSpeechAvailable(): boolean;
5
+ export interface WebSpeechClientOptions {
6
+ onEvent: (evt: AsrEvent) => void;
7
+ onError?: (err: Error) => void;
8
+ onEnd?: () => void;
9
+ /** BCP-47 language tag. Defaults to "en-US". */
10
+ lang?: string;
11
+ }
12
+ export declare class WebSpeechClient implements AsrClient {
13
+ private recognition;
14
+ private opts;
15
+ private closed;
16
+ private endResolvers;
17
+ constructor(opts: WebSpeechClientOptions);
18
+ start(): Promise<void>;
19
+ stop(): void;
20
+ drainAndStop(): Promise<void>;
21
+ private settleEnd;
22
+ }
23
+ //# sourceMappingURL=asr-webspeech.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asr-webspeech.d.ts","sourceRoot":"","sources":["../../src/voice/asr-webspeech.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAqClE,0DAA0D;AAC1D,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,IAAI,CAAC;IACjC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,eAAgB,YAAW,SAAS;IAC/C,OAAO,CAAC,WAAW,CAAsC;IACzD,OAAO,CAAC,IAAI,CAAyB;IACrC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAAyB;gBAEjC,IAAI,EAAE,sBAAsB;IAIlC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsC5B,IAAI;IASE,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAenC,OAAO,CAAC,SAAS;CAKlB"}
@@ -0,0 +1,93 @@
1
+ // Web Speech API ASR client — the `webspeech` provider. Recognition runs
2
+ // entirely in the browser (no server session, no token), so this is the
3
+ // zero-dependency / zero-cost STT path. Availability varies by browser
4
+ // (Chrome/Edge yes, Firefox no), so callers must guard with
5
+ // isWebSpeechAvailable() before selecting it.
6
+ function getRecognitionCtor() {
7
+ if (typeof window === "undefined")
8
+ return null;
9
+ const w = window;
10
+ return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;
11
+ }
12
+ /** True when this browser supports the Web Speech API. */
13
+ export function isWebSpeechAvailable() {
14
+ return getRecognitionCtor() !== null;
15
+ }
16
+ export class WebSpeechClient {
17
+ recognition = null;
18
+ opts;
19
+ closed = false;
20
+ endResolvers = [];
21
+ constructor(opts) {
22
+ this.opts = opts;
23
+ }
24
+ async start() {
25
+ const Ctor = getRecognitionCtor();
26
+ if (!Ctor) {
27
+ throw new Error("Web Speech API is not available in this browser.");
28
+ }
29
+ const recognition = new Ctor();
30
+ recognition.lang = this.opts.lang ?? "en-US";
31
+ recognition.continuous = true;
32
+ recognition.interimResults = true;
33
+ recognition.onresult = (event) => {
34
+ for (let i = event.resultIndex; i < event.results.length; i++) {
35
+ const result = event.results[i];
36
+ const transcript = result[0]?.transcript ?? "";
37
+ if (!transcript)
38
+ continue;
39
+ if (result.isFinal) {
40
+ // No reliable end-of-utterance signal, so never auto-end the turn.
41
+ this.opts.onEvent({ type: "final", text: transcript, endsTurn: false });
42
+ }
43
+ else {
44
+ this.opts.onEvent({ type: "partial", text: transcript });
45
+ }
46
+ }
47
+ };
48
+ recognition.onerror = (event) => {
49
+ if (this.closed)
50
+ return;
51
+ this.opts.onError?.(new Error(`Speech recognition error: ${event.error ?? "unknown"}`));
52
+ };
53
+ recognition.onend = () => {
54
+ this.settleEnd();
55
+ if (!this.closed)
56
+ this.opts.onEnd?.();
57
+ };
58
+ this.recognition = recognition;
59
+ recognition.start();
60
+ }
61
+ stop() {
62
+ this.closed = true;
63
+ try {
64
+ this.recognition?.abort();
65
+ }
66
+ catch { }
67
+ this.recognition = null;
68
+ this.settleEnd();
69
+ }
70
+ async drainAndStop() {
71
+ this.closed = true;
72
+ if (!this.recognition)
73
+ return;
74
+ // stop() flushes the pending final result, then fires onend.
75
+ const done = new Promise((resolve) => this.endResolvers.push(resolve));
76
+ try {
77
+ this.recognition.stop();
78
+ }
79
+ catch {
80
+ this.settleEnd();
81
+ }
82
+ // Bound the wait so a browser that never fires onend can't hang the UI.
83
+ await Promise.race([done, new Promise((r) => setTimeout(r, 1500))]);
84
+ this.recognition = null;
85
+ }
86
+ settleEnd() {
87
+ const resolvers = this.endResolvers;
88
+ this.endResolvers = [];
89
+ for (const resolve of resolvers)
90
+ resolve();
91
+ }
92
+ }
93
+ //# sourceMappingURL=asr-webspeech.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asr-webspeech.js","sourceRoot":"","sources":["../../src/voice/asr-webspeech.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export declare function useDictation(): {
2
+ start: () => Promise<void>;
3
+ stop: (commitToReview?: boolean) => Promise<void>;
4
+ cancel: () => void;
5
+ };
6
+ //# sourceMappingURL=use-dictation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-dictation.d.ts","sourceRoot":"","sources":["../../src/voice/use-dictation.ts"],"names":[],"mappings":"AA6BA,wBAAgB,YAAY;;;;EA6J3B"}