@ephia/dova-sdk 1.0.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 (247) hide show
  1. package/README.md +89 -0
  2. package/dist/EphiaBinding-BvRmlqqC.d.ts +36 -0
  3. package/dist/EphiaFloatingButton-CxiF86VW.d.ts +65 -0
  4. package/dist/EphiaTextarea-B4_CAVUg.d.ts +183 -0
  5. package/dist/NativeBinding-ChG0GeSz.d.ts +53 -0
  6. package/dist/TargetBinding-BKGQwUMc.d.ts +89 -0
  7. package/dist/TiptapBinding-B-agfV2H.d.ts +45 -0
  8. package/dist/Transport-zdeA4Pou.d.ts +63 -0
  9. package/dist/audio-state-kZ3KSvux.d.ts +39 -0
  10. package/dist/chunk-35AJK2IO.js +1 -0
  11. package/dist/chunk-35AJK2IO.js.map +1 -0
  12. package/dist/chunk-3LXZODL4.js +886 -0
  13. package/dist/chunk-3LXZODL4.js.map +1 -0
  14. package/dist/chunk-5IK5TLSK.js +67 -0
  15. package/dist/chunk-5IK5TLSK.js.map +1 -0
  16. package/dist/chunk-7E43RY75.js +9 -0
  17. package/dist/chunk-7E43RY75.js.map +1 -0
  18. package/dist/chunk-A5UEXJ5R.js +183 -0
  19. package/dist/chunk-A5UEXJ5R.js.map +1 -0
  20. package/dist/chunk-AEE554FT.js +51 -0
  21. package/dist/chunk-AEE554FT.js.map +1 -0
  22. package/dist/chunk-DIEWY3IT.js +1332 -0
  23. package/dist/chunk-DIEWY3IT.js.map +1 -0
  24. package/dist/chunk-EGIAN7FH.js +18 -0
  25. package/dist/chunk-EGIAN7FH.js.map +1 -0
  26. package/dist/chunk-EMOEAPVU.js +486 -0
  27. package/dist/chunk-EMOEAPVU.js.map +1 -0
  28. package/dist/chunk-IDC7FHIZ.js +40 -0
  29. package/dist/chunk-IDC7FHIZ.js.map +1 -0
  30. package/dist/chunk-ITJFN3VM.js +601 -0
  31. package/dist/chunk-ITJFN3VM.js.map +1 -0
  32. package/dist/chunk-K24GNU27.js +22 -0
  33. package/dist/chunk-K24GNU27.js.map +1 -0
  34. package/dist/chunk-LXMCRXXF.js +778 -0
  35. package/dist/chunk-LXMCRXXF.js.map +1 -0
  36. package/dist/chunk-MJCEOOLW.js +122 -0
  37. package/dist/chunk-MJCEOOLW.js.map +1 -0
  38. package/dist/chunk-N7U5M3VZ.js +33 -0
  39. package/dist/chunk-N7U5M3VZ.js.map +1 -0
  40. package/dist/chunk-PSYX674B.js +27 -0
  41. package/dist/chunk-PSYX674B.js.map +1 -0
  42. package/dist/chunk-RFQRV7ML.js +33 -0
  43. package/dist/chunk-RFQRV7ML.js.map +1 -0
  44. package/dist/chunk-THNHRV2B.js +18 -0
  45. package/dist/chunk-THNHRV2B.js.map +1 -0
  46. package/dist/chunk-VSLGR64U.js +62 -0
  47. package/dist/chunk-VSLGR64U.js.map +1 -0
  48. package/dist/chunk-W2ZP674X.js +346 -0
  49. package/dist/chunk-W2ZP674X.js.map +1 -0
  50. package/dist/chunk-YWZUMUYE.js +695 -0
  51. package/dist/chunk-YWZUMUYE.js.map +1 -0
  52. package/dist/client-options-Uo6jXO8k.d.ts +64 -0
  53. package/dist/connection-state-Bk33YprE.d.ts +32 -0
  54. package/dist/core/bindings/index.d.ts +24 -0
  55. package/dist/core/bindings/index.js +1025 -0
  56. package/dist/core/bindings/index.js.map +1 -0
  57. package/dist/core/index.d.ts +383 -0
  58. package/dist/core/index.js +1284 -0
  59. package/dist/core/index.js.map +1 -0
  60. package/dist/createEphiaClient-BhdZ183V.d.ts +69 -0
  61. package/dist/devices/speechmike/index.d.ts +148 -0
  62. package/dist/devices/speechmike/index.js +40 -0
  63. package/dist/devices/speechmike/index.js.map +1 -0
  64. package/dist/headless/index.d.ts +10 -0
  65. package/dist/headless/index.js +25 -0
  66. package/dist/headless/index.js.map +1 -0
  67. package/dist/index.d.ts +18 -0
  68. package/dist/index.js +119 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/react/index.d.ts +38 -0
  71. package/dist/react/index.js +70 -0
  72. package/dist/react/index.js.map +1 -0
  73. package/dist/rich-editor/index.d.ts +46 -0
  74. package/dist/rich-editor/index.js +13 -0
  75. package/dist/rich-editor/index.js.map +1 -0
  76. package/dist/schema-B2ycPlNB.d.ts +87 -0
  77. package/dist/session-APaXR48R.d.ts +12 -0
  78. package/dist/shared/index.d.ts +16 -0
  79. package/dist/shared/index.js +30 -0
  80. package/dist/shared/index.js.map +1 -0
  81. package/dist/style.css +1093 -0
  82. package/dist/testing/index.d.ts +84 -0
  83. package/dist/testing/index.js +36 -0
  84. package/dist/testing/index.js.map +1 -0
  85. package/dist/types-D5SXPSwR.d.ts +32 -0
  86. package/dist/ui/index.d.ts +30 -0
  87. package/dist/ui/index.js +34 -0
  88. package/dist/ui/index.js.map +1 -0
  89. package/dist/useEphiaSpeechMike-CjD7DWnh.d.ts +64 -0
  90. package/package.json +110 -0
  91. package/src/core/audio/audio-worklet-source.ts +30 -0
  92. package/src/core/audio/index.ts +3 -0
  93. package/src/core/audio/voice-level-meter.test.ts +27 -0
  94. package/src/core/audio/voice-level-meter.ts +270 -0
  95. package/src/core/bindings/EphiaBinding.ts +41 -0
  96. package/src/core/bindings/SegmentBindingBridge.test.ts +422 -0
  97. package/src/core/bindings/SegmentBindingBridge.ts +377 -0
  98. package/src/core/bindings/TargetBinding.ts +142 -0
  99. package/src/core/bindings/adapters/NativeAdapter.test.ts +85 -0
  100. package/src/core/bindings/adapters/NativeAdapter.ts +216 -0
  101. package/src/core/bindings/adapters/ProseMirrorAdapter.ts +231 -0
  102. package/src/core/bindings/adapters/index.ts +2 -0
  103. package/src/core/bindings/binding-factory.ts +78 -0
  104. package/src/core/bindings/detect-editor-type.ts +87 -0
  105. package/src/core/bindings/index.ts +13 -0
  106. package/src/core/bindings/insertion-boundary.test.ts +38 -0
  107. package/src/core/bindings/insertion-boundary.ts +26 -0
  108. package/src/core/bindings/native/NativeBinding.test.ts +277 -0
  109. package/src/core/bindings/native/NativeBinding.ts +239 -0
  110. package/src/core/bindings/resolver.ts +18 -0
  111. package/src/core/bindings/targets/codemirror.binding.ts +293 -0
  112. package/src/core/bindings/targets/contenteditable.binding.ts +452 -0
  113. package/src/core/bindings/targets/index.ts +10 -0
  114. package/src/core/bindings/targets/monaco.binding.ts +315 -0
  115. package/src/core/bindings/targets/tiptap.binding.test.ts +417 -0
  116. package/src/core/bindings/targets/tiptap.binding.ts +1192 -0
  117. package/src/core/bindings/tiptap/TiptapBinding.test.ts +63 -0
  118. package/src/core/bindings/tiptap/TiptapBinding.ts +464 -0
  119. package/src/core/bindings/types.ts +41 -0
  120. package/src/core/client/EphiaAudioClient.ts +654 -0
  121. package/src/core/client/audio-capture.ts +263 -0
  122. package/src/core/client/client-options.ts +39 -0
  123. package/src/core/client/client-state.ts +18 -0
  124. package/src/core/client/constants.ts +23 -0
  125. package/src/core/client/session-api.ts +415 -0
  126. package/src/core/connection/connection-state.ts +78 -0
  127. package/src/core/connection/index.ts +6 -0
  128. package/src/core/index.ts +47 -0
  129. package/src/core/operations/textToDocumentOperations.test.ts +69 -0
  130. package/src/core/operations/textToDocumentOperations.ts +92 -0
  131. package/src/core/runtime/DictationRuntime.test.ts +578 -0
  132. package/src/core/runtime/DictationRuntime.ts +434 -0
  133. package/src/core/runtime/TranscriptApplier.test.ts +355 -0
  134. package/src/core/runtime/TranscriptApplier.ts +229 -0
  135. package/src/core/runtime/index.ts +18 -0
  136. package/src/core/session/index.ts +2 -0
  137. package/src/core/session/session-machine.test.ts +16 -0
  138. package/src/core/session/session-machine.ts +59 -0
  139. package/src/core/targets/EditorContextCollector.ts +71 -0
  140. package/src/core/targets/TargetManager.test.ts +194 -0
  141. package/src/core/targets/TargetManager.ts +194 -0
  142. package/src/core/targets/index.ts +10 -0
  143. package/src/core/text-processing/index.ts +11 -0
  144. package/src/core/text-processing/overlap.test.ts +35 -0
  145. package/src/core/text-processing/overlap.ts +101 -0
  146. package/src/core/text-processing/voice-formatting.normalizer.test.ts +132 -0
  147. package/src/core/text-processing/voice-formatting.normalizer.ts +284 -0
  148. package/src/core/transcript/client-transcript.reducer.ts +366 -0
  149. package/src/core/transcript/client-transcript.state.ts +25 -0
  150. package/src/core/transcript/index.ts +19 -0
  151. package/src/core/transcript/transcript.assembler.test.ts +205 -0
  152. package/src/core/transcript/transcript.assembler.ts +152 -0
  153. package/src/core/transcript/transcript.reducer.test.ts +199 -0
  154. package/src/core/transcript/transcript.reducer.ts +771 -0
  155. package/src/core/transcript/transcript.state.ts +123 -0
  156. package/src/core/transport/LiveKitTransport.publish.test.ts +226 -0
  157. package/src/core/transport/LiveKitTransport.ts +459 -0
  158. package/src/core/transport/MockTransport.ts +231 -0
  159. package/src/core/transport/Transport.ts +82 -0
  160. package/src/debug/sdk-debug-collector.ts +79 -0
  161. package/src/devices/index.ts +2 -0
  162. package/src/devices/speechmike/__tests__/EphiaSpeechMikeProvider.test.tsx +99 -0
  163. package/src/devices/speechmike/__tests__/speechmike-audio-resolver.test.ts +96 -0
  164. package/src/devices/speechmike/__tests__/speechmike-button-router.test.ts +66 -0
  165. package/src/devices/speechmike/__tests__/speechmike-device-manager.test.ts +201 -0
  166. package/src/devices/speechmike/__tests__/speechmike-led-controller.test.ts +68 -0
  167. package/src/devices/speechmike/browser.ts +80 -0
  168. package/src/devices/speechmike/constants.ts +74 -0
  169. package/src/devices/speechmike/dictation-support-loader.ts +81 -0
  170. package/src/devices/speechmike/index.ts +11 -0
  171. package/src/devices/speechmike/react/EphiaSpeechMikeContext.ts +34 -0
  172. package/src/devices/speechmike/react/EphiaSpeechMikeProvider.tsx +287 -0
  173. package/src/devices/speechmike/react/useEphiaSpeechMike.ts +26 -0
  174. package/src/devices/speechmike/speechmike-audio-resolver.ts +58 -0
  175. package/src/devices/speechmike/speechmike-button-router.ts +73 -0
  176. package/src/devices/speechmike/speechmike-device-manager.ts +461 -0
  177. package/src/devices/speechmike/speechmike-led-controller.ts +78 -0
  178. package/src/devices/speechmike/types.ts +96 -0
  179. package/src/dictation_support.d.ts +31 -0
  180. package/src/global.d.ts +10 -0
  181. package/src/headless/createEphiaClient.ts +220 -0
  182. package/src/headless/index.ts +18 -0
  183. package/src/index.ts +89 -0
  184. package/src/react/EphiaAuto.tsx +87 -0
  185. package/src/react/components/EphiaDictationButton.tsx +88 -0
  186. package/src/react/components/EphiaStatusBar.tsx +59 -0
  187. package/src/react/components/EphiaTextarea.tsx +295 -0
  188. package/src/react/ephia-react.css +318 -0
  189. package/src/react/hooks/targets/index.ts +3 -0
  190. package/src/react/hooks/targets/useEphiaCodemirror.ts +35 -0
  191. package/src/react/hooks/targets/useEphiaMonaco.ts +35 -0
  192. package/src/react/hooks/targets/useEphiaTiptap.ts +23 -0
  193. package/src/react/hooks/useEphia.lifecycle.test.tsx +389 -0
  194. package/src/react/hooks/useEphia.ts +367 -0
  195. package/src/react/hooks/useEphiaDiscardTarget.ts +53 -0
  196. package/src/react/hooks/useEphiaServerEvent.ts +33 -0
  197. package/src/react/hooks/useEphiaTarget.ts +47 -0
  198. package/src/react/hooks/useEphiaTranscript.ts +22 -0
  199. package/src/react/index.ts +58 -0
  200. package/src/react/provider/EphiaContext.ts +63 -0
  201. package/src/react/provider/EphiaInternalContext.ts +32 -0
  202. package/src/react/provider/EphiaProvider.tsx +373 -0
  203. package/src/react/registry/binding-factory.ts +7 -0
  204. package/src/react/registry/detect-editor-type.ts +2 -0
  205. package/src/react/registry/events.ts +37 -0
  206. package/src/react/registry/registries/CodeMirrorInstanceRegistry.ts +24 -0
  207. package/src/react/registry/registries/MonacoInstanceRegistry.ts +23 -0
  208. package/src/react/registry/registries/TargetRegistry.ts +327 -0
  209. package/src/react/registry/registries/TiptapInstanceRegistry.ts +43 -0
  210. package/src/react/registry/registries/index.ts +5 -0
  211. package/src/react/store/create-ephia-store.ts +36 -0
  212. package/src/react/store/types.ts +41 -0
  213. package/src/react/utils/flash-range.ts +24 -0
  214. package/src/react/utils/index.ts +1 -0
  215. package/src/rich-editor/adapters/tiptap.test.ts +86 -0
  216. package/src/rich-editor/adapters/tiptap.ts +23 -0
  217. package/src/rich-editor/index.ts +3 -0
  218. package/src/rich-editor/types.ts +24 -0
  219. package/src/rich-editor/use-ephia-rich-editor.test.tsx +202 -0
  220. package/src/rich-editor/use-ephia-rich-editor.ts +47 -0
  221. package/src/shared/config/endpoint.test.ts +45 -0
  222. package/src/shared/config/endpoint.ts +39 -0
  223. package/src/shared/config/schema.ts +32 -0
  224. package/src/shared/effective-text.ts +13 -0
  225. package/src/shared/errors/EphiaSdkError.ts +54 -0
  226. package/src/shared/errors/messages.ts +40 -0
  227. package/src/shared/index.ts +27 -0
  228. package/src/shared/state/audio-state.ts +45 -0
  229. package/src/shared/state/index.ts +2 -0
  230. package/src/shared/store/document-store.ts +32 -0
  231. package/src/shared/store/index.ts +2 -0
  232. package/src/shared/types/editors.ts +28 -0
  233. package/src/shared/types/session.ts +12 -0
  234. package/src/style.css +2 -0
  235. package/src/testing/index.tsx +60 -0
  236. package/src/ui/assets/ephia-logo.svg +4 -0
  237. package/src/ui/components/EphiaLogo.tsx +77 -0
  238. package/src/ui/index.ts +24 -0
  239. package/src/ui/primitives/Button.tsx +53 -0
  240. package/src/ui/primitives/Spinner.tsx +21 -0
  241. package/src/ui/primitives/index.ts +5 -0
  242. package/src/ui/recorder/EphiaFloatingButton.tsx +489 -0
  243. package/src/ui/recorder/MinimalProcessingBars.tsx +122 -0
  244. package/src/ui/recorder/StandardIntensityVisualizer.tsx +148 -0
  245. package/src/ui/recorder/appearance.ts +9 -0
  246. package/src/ui/recorder/index.ts +8 -0
  247. package/src/ui/theme.css +775 -0
