@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 @@
1
+ {"version":3,"sources":["../src/ui/components/EphiaLogo.tsx","../src/ui/primitives/Button.tsx","../src/ui/primitives/Spinner.tsx","../src/ui/recorder/EphiaFloatingButton.tsx","../src/ui/recorder/appearance.ts","../src/ui/recorder/MinimalProcessingBars.tsx","../src/ui/recorder/StandardIntensityVisualizer.tsx"],"sourcesContent":["\"use client\";\n\nimport type { CSSProperties } from \"react\";\n\n/** Source of truth: `src/ui/assets/ephia-logo.svg` (same as apps/ephia-website/public/ephia_logo.svg). */\nexport const EPHIA_LOGO_VIEWBOX = \"0 0 220 207\";\nexport const EPHIA_LOGO_ASPECT = 220 / 207;\n\nexport const EPHIA_BRAND_PURPLE = \"#723ADE\";\nexport const EPHIA_BRAND_TEAL = \"#00EBD0\";\n\nconst LOGO_PURPLE_PATH =\n \"M130.184 22.8784C110.496 11.7584 85.4808 18.5556 74.1504 38.1772C66.8151 50.8807 67.004 65.8635 73.334 77.9663C76.4886 83.9978 77.6606 91.3208 74.2568 97.2153C70.8529 103.109 63.9255 105.756 57.1241 106.04C55.4205 106.111 53.7254 106.287 52.0498 106.566C51.9939 106.576 51.9377 106.586 51.8818 106.596C51.8243 106.606 51.7665 106.615 51.709 106.625C47.369 107.377 43.2604 108.806 39.4981 110.8C34.2218 113.586 29.4987 117.542 25.7589 122.554C25.7424 122.576 25.7254 122.598 25.709 122.62C25.6788 122.661 25.6482 122.701 25.6182 122.742C21.6987 128.032 19.015 134.293 17.9923 141.097C17.9778 141.192 17.964 141.289 17.9502 141.385C17.9421 141.442 17.9337 141.499 17.9258 141.556C17.6653 143.422 17.5345 145.296 17.5312 147.163V147.28C17.5314 157.14 20.9911 166.192 26.7617 173.29C26.796 173.332 26.8307 173.375 26.8652 173.417C26.928 173.494 26.9915 173.57 27.0548 173.646C27.1098 173.713 27.1653 173.779 27.2207 173.845C27.255 173.885 27.2898 173.927 27.3242 173.968C27.6222 174.319 27.9266 174.666 28.2374 175.009C28.2534 175.026 28.2701 175.044 28.2861 175.061C28.3779 175.162 28.4706 175.263 28.5635 175.363C28.5869 175.388 28.6114 175.413 28.6349 175.438C28.7264 175.536 28.8185 175.635 28.9111 175.732C28.9284 175.75 28.9456 175.769 28.9629 175.787C29.1492 175.982 29.3377 176.176 29.5283 176.367L29.6191 176.458C29.6653 176.504 29.7125 176.55 29.7589 176.596C29.8119 176.648 29.8646 176.702 29.918 176.754C36.4444 183.148 45.0865 187.393 54.6963 188.342C54.7093 188.343 54.7223 188.344 54.7354 188.346C55.0558 188.377 55.3777 188.405 55.7002 188.429C55.7229 188.43 55.746 188.432 55.7687 188.434C55.8996 188.443 56.031 188.452 56.1622 188.46C57.0288 188.515 57.8957 188.543 58.7607 188.543C58.774 188.543 58.7875 188.543 58.8008 188.543C58.8551 188.543 58.9096 188.542 58.9639 188.542C59.0597 188.542 59.1561 188.541 59.252 188.54C66.9707 188.457 74.1811 186.256 80.3292 182.49C80.3971 182.449 80.4656 182.407 80.5333 182.365C80.5789 182.337 80.6245 182.308 80.6699 182.279C85.3019 179.393 89.4161 175.551 92.6865 170.84C92.7302 170.777 92.7741 170.714 92.8174 170.651C92.8427 170.614 92.8684 170.577 92.8936 170.54C94.8281 167.711 96.419 164.628 97.6074 161.353C99.3661 156.525 100.166 151.511 100.075 146.558C100.266 130.196 106.911 115.387 117.584 104.555C120.604 101.544 123.936 98.8469 127.529 96.5151C128.082 96.1565 127.539 96.5073 129.953 95.0288C135.565 91.8709 140.505 87.373 144.245 81.6763C144.281 81.623 144.315 81.5686 144.351 81.5151C144.368 81.489 144.385 81.4622 144.402 81.436C144.828 80.7863 145.239 80.1208 145.632 79.4399C155.114 63.0194 152.024 42.7913 139.35 29.896C136.719 27.1793 133.672 24.7768 130.236 22.7886L130.184 22.8784Z\";\n\nconst LOGO_TEAL_PATH =\n \"M151.646 107.073C154.645 106.38 157.768 106.016 160.977 106.016C183.769 106.016 202.246 124.49 202.246 147.278C202.246 167.41 187.826 184.171 168.751 187.807C167.995 187.951 167.233 188.074 166.463 188.177C166.383 188.187 166.304 188.201 166.224 188.211C164.503 188.43 162.749 188.543 160.969 188.543C138.176 188.543 119.699 170.069 119.699 147.28C119.699 127.697 133.344 111.298 151.646 107.073Z\";\n\nexport type EphiaLogoVariant = \"color\" | \"mono\";\n\nexport interface EphiaLogoProps {\n height?: number;\n width?: number;\n className?: string;\n style?: CSSProperties;\n variant?: EphiaLogoVariant;\n /** Mono fill (custom brand colors). Ignored when variant is \"color\". */\n fill?: string;\n isRecording?: boolean;\n}\n\nexport function logoWidthFromHeight(heightPx: number): number {\n return Math.round(heightPx * EPHIA_LOGO_ASPECT);\n}\n\nexport function EphiaLogo({\n height,\n width,\n className,\n style,\n variant = \"color\",\n fill = \"currentColor\",\n isRecording = false,\n}: EphiaLogoProps) {\n const resolvedHeight = height ?? (width != null ? Math.round(width / EPHIA_LOGO_ASPECT) : 24);\n const resolvedWidth = width ?? logoWidthFromHeight(resolvedHeight);\n\n const svgClass = [\n \"ephia-transcribe-logo-svg\",\n isRecording ? \"ephia-transcribe-logo-svg--recording\" : \"\",\n className ?? \"\",\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <svg\n viewBox={EPHIA_LOGO_VIEWBOX}\n className={svgClass}\n style={{ height: resolvedHeight, width: resolvedWidth, ...style }}\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden\n >\n {variant === \"color\" ? (\n <>\n <path d={LOGO_PURPLE_PATH} fill={EPHIA_BRAND_PURPLE} />\n <path d={LOGO_TEAL_PATH} fill={EPHIA_BRAND_TEAL} />\n </>\n ) : (\n <>\n <path d={LOGO_PURPLE_PATH} fill={fill} />\n <path d={LOGO_TEAL_PATH} fill={fill} />\n </>\n )}\n </svg>\n );\n}\n","\"use client\";\n\nimport type { ButtonHTMLAttributes, ReactNode } from \"react\";\n\nexport type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {\n variant?: \"primary\" | \"danger\" | \"ghost\" | \"outline\";\n size?: \"sm\" | \"md\" | \"lg\";\n isLoading?: boolean;\n children: ReactNode;\n};\n\nexport function Button({\n variant = \"primary\",\n size = \"md\",\n isLoading,\n children,\n className = \"\",\n disabled,\n ...rest\n}: ButtonProps) {\n const base =\n \"inline-flex items-center justify-center font-medium rounded-full transition focus:outline-none focus:ring-2 focus:ring-offset-1\";\n\n const variants = {\n primary:\n \"bg-[var(--ephia-primary)] text-white hover:opacity-90 focus:ring-[var(--ephia-primary)]\",\n danger:\n \"bg-[var(--ephia-danger)] text-white hover:opacity-90 focus:ring-[var(--ephia-danger)]\",\n ghost:\n \"bg-transparent text-neutral-700 hover:bg-neutral-100 focus:ring-neutral-300\",\n outline:\n \"border border-neutral-200 bg-white text-neutral-700 hover:bg-neutral-50 focus:ring-neutral-300\",\n };\n\n const sizes = {\n sm: \"px-3 py-1.5 text-xs\",\n md: \"px-4 py-2 text-sm\",\n lg: \"px-6 py-3 text-base\",\n };\n\n return (\n <button\n className={`${base} ${variants[variant]} ${sizes[size]} ${className}`}\n disabled={disabled || isLoading}\n {...rest}\n >\n {isLoading ? (\n <span className=\"mr-2 inline-block h-4 w-4 animate-spin rounded-full border-2 border-white/30 border-t-white\" />\n ) : null}\n {children}\n </button>\n );\n}\n","\"use client\";\n\nexport type SpinnerProps = {\n size?: \"sm\" | \"md\" | \"lg\";\n className?: string;\n};\n\nexport function Spinner({ size = \"md\", className = \"\" }: SpinnerProps) {\n const sizes = {\n sm: \"h-3 w-3 border-2\",\n md: \"h-5 w-5 border-2\",\n lg: \"h-8 w-8 border-[3px]\",\n };\n\n return (\n <span\n className={`inline-block animate-spin rounded-full border-neutral-300 border-t-[var(--ephia-primary)] ${sizes[size]} ${className}`}\n aria-label=\"Chargement\"\n />\n );\n}\n","\"use client\";\n\nimport React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport { EphiaProvider } from \"../../react/provider/EphiaProvider\";\nimport { useOptionalEphiaContext } from \"../../react/provider/EphiaContext\";\nimport { useEphia, useEphiaAudioLevel } from \"../../react/hooks/useEphia\";\nimport type { EphiaStatus } from \"../../react/store/types\";\nimport type { EphiaSessionOptions } from \"../../shared/types/session\";\nimport type { Transport } from \"../../core/transport/Transport\";\nimport {\n EphiaLogo,\n EPHIA_BRAND_PURPLE,\n logoWidthFromHeight,\n} from \"../components/EphiaLogo\";\nimport {\n EPHIA_FLOATING_BUTTON_RADIUS_CSS,\n type EphiaFloatingButtonBorderRadius,\n} from \"./appearance\";\nimport { MinimalProcessingBars } from \"./MinimalProcessingBars\";\nimport { StandardIntensityVisualizer } from \"./StandardIntensityVisualizer\";\n\nexport type { EphiaFloatingButtonBorderRadius } from \"./appearance\";\n\nconst EPHIA_APP_URL = \"https://ephia.app\";\n\nfunction EphiaExternalLinkIcon() {\n return (\n <svg\n className=\"ephia-transcribe-poweredby-icon\"\n viewBox=\"0 0 24 24\"\n aria-hidden\n focusable=\"false\"\n >\n <path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\" />\n <path d=\"M15 3h6v6\" />\n <path d=\"M10 14 21 3\" />\n </svg>\n );\n}\n\n/* ── Types ───────────────────────────────────────────────────────── */\n\nexport type EphiaFloatingButtonPosition =\n | \"bottom-right\"\n | \"bottom-left\"\n | \"bottom-center\"\n | \"top-right\"\n | \"top-left\"\n | \"top-center\";\nexport type EphiaFloatingButtonTheme = \"light\" | \"dark\";\nexport type EphiaFloatingButtonSize = \"S\" | \"M\" | \"L\";\nexport type EphiaFloatingButtonVariant = \"minimal\" | \"standard\";\nexport type EphiaFloatingButtonInteractionMode = \"toggle\" | \"push-to-talk\";\nexport type EphiaFloatingButtonCustomColors = {\n /** Ink color: logo, visualiseur Standard, texte. Any valid CSS color. */\n primary: string;\n /** Background color. Any valid CSS color. */\n secondary: string;\n};\n\nexport interface EphiaFloatingButtonProps {\n /**\n * API URL. Optional override (ex. http://localhost:8000 en dev).\n * Sans override : EPHIA_SDK_ENDPOINT, sinon https://api.ephia.app.\n */\n apiUrl?: string;\n apiKey?: string;\n bearerToken?: string;\n clientType?: string;\n transport?: Transport;\n position?: EphiaFloatingButtonPosition;\n theme?: EphiaFloatingButtonTheme;\n size?: EphiaFloatingButtonSize;\n variant?: EphiaFloatingButtonVariant;\n colors?: EphiaFloatingButtonCustomColors;\n /** Corner radius of the dictation control. Default `md`. */\n borderRadius?: EphiaFloatingButtonBorderRadius;\n /** Interaction model. `\"toggle\"` (default): click to start, click to stop. `\"push-to-talk\"`: hold to record, release to stop. */\n interactionMode?: EphiaFloatingButtonInteractionMode;\n className?: string;\n style?: React.CSSProperties;\n sessionOptions?: EphiaSessionOptions;\n onError?: (error: { code: string; message: string }) => void;\n onStatusChange?: (status: EphiaStatus) => void;\n}\n\nconst SIZE_CONFIG: Record<EphiaFloatingButtonSize, { logoHeightPx: number }> = {\n S: { logoHeightPx: 24 },\n M: { logoHeightPx: 32 },\n L: { logoHeightPx: 44 },\n};\n\n/** Logo légèrement réduit en pilule (full) pour l’équilibre visuel. */\nconst LOGO_SCALE_WHEN_RADIUS_FULL = 0.92;\n\nconst POSITION_STYLE: Record<EphiaFloatingButtonPosition, React.CSSProperties> = {\n \"bottom-right\": { bottom: 24, right: 24 },\n \"bottom-left\": { bottom: 24, left: 24 },\n \"bottom-center\": { bottom: 24, left: \"50%\", transform: \"translateX(-50%)\" },\n \"top-right\": { top: 24, right: 24 },\n \"top-left\": { top: 24, left: 24 },\n \"top-center\": { top: 24, left: \"50%\", transform: \"translateX(-50%)\" },\n};\n\n/* ── Inner button — requires Provider in scope ───────────────────── */\n\ninterface InnerProps {\n position: EphiaFloatingButtonPosition;\n theme: EphiaFloatingButtonTheme;\n size: EphiaFloatingButtonSize;\n variant: EphiaFloatingButtonVariant;\n borderRadius: EphiaFloatingButtonBorderRadius;\n interactionMode: EphiaFloatingButtonInteractionMode;\n colors?: EphiaFloatingButtonCustomColors;\n className?: string;\n style?: React.CSSProperties;\n onError?: (error: { code: string; message: string }) => void;\n onStatusChange?: (status: EphiaStatus) => void;\n}\n\nfunction EphiaFloatingButtonInner({\n position,\n theme,\n size,\n variant,\n borderRadius,\n interactionMode,\n colors,\n className,\n style,\n onError,\n onStatusChange,\n}: InnerProps) {\n const { status, isRecording, isProcessing, start, stop, error, activeTargetId } = useEphia();\n const audio = useEphiaAudioLevel();\n\n // Traitement sans micro ouvert : bouton en attente.\n const isArming = isProcessing;\n const voiceLevel = Math.min(1, Math.max(0, audio?.level ?? audio?.rms ?? 0));\n const isLiveSession =\n isRecording || (!!audio?.localAudioPublished && !!audio?.micReady);\n\n // Callbacks — compare by value, not reference, to catch same-code re-errors\n const lastErrorRef = useRef<{ code: string; message: string } | null>(null);\n useEffect(() => {\n if (error && (lastErrorRef.current?.code !== error.code || lastErrorRef.current?.message !== error.message)) {\n lastErrorRef.current = error;\n onError?.(error);\n }\n }, [error, onError]);\n\n const lastStatusRef = useRef(status);\n useEffect(() => {\n if (status !== lastStatusRef.current) {\n lastStatusRef.current = status;\n onStatusChange?.(status);\n }\n }, [status, onStatusChange]);\n\n // Screen reader status\n const statusAnnouncement =\n isLiveSession ? \"Dictée en cours\" :\n status === \"processing\" ? \"Traitement\" :\n status === \"error\" ? \"Erreur de dictée\" : \"\";\n\n // Colors — validate non-empty strings to avoid silent CSS failures\n const isLight = theme === \"light\";\n const isValidColor = (c: string | undefined): boolean => Boolean(c?.trim());\n const hasCustomColors = isValidColor(colors?.primary) && isValidColor(colors?.secondary);\n const swapForLight = hasCustomColors && isLight;\n const effectivePrimary = hasCustomColors\n ? (swapForLight ? colors!.secondary.trim() : colors!.primary.trim())\n : (isLight ? \"#ffffff\" : \"#262626\");\n const effectiveSecondary = hasCustomColors\n ? (swapForLight ? colors!.primary.trim() : colors!.secondary.trim())\n : null;\n\n const safeSize = (size && size in SIZE_CONFIG) ? size : \"M\";\n const { logoHeightPx: baseLogoHeightPx } = SIZE_CONFIG[safeSize];\n const logoHeightPx =\n borderRadius === \"full\"\n ? Math.round(baseLogoHeightPx * LOGO_SCALE_WHEN_RADIUS_FULL)\n : baseLogoHeightPx;\n const logoWidthPx = logoWidthFromHeight(logoHeightPx);\n\n const isStandardVariant = variant === \"standard\";\n const showTimer = isStandardVariant && isRecording;\n const showProcessing = isStandardVariant && isArming && !isRecording;\n const showStandardPrompt = isStandardVariant && !isRecording && !isArming;\n const [sessionElapsedMs, setSessionElapsedMs] = useState(0);\n const sessionStartRef = useRef<number | null>(null);\n const timerMeasureRef = useRef<HTMLSpanElement | null>(null);\n const [logoSlotWidthPx, setLogoSlotWidthPx] = useState(logoHeightPx);\n\n useEffect(() => {\n if (showTimer) {\n sessionStartRef.current = Date.now();\n setSessionElapsedMs(0);\n const id = setInterval(() => {\n if (sessionStartRef.current != null) setSessionElapsedMs(Date.now() - sessionStartRef.current);\n }, 200);\n return () => clearInterval(id);\n }\n sessionStartRef.current = null;\n }, [showTimer]);\n\n const timerLabel = useMemo(() => {\n const s = Math.max(0, Math.floor(sessionElapsedMs / 1000));\n return `${Math.floor(s / 60).toString().padStart(2, \"0\")}:${(s % 60).toString().padStart(2, \"0\")}`;\n }, [sessionElapsedMs]);\n\n // useLayoutEffect is intentional: measures DOM before paint to avoid width flicker.\n // Only runs when recording (showTimer=true), so SSR hydration mismatch is not a concern.\n useLayoutEffect(() => {\n if (!showTimer) { setLogoSlotWidthPx(logoHeightPx); return; }\n const el = timerMeasureRef.current;\n const measured = el ? Math.ceil(el.getBoundingClientRect().width) : 0;\n setLogoSlotWidthPx(Math.max(logoHeightPx, measured + 12));\n }, [showTimer, timerLabel, logoHeightPx]);\n\n const logoVariant = hasCustomColors ? \"mono\" : \"color\";\n const waveformColor = hasCustomColors ? effectivePrimary : EPHIA_BRAND_PURPLE;\n const radiusCss =\n EPHIA_FLOATING_BUTTON_RADIUS_CSS[borderRadius] ?? EPHIA_FLOATING_BUTTON_RADIUS_CSS.md;\n\n // useState initializer so SSR gives false without crashing, client gets real value on mount\n const [reducedMotion] = useState(\n () => typeof window !== \"undefined\" && window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n );\n\n return (\n <div\n className=\"ephia-transcribe-floating-root\"\n data-ephia-control=\"true\"\n style={{ ...POSITION_STYLE[position], ...style }}\n >\n {/* Screen-reader live region */}\n <div\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n style={{ position: \"absolute\", width: 1, height: 1, overflow: \"hidden\", clip: \"rect(0,0,0,0)\", whiteSpace: \"nowrap\" }}\n >\n {statusAnnouncement}\n </div>\n\n {/* Inner Ephia button — same markup as the original TranscriptionButton */}\n <div\n className={`ephia-transcribe-root ephia-transcribe-root--${theme} ephia-transcribe-size--${size} ephia-transcribe-radius--${borderRadius}${className ? ` ${className}` : \"\"}`}\n style={\n {\n [\"--ephia-transcribe-radius\" as string]: radiusCss,\n ...(hasCustomColors\n ? {\n backgroundColor: effectiveSecondary ?? undefined,\n [\"--ephia-transcribe-ink\" as string]: effectivePrimary,\n }\n : {}),\n } as React.CSSProperties\n }\n >\n {/* Lien externe ephia.app */}\n <div className={`ephia-transcribe-poweredby ephia-transcribe-poweredby--${theme}`}>\n <a\n href={EPHIA_APP_URL}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"ephia-transcribe-poweredby-btn\"\n aria-label=\"En savoir plus sur Ephia — ouvre ephia.app dans un nouvel onglet\"\n onClick={(e) => e.stopPropagation()}\n onPointerDown={(e) => e.stopPropagation()}\n >\n <EphiaExternalLinkIcon />\n </a>\n <div className=\"ephia-transcribe-poweredby-tooltip\">ephia.app</div>\n </div>\n\n {/* Recording glow ring */}\n {isLiveSession && !reducedMotion && (\n <div\n className=\"ephia-transcribe-ring\"\n style={\n hasCustomColors\n ? {\n boxShadow: `0 0 20px color-mix(in srgb, ${effectivePrimary} 55%, transparent)`,\n }\n : {\n boxShadow:\n \"0 0 24px rgba(114, 58, 222, 0.55), 0 0 14px rgba(0, 235, 208, 0.45)\",\n }\n }\n />\n )}\n\n <button\n type=\"button\"\n {...(interactionMode === \"push-to-talk\"\n ? {\n onPointerDown: (e: React.PointerEvent) => {\n e.preventDefault();\n if (typeof window !== \"undefined\") {\n (window as any).__ephia_record_click_ts = Date.now();\n window.dispatchEvent(\n new CustomEvent(\"ephia:sdk-debug\", {\n detail: {\n type: \"sdk.record.click\",\n sessionId: null,\n payload: { message: \"Push-to-talk press\", ts: Date.now() },\n },\n })\n );\n }\n if (!isLiveSession && !isArming) void start(activeTargetId ?? undefined, { startupBufferMs: 5000 });\n },\n onPointerUp: () => { if (isLiveSession) void stop(); },\n onPointerLeave: () => { if (isLiveSession) void stop(); },\n }\n : {\n onClick: () => {\n if (isArming) return;\n if (typeof window !== \"undefined\") {\n (window as any).__ephia_record_click_ts = Date.now();\n window.dispatchEvent(\n new CustomEvent(\"ephia:sdk-debug\", {\n detail: {\n type: \"sdk.record.click\",\n sessionId: null,\n payload: { message: \"Clic sur Record\", ts: Date.now() },\n },\n })\n );\n }\n if (isLiveSession) { void stop(); } else { void start(activeTargetId ?? undefined, { startupBufferMs: 5000 }); }\n },\n })}\n disabled={isArming}\n className=\"ephia-transcribe-btn\"\n aria-pressed={isLiveSession}\n aria-busy={isArming}\n aria-label={\n isLiveSession ? \"Arrêter la dictée\" :\n isArming ? \"Traitement en cours…\" :\n \"Démarrer la dictée\"\n }\n >\n <span\n className={`ephia-transcribe-btn-inner${isStandardVariant ? \" ephia-transcribe-btn-inner--standard\" : \"\"}${showProcessing ? \" ephia-transcribe-btn-inner--processing\" : \"\"}`}\n >\n {variant === \"minimal\" ? (\n <span\n className={`ephia-transcribe-minimal-swap${isRecording || isArming ? \" ephia-transcribe-minimal-swap--recording\" : \"\"}`}\n style={{ width: logoWidthPx, height: logoHeightPx }}\n >\n <span className=\"ephia-transcribe-minimal-swap-layer ephia-transcribe-minimal-swap-layer--logo\" aria-hidden>\n <span className=\"ephia-transcribe-logo-clip ephia-transcribe-logo-slot\" style={{ width: logoWidthPx, height: logoHeightPx }}>\n <span className=\"ephia-transcribe-logo-slot-layer ephia-transcribe-logo-slot-layer--logo\" aria-hidden>\n <EphiaLogo\n height={logoHeightPx}\n width={logoWidthPx}\n variant={logoVariant}\n fill={effectivePrimary}\n isRecording={isLiveSession}\n />\n </span>\n </span>\n </span>\n <span className=\"ephia-transcribe-minimal-swap-layer ephia-transcribe-minimal-swap-layer--activity\" aria-hidden>\n <MinimalProcessingBars\n active={isRecording || isArming}\n size={safeSize}\n color={waveformColor}\n />\n </span>\n </span>\n ) : (\n // standard variant\n <span\n className={`ephia-transcribe-logo-clip ephia-transcribe-logo-slot${showTimer ? \" ephia-transcribe-logo-slot--recording\" : \"\"}`}\n style={{ width: logoSlotWidthPx, height: logoHeightPx }}\n >\n <span className=\"ephia-transcribe-logo-slot-layer ephia-transcribe-logo-slot-layer--logo\" aria-hidden>\n <EphiaLogo\n height={logoHeightPx}\n width={logoWidthPx}\n variant={logoVariant}\n fill={effectivePrimary}\n isRecording={isLiveSession}\n />\n </span>\n {showTimer && (\n <>\n <span className=\"ephia-transcribe-logo-slot-layer ephia-transcribe-logo-slot-layer--timer\" aria-hidden>\n <span className=\"ephia-transcribe-session-timer\">{timerLabel}</span>\n </span>\n <span className=\"ephia-transcribe-session-timer ephia-transcribe-session-timer--measure\" ref={timerMeasureRef}>\n {timerLabel}\n </span>\n </>\n )}\n </span>\n )}\n\n {isStandardVariant && (\n <span\n className={`ephia-transcribe-standard-right-swap${showStandardPrompt ? \" ephia-transcribe-standard-right-swap--prompt\" : showProcessing ? \" ephia-transcribe-standard-right-swap--processing\" : \" ephia-transcribe-standard-right-swap--waveform\"}`}\n aria-hidden\n >\n <span className=\"ephia-transcribe-standard-right-layer ephia-transcribe-standard-right-layer--prompt\">\n Commencer à dicter\n </span>\n <span className=\"ephia-transcribe-standard-right-layer ephia-transcribe-standard-right-layer--processing\">\n Traitement…\n </span>\n {!showProcessing ? (\n <span className=\"ephia-transcribe-standard-right-layer ephia-transcribe-standard-right-layer--waveform\">\n <StandardIntensityVisualizer\n enabled={isRecording}\n level={voiceLevel}\n />\n </span>\n ) : null}\n </span>\n )}\n </span>\n </button>\n </div>\n </div>\n );\n}\n\n/* ── Public component ────────────────────────────────────────────── */\n\nexport function EphiaFloatingButton({\n apiUrl,\n apiKey,\n bearerToken,\n clientType,\n transport,\n position = \"bottom-center\",\n theme = \"light\",\n size = \"M\",\n variant = \"standard\",\n borderRadius = \"lg\",\n interactionMode = \"toggle\",\n colors,\n className,\n style,\n sessionOptions,\n onError,\n onStatusChange,\n}: EphiaFloatingButtonProps) {\n const ctx = useOptionalEphiaContext();\n const safeRadius: EphiaFloatingButtonBorderRadius =\n borderRadius && borderRadius in EPHIA_FLOATING_BUTTON_RADIUS_CSS ? borderRadius : \"lg\";\n\n const innerProps = useMemo<InnerProps>(() => ({\n position,\n theme,\n size,\n variant,\n borderRadius: safeRadius,\n interactionMode,\n colors,\n className,\n style,\n onError,\n onStatusChange,\n }), [position, theme, size, variant, safeRadius, interactionMode, colors, className, style, onError, onStatusChange]);\n\n if (ctx) {\n return <EphiaFloatingButtonInner {...innerProps} />;\n }\n\n return (\n <EphiaProvider\n apiUrl={apiUrl}\n apiKey={apiKey}\n bearerToken={bearerToken}\n clientType={clientType}\n transport={transport}\n options={{\n language: sessionOptions?.language as string | undefined,\n sessionOptions: sessionOptions,\n }}\n >\n <EphiaFloatingButtonInner {...innerProps} />\n </EphiaProvider>\n );\n}\n","/** Border radius presets for {@link EphiaFloatingButton}. */\nexport type EphiaFloatingButtonBorderRadius = \"sm\" | \"md\" | \"lg\" | \"full\";\n\nexport const EPHIA_FLOATING_BUTTON_RADIUS_CSS: Record<EphiaFloatingButtonBorderRadius, string> = {\n sm: \"0.375rem\",\n md: \"0.625rem\",\n lg: \"1rem\",\n full: \"9999px\",\n};\n","\"use client\";\n\nimport { useEffect, useRef } from \"react\";\n\ntype MinimalBarsSize = \"S\" | \"M\" | \"L\";\n\nconst BAR_LAYOUT: Record<MinimalBarsSize, { barWidthPx: number; gapPx: number }> = {\n S: { barWidthPx: 3, gapPx: 2.5 },\n M: { barWidthPx: 3.5, gapPx: 3 },\n L: { barWidthPx: 4, gapPx: 3.5 },\n};\n\nconst TAU = Math.PI * 2;\n\n/** Chaque barre : fréquences différentes + petite amplitude → pas de cycle min/max synchronisé. */\nconst BAR_MOTION = [\n { base: 0.62, a1: 0.14, a2: 0.09, f1: 1.25, f2: 2.05, p1: 0, p2: 0.7 },\n { base: 0.64, a1: 0.15, a2: 0.07, f1: 1.65, f2: 2.35, p1: 1.4, p2: 2.2 },\n { base: 0.6, a1: 0.12, a2: 0.1, f1: 1.05, f2: 1.85, p1: 2.5, p2: 0.4 },\n] as const;\n\nconst LERP = 0.14;\n\nfunction clamp01(x: number): number {\n return Math.max(0.36, Math.min(0.9, x));\n}\n\nfunction targetHeight(phaseSec: number, spec: (typeof BAR_MOTION)[number]): number {\n const t = phaseSec;\n const v =\n spec.base +\n spec.a1 * Math.sin(t * spec.f1 * TAU + spec.p1) +\n spec.a2 * Math.sin(t * spec.f2 * TAU + spec.p2);\n return clamp01(v);\n}\n\n/**\n * Indicateur minimal : 3 barres, même couleur, respiration douce (pas de balayage min→max).\n */\nexport function MinimalProcessingBars({\n active,\n size = \"M\",\n color,\n}: {\n active: boolean;\n size?: MinimalBarsSize;\n color: string;\n}) {\n const rootRef = useRef<HTMLSpanElement>(null);\n const heightsRef = useRef([0.55, 0.58, 0.54]);\n const layout = BAR_LAYOUT[size] ?? BAR_LAYOUT.M;\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n\n const applyHeights = (h: number[]) => {\n root.style.setProperty(\"--ephia-bar-0\", String(h[0]));\n root.style.setProperty(\"--ephia-bar-1\", String(h[1]));\n root.style.setProperty(\"--ephia-bar-2\", String(h[2]));\n };\n\n if (!active) {\n heightsRef.current = [0.52, 0.56, 0.54];\n applyHeights(heightsRef.current);\n return;\n }\n\n const reducedMotion =\n typeof window !== \"undefined\" &&\n window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n\n if (reducedMotion) {\n applyHeights([0.55, 0.58, 0.54]);\n return;\n }\n\n let raf = 0;\n const start = performance.now();\n\n const tick = (now: number) => {\n const phase = (now - start) / 1000;\n const current = heightsRef.current;\n\n for (let i = 0; i < 3; i++) {\n const target = targetHeight(phase, BAR_MOTION[i]);\n current[i] += (target - current[i]) * LERP;\n }\n\n applyHeights(current);\n raf = requestAnimationFrame(tick);\n };\n\n raf = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(raf);\n }, [active]);\n\n return (\n <span\n ref={rootRef}\n className=\"ephia-transcribe-minimal-bars\"\n style={\n {\n [\"--ephia-minimal-bar-gap\" as string]: `${layout.gapPx}px`,\n } as React.CSSProperties\n }\n aria-hidden\n >\n {BAR_MOTION.map((_, i) => (\n <span\n key={i}\n className=\"ephia-transcribe-minimal-bar\"\n style={{\n width: layout.barWidthPx,\n backgroundColor: color,\n [\"--ephia-bar-scale\" as string]: `var(--ephia-bar-${i}, 0.5)`,\n }}\n />\n ))}\n </span>\n );\n}\n","\"use client\";\n\nimport { useEffect, useLayoutEffect, useReducer, useRef } from \"react\";\n\n/** Fenêtre d'échantillonnage (ms) : moyenne du niveau vocal puis ajout d'une barre. */\nexport const INTENSITY_SAMPLE_MS = 300;\n\nconst BAR_WIDTH_PX = 3;\nconst BAR_GAP_PX = 2;\nconst MIN_BAR_HEIGHT_RATIO = 0.1;\n\nexport type StandardIntensityVisualizerProps = {\n enabled: boolean;\n /** Niveau instantané 0..1 (alimenté par le micro, ~33 ms). */\n level: number;\n};\n\nfunction clamp01(value: number): number {\n return Math.max(0, Math.min(1, value));\n}\n\nfunction barHeightRatio(level: number): number {\n const v = clamp01(level);\n return MIN_BAR_HEIGHT_RATIO + v * (1 - MIN_BAR_HEIGHT_RATIO);\n}\n\nfunction barStepPx(): number {\n return BAR_WIDTH_PX + BAR_GAP_PX;\n}\n\nfunction scrollSpeedPxPerSec(): number {\n return barStepPx() / (INTENSITY_SAMPLE_MS / 1000);\n}\n\n/**\n * Historique de barres : une barre toutes les INTENSITY_SAMPLE_MS (moyenne),\n * défilement continu vers la gauche, fondu sur les bords du cadre.\n */\nexport function StandardIntensityVisualizer({ enabled, level }: StandardIntensityVisualizerProps) {\n const rectRef = useRef<HTMLSpanElement>(null);\n const trackRef = useRef<HTMLSpanElement>(null);\n const bucketRef = useRef({ sum: 0, count: 0 });\n const scrollOffsetRef = useRef(0);\n const barsRef = useRef<number[]>([]);\n const maxBarsRef = useRef(48);\n const [barRevision, bumpBars] = useReducer((n: number) => n + 1, 0);\n\n useEffect(() => {\n if (!enabled) return;\n const v = clamp01(level);\n bucketRef.current.sum += v;\n bucketRef.current.count += 1;\n }, [enabled, level]);\n\n useLayoutEffect(() => {\n const rect = rectRef.current;\n if (!rect) return;\n const updateMax = () => {\n const w = rect.clientWidth;\n if (w > 0) {\n maxBarsRef.current = Math.max(12, Math.ceil(w / barStepPx()) + 4);\n }\n };\n updateMax();\n const ro = new ResizeObserver(updateMax);\n ro.observe(rect);\n return () => ro.disconnect();\n }, [enabled]);\n\n useEffect(() => {\n if (!enabled) {\n barsRef.current = [];\n bucketRef.current = { sum: 0, count: 0 };\n scrollOffsetRef.current = 0;\n if (trackRef.current) trackRef.current.style.transform = \"translateX(0)\";\n return;\n }\n\n let rafId = 0;\n let lastTs = performance.now();\n\n const consumeBucket = (): number => {\n const { sum, count } = bucketRef.current;\n bucketRef.current = { sum: 0, count: 0 };\n return count > 0 ? sum / count : 0;\n };\n\n const pushBar = () => {\n const avg = consumeBucket();\n const next = [...barsRef.current, avg];\n const max = maxBarsRef.current;\n barsRef.current = next.length > max ? next.slice(-max) : next;\n bumpBars();\n };\n\n const tick = (now: number) => {\n const track = trackRef.current;\n if (!track) {\n rafId = requestAnimationFrame(tick);\n return;\n }\n\n const dt = Math.min(0.05, (now - lastTs) / 1000);\n lastTs = now;\n const step = barStepPx();\n\n scrollOffsetRef.current += scrollSpeedPxPerSec() * dt;\n while (scrollOffsetRef.current >= step) {\n scrollOffsetRef.current -= step;\n pushBar();\n }\n\n track.style.transform = `translate3d(${-scrollOffsetRef.current}px, 0, 0)`;\n rafId = requestAnimationFrame(tick);\n };\n\n rafId = requestAnimationFrame(tick);\n return () => {\n cancelAnimationFrame(rafId);\n barsRef.current = [];\n bucketRef.current = { sum: 0, count: 0 };\n scrollOffsetRef.current = 0;\n if (trackRef.current) trackRef.current.style.transform = \"translateX(0)\";\n };\n }, [enabled]);\n\n if (!enabled) return null;\n\n const bars = barsRef.current;\n\n return (\n <span className=\"ephia-transcribe-standard-placeholder\" data-ephia-ui=\"standard-intensity\" aria-hidden>\n <span ref={rectRef} className=\"ephia-transcribe-standard-placeholder__rect\">\n <span className=\"ephia-transcribe-standard-placeholder__viewport\">\n <span ref={trackRef} className=\"ephia-transcribe-standard-placeholder__track\">\n {bars.map((barLevel, index) => (\n <span\n key={`${barRevision}-${index}`}\n className=\"ephia-transcribe-standard-placeholder__history-bar\"\n style={{ height: `${barHeightRatio(barLevel) * 100}%` }}\n />\n ))}\n </span>\n </span>\n </span>\n </span>\n );\n}\n"],"mappings":"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","names":["jsx","jsxs","jsx","useEffect","useLayoutEffect","useRef","jsx","useEffect","useRef","jsx","clamp01","Fragment","jsx","jsxs","useRef","useEffect","useLayoutEffect"]}
@@ -0,0 +1,64 @@
1
+ import { EphiaAudioEvent } from 'ephia-protocol';
2
+ import { T as Transport } from './Transport-zdeA4Pou.js';
3
+ import { E as EphiaSessionOptions } from './session-APaXR48R.js';
4
+ import { a as EphiaAudioState } from './audio-state-kZ3KSvux.js';
5
+
6
+ /**
7
+ * Machine à états stricte pour les sessions Ephia Audio.
8
+ *
9
+ * Interdit les transitions invalides (ex: recording -> creating_session).
10
+ */
11
+ type EphiaSessionStatus = "idle" | "creating_session" | "connecting_transport" | "ready" | "recording" | "paused" | "finalizing" | "ended" | "error" | "disposed";
12
+ declare function transitionSessionStatus(current: EphiaSessionStatus, next: EphiaSessionStatus): EphiaSessionStatus;
13
+ declare function canTransition(current: EphiaSessionStatus, next: EphiaSessionStatus): boolean;
14
+
15
+ /** Alias de EphiaSessionStatus — tous les statuts sont des états valides de la machine. */
16
+ type EphiaClientStatus = EphiaSessionStatus;
17
+ interface EphiaClientState {
18
+ status: EphiaClientStatus;
19
+ sessionId: string | null;
20
+ roomName: string | null;
21
+ error: {
22
+ code: string;
23
+ message: string;
24
+ } | null;
25
+ isMicEnabled: boolean;
26
+ }
27
+ declare const initialClientState: EphiaClientState;
28
+
29
+ type EphiaStartOptions = {
30
+ startupBufferMs?: number;
31
+ /**
32
+ * Target SDK initiale à transmettre au backend dès la création de session.
33
+ * Évite que l'agent démarre sur sa target par défaut avant le premier event data-channel.
34
+ */
35
+ initialTargetId?: string;
36
+ /** Contexte local envoyé au backend lors d'un session.resume. */
37
+ context?: string;
38
+ };
39
+ type EphiaStartResult = {
40
+ /** Identifiant de la session démarrée, ou null si aucune session n'a été créée. */
41
+ sessionId: string | null;
42
+ };
43
+ interface EphiaAudioClientOptions {
44
+ apiKey?: string;
45
+ bearerToken?: string;
46
+ clientType?: string;
47
+ apiUrl?: string;
48
+ transport?: Transport;
49
+ sessionOptions?: EphiaSessionOptions;
50
+ noiseFilter?: boolean;
51
+ /** Active le VAD gate RMS (mute track en silence). Défaut: true */
52
+ vadGate?: boolean;
53
+ /** deviceId audioinput à utiliser pour acquireMicStream (ex. BlackHole). */
54
+ preferredAudioInputDeviceId?: string;
55
+ onEvent?: (event: EphiaAudioEvent) => void;
56
+ onStateChange?: (state: EphiaClientState) => void;
57
+ onError?: (error: {
58
+ code: string;
59
+ message: string;
60
+ }) => void;
61
+ onAudioState?: (state: EphiaAudioState) => void;
62
+ }
63
+
64
+ export { type EphiaStartResult as E, type EphiaStartOptions as a, type EphiaAudioClientOptions as b, type EphiaClientState as c, type EphiaClientStatus as d, type EphiaSessionStatus as e, canTransition as f, initialClientState as i, transitionSessionStatus as t };
@@ -0,0 +1,32 @@
1
+ import { a as TransportState } from './Transport-zdeA4Pou.js';
2
+
3
+ /**
4
+ * État de connexion transport + qualité.
5
+ *
6
+ * Source de vérité pour les composants UI de connexion.
7
+ * Alimenté par LiveKitTransport via un mapper depuis TransportState.
8
+ */
9
+
10
+ type EphiaConnectionStatus = "idle" | "connecting" | "connected" | "reconnecting" | "reconnected" | "disconnected" | "error";
11
+ type EphiaConnectionQuality = "unknown" | "poor" | "good" | "excellent";
12
+ type EphiaConnectionState = {
13
+ status: EphiaConnectionStatus;
14
+ quality: EphiaConnectionQuality;
15
+ roomName?: string;
16
+ participantIdentity?: string;
17
+ reconnectCount: number;
18
+ lastConnectedAt?: number;
19
+ lastReconnectedAt?: number;
20
+ lastDisconnectedAt?: number;
21
+ localAudioPublished: boolean;
22
+ localAudioMuted: boolean;
23
+ dataChannelReady: boolean;
24
+ lastError?: {
25
+ code: string;
26
+ message: string;
27
+ };
28
+ };
29
+ declare const initialConnectionState: EphiaConnectionState;
30
+ declare function mapTransportStateToConnectionState(ts: TransportState, prev?: EphiaConnectionState): EphiaConnectionState;
31
+
32
+ export { type EphiaConnectionState as E, type EphiaConnectionQuality as a, type EphiaConnectionStatus as b, initialConnectionState as i, mapTransportStateToConnectionState as m };
@@ -0,0 +1,24 @@
1
+ export { E as EphiaBinding, P as PreviewSegmentInput, U as UpsertSegmentInput } from '../../EphiaBinding-BvRmlqqC.js';
2
+ export { N as NativeBinding } from '../../NativeBinding-ChG0GeSz.js';
3
+ export { E as EphiaV2CommittedMark, a as EphiaV2PreviewMark, T as TiptapBinding } from '../../TiptapBinding-B-agfV2H.js';
4
+ import { Mark, Editor } from '@tiptap/core';
5
+ import { T as TargetBinding } from '../../TargetBinding-BKGQwUMc.js';
6
+ import 'ephia-protocol';
7
+
8
+ /**
9
+ * Binding TipTap historique mais encore actif.
10
+ * Ne pas supprimer tant que createTiptapBinding est utilisé par les intégrations.
11
+ * Les corrections d'insertion realtime TipTap doivent être faites ici, pas dans ProseMirrorAdapter.
12
+ */
13
+
14
+ /** Mark affiché sur le texte en cours de streaming (preview temps réel). */
15
+ declare const EphiaPreviewMark: Mark<any, any>;
16
+ /** Mark temporaire affiché pendant la transition committed (streaming → normal). */
17
+ declare const EphiaCommittedMark: Mark<any, any>;
18
+ /** Mark affiché sur le texte corrigé par le review pipeline. */
19
+ declare const EphiaRevisedMark: Mark<any, any>;
20
+ /** Mark affiché sur le placeholder de démarrage (interim avant le premier partial). */
21
+ declare const EphiaPlaceholderMark: Mark<any, any>;
22
+ declare function createTiptapBinding(editor: Editor, wrapperEl?: HTMLElement): TargetBinding;
23
+
24
+ export { EphiaCommittedMark, EphiaPlaceholderMark, EphiaPreviewMark, EphiaRevisedMark, createTiptapBinding as createLegacyTiptapBinding };