@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,486 @@
1
+ import {
2
+ useEphia,
3
+ useEphiaPartial,
4
+ useEphiaStatus
5
+ } from "./chunk-ITJFN3VM.js";
6
+ import {
7
+ NativeBinding
8
+ } from "./chunk-A5UEXJ5R.js";
9
+ import {
10
+ createTiptapEphiaAdapter,
11
+ useEphiaRichEditor
12
+ } from "./chunk-AEE554FT.js";
13
+ import {
14
+ useEphiaInternal
15
+ } from "./chunk-THNHRV2B.js";
16
+ import {
17
+ useEphiaContext
18
+ } from "./chunk-K24GNU27.js";
19
+
20
+ // src/react/EphiaAuto.tsx
21
+ import { useEffect, useRef } from "react";
22
+ import { jsx } from "react/jsx-runtime";
23
+ function EphiaAuto({ children, selector = "[data-ephia-target]" }) {
24
+ const rootRef = useRef(null);
25
+ const { registerTarget } = useEphiaContext();
26
+ const { clientEpoch } = useEphiaInternal();
27
+ const registerTargetRef = useRef(registerTarget);
28
+ registerTargetRef.current = registerTarget;
29
+ useEffect(() => {
30
+ const root = rootRef.current;
31
+ if (!root) return;
32
+ const registered = /* @__PURE__ */ new Map();
33
+ const tryRegister = (el) => {
34
+ if (registered.has(el)) return;
35
+ if (!(el instanceof HTMLTextAreaElement) && !(el instanceof HTMLInputElement)) {
36
+ console.warn("[ephia:auto] unsupported element type \u2014 only textarea/input supported", el);
37
+ return;
38
+ }
39
+ const id = el.getAttribute("data-ephia-target");
40
+ if (!id) return;
41
+ const mode = el.getAttribute("data-ephia-mode") ?? "write";
42
+ const binding = new NativeBinding(el);
43
+ const cleanup = registerTargetRef.current(id, binding, { mode, element: el });
44
+ registered.set(el, cleanup);
45
+ };
46
+ const tryUnregister = (el) => {
47
+ const cleanup = registered.get(el);
48
+ if (cleanup) {
49
+ cleanup();
50
+ registered.delete(el);
51
+ }
52
+ };
53
+ const scan = () => {
54
+ const elements = Array.from(root.querySelectorAll(selector));
55
+ for (const el of elements) tryRegister(el);
56
+ for (const [el] of registered) {
57
+ if (!root.contains(el)) tryUnregister(el);
58
+ }
59
+ };
60
+ scan();
61
+ const observer = new MutationObserver(scan);
62
+ observer.observe(root, { childList: true, subtree: true });
63
+ return () => {
64
+ observer.disconnect();
65
+ for (const [, cleanup] of registered) cleanup();
66
+ registered.clear();
67
+ };
68
+ }, [selector, clientEpoch]);
69
+ return /* @__PURE__ */ jsx("div", { ref: rootRef, children });
70
+ }
71
+
72
+ // src/react/hooks/useEphiaTarget.ts
73
+ import { useEffect as useEffect2 } from "react";
74
+ function useEphiaNativeTarget(ref, options) {
75
+ const { registerTarget } = useEphiaContext();
76
+ const { clientEpoch } = useEphiaInternal();
77
+ const { id, mode, enabled } = options;
78
+ useEffect2(() => {
79
+ if (enabled === false) return;
80
+ const el = ref.current;
81
+ if (!el) return;
82
+ return registerTarget(id, new NativeBinding(el), {
83
+ mode: mode ?? "write",
84
+ element: el
85
+ });
86
+ }, [ref, id, mode, enabled, registerTarget, clientEpoch]);
87
+ }
88
+ var useEphiaTarget = useEphiaNativeTarget;
89
+
90
+ // src/react/hooks/targets/useEphiaTiptap.ts
91
+ function useEphiaTiptap(editor, options) {
92
+ return useEphiaRichEditor(createTiptapEphiaAdapter(editor), options);
93
+ }
94
+
95
+ // src/react/hooks/useEphiaServerEvent.ts
96
+ import { useEffect as useEffect3, useRef as useRef2 } from "react";
97
+ function useEphiaServerEvent(type, handler) {
98
+ const { clientRef, clientEpoch } = useEphiaInternal();
99
+ const handlerRef = useRef2(handler);
100
+ handlerRef.current = handler;
101
+ useEffect3(() => {
102
+ const client = clientRef.current;
103
+ if (!client) return;
104
+ return client.onServerEvent((event) => {
105
+ if (event.type === type) {
106
+ handlerRef.current(event);
107
+ }
108
+ });
109
+ }, [clientRef, clientEpoch, type]);
110
+ }
111
+
112
+ // src/react/hooks/useEphiaDiscardTarget.ts
113
+ import { useEffect as useEffect4, useMemo } from "react";
114
+ function createDiscardBinding() {
115
+ return {
116
+ kind: "discard",
117
+ attach() {
118
+ },
119
+ detach() {
120
+ },
121
+ getText() {
122
+ return "";
123
+ },
124
+ getEditorContext(targetId = "") {
125
+ return {
126
+ targetId,
127
+ documentEmpty: true,
128
+ insertionMode: "insert_at_cursor",
129
+ leftContext: "",
130
+ rightContext: "",
131
+ selectedText: null,
132
+ cursorOffset: 0
133
+ };
134
+ },
135
+ previewSegment() {
136
+ },
137
+ upsertSegment() {
138
+ },
139
+ removeSegment() {
140
+ },
141
+ removeSegments() {
142
+ }
143
+ };
144
+ }
145
+ function useEphiaDiscardTarget(id, options = {}) {
146
+ const { registerTarget } = useEphiaContext();
147
+ const { enabled = true } = options;
148
+ const binding = useMemo(() => createDiscardBinding(), []);
149
+ useEffect4(() => {
150
+ if (!enabled) return;
151
+ return registerTarget(id, binding, { mode: "write" });
152
+ }, [binding, enabled, id, registerTarget]);
153
+ }
154
+
155
+ // src/react/components/EphiaDictationButton.tsx
156
+ import { useCallback, useState } from "react";
157
+ import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
158
+ function EphiaDictationButton({
159
+ targetId,
160
+ disabled = false,
161
+ className,
162
+ onError,
163
+ children
164
+ }) {
165
+ const { status, isRecording, isProcessing, start, stop } = useEphia();
166
+ const [localError, setLocalError] = useState(null);
167
+ const isDisabled = disabled || isProcessing;
168
+ const toggle = useCallback(async () => {
169
+ setLocalError(null);
170
+ try {
171
+ if (isRecording) {
172
+ await stop();
173
+ } else {
174
+ await start(targetId);
175
+ }
176
+ } catch (err) {
177
+ const message = err instanceof Error ? err.message : String(err);
178
+ setLocalError(message);
179
+ onError?.(err);
180
+ }
181
+ }, [isRecording, start, stop, targetId, onError]);
182
+ const renderProps = {
183
+ status,
184
+ isRecording,
185
+ toggle,
186
+ disabled: isDisabled
187
+ };
188
+ if (children) {
189
+ return /* @__PURE__ */ jsx2(Fragment, { children: children(renderProps) });
190
+ }
191
+ return /* @__PURE__ */ jsx2(
192
+ "button",
193
+ {
194
+ type: "button",
195
+ onClick: toggle,
196
+ disabled: isDisabled,
197
+ className,
198
+ "aria-label": isRecording ? "Arr\xEAter la dict\xE9e" : "D\xE9marrer la dict\xE9e",
199
+ "aria-pressed": isRecording,
200
+ "aria-busy": isProcessing,
201
+ "data-ephia-control": "true",
202
+ "data-ephia-status": status,
203
+ title: localError ?? void 0,
204
+ children: isRecording ? "\u23F9" : "\u{1F3A4}"
205
+ }
206
+ );
207
+ }
208
+
209
+ // src/react/components/EphiaStatusBar.tsx
210
+ import { Fragment as Fragment2, jsx as jsx3, jsxs } from "react/jsx-runtime";
211
+ function EphiaStatusBar({
212
+ className,
213
+ children
214
+ }) {
215
+ const status = useEphiaStatus();
216
+ const partial = useEphiaPartial();
217
+ if (status === "idle" || status === "recording") return null;
218
+ const partialText = partial?.text ?? null;
219
+ const renderProps = {
220
+ status,
221
+ partialText
222
+ };
223
+ if (children) {
224
+ return /* @__PURE__ */ jsx3(Fragment2, { children: children(renderProps) });
225
+ }
226
+ return /* @__PURE__ */ jsxs(
227
+ "div",
228
+ {
229
+ className,
230
+ "data-ephia-control": "true",
231
+ "data-ephia-status": status,
232
+ role: "status",
233
+ "aria-live": "polite",
234
+ "aria-atomic": "false",
235
+ children: [
236
+ /* @__PURE__ */ jsx3("span", { "data-ephia-indicator": true, "aria-hidden": "true" }),
237
+ status === "processing" && /* @__PURE__ */ jsx3("span", { "data-ephia-state": "processing", "aria-label": "Traitement en cours" }),
238
+ status === "error" && /* @__PURE__ */ jsx3("span", { "data-ephia-state": "error", role: "alert", children: "Erreur de connexion" })
239
+ ]
240
+ }
241
+ );
242
+ }
243
+
244
+ // src/react/components/EphiaTextarea.tsx
245
+ import React3, { useCallback as useCallback2, useEffect as useEffect5, useRef as useRef3, useState as useState2 } from "react";
246
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
247
+ var ASCII_CURSOR_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
248
+ var ASCII_CURSOR_INTERVAL_MS = 90;
249
+ var EphiaTextarea = React3.forwardRef(function EphiaTextarea2({ targetId, mode = "write", insertion = "preview-inline", className = "", style, ...rest }, forwardedRef) {
250
+ const wrapperRef = useRef3(null);
251
+ const textareaRef = useRef3(null);
252
+ const overlayRef = useRef3(null);
253
+ const partialRef = useRef3(null);
254
+ const insertionRef = useRef3(insertion);
255
+ insertionRef.current = insertion;
256
+ const anchorEndRef = useRef3(null);
257
+ const asciiFrameRef = useRef3(0);
258
+ const asciiTimerRef = useRef3(null);
259
+ const appendAsciiCursor = (ov) => {
260
+ const span = document.createElement("span");
261
+ span.className = "ephia-cursor";
262
+ span.setAttribute("aria-hidden", "true");
263
+ span.textContent = ASCII_CURSOR_FRAMES[asciiFrameRef.current] ?? "\u280B";
264
+ ov.appendChild(span);
265
+ };
266
+ const setTextareaRef = useCallback2(
267
+ (node) => {
268
+ textareaRef.current = node;
269
+ if (typeof forwardedRef === "function") {
270
+ forwardedRef(node);
271
+ } else if (forwardedRef) {
272
+ forwardedRef.current = node;
273
+ }
274
+ },
275
+ [forwardedRef]
276
+ );
277
+ const [isActive, setIsActive] = useState2(false);
278
+ const clearOverlay = (ov) => {
279
+ while (ov.firstChild) ov.removeChild(ov.firstChild);
280
+ };
281
+ const appendText = (ov, text) => {
282
+ if (text) ov.appendChild(document.createTextNode(text));
283
+ };
284
+ const appendBrNbsp = (ov) => {
285
+ ov.appendChild(document.createElement("br"));
286
+ ov.appendChild(document.createTextNode("\xA0"));
287
+ };
288
+ const updateOverlay = () => {
289
+ const ov = overlayRef.current;
290
+ const ta = textareaRef.current;
291
+ if (!ov || !ta) return;
292
+ const rawText = ta.value;
293
+ const partial = partialRef.current;
294
+ const isRecording = ta.hasAttribute("data-ephia-recording");
295
+ const active = isRecording;
296
+ clearOverlay(ov);
297
+ if (!partial || insertionRef.current === "none") {
298
+ if (active) {
299
+ const cursorPos = Math.min(
300
+ anchorEndRef.current ?? rawText.length,
301
+ rawText.length
302
+ );
303
+ appendText(ov, rawText.slice(0, cursorPos));
304
+ appendAsciiCursor(ov);
305
+ appendText(ov, rawText.slice(cursorPos));
306
+ appendBrNbsp(ov);
307
+ return;
308
+ }
309
+ appendText(ov, rawText);
310
+ appendBrNbsp(ov);
311
+ return;
312
+ }
313
+ const insertAt = Math.min(partial.insertAt, rawText.length);
314
+ const textAtInsert = rawText.slice(insertAt, insertAt + partial.text.length);
315
+ const isAbsorbed = textAtInsert === partial.text;
316
+ appendText(ov, rawText.slice(0, insertAt));
317
+ if (!isAbsorbed) {
318
+ const span = document.createElement("span");
319
+ span.className = "ephia-text--streaming";
320
+ span.textContent = partial.text;
321
+ ov.appendChild(span);
322
+ } else if (active) {
323
+ appendAsciiCursor(ov);
324
+ }
325
+ appendText(ov, rawText.slice(insertAt));
326
+ appendBrNbsp(ov);
327
+ };
328
+ useEffect5(() => {
329
+ const ta = textareaRef.current;
330
+ const wr = wrapperRef.current;
331
+ if (!ta) return;
332
+ const onAttributeChange = () => {
333
+ const isRecording = ta.hasAttribute("data-ephia-recording");
334
+ setIsActive(isRecording);
335
+ if (!isRecording) {
336
+ anchorEndRef.current = null;
337
+ if (asciiTimerRef.current !== null) {
338
+ clearInterval(asciiTimerRef.current);
339
+ asciiTimerRef.current = null;
340
+ }
341
+ } else if (asciiTimerRef.current === null) {
342
+ asciiTimerRef.current = setInterval(() => {
343
+ asciiFrameRef.current = (asciiFrameRef.current + 1) % ASCII_CURSOR_FRAMES.length;
344
+ updateOverlay();
345
+ }, ASCII_CURSOR_INTERVAL_MS);
346
+ }
347
+ updateOverlay();
348
+ };
349
+ const observer = new MutationObserver(onAttributeChange);
350
+ observer.observe(ta, {
351
+ attributes: true,
352
+ attributeFilter: ["data-ephia-recording"]
353
+ });
354
+ return () => {
355
+ observer.disconnect();
356
+ if (asciiTimerRef.current !== null) {
357
+ clearInterval(asciiTimerRef.current);
358
+ asciiTimerRef.current = null;
359
+ }
360
+ };
361
+ }, []);
362
+ useEffect5(() => {
363
+ const ta = textareaRef.current;
364
+ if (!ta) return;
365
+ const onPartial = (e) => {
366
+ const { text, insertAt } = e.detail;
367
+ partialRef.current = { text, insertAt };
368
+ updateOverlay();
369
+ };
370
+ const onPartialCleared = () => {
371
+ partialRef.current = null;
372
+ updateOverlay();
373
+ };
374
+ const onCursorPosition = (e) => {
375
+ const { position } = e.detail;
376
+ anchorEndRef.current = position;
377
+ updateOverlay();
378
+ };
379
+ ta.addEventListener("ephia:partial", onPartial);
380
+ ta.addEventListener("ephia:partial-cleared", onPartialCleared);
381
+ ta.addEventListener("ephia:cursor-position", onCursorPosition);
382
+ return () => {
383
+ ta.removeEventListener("ephia:partial", onPartial);
384
+ ta.removeEventListener("ephia:partial-cleared", onPartialCleared);
385
+ ta.removeEventListener("ephia:cursor-position", onCursorPosition);
386
+ };
387
+ }, []);
388
+ useEffect5(() => {
389
+ const ta = textareaRef.current;
390
+ const ov = overlayRef.current;
391
+ if (!ta || !ov) return;
392
+ const syncScroll = () => {
393
+ ov.scrollTop = ta.scrollTop;
394
+ ov.scrollLeft = ta.scrollLeft;
395
+ };
396
+ ta.addEventListener("scroll", syncScroll, { passive: true });
397
+ return () => ta.removeEventListener("scroll", syncScroll);
398
+ }, []);
399
+ useEffect5(() => {
400
+ const ta = textareaRef.current;
401
+ const ov = overlayRef.current;
402
+ if (!ta || !ov) return;
403
+ const applyStyles = () => {
404
+ const cs = window.getComputedStyle(ta);
405
+ const s = ov.style;
406
+ s.fontFamily = cs.fontFamily;
407
+ s.fontSize = cs.fontSize;
408
+ s.fontWeight = cs.fontWeight;
409
+ s.fontStyle = cs.fontStyle;
410
+ s.lineHeight = cs.lineHeight;
411
+ s.letterSpacing = cs.letterSpacing;
412
+ s.wordSpacing = cs.wordSpacing;
413
+ s.textTransform = cs.textTransform;
414
+ s.textIndent = cs.textIndent;
415
+ s.paddingTop = cs.paddingTop;
416
+ s.paddingRight = cs.paddingRight;
417
+ s.paddingBottom = cs.paddingBottom;
418
+ s.paddingLeft = cs.paddingLeft;
419
+ s.borderTopWidth = cs.borderTopWidth;
420
+ s.borderRightWidth = cs.borderRightWidth;
421
+ s.borderBottomWidth = cs.borderBottomWidth;
422
+ s.borderLeftWidth = cs.borderLeftWidth;
423
+ s.boxSizing = cs.boxSizing;
424
+ s.width = cs.width;
425
+ s.height = cs.height;
426
+ };
427
+ applyStyles();
428
+ let ro = null;
429
+ if (typeof ResizeObserver !== "undefined") {
430
+ ro = new ResizeObserver(applyStyles);
431
+ ro.observe(ta);
432
+ }
433
+ return () => ro?.disconnect();
434
+ }, []);
435
+ useEffect5(() => {
436
+ const ta = textareaRef.current;
437
+ if (!ta) return;
438
+ const onInput = () => updateOverlay();
439
+ ta.addEventListener("input", onInput);
440
+ return () => ta.removeEventListener("input", onInput);
441
+ }, []);
442
+ useEffect5(() => {
443
+ updateOverlay();
444
+ }, []);
445
+ useEffect5(() => {
446
+ const ta = textareaRef.current;
447
+ if (!ta) return;
448
+ requestAnimationFrame(() => updateOverlay());
449
+ }, [rest.value]);
450
+ const inlineStyle = isActive ? { ...style, caretColor: "transparent" } : style;
451
+ return /* @__PURE__ */ jsxs2("div", { ref: wrapperRef, className: "ephia-textarea-wrapper", style: { position: "relative" }, children: [
452
+ /* @__PURE__ */ jsx4(
453
+ "div",
454
+ {
455
+ ref: overlayRef,
456
+ className: "ephia-textarea-overlay",
457
+ "aria-hidden": "true"
458
+ }
459
+ ),
460
+ /* @__PURE__ */ jsx4(
461
+ "textarea",
462
+ {
463
+ ref: setTextareaRef,
464
+ "data-ephia-target": targetId,
465
+ "data-ephia-mode": mode,
466
+ "data-ephia-insertion": insertion,
467
+ className: `ephia-textarea-input ${className}`,
468
+ style: inlineStyle,
469
+ ...rest
470
+ }
471
+ )
472
+ ] });
473
+ });
474
+
475
+ export {
476
+ EphiaAuto,
477
+ useEphiaNativeTarget,
478
+ useEphiaTarget,
479
+ useEphiaTiptap,
480
+ useEphiaServerEvent,
481
+ useEphiaDiscardTarget,
482
+ EphiaDictationButton,
483
+ EphiaStatusBar,
484
+ EphiaTextarea
485
+ };
486
+ //# sourceMappingURL=chunk-EMOEAPVU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/EphiaAuto.tsx","../src/react/hooks/useEphiaTarget.ts","../src/react/hooks/targets/useEphiaTiptap.ts","../src/react/hooks/useEphiaServerEvent.ts","../src/react/hooks/useEphiaDiscardTarget.ts","../src/react/components/EphiaDictationButton.tsx","../src/react/components/EphiaStatusBar.tsx","../src/react/components/EphiaTextarea.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useEffect, useRef } from \"react\";\nimport { useEphiaContext } from \"./provider/EphiaContext\";\nimport { useEphiaInternal } from \"./provider/EphiaInternalContext\";\nimport { NativeBinding } from \"../core/bindings/native/NativeBinding\";\n\nexport interface EphiaAutoProps {\n children: React.ReactNode;\n /** CSS selector for target elements. Defaults to [data-ephia-target]. */\n selector?: string;\n}\n\n/**\n * Scans its descendants for [data-ephia-target] elements (textarea/input only)\n * and registers them automatically as V2 NativeBinding targets.\n *\n * Uses a MutationObserver to handle dynamically added/removed elements.\n *\n * @example\n * <EphiaProvider getToken={getToken}>\n * <EphiaAuto>\n * <textarea data-ephia-target=\"report\" />\n * </EphiaAuto>\n * </EphiaProvider>\n */\nexport function EphiaAuto({ children, selector = \"[data-ephia-target]\" }: EphiaAutoProps) {\n const rootRef = useRef<HTMLDivElement | null>(null);\n const { registerTarget } = useEphiaContext();\n const { clientEpoch } = useEphiaInternal();\n const registerTargetRef = useRef(registerTarget);\n registerTargetRef.current = registerTarget;\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n\n const registered = new Map<Element, () => void>();\n\n const tryRegister = (el: Element) => {\n if (registered.has(el)) return;\n if (\n !(el instanceof HTMLTextAreaElement) &&\n !(el instanceof HTMLInputElement)\n ) {\n console.warn(\"[ephia:auto] unsupported element type — only textarea/input supported\", el);\n return;\n }\n const id = el.getAttribute(\"data-ephia-target\");\n if (!id) return;\n\n const mode = el.getAttribute(\"data-ephia-mode\") ?? \"write\";\n const binding = new NativeBinding(el);\n const cleanup = registerTargetRef.current(id, binding, { mode, element: el });\n registered.set(el, cleanup);\n };\n\n const tryUnregister = (el: Element) => {\n const cleanup = registered.get(el);\n if (cleanup) {\n cleanup();\n registered.delete(el);\n }\n };\n\n const scan = () => {\n const elements = Array.from(root.querySelectorAll(selector));\n for (const el of elements) tryRegister(el);\n for (const [el] of registered) {\n if (!root.contains(el)) tryUnregister(el);\n }\n };\n\n scan();\n\n const observer = new MutationObserver(scan);\n observer.observe(root, { childList: true, subtree: true });\n\n return () => {\n observer.disconnect();\n for (const [, cleanup] of registered) cleanup();\n registered.clear();\n };\n }, [selector, clientEpoch]);\n\n return <div ref={rootRef}>{children}</div>;\n}\n","import { useEffect } from \"react\";\nimport { useEphiaContext } from \"../provider/EphiaContext\";\nimport { useEphiaInternal } from \"../provider/EphiaInternalContext\";\nimport { NativeBinding } from \"../../core/bindings/native/NativeBinding\";\n\nexport interface UseEphiaNativeTargetOptions {\n id: string;\n mode?: string;\n label?: string;\n enabled?: boolean;\n}\n\n/** @deprecated Use `UseEphiaNativeTargetOptions` instead. */\nexport type UseEphiaTargetOptions = UseEphiaNativeTargetOptions;\n\n/**\n * Registers a textarea or input element as a V2 Ephia target.\n * Creates a NativeBinding and wires it to the TargetManager via context.\n *\n * @example\n * const ref = useRef<HTMLTextAreaElement>(null)\n * useEphiaNativeTarget(ref, { id: 'report', label: 'Compte rendu' })\n * return <textarea ref={ref} />\n */\nexport function useEphiaNativeTarget<T extends HTMLInputElement | HTMLTextAreaElement>(\n ref: React.RefObject<T | null>,\n options: UseEphiaNativeTargetOptions,\n): void {\n const { registerTarget } = useEphiaContext();\n const { clientEpoch } = useEphiaInternal();\n const { id, mode, enabled } = options;\n\n useEffect(() => {\n if (enabled === false) return;\n const el = ref.current;\n if (!el) return;\n return registerTarget(id, new NativeBinding(el), {\n mode: mode ?? \"write\",\n element: el,\n });\n }, [ref, id, mode, enabled, registerTarget, clientEpoch]);\n}\n\n/**\n * @deprecated Use `useEphiaNativeTarget` for textarea/input fields.\n */\nexport const useEphiaTarget = useEphiaNativeTarget;\n","import type { Editor } from \"@tiptap/core\";\nimport { createTiptapEphiaAdapter } from \"../../../rich-editor/adapters/tiptap\";\nimport { useEphiaRichEditor } from \"../../../rich-editor/use-ephia-rich-editor\";\nimport type { UseEphiaNativeTargetOptions } from \"../useEphiaTarget\";\n\n/** @deprecated Use `UseEphiaNativeTargetOptions` instead. */\nexport type UseEphiaTargetOptions = UseEphiaNativeTargetOptions;\n\n/**\n * @deprecated Use `useEphiaRichEditor(createTiptapEphiaAdapter(editor), options)`.\n *\n * @example\n * const ref = useEphiaRichEditor(\n * createTiptapEphiaAdapter(editor),\n * { id: 'compte-rendu', label: 'Compte rendu' },\n * );\n */\nexport function useEphiaTiptap(\n editor: Editor | null,\n options: UseEphiaNativeTargetOptions,\n): React.RefObject<HTMLDivElement | null> {\n return useEphiaRichEditor(createTiptapEphiaAdapter(editor), options);\n}\n","\"use client\";\n\nimport { useEffect, useRef } from \"react\";\nimport type { EphiaServerEvent } from \"ephia-protocol\";\nimport { useEphiaInternal } from \"../provider/EphiaInternalContext\";\n\n/**\n * Subscribe to a specific server event emitted by the underlying SDK client.\n *\n * @example\n * useEphiaServerEvent(\"segment.operation\", (event) => {\n * console.log(\"segment\", event.payload.segmentId, event.payload.text);\n * });\n */\nexport function useEphiaServerEvent<T extends EphiaServerEvent[\"type\"]>(\n type: T,\n handler: (event: Extract<EphiaServerEvent, { type: T }>) => void\n): void {\n const { clientRef, clientEpoch } = useEphiaInternal();\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n\n useEffect(() => {\n const client = clientRef.current;\n if (!client) return;\n\n return client.onServerEvent((event) => {\n if (event.type === type) {\n handlerRef.current(event as Extract<EphiaServerEvent, { type: T }>);\n }\n });\n }, [clientRef, clientEpoch, type]);\n}\n","import { useEffect, useMemo } from \"react\";\nimport type { EditorContext } from \"ephia-protocol\";\nimport type { EphiaBinding } from \"../../core/bindings/EphiaBinding\";\nimport { useEphiaContext } from \"../provider/EphiaContext\";\n\nfunction createDiscardBinding(): EphiaBinding {\n return {\n kind: \"discard\",\n attach() {},\n detach() {},\n getText() {\n return \"\";\n },\n getEditorContext(targetId = \"\"): EditorContext {\n return {\n targetId,\n documentEmpty: true,\n insertionMode: \"insert_at_cursor\",\n leftContext: \"\",\n rightContext: \"\",\n selectedText: null,\n cursorOffset: 0,\n };\n },\n previewSegment() {},\n upsertSegment() {},\n removeSegment() {},\n removeSegments() {},\n };\n}\n\nexport interface UseEphiaDiscardTargetOptions {\n enabled?: boolean;\n}\n\n/**\n * Enregistre une cible no-op (segments ignorés côté éditeur).\n * Utile en mode « event-only » : l'app affiche le transcript via useEphiaServerEvent\n * sans textarea/TipTap, mais le backend route quand même vers un targetId.\n */\nexport function useEphiaDiscardTarget(\n id: string,\n options: UseEphiaDiscardTargetOptions = {},\n): void {\n const { registerTarget } = useEphiaContext();\n const { enabled = true } = options;\n const binding = useMemo(() => createDiscardBinding(), []);\n\n useEffect(() => {\n if (!enabled) return;\n return registerTarget(id, binding, { mode: \"write\" });\n }, [binding, enabled, id, registerTarget]);\n}\n","\"use client\";\n\nimport React, { useCallback, useState } from \"react\";\nimport { useEphia } from \"../hooks/useEphia\";\nimport type { EphiaStatus } from \"../store/types\";\n\nexport interface EphiaDictationButtonRenderProps {\n status: EphiaStatus;\n isRecording: boolean;\n toggle: () => void;\n disabled: boolean;\n}\n\nexport interface EphiaDictationButtonProps {\n targetId?: string;\n disabled?: boolean;\n className?: string;\n onError?: (error: unknown) => void;\n\n /**\n * Render prop — le dev fournit son propre rendu.\n * Si absent, un bouton minimaliste est rendu.\n *\n * @example\n * <EphiaDictationButton>\n * {({ isRecording, toggle }) => (\n * <MyButton onClick={toggle} variant={isRecording ? 'danger' : 'primary'} />\n * )}\n * </EphiaDictationButton>\n */\n children?: (props: EphiaDictationButtonRenderProps) => React.ReactNode;\n}\n\nexport function EphiaDictationButton({\n targetId,\n disabled = false,\n className,\n onError,\n children,\n}: EphiaDictationButtonProps) {\n const { status, isRecording, isProcessing, start, stop } = useEphia();\n const [localError, setLocalError] = useState<string | null>(null);\n\n const isDisabled = disabled || isProcessing;\n\n const toggle = useCallback(async () => {\n setLocalError(null);\n try {\n if (isRecording) {\n await stop();\n } else {\n await start(targetId);\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n setLocalError(message);\n onError?.(err);\n }\n }, [isRecording, start, stop, targetId, onError]);\n\n const renderProps: EphiaDictationButtonRenderProps = {\n status,\n isRecording,\n toggle,\n disabled: isDisabled,\n };\n\n if (children) {\n return <>{children(renderProps)}</>;\n }\n\n return (\n <button\n type=\"button\"\n onClick={toggle}\n disabled={isDisabled}\n className={className}\n aria-label={isRecording ? \"Arrêter la dictée\" : \"Démarrer la dictée\"}\n aria-pressed={isRecording}\n aria-busy={isProcessing}\n data-ephia-control=\"true\"\n data-ephia-status={status}\n title={localError ?? undefined}\n >\n {isRecording ? \"⏹\" : \"🎤\"}\n </button>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { useEphiaStatus, useEphiaPartial } from \"../hooks/useEphia\";\nimport type { EphiaStatus } from \"../store/types\";\n\nexport interface EphiaStatusBarRenderProps {\n status: EphiaStatus;\n partialText: string | null;\n}\n\nexport interface EphiaStatusBarProps {\n className?: string;\n children?: (props: EphiaStatusBarRenderProps) => React.ReactNode;\n}\n\nexport function EphiaStatusBar({\n className,\n children,\n}: EphiaStatusBarProps) {\n const status = useEphiaStatus();\n const partial = useEphiaPartial();\n\n if (status === \"idle\" || status === \"recording\") return null;\n\n const partialText = partial?.text ?? null;\n\n const renderProps: EphiaStatusBarRenderProps = {\n status,\n partialText,\n };\n\n if (children) {\n return <>{children(renderProps)}</>;\n }\n\n return (\n <div\n className={className}\n data-ephia-control=\"true\"\n data-ephia-status={status}\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"false\"\n >\n <span data-ephia-indicator aria-hidden=\"true\" />\n\n {status === \"processing\" && (\n <span data-ephia-state=\"processing\" aria-label=\"Traitement en cours\" />\n )}\n\n {status === \"error\" && (\n <span data-ephia-state=\"error\" role=\"alert\">\n Erreur de connexion\n </span>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport React, { useCallback, useEffect, useRef, useState } from \"react\";\n\nconst ASCII_CURSOR_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"] as const;\nconst ASCII_CURSOR_INTERVAL_MS = 90;\n\nexport interface EphiaTextareaProps\n extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {\n targetId: string;\n mode?: string;\n insertion?: \"preview-inline\" | \"preview-floating\" | \"final-only\" | \"none\";\n}\n\n/**\n * Textarea avec overlay de preview du partial en streaming.\n *\n * Stratégie (Dragon Medical-style) :\n * - La `<textarea>` contient uniquement le texte committed (color: transparent).\n * - L'overlay div par-dessous affiche : texte committed + partial grisé.\n */\nexport const EphiaTextarea = React.forwardRef<\n HTMLTextAreaElement,\n EphiaTextareaProps\n>(function EphiaTextarea(\n { targetId, mode = \"write\", insertion = \"preview-inline\", className = \"\", style, ...rest },\n forwardedRef\n) {\n const wrapperRef = useRef<HTMLDivElement>(null);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const overlayRef = useRef<HTMLDivElement>(null);\n const partialRef = useRef<{ text: string; insertAt: number } | null>(null);\n const insertionRef = useRef(insertion);\n insertionRef.current = insertion;\n const anchorEndRef = useRef<number | null>(null);\n const asciiFrameRef = useRef(0);\n const asciiTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n const appendAsciiCursor = (ov: HTMLDivElement) => {\n const span = document.createElement(\"span\");\n span.className = \"ephia-cursor\";\n span.setAttribute(\"aria-hidden\", \"true\");\n span.textContent = ASCII_CURSOR_FRAMES[asciiFrameRef.current] ?? \"⠋\";\n ov.appendChild(span);\n };\n\n const setTextareaRef = useCallback(\n (node: HTMLTextAreaElement | null) => {\n (textareaRef as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;\n if (typeof forwardedRef === \"function\") {\n forwardedRef(node);\n } else if (forwardedRef) {\n (forwardedRef as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;\n }\n },\n [forwardedRef]\n );\n\n const [isActive, setIsActive] = useState(false);\n\n // ─── Helpers DOM overlay (évite innerHTML + escapeHtml à chaque frame) ───\n const clearOverlay = (ov: HTMLDivElement) => {\n while (ov.firstChild) ov.removeChild(ov.firstChild);\n };\n\n const appendText = (ov: HTMLDivElement, text: string) => {\n if (text) ov.appendChild(document.createTextNode(text));\n };\n\n const appendBrNbsp = (ov: HTMLDivElement) => {\n ov.appendChild(document.createElement(\"br\"));\n ov.appendChild(document.createTextNode(\"\\u00A0\"));\n };\n\n // ─── Mise à jour impérative de l'overlay ─────────────────────────────────\n const updateOverlay = () => {\n const ov = overlayRef.current;\n const ta = textareaRef.current;\n if (!ov || !ta) return;\n\n const rawText = ta.value;\n const partial = partialRef.current;\n const isRecording = ta.hasAttribute(\"data-ephia-recording\");\n const active = isRecording;\n\n clearOverlay(ov);\n\n // ── Recording / idle : texte committed + partial ───────────────────────\n if (!partial || insertionRef.current === \"none\") {\n if (active) {\n const cursorPos = Math.min(\n anchorEndRef.current ?? rawText.length,\n rawText.length\n );\n appendText(ov, rawText.slice(0, cursorPos));\n appendAsciiCursor(ov);\n appendText(ov, rawText.slice(cursorPos));\n appendBrNbsp(ov);\n return;\n }\n appendText(ov, rawText);\n appendBrNbsp(ov);\n return;\n }\n\n const insertAt = Math.min(partial.insertAt, rawText.length);\n const textAtInsert = rawText.slice(insertAt, insertAt + partial.text.length);\n const isAbsorbed = textAtInsert === partial.text;\n\n appendText(ov, rawText.slice(0, insertAt));\n if (!isAbsorbed) {\n const span = document.createElement(\"span\");\n span.className = \"ephia-text--streaming\";\n span.textContent = partial.text;\n ov.appendChild(span);\n } else if (active) {\n appendAsciiCursor(ov);\n }\n appendText(ov, rawText.slice(insertAt));\n appendBrNbsp(ov);\n };\n\n // ─── MutationObserver : synchroniser l'état actif ────────────────────────\n useEffect(() => {\n const ta = textareaRef.current;\n const wr = wrapperRef.current;\n if (!ta) return;\n\n const onAttributeChange = () => {\n const isRecording = ta.hasAttribute(\"data-ephia-recording\");\n setIsActive(isRecording);\n if (!isRecording) {\n anchorEndRef.current = null;\n if (asciiTimerRef.current !== null) {\n clearInterval(asciiTimerRef.current);\n asciiTimerRef.current = null;\n }\n } else if (asciiTimerRef.current === null) {\n asciiTimerRef.current = setInterval(() => {\n asciiFrameRef.current = (asciiFrameRef.current + 1) % ASCII_CURSOR_FRAMES.length;\n updateOverlay();\n }, ASCII_CURSOR_INTERVAL_MS);\n }\n updateOverlay();\n };\n\n const observer = new MutationObserver(onAttributeChange);\n observer.observe(ta, {\n attributes: true,\n attributeFilter: [\"data-ephia-recording\"],\n });\n\n return () => {\n observer.disconnect();\n if (asciiTimerRef.current !== null) {\n clearInterval(asciiTimerRef.current);\n asciiTimerRef.current = null;\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // ─── Écoute des events DOM du binding ────────────────────────────────────\n useEffect(() => {\n const ta = textareaRef.current;\n if (!ta) return;\n\n const onPartial = (e: Event) => {\n const { text, insertAt } = (e as CustomEvent<{ text: string; insertAt: number }>).detail;\n partialRef.current = { text, insertAt };\n updateOverlay();\n };\n\n const onPartialCleared = () => {\n partialRef.current = null;\n updateOverlay();\n };\n\n const onCursorPosition = (e: Event) => {\n const { position } = (e as CustomEvent<{ position: number }>).detail;\n anchorEndRef.current = position;\n updateOverlay();\n };\n\n ta.addEventListener(\"ephia:partial\", onPartial);\n ta.addEventListener(\"ephia:partial-cleared\", onPartialCleared);\n ta.addEventListener(\"ephia:cursor-position\", onCursorPosition);\n return () => {\n ta.removeEventListener(\"ephia:partial\", onPartial);\n ta.removeEventListener(\"ephia:partial-cleared\", onPartialCleared);\n ta.removeEventListener(\"ephia:cursor-position\", onCursorPosition);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // ─── Sync scroll overlay ↔ textarea ──────────────────────────────────────\n useEffect(() => {\n const ta = textareaRef.current;\n const ov = overlayRef.current;\n if (!ta || !ov) return;\n const syncScroll = () => {\n ov.scrollTop = ta.scrollTop;\n ov.scrollLeft = ta.scrollLeft;\n };\n ta.addEventListener(\"scroll\", syncScroll, { passive: true });\n return () => ta.removeEventListener(\"scroll\", syncScroll);\n }, []);\n\n // ─── Sync styles computed → overlay (font, padding, etc.) ─────────────────\n useEffect(() => {\n const ta = textareaRef.current;\n const ov = overlayRef.current;\n if (!ta || !ov) return;\n\n const applyStyles = () => {\n const cs = window.getComputedStyle(ta);\n const s = ov.style;\n s.fontFamily = cs.fontFamily;\n s.fontSize = cs.fontSize;\n s.fontWeight = cs.fontWeight;\n s.fontStyle = cs.fontStyle;\n s.lineHeight = cs.lineHeight;\n s.letterSpacing = cs.letterSpacing;\n s.wordSpacing = cs.wordSpacing;\n s.textTransform = cs.textTransform;\n s.textIndent = cs.textIndent;\n s.paddingTop = cs.paddingTop;\n s.paddingRight = cs.paddingRight;\n s.paddingBottom = cs.paddingBottom;\n s.paddingLeft = cs.paddingLeft;\n s.borderTopWidth = cs.borderTopWidth;\n s.borderRightWidth = cs.borderRightWidth;\n s.borderBottomWidth = cs.borderBottomWidth;\n s.borderLeftWidth = cs.borderLeftWidth;\n s.boxSizing = cs.boxSizing;\n s.width = cs.width;\n s.height = cs.height;\n };\n\n applyStyles();\n let ro: ResizeObserver | null = null;\n if (typeof ResizeObserver !== \"undefined\") {\n ro = new ResizeObserver(applyStyles);\n ro.observe(ta);\n }\n return () => ro?.disconnect();\n }, []);\n\n // ─── Mise à jour overlay sur input utilisateur ────────────────────────────\n useEffect(() => {\n const ta = textareaRef.current;\n if (!ta) return;\n const onInput = () => updateOverlay();\n ta.addEventListener(\"input\", onInput);\n return () => ta.removeEventListener(\"input\", onInput);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // ─── Initialisation overlay au montage ───────────────────────────────────\n useEffect(() => {\n updateOverlay();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // ─── Sync overlay quand value change via React (controlled component) ─────\n useEffect(() => {\n const ta = textareaRef.current;\n if (!ta) return;\n requestAnimationFrame(() => updateOverlay());\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [rest.value]);\n\n const inlineStyle = isActive\n ? { ...style, caretColor: \"transparent\" as const }\n : style;\n\n return (\n <div ref={wrapperRef} className=\"ephia-textarea-wrapper\" style={{ position: \"relative\" }}>\n <div\n ref={overlayRef}\n className=\"ephia-textarea-overlay\"\n aria-hidden=\"true\"\n />\n <textarea\n ref={setTextareaRef}\n data-ephia-target={targetId}\n data-ephia-mode={mode}\n data-ephia-insertion={insertion}\n className={`ephia-textarea-input ${className}`}\n style={inlineStyle}\n {...rest}\n />\n </div>\n );\n});\n"],"mappings":"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","names":["useEffect","useEffect","useEffect","useRef","useRef","useEffect","useEffect","useEffect","jsx","Fragment","jsx","React","useCallback","useEffect","useRef","useState","jsx","jsxs","EphiaTextarea"]}
@@ -0,0 +1,40 @@
1
+ import {
2
+ initialConnectionState
3
+ } from "./chunk-RFQRV7ML.js";
4
+ import {
5
+ initialAudioState
6
+ } from "./chunk-EGIAN7FH.js";
7
+
8
+ // src/react/store/create-ephia-store.ts
9
+ import { createStore } from "zustand/vanilla";
10
+ var baseState = {
11
+ status: "idle",
12
+ sessionId: null,
13
+ error: null,
14
+ activeTargetId: null,
15
+ partial: null,
16
+ connection: initialConnectionState,
17
+ audio: initialAudioState
18
+ };
19
+ function createEphiaStore() {
20
+ return createStore((set) => ({
21
+ ...baseState,
22
+ _setStatus: (status) => set({ status }),
23
+ _setSessionId: (sessionId) => set({ sessionId }),
24
+ _setError: (error) => set({ error }),
25
+ _setActiveTarget: (activeTargetId) => set({ activeTargetId }),
26
+ _setPartial: (partial) => set({ partial }),
27
+ _setConnection: (connection) => set({ connection }),
28
+ _setAudio: (audio) => set({ audio }),
29
+ _reset: () => set({
30
+ ...baseState,
31
+ connection: { ...initialConnectionState },
32
+ audio: { ...initialAudioState }
33
+ })
34
+ }));
35
+ }
36
+
37
+ export {
38
+ createEphiaStore
39
+ };
40
+ //# sourceMappingURL=chunk-IDC7FHIZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/store/create-ephia-store.ts"],"sourcesContent":["import { createStore } from \"zustand/vanilla\";\nimport type { StoreApi } from \"zustand/vanilla\";\nimport { initialConnectionState } from \"../../core/connection/connection-state\";\nimport { initialAudioState } from \"../../shared/state/audio-state\";\nimport type { EphiaStoreState } from \"./types\";\n\nexport type EphiaStore = StoreApi<EphiaStoreState>;\n\nconst baseState = {\n status: \"idle\" as const,\n sessionId: null,\n error: null,\n activeTargetId: null,\n partial: null,\n connection: initialConnectionState,\n audio: initialAudioState,\n};\n\nexport function createEphiaStore(): EphiaStore {\n return createStore<EphiaStoreState>((set) => ({\n ...baseState,\n\n _setStatus: (status) => set({ status }),\n _setSessionId: (sessionId) => set({ sessionId }),\n _setError: (error) => set({ error }),\n _setActiveTarget: (activeTargetId) => set({ activeTargetId }),\n _setPartial: (partial) => set({ partial }),\n _setConnection: (connection) => set({ connection }),\n _setAudio: (audio) => set({ audio }),\n _reset: () => set({\n ...baseState,\n connection: { ...initialConnectionState },\n audio: { ...initialAudioState },\n }),\n }));\n}\n"],"mappings":";;;;;;;;AAAA,SAAS,mBAAmB;AAQ5B,IAAM,YAAY;AAAA,EAChB,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,OAAO;AACT;AAEO,SAAS,mBAA+B;AAC7C,SAAO,YAA6B,CAAC,SAAS;AAAA,IAC5C,GAAG;AAAA,IAEH,YAAY,CAAC,WAAW,IAAI,EAAE,OAAO,CAAC;AAAA,IACtC,eAAe,CAAC,cAAc,IAAI,EAAE,UAAU,CAAC;AAAA,IAC/C,WAAW,CAAC,UAAU,IAAI,EAAE,MAAM,CAAC;AAAA,IACnC,kBAAkB,CAAC,mBAAmB,IAAI,EAAE,eAAe,CAAC;AAAA,IAC5D,aAAa,CAAC,YAAY,IAAI,EAAE,QAAQ,CAAC;AAAA,IACzC,gBAAgB,CAAC,eAAe,IAAI,EAAE,WAAW,CAAC;AAAA,IAClD,WAAW,CAAC,UAAU,IAAI,EAAE,MAAM,CAAC;AAAA,IACnC,QAAQ,MAAM,IAAI;AAAA,MAChB,GAAG;AAAA,MACH,YAAY,EAAE,GAAG,uBAAuB;AAAA,MACxC,OAAO,EAAE,GAAG,kBAAkB;AAAA,IAChC,CAAC;AAAA,EACH,EAAE;AACJ;","names":[]}