@@ -0,0 +1,601 @@
1
+ import {
2
+ createEphiaStore
3
+ } from "./chunk-IDC7FHIZ.js";
4
+ import {
5
+ ephiaClientConfigSchema,
6
+ getMessage
7
+ } from "./chunk-5IK5TLSK.js";
8
+ import {
9
+ mapTransportStateToConnectionState
10
+ } from "./chunk-RFQRV7ML.js";
11
+ import {
12
+ EphiaAudioClient
13
+ } from "./chunk-DIEWY3IT.js";
14
+ import {
15
+ resolveEphiaSdkApiUrl
16
+ } from "./chunk-PSYX674B.js";
17
+ import {
18
+ TargetManager,
19
+ TranscriptApplier,
20
+ dbgState
21
+ } from "./chunk-LXMCRXXF.js";
22
+ import {
23
+ toEphiaError
24
+ } from "./chunk-N7U5M3VZ.js";
25
+ import {
26
+ EphiaInternalContext,
27
+ useEphiaInternal
28
+ } from "./chunk-THNHRV2B.js";
29
+ import {
30
+ EphiaContext,
31
+ useEphiaContext,
32
+ useOptionalEphiaContext
33
+ } from "./chunk-K24GNU27.js";
34
+
35
+ // src/react/provider/EphiaProvider.tsx
36
+ import { useEffect, useMemo, useRef, useState } from "react";
37
+ import { jsx } from "react/jsx-runtime";
38
+ async function preloadWithRetry(client, signal, options, maxRetries = 3) {
39
+ for (let attempt = 0; attempt < maxRetries; attempt += 1) {
40
+ if (signal.aborted) return;
41
+ try {
42
+ await client.preload(options, signal);
43
+ return;
44
+ } catch {
45
+ if (attempt < maxRetries - 1 && !signal.aborted) {
46
+ await new Promise((resolve) => {
47
+ setTimeout(resolve, 500 * 2 ** attempt);
48
+ });
49
+ }
50
+ }
51
+ }
52
+ }
53
+ function mapClientStatus(clientStatus, currentStatus) {
54
+ switch (clientStatus) {
55
+ case "recording":
56
+ return "recording";
57
+ case "creating_session":
58
+ case "connecting_transport":
59
+ case "connecting":
60
+ case "ready":
61
+ if (currentStatus === "recording") return "recording";
62
+ if (currentStatus === "processing") return "processing";
63
+ return "idle";
64
+ case "paused":
65
+ if (currentStatus === "processing") return "processing";
66
+ return "idle";
67
+ case "finalizing":
68
+ if (currentStatus === "recording" || currentStatus === "processing") {
69
+ return "processing";
70
+ }
71
+ return "idle";
72
+ case "ended":
73
+ return "idle";
74
+ case "error":
75
+ return "error";
76
+ case "idle":
77
+ case "disposed":
78
+ case "disconnected":
79
+ if (currentStatus === "processing") return "processing";
80
+ return "idle";
81
+ default:
82
+ return currentStatus;
83
+ }
84
+ }
85
+ function EphiaProvider({
86
+ children,
87
+ apiUrl,
88
+ apiKey,
89
+ bearerToken,
90
+ clientType,
91
+ transport,
92
+ options = {},
93
+ onError,
94
+ onStatusChange,
95
+ preloadSession = true,
96
+ preloadInitialTargetId,
97
+ noiseFilter = false,
98
+ vadGate = true,
99
+ audioInputDeviceId
100
+ }) {
101
+ const resolvedApiUrl = useMemo(
102
+ () => resolveEphiaSdkApiUrl(apiUrl),
103
+ [apiUrl]
104
+ );
105
+ const storeRef = useRef(null);
106
+ const clientRef = useRef(null);
107
+ const targetManagerRef = useRef(null);
108
+ const applierRef = useRef(null);
109
+ const applyServerEventRef = useRef(() => {
110
+ });
111
+ const [clientEpoch, setClientEpoch] = useState(0);
112
+ const onErrorRef = useRef(onError);
113
+ onErrorRef.current = onError;
114
+ const onStatusChangeRef = useRef(onStatusChange);
115
+ onStatusChangeRef.current = onStatusChange;
116
+ const sessionOptionsKey = useMemo(
117
+ () => JSON.stringify(options.sessionOptions ?? null),
118
+ [options.sessionOptions]
119
+ );
120
+ if (!storeRef.current) {
121
+ storeRef.current = createEphiaStore();
122
+ }
123
+ const store = storeRef.current;
124
+ useEffect(() => {
125
+ const cfgValidation = ephiaClientConfigSchema.partial().safeParse({
126
+ apiUrl: resolvedApiUrl,
127
+ apiKey
128
+ });
129
+ if (!cfgValidation.success) {
130
+ console.warn("[ephia] invalid provider config:", cfgValidation.error.issues);
131
+ }
132
+ const client = new EphiaAudioClient({
133
+ apiUrl: resolvedApiUrl,
134
+ apiKey,
135
+ bearerToken,
136
+ clientType,
137
+ transport,
138
+ noiseFilter,
139
+ vadGate,
140
+ sessionOptions: options.sessionOptions,
141
+ onStateChange: (clientState) => {
142
+ const currentStatus = store.getState().status;
143
+ const nextStatus = mapClientStatus(clientState.status, currentStatus);
144
+ if (nextStatus !== currentStatus) {
145
+ store.getState()._setStatus(nextStatus);
146
+ onStatusChangeRef.current?.(nextStatus);
147
+ }
148
+ if (clientState.sessionId !== store.getState().sessionId) {
149
+ store.getState()._setSessionId(clientState.sessionId);
150
+ }
151
+ if (clientState.error) {
152
+ store.getState()._setError(clientState.error);
153
+ }
154
+ },
155
+ onAudioState: (audioState) => {
156
+ store.getState()._setAudio({ ...store.getState().audio, ...audioState });
157
+ },
158
+ onError: (err) => {
159
+ store.getState()._setError(err);
160
+ store.getState()._setStatus("error");
161
+ onErrorRef.current?.(err);
162
+ }
163
+ });
164
+ const targetManager = new TargetManager({
165
+ sendMessage: (message) => client.sendMessage(message),
166
+ onActiveTargetChange: (targetId) => store.getState()._setActiveTarget(targetId),
167
+ onTargetRegistered: (targetId) => {
168
+ const applierInstance = applierRef.current;
169
+ if (applierInstance) {
170
+ applierInstance.flushPendingEvents(targetId);
171
+ }
172
+ }
173
+ });
174
+ targetManagerRef.current = targetManager;
175
+ clientRef.current = client;
176
+ setClientEpoch((epoch) => epoch + 1);
177
+ dbgState("sdk.store", store.getState());
178
+ dbgState("sdk.client", client.getDebugSnapshot());
179
+ const preloadAbort = new AbortController();
180
+ if (preloadSession) {
181
+ preloadWithRetry(
182
+ client,
183
+ preloadAbort.signal,
184
+ preloadInitialTargetId ? { initialTargetId: preloadInitialTargetId } : void 0
185
+ ).catch(() => {
186
+ });
187
+ }
188
+ const warmupEvents = [
189
+ "pointerover",
190
+ "pointerdown",
191
+ "focusin",
192
+ "keydown",
193
+ "touchstart"
194
+ ];
195
+ const _isSafari = typeof navigator !== "undefined" && /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
196
+ const triggerWarmup = (_event) => {
197
+ warmupEvents.forEach(
198
+ (evt) => document.removeEventListener(evt, triggerWarmup)
199
+ );
200
+ if (!_isSafari) {
201
+ client.warmupMic().catch(() => {
202
+ });
203
+ }
204
+ };
205
+ warmupEvents.forEach(
206
+ (evt) => document.addEventListener(evt, triggerWarmup, { once: true })
207
+ );
208
+ const unsubPageHide = client.registerPageHideStop();
209
+ const unsubTransport = client.onTransportState((transportState) => {
210
+ const prev = store.getState().connection;
211
+ store.getState()._setConnection(
212
+ mapTransportStateToConnectionState(transportState, prev)
213
+ );
214
+ });
215
+ const unsubStore = store.subscribe((state) => {
216
+ dbgState("sdk.store", state);
217
+ });
218
+ const mountedActiveId = store.getState().activeTargetId;
219
+ if (mountedActiveId) targetManager.setActive(mountedActiveId);
220
+ const applier = new TranscriptApplier({ targetManager });
221
+ applierRef.current = applier;
222
+ applyServerEventRef.current = (event) => applier.handleEvent(event);
223
+ const unsubServerEvent = client.onServerEvent((event) => applier.handleEvent(event));
224
+ let prevActiveId = store.getState().activeTargetId;
225
+ if (prevActiveId) {
226
+ targetManager.get(prevActiveId)?.element?.setAttribute("data-ephia-target-active", "true");
227
+ }
228
+ const unsubActive = store.subscribe((state) => {
229
+ if (state.activeTargetId === prevActiveId) return;
230
+ if (prevActiveId) {
231
+ targetManager.get(prevActiveId)?.element?.removeAttribute("data-ephia-target-active");
232
+ }
233
+ if (state.activeTargetId) {
234
+ targetManager.get(state.activeTargetId)?.element?.setAttribute("data-ephia-target-active", "true");
235
+ targetManager.setActive(state.activeTargetId);
236
+ }
237
+ prevActiveId = state.activeTargetId;
238
+ });
239
+ let prevSessionActive = false;
240
+ const unsubSession = store.subscribe((state) => {
241
+ const isActive = state.status !== "idle" && state.status !== "error";
242
+ if (isActive && !prevSessionActive) {
243
+ targetManager.syncActiveTarget();
244
+ }
245
+ prevSessionActive = isActive;
246
+ if (isActive) {
247
+ document.body.setAttribute("data-ephia-session-active", "true");
248
+ } else {
249
+ document.body.removeAttribute("data-ephia-session-active");
250
+ }
251
+ });
252
+ return () => {
253
+ preloadAbort.abort();
254
+ unsubPageHide();
255
+ unsubTransport();
256
+ unsubStore();
257
+ unsubActive();
258
+ unsubSession();
259
+ unsubServerEvent();
260
+ applyServerEventRef.current = () => {
261
+ };
262
+ targetManager.dispose();
263
+ targetManagerRef.current = null;
264
+ document.body.removeAttribute("data-ephia-session-active");
265
+ warmupEvents.forEach(
266
+ (evt) => document.removeEventListener(evt, triggerWarmup)
267
+ );
268
+ client.releasePreloadedSession();
269
+ client.dispose().catch(() => {
270
+ });
271
+ clientRef.current = null;
272
+ setClientEpoch((epoch) => epoch + 1);
273
+ };
274
+ }, [resolvedApiUrl, apiKey, bearerToken, clientType, transport, sessionOptionsKey, preloadSession, preloadInitialTargetId, noiseFilter, vadGate]);
275
+ useEffect(() => {
276
+ clientRef.current?.setPreferredAudioInputDeviceId(audioInputDeviceId);
277
+ }, [audioInputDeviceId]);
278
+ const config = useMemo(() => {
279
+ return {
280
+ apiUrl: resolvedApiUrl,
281
+ apiKey,
282
+ language: options.language ?? "fr",
283
+ medicalDomain: options.medicalDomain
284
+ };
285
+ }, [
286
+ resolvedApiUrl,
287
+ apiKey,
288
+ options.language,
289
+ options.medicalDomain
290
+ ]);
291
+ const registerTarget = useRef(
292
+ (id, binding, opts) => {
293
+ const tm = targetManagerRef.current;
294
+ if (!tm) return () => {
295
+ };
296
+ binding.attach();
297
+ const token = tm.register({ id, binding, mode: opts?.mode ?? "write", element: opts?.element });
298
+ return () => {
299
+ const removed = tm.unregister(id, token);
300
+ if (removed) binding.detach();
301
+ };
302
+ }
303
+ );
304
+ const applyServerEvent = useRef((event) => {
305
+ applyServerEventRef.current(event);
306
+ });
307
+ const publicCtxValue = useMemo(
308
+ () => ({
309
+ store,
310
+ registerTarget: registerTarget.current,
311
+ applyServerEvent: applyServerEvent.current,
312
+ config
313
+ }),
314
+ [store, config]
315
+ );
316
+ const internalCtxValue = useMemo(
317
+ () => ({ store, clientRef, targetManagerRef, clientEpoch }),
318
+ [store, clientEpoch]
319
+ );
320
+ return /* @__PURE__ */ jsx(EphiaInternalContext.Provider, { value: internalCtxValue, children: /* @__PURE__ */ jsx(EphiaContext.Provider, { value: publicCtxValue, children }) });
321
+ }
322
+
323
+ // src/react/hooks/useEphia.ts
324
+ import { useCallback, useSyncExternalStore } from "react";
325
+ import { useStore } from "zustand";
326
+ import { useShallow } from "zustand/react/shallow";
327
+ var FINALIZATION_TIMEOUT_MS = 3e4;
328
+ function useEphia() {
329
+ const { store, applyServerEvent } = useEphiaContext();
330
+ const internal = useEphiaInternal();
331
+ const status = useStore(store, (s) => s.status);
332
+ const sessionId = useStore(store, (s) => s.sessionId);
333
+ const activeTargetId = useStore(store, (s) => s.activeTargetId);
334
+ const error = useStore(store, useShallow((s) => s.error));
335
+ const start = useCallback(
336
+ async (targetId, options) => {
337
+ const client = internal.clientRef.current;
338
+ const targetManager = internal.targetManagerRef.current;
339
+ if (!client || !targetManager) {
340
+ throw new Error(getMessage("provider.not_initialized"));
341
+ }
342
+ const currentStatus = store.getState().status;
343
+ if (currentStatus !== "idle" && currentStatus !== "error") {
344
+ return { sessionId: store.getState().sessionId };
345
+ }
346
+ const clickTs = typeof window !== "undefined" ? window.__ephia_record_click_ts : void 0;
347
+ const startCallTs = Date.now();
348
+ const deltaFromClick = clickTs ? startCallTs - clickTs : void 0;
349
+ window.dispatchEvent(
350
+ new CustomEvent("ephia:sdk-debug", {
351
+ detail: {
352
+ type: "sdk.record.start_called",
353
+ sessionId: null,
354
+ payload: {
355
+ message: `start() appel\xE9${deltaFromClick !== void 0 ? ` (${deltaFromClick}ms apr\xE8s clic)` : ""}`,
356
+ deltaFromClick,
357
+ ts: startCallTs
358
+ }
359
+ }
360
+ })
361
+ );
362
+ if (targetId) {
363
+ targetManager.setActive(targetId);
364
+ }
365
+ const activeTargetId2 = targetId ?? targetManager.getActiveTargetId() ?? void 0;
366
+ targetManager.prepareForDictation(activeTargetId2);
367
+ store.getState()._setStatus("processing");
368
+ try {
369
+ const result = await client.start(
370
+ targetId ? { ...options, initialTargetId: targetId } : options
371
+ );
372
+ targetManager.syncActiveTarget();
373
+ targetManager.flushEditorContext(
374
+ targetId ?? targetManager.getActiveTargetId() ?? void 0
375
+ );
376
+ store.getState()._setStatus("recording");
377
+ store.getState()._setSessionId(result.sessionId);
378
+ return result;
379
+ } catch (err) {
380
+ store.getState()._setError(toEphiaError(err));
381
+ store.getState()._setStatus("error");
382
+ throw err;
383
+ }
384
+ },
385
+ [store, internal]
386
+ );
387
+ const stop = useCallback(async () => {
388
+ const client = internal.clientRef.current;
389
+ if (!client) return;
390
+ const current = store.getState().status;
391
+ if (current === "idle" || current === "error") return;
392
+ store.getState()._setStatus("processing");
393
+ try {
394
+ await client.stop();
395
+ const clientState = client.getState();
396
+ if (clientState.status === "idle" || clientState.status === "paused" || clientState.status === "ended") {
397
+ store.getState()._setStatus("idle");
398
+ } else if (store.getState().status === "processing") {
399
+ store.getState()._setStatus("idle");
400
+ }
401
+ } catch (err) {
402
+ store.getState()._setError(toEphiaError(err));
403
+ store.getState()._setStatus("error");
404
+ throw err;
405
+ }
406
+ }, [store, internal]);
407
+ const toggle = useCallback(
408
+ async (targetId, options) => {
409
+ const { status: status2 } = store.getState();
410
+ if (status2 === "recording") {
411
+ await stop();
412
+ } else {
413
+ await start(targetId, options);
414
+ }
415
+ },
416
+ [store, start, stop]
417
+ );
418
+ const pause = useCallback(async () => {
419
+ await stop();
420
+ }, [stop]);
421
+ const resume = useCallback(
422
+ async (options) => {
423
+ const client = internal.clientRef.current;
424
+ const targetManager = internal.targetManagerRef.current;
425
+ if (!client) return;
426
+ const clientStatus = client.getState().status;
427
+ if (clientStatus === "paused") {
428
+ targetManager?.prepareForDictation(
429
+ store.getState().activeTargetId ?? targetManager.getActiveTargetId() ?? void 0
430
+ );
431
+ await client.start(options);
432
+ return;
433
+ }
434
+ await start(void 0, options);
435
+ },
436
+ [internal, start, store]
437
+ );
438
+ const endSession = useCallback(async () => {
439
+ const client = internal.clientRef.current;
440
+ if (!client) return;
441
+ const current = store.getState().status;
442
+ if (current !== "recording" && current !== "processing") return;
443
+ store.getState()._setStatus("processing");
444
+ const timeout = setTimeout(() => {
445
+ store.getState()._setStatus("error");
446
+ store.getState()._setError({
447
+ code: "client.finalization_timeout",
448
+ message: getMessage("client.finalization_timeout")
449
+ });
450
+ }, FINALIZATION_TIMEOUT_MS);
451
+ try {
452
+ await client.endSession();
453
+ } catch (err) {
454
+ store.getState()._setError(toEphiaError(err));
455
+ store.getState()._setStatus("error");
456
+ throw err;
457
+ } finally {
458
+ clearTimeout(timeout);
459
+ }
460
+ const clientState = client.getState();
461
+ if (clientState.status === "idle") {
462
+ store.getState()._setStatus("idle");
463
+ return;
464
+ }
465
+ if (clientState.status === "error" || clientState.error) {
466
+ if (clientState.error) {
467
+ store.getState()._setError(clientState.error);
468
+ }
469
+ store.getState()._setStatus("error");
470
+ return;
471
+ }
472
+ if (store.getState().status === "processing") {
473
+ store.getState()._setStatus("idle");
474
+ }
475
+ }, [store, internal]);
476
+ const warmupMic = useCallback(async () => {
477
+ const client = internal.clientRef.current;
478
+ if (!client) return;
479
+ await client.warmupMic().catch(() => {
480
+ });
481
+ }, [internal]);
482
+ const setActiveTarget = useCallback(
483
+ (id) => {
484
+ const targetManager = internal.targetManagerRef.current;
485
+ if (!targetManager) return;
486
+ targetManager.setActive(id);
487
+ },
488
+ [internal]
489
+ );
490
+ const sendEditorContext = useCallback(
491
+ async (id) => {
492
+ const targetManager = internal.targetManagerRef.current;
493
+ if (!targetManager) return;
494
+ targetManager.flushEditorContext(id);
495
+ },
496
+ [internal]
497
+ );
498
+ const resetTarget = useCallback(
499
+ async (id, reason = "user_explicit") => {
500
+ const targetManager = internal.targetManagerRef.current;
501
+ if (!targetManager) return;
502
+ const targetId = id ?? store.getState().activeTargetId;
503
+ if (!targetId) return;
504
+ targetManager.reset("target", targetId, reason);
505
+ },
506
+ [store, internal]
507
+ );
508
+ const resetSession = useCallback(
509
+ async (reason = "user_explicit") => {
510
+ const targetManager = internal.targetManagerRef.current;
511
+ if (!targetManager) return;
512
+ targetManager.reset("global", void 0, reason);
513
+ },
514
+ [internal]
515
+ );
516
+ return {
517
+ status,
518
+ sessionId,
519
+ activeTargetId,
520
+ error,
521
+ isIdle: status === "idle",
522
+ isRecording: status === "recording",
523
+ isProcessing: status === "processing",
524
+ isBusy: status !== "idle" && status !== "error",
525
+ hasError: status === "error",
526
+ start,
527
+ stop,
528
+ toggle,
529
+ pause,
530
+ resume,
531
+ endSession,
532
+ warmupMic,
533
+ setActiveTarget,
534
+ resetTarget,
535
+ resetSession,
536
+ sendEditorContext,
537
+ applyServerEvent
538
+ };
539
+ }
540
+ function useEphiaStatus() {
541
+ const { store } = useEphiaContext();
542
+ return useStore(store, (s) => s.status);
543
+ }
544
+ function useOptionalEphiaStatus() {
545
+ const ctx = useOptionalEphiaContext();
546
+ return useSyncExternalStore(
547
+ (callback) => {
548
+ if (!ctx) return () => {
549
+ };
550
+ return ctx.store.subscribe(callback);
551
+ },
552
+ () => ctx ? ctx.store.getState().status : void 0,
553
+ () => void 0
554
+ );
555
+ }
556
+ function useEphiaError() {
557
+ const { store } = useEphiaContext();
558
+ return useStore(store, (s) => s.error);
559
+ }
560
+ function useEphiaPartial() {
561
+ const { store } = useEphiaContext();
562
+ return useStore(store, useShallow((s) => s.partial));
563
+ }
564
+ function useEphiaConnection() {
565
+ const { store } = useEphiaContext();
566
+ return useStore(store, useShallow((s) => s.connection));
567
+ }
568
+ function useOptionalEphiaConnection() {
569
+ const ctx = useOptionalEphiaContext();
570
+ return useSyncExternalStore(
571
+ (callback) => {
572
+ if (!ctx) return () => {
573
+ };
574
+ return ctx.store.subscribe(callback);
575
+ },
576
+ () => ctx ? ctx.store.getState().connection : void 0,
577
+ () => void 0
578
+ );
579
+ }
580
+ function useEphiaAudioLevel() {
581
+ const { store } = useEphiaContext();
582
+ return useStore(store, useShallow((s) => s.audio));
583
+ }
584
+ function useEphiaSpeaking() {
585
+ const { store } = useEphiaContext();
586
+ return useStore(store, (s) => s.audio.speaking);
587
+ }
588
+
589
+ export {
590
+ EphiaProvider,
591
+ useEphia,
592
+ useEphiaStatus,
593
+ useOptionalEphiaStatus,
594
+ useEphiaError,
595
+ useEphiaPartial,
596
+ useEphiaConnection,
597
+ useOptionalEphiaConnection,
598
+ useEphiaAudioLevel,
599
+ useEphiaSpeaking
600
+ };
601
+ //# sourceMappingURL=chunk-ITJFN3VM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/provider/EphiaProvider.tsx","../src/react/hooks/useEphia.ts"],"sourcesContent":["\"use client\";\n\nimport React, { useEffect, useMemo, useRef, useState } from \"react\";\nimport type { EphiaServerEvent } from \"ephia-protocol\";\nimport { EphiaAudioClient } from \"../../core/client/EphiaAudioClient\";\nimport type { EphiaAudioClientOptions } from \"../../core/client/EphiaAudioClient\";\nimport type { EphiaStartOptions } from \"../../core/client/client-options\";\nimport { mapTransportStateToConnectionState } from \"../../core/connection/connection-state\";\nimport { TranscriptApplier } from \"../../core/runtime/TranscriptApplier\";\nimport { TargetManager } from \"../../core/targets/TargetManager\";\nimport { createEphiaStore } from \"../store/create-ephia-store\";\nimport type { EphiaStore } from \"../store/create-ephia-store\";\nimport type { EphiaStatus } from \"../store/types\";\nimport {\n EphiaContext,\n type EphiaProviderConfig,\n} from \"./EphiaContext\";\nimport {\n EphiaInternalContext,\n type EphiaInternalContextValue,\n} from \"./EphiaInternalContext\";\nimport { ephiaClientConfigSchema } from \"../../shared/config/schema\";\nimport { resolveEphiaSdkApiUrl } from \"../../shared/config/endpoint\";\nimport { dbgState } from \"../../debug/sdk-debug-collector\";\n\nexport interface EphiaProviderProps {\n children: React.ReactNode;\n\n apiUrl?: string;\n apiKey?: string;\n bearerToken?: string;\n clientType?: string;\n\n transport?: EphiaAudioClientOptions[\"transport\"];\n\n options?: {\n language?: string;\n medicalDomain?: string;\n sessionOptions?: EphiaAudioClientOptions[\"sessionOptions\"];\n };\n\n onError?: (error: { code: string; message: string }) => void;\n onStatusChange?: (status: EphiaStatus) => void;\n\n preloadSession?: boolean;\n preloadInitialTargetId?: string;\n noiseFilter?: boolean;\n vadGate?: boolean;\n audioInputDeviceId?: string;\n}\n\nasync function preloadWithRetry(\n client: EphiaAudioClient,\n signal: AbortSignal,\n options?: EphiaStartOptions,\n maxRetries = 3\n): Promise<void> {\n for (let attempt = 0; attempt < maxRetries; attempt += 1) {\n if (signal.aborted) return;\n try {\n await client.preload(options, signal);\n return;\n } catch {\n if (attempt < maxRetries - 1 && !signal.aborted) {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, 500 * 2 ** attempt);\n });\n }\n }\n }\n}\n\nfunction mapClientStatus(\n clientStatus: string,\n currentStatus: EphiaStatus\n): EphiaStatus {\n switch (clientStatus) {\n case \"recording\":\n return \"recording\";\n case \"creating_session\":\n case \"connecting_transport\":\n case \"connecting\":\n case \"ready\":\n if (currentStatus === \"recording\") return \"recording\";\n if (currentStatus === \"processing\") return \"processing\";\n return \"idle\";\n case \"paused\":\n if (currentStatus === \"processing\") return \"processing\";\n return \"idle\";\n case \"finalizing\":\n if (currentStatus === \"recording\" || currentStatus === \"processing\") {\n return \"processing\";\n }\n return \"idle\";\n case \"ended\":\n return \"idle\";\n case \"error\":\n return \"error\";\n case \"idle\":\n case \"disposed\":\n case \"disconnected\":\n if (currentStatus === \"processing\") return \"processing\";\n return \"idle\";\n default:\n return currentStatus;\n }\n}\n\nexport function EphiaProvider({\n children,\n apiUrl,\n apiKey,\n bearerToken,\n clientType,\n transport,\n options = {},\n onError,\n onStatusChange,\n preloadSession = true,\n preloadInitialTargetId,\n noiseFilter = false,\n vadGate = true,\n audioInputDeviceId,\n}: EphiaProviderProps) {\n const resolvedApiUrl = useMemo(\n () => resolveEphiaSdkApiUrl(apiUrl),\n [apiUrl],\n );\n\n const storeRef = useRef<EphiaStore | null>(null);\n const clientRef = useRef<EphiaAudioClient | null>(null);\n const targetManagerRef = useRef<TargetManager | null>(null);\n const applierRef = useRef<TranscriptApplier | null>(null);\n const applyServerEventRef = useRef<(event: EphiaServerEvent) => void>(() => {});\n const [clientEpoch, setClientEpoch] = useState(0);\n\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n const onStatusChangeRef = useRef(onStatusChange);\n onStatusChangeRef.current = onStatusChange;\n\n const sessionOptionsKey = useMemo(\n () => JSON.stringify(options.sessionOptions ?? null),\n [options.sessionOptions],\n );\n\n if (!storeRef.current) {\n storeRef.current = createEphiaStore();\n }\n const store = storeRef.current;\n\n useEffect(() => {\n const cfgValidation = ephiaClientConfigSchema.partial().safeParse({\n apiUrl: resolvedApiUrl,\n apiKey,\n });\n if (!cfgValidation.success) {\n console.warn(\"[ephia] invalid provider config:\", cfgValidation.error.issues);\n }\n\n const client = new EphiaAudioClient({\n apiUrl: resolvedApiUrl,\n apiKey,\n bearerToken,\n clientType,\n transport,\n noiseFilter,\n vadGate,\n sessionOptions: options.sessionOptions,\n onStateChange: (clientState) => {\n const currentStatus = store.getState().status;\n const nextStatus = mapClientStatus(clientState.status, currentStatus);\n if (nextStatus !== currentStatus) {\n store.getState()._setStatus(nextStatus);\n onStatusChangeRef.current?.(nextStatus);\n }\n if (clientState.sessionId !== store.getState().sessionId) {\n store.getState()._setSessionId(clientState.sessionId);\n }\n if (clientState.error) {\n store.getState()._setError(clientState.error);\n }\n },\n onAudioState: (audioState) => {\n store.getState()._setAudio({ ...store.getState().audio, ...audioState });\n },\n onError: (err) => {\n store.getState()._setError(err);\n store.getState()._setStatus(\"error\");\n onErrorRef.current?.(err);\n },\n });\n\n const targetManager = new TargetManager({\n sendMessage: (message) => client.sendMessage(message as any),\n onActiveTargetChange: (targetId) => store.getState()._setActiveTarget(targetId),\n onTargetRegistered: (targetId) => {\n // Flush pending events for this target when it becomes available.\n const applierInstance = applierRef.current;\n if (applierInstance) {\n applierInstance.flushPendingEvents(targetId);\n }\n },\n });\n targetManagerRef.current = targetManager;\n clientRef.current = client;\n setClientEpoch((epoch) => epoch + 1);\n\n dbgState(\"sdk.store\", store.getState());\n dbgState(\"sdk.client\", client.getDebugSnapshot());\n\n const preloadAbort = new AbortController();\n if (preloadSession) {\n preloadWithRetry(\n client,\n preloadAbort.signal,\n preloadInitialTargetId ? { initialTargetId: preloadInitialTargetId } : undefined,\n ).catch(() => {});\n }\n\n const warmupEvents = [\n \"pointerover\",\n \"pointerdown\",\n \"focusin\",\n \"keydown\",\n \"touchstart\",\n ] as const;\n const _isSafari =\n typeof navigator !== \"undefined\" &&\n /^((?!chrome|android).)*safari/i.test(navigator.userAgent);\n const triggerWarmup = (_event?: Event) => {\n warmupEvents.forEach((evt) =>\n document.removeEventListener(evt, triggerWarmup)\n );\n if (!_isSafari) {\n client.warmupMic().catch(() => {});\n }\n };\n warmupEvents.forEach((evt) =>\n document.addEventListener(evt, triggerWarmup, { once: true })\n );\n\n const unsubPageHide = client.registerPageHideStop();\n\n const unsubTransport = client.onTransportState((transportState) => {\n const prev = store.getState().connection;\n store.getState()._setConnection(\n mapTransportStateToConnectionState(transportState, prev)\n );\n });\n\n const unsubStore = store.subscribe((state) => {\n dbgState(\"sdk.store\", state);\n });\n\n const mountedActiveId = store.getState().activeTargetId;\n if (mountedActiveId) targetManager.setActive(mountedActiveId);\n\n const applier = new TranscriptApplier({ targetManager });\n applierRef.current = applier;\n applyServerEventRef.current = (event) => applier.handleEvent(event);\n const unsubServerEvent = client.onServerEvent((event) => applier.handleEvent(event));\n\n // Highlight the active target via data-ephia-target-active\n let prevActiveId: string | null = store.getState().activeTargetId;\n if (prevActiveId) {\n targetManager.get(prevActiveId)?.element?.setAttribute(\"data-ephia-target-active\", \"true\");\n }\n const unsubActive = store.subscribe((state) => {\n if (state.activeTargetId === prevActiveId) return;\n\n if (prevActiveId) {\n targetManager.get(prevActiveId)?.element?.removeAttribute(\"data-ephia-target-active\");\n }\n if (state.activeTargetId) {\n targetManager.get(state.activeTargetId)?.element?.setAttribute(\"data-ephia-target-active\", \"true\");\n targetManager.setActive(state.activeTargetId);\n }\n prevActiveId = state.activeTargetId;\n });\n\n let prevSessionActive = false;\n const unsubSession = store.subscribe((state) => {\n const isActive = state.status !== \"idle\" && state.status !== \"error\";\n if (isActive && !prevSessionActive) {\n targetManager.syncActiveTarget();\n }\n prevSessionActive = isActive;\n if (isActive) {\n document.body.setAttribute(\"data-ephia-session-active\", \"true\");\n } else {\n document.body.removeAttribute(\"data-ephia-session-active\");\n }\n });\n\n return () => {\n preloadAbort.abort();\n unsubPageHide();\n unsubTransport();\n unsubStore();\n unsubActive();\n unsubSession();\n unsubServerEvent();\n applyServerEventRef.current = () => {};\n targetManager.dispose();\n targetManagerRef.current = null;\n document.body.removeAttribute(\"data-ephia-session-active\");\n warmupEvents.forEach((evt) =>\n document.removeEventListener(evt, triggerWarmup)\n );\n client.releasePreloadedSession();\n client.dispose().catch(() => {});\n clientRef.current = null;\n setClientEpoch((epoch) => epoch + 1);\n };\n }, [resolvedApiUrl, apiKey, bearerToken, clientType, transport, sessionOptionsKey, preloadSession, preloadInitialTargetId, noiseFilter, vadGate]); // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => {\n clientRef.current?.setPreferredAudioInputDeviceId(audioInputDeviceId);\n }, [audioInputDeviceId]);\n\n const config: EphiaProviderConfig = useMemo(() => {\n return {\n apiUrl: resolvedApiUrl,\n apiKey,\n language: options.language ?? \"fr\",\n medicalDomain: options.medicalDomain,\n };\n }, [\n resolvedApiUrl,\n apiKey,\n options.language,\n options.medicalDomain,\n ]);\n\n const registerTarget = useRef(\n (id: string, binding: import(\"../../core/bindings/EphiaBinding\").EphiaBinding, opts?: { mode?: string; element?: HTMLElement }) => {\n const tm = targetManagerRef.current;\n if (!tm) return () => {};\n binding.attach();\n const token = tm.register({ id, binding, mode: opts?.mode ?? \"write\", element: opts?.element });\n return () => {\n const removed = tm.unregister(id, token);\n if (removed) binding.detach();\n };\n }\n );\n\n const applyServerEvent = useRef((event: EphiaServerEvent) => {\n applyServerEventRef.current(event);\n });\n\n const publicCtxValue = useMemo(\n () => ({\n store,\n registerTarget: registerTarget.current,\n applyServerEvent: applyServerEvent.current,\n config,\n }),\n [store, config]\n );\n\n const internalCtxValue = useMemo<EphiaInternalContextValue>(\n () => ({ store, clientRef, targetManagerRef, clientEpoch }),\n [store, clientEpoch]\n );\n\n return (\n <EphiaInternalContext.Provider value={internalCtxValue}>\n <EphiaContext.Provider value={publicCtxValue}>{children}</EphiaContext.Provider>\n </EphiaInternalContext.Provider>\n );\n}\n","import { useCallback, useSyncExternalStore } from \"react\";\nimport { useStore } from \"zustand\";\nimport { useShallow } from \"zustand/react/shallow\";\nimport { useEphiaContext, useOptionalEphiaContext } from \"../provider/EphiaContext\";\nimport { useEphiaInternal } from \"../provider/EphiaInternalContext\";\nimport type { EphiaError, EphiaStatus, EphiaPartial } from \"../store/types\";\nimport type { EphiaConnectionState } from \"../../core/connection/connection-state\";\nimport type { EphiaAudioState } from \"../../shared/state/audio-state\";\nimport type { EphiaStartOptions, EphiaStartResult } from \"../../core/client/client-options\";\nimport type { EphiaServerEvent, ResetReason } from \"ephia-protocol\";\nimport { getMessage } from \"../../shared/errors/messages\";\nimport { toEphiaError } from \"../../shared/errors/EphiaSdkError\";\n\nconst FINALIZATION_TIMEOUT_MS = 30_000;\n\n// ─── Hook principal ───────────────────────────────────────────────────────────\n\nexport interface UseEphiaReturn {\n // État\n status: EphiaStatus;\n sessionId: string | null;\n activeTargetId: string | null;\n error: { code: string; message: string } | null;\n\n // Booléens commodité\n isIdle: boolean;\n isRecording: boolean;\n isProcessing: boolean;\n isBusy: boolean;\n hasError: boolean;\n\n // Actions\n start: (targetId?: string, options?: EphiaStartOptions) => Promise<EphiaStartResult>;\n stop: () => Promise<void>;\n toggle: (targetId?: string, options?: EphiaStartOptions) => Promise<void>;\n\n pause: () => Promise<void>;\n resume: () => Promise<void>;\n endSession: () => Promise<void>;\n warmupMic: () => Promise<void>;\n\n setActiveTarget: (id: string) => void;\n resetTarget: (id?: string, reason?: ResetReason) => Promise<void>;\n resetSession: (reason?: ResetReason) => Promise<void>;\n sendEditorContext: (id?: string) => Promise<void>;\n applyServerEvent: (event: EphiaServerEvent) => void;\n}\n\nexport function useEphia(): UseEphiaReturn {\n const { store, applyServerEvent } = useEphiaContext();\n const internal = useEphiaInternal();\n\n const status = useStore(store, (s) => s.status);\n const sessionId = useStore(store, (s) => s.sessionId);\n const activeTargetId = useStore(store, (s) => s.activeTargetId);\n const error = useStore(store, useShallow((s) => s.error));\n\n const start = useCallback(\n async (targetId?: string, options?: EphiaStartOptions): Promise<EphiaStartResult> => {\n const client = internal.clientRef.current;\n const targetManager = internal.targetManagerRef.current;\n if (!client || !targetManager) {\n throw new Error(getMessage(\"provider.not_initialized\"));\n }\n\n const currentStatus = store.getState().status;\n if (currentStatus !== \"idle\" && currentStatus !== \"error\") {\n return { sessionId: store.getState().sessionId };\n }\n\n const clickTs = typeof window !== \"undefined\" ? window.__ephia_record_click_ts : undefined;\n const startCallTs = Date.now();\n const deltaFromClick = clickTs ? startCallTs - clickTs : undefined;\n\n window.dispatchEvent(\n new CustomEvent(\"ephia:sdk-debug\", {\n detail: {\n type: \"sdk.record.start_called\",\n sessionId: null,\n payload: {\n message: `start() appelé${deltaFromClick !== undefined ? ` (${deltaFromClick}ms après clic)` : \"\"}`,\n deltaFromClick,\n ts: startCallTs,\n },\n },\n })\n );\n\n if (targetId) {\n targetManager.setActive(targetId);\n }\n\n const activeTargetId = targetId ?? targetManager.getActiveTargetId() ?? undefined;\n targetManager.prepareForDictation(activeTargetId);\n\n store.getState()._setStatus(\"processing\");\n\n try {\n const result = await client.start(\n targetId ? { ...options, initialTargetId: targetId } : options\n );\n targetManager.syncActiveTarget();\n targetManager.flushEditorContext(\n targetId ?? targetManager.getActiveTargetId() ?? undefined\n );\n store.getState()._setStatus(\"recording\");\n store.getState()._setSessionId(result.sessionId);\n return result;\n } catch (err) {\n store.getState()._setError(toEphiaError(err));\n store.getState()._setStatus(\"error\");\n throw err;\n }\n },\n [store, internal]\n );\n\n const stop = useCallback(async () => {\n const client = internal.clientRef.current;\n if (!client) return;\n\n const current = store.getState().status;\n // Permettre l'arrêt pendant le démarrage (processing) pour éviter qu'un\n // push-to-talk très court laisse le micro ouvert (race start/stop).\n if (current === \"idle\" || current === \"error\") return;\n\n store.getState()._setStatus(\"processing\");\n\n try {\n await client.stop();\n\n const clientState = client.getState();\n\n if (\n clientState.status === \"idle\" ||\n clientState.status === \"paused\" ||\n clientState.status === \"ended\"\n ) {\n store.getState()._setStatus(\"idle\");\n } else if (store.getState().status === \"processing\") {\n store.getState()._setStatus(\"idle\");\n }\n } catch (err) {\n store.getState()._setError(toEphiaError(err));\n store.getState()._setStatus(\"error\");\n throw err;\n }\n }, [store, internal]);\n\n const toggle = useCallback(\n async (targetId?: string, options?: EphiaStartOptions) => {\n const { status } = store.getState();\n if (status === \"recording\") {\n await stop();\n } else {\n await start(targetId, options);\n }\n },\n [store, start, stop]\n );\n\n const pause = useCallback(async () => {\n // V2 initial: pause is equivalent to stop (session stays alive on the backend/client).\n await stop();\n }, [stop]);\n\n const resume = useCallback(\n async (options?: EphiaStartOptions) => {\n const client = internal.clientRef.current;\n const targetManager = internal.targetManagerRef.current;\n if (!client) return;\n\n const clientStatus = client.getState().status;\n if (clientStatus === \"paused\") {\n targetManager?.prepareForDictation(\n store.getState().activeTargetId ?? targetManager.getActiveTargetId() ?? undefined,\n );\n await client.start(options);\n return;\n }\n\n // Fallback: if the client is not paused, start a fresh session.\n await start(undefined, options);\n },\n [internal, start, store]\n );\n\n const endSession = useCallback(async () => {\n const client = internal.clientRef.current;\n if (!client) return;\n\n const current = store.getState().status;\n if (current !== \"recording\" && current !== \"processing\") return;\n\n store.getState()._setStatus(\"processing\");\n\n const timeout = setTimeout(() => {\n store.getState()._setStatus(\"error\");\n store.getState()._setError({\n code: \"client.finalization_timeout\",\n message: getMessage(\"client.finalization_timeout\"),\n });\n }, FINALIZATION_TIMEOUT_MS);\n\n try {\n await client.endSession();\n } catch (err) {\n store.getState()._setError(toEphiaError(err));\n store.getState()._setStatus(\"error\");\n throw err;\n } finally {\n clearTimeout(timeout);\n }\n\n const clientState = client.getState();\n if (clientState.status === \"idle\") {\n store.getState()._setStatus(\"idle\");\n return;\n }\n\n if (clientState.status === \"error\" || clientState.error) {\n if (clientState.error) {\n store.getState()._setError(clientState.error);\n }\n store.getState()._setStatus(\"error\");\n return;\n }\n\n if (store.getState().status === \"processing\") {\n store.getState()._setStatus(\"idle\");\n }\n }, [store, internal]);\n\n const warmupMic = useCallback(async () => {\n const client = internal.clientRef.current;\n if (!client) return;\n await client.warmupMic().catch(() => {});\n }, [internal]);\n\n const setActiveTarget = useCallback(\n (id: string) => {\n const targetManager = internal.targetManagerRef.current;\n if (!targetManager) return;\n targetManager.setActive(id);\n },\n [internal]\n );\n\n const sendEditorContext = useCallback(\n async (id?: string) => {\n const targetManager = internal.targetManagerRef.current;\n if (!targetManager) return;\n targetManager.flushEditorContext(id);\n },\n [internal]\n );\n\n const resetTarget = useCallback(\n async (id?: string, reason: ResetReason = \"user_explicit\") => {\n const targetManager = internal.targetManagerRef.current;\n if (!targetManager) return;\n\n const targetId = id ?? store.getState().activeTargetId;\n if (!targetId) return;\n\n targetManager.reset(\"target\", targetId, reason);\n },\n [store, internal]\n );\n\n const resetSession = useCallback(\n async (reason: ResetReason = \"user_explicit\") => {\n const targetManager = internal.targetManagerRef.current;\n if (!targetManager) return;\n targetManager.reset(\"global\", undefined, reason);\n },\n [internal]\n );\n\n return {\n status,\n sessionId,\n activeTargetId,\n error,\n isIdle: status === \"idle\",\n isRecording: status === \"recording\",\n isProcessing: status === \"processing\",\n isBusy: status !== \"idle\" && status !== \"error\",\n hasError: status === \"error\",\n start,\n stop,\n toggle,\n pause,\n resume,\n endSession,\n warmupMic,\n setActiveTarget,\n resetTarget,\n resetSession,\n sendEditorContext,\n applyServerEvent,\n };\n}\n\n// ─── Hooks sélecteurs légers (anti re-render) ─────────────────────────────────\n\n/** Re-rend uniquement sur changement de status. */\nexport function useEphiaStatus(): EphiaStatus {\n const { store } = useEphiaContext();\n return useStore(store, (s) => s.status);\n}\n\n/** Comme useEphiaStatus mais retourne undefined en dehors d'un EphiaProvider. */\nexport function useOptionalEphiaStatus(): EphiaStatus | undefined {\n const ctx = useOptionalEphiaContext();\n return useSyncExternalStore(\n (callback) => {\n if (!ctx) return () => {};\n return ctx.store.subscribe(callback);\n },\n () => (ctx ? ctx.store.getState().status : undefined),\n () => undefined,\n );\n}\n\n/** Re-rend uniquement sur changement d'erreur session (ex. session.error STT). */\nexport function useEphiaError(): EphiaError | null {\n const { store } = useEphiaContext();\n return useStore(store, (s) => s.error);\n}\n\n/** Re-rend sur chaque partial — à utiliser uniquement dans les composants d'affichage. */\nexport function useEphiaPartial(): EphiaPartial | null {\n const { store } = useEphiaContext();\n return useStore(store, useShallow((s) => s.partial));\n}\n\n/** Re-rend sur changement de connexion. */\nexport function useEphiaConnection(): EphiaConnectionState {\n const { store } = useEphiaContext();\n return useStore(store, useShallow((s) => s.connection));\n}\n\n/** Comme useEphiaConnection mais retourne undefined en dehors d'un EphiaProvider. */\nexport function useOptionalEphiaConnection(): EphiaConnectionState | undefined {\n const ctx = useOptionalEphiaContext();\n return useSyncExternalStore(\n (callback) => {\n if (!ctx) return () => {};\n return ctx.store.subscribe(callback);\n },\n () => (ctx ? ctx.store.getState().connection : undefined),\n () => undefined,\n );\n}\n\n/** Re-rend sur changement de niveau audio. */\nexport function useEphiaAudioLevel(): EphiaAudioState {\n const { store } = useEphiaContext();\n return useStore(store, useShallow((s) => s.audio));\n}\n\n/** Re-rend uniquement sur changement d'état parole (VAD gate). */\nexport function useEphiaSpeaking(): boolean {\n const { store } = useEphiaContext();\n return useStore(store, (s) => s.audio.speaking);\n}\n"],"mappings":"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","names":["activeTargetId","status"]}