@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
package/dist/style.css ADDED
@@ -0,0 +1,1093 @@
1
+ /**
2
+ * Ephia Audio — Theme Variables + Floating Button Styles
3
+ *
4
+ * Import this file to get default styling:
5
+ * import "ephia-audio/style.css";
6
+ *
7
+ * All colors use CSS custom properties so you can override them
8
+ * without a build step.
9
+ */
10
+
11
+ /* ── Root Tokens ───────────────────────────────────── */
12
+ :root {
13
+ /* Ephia palette */
14
+ --ephia-bg: #ffffff;
15
+ --ephia-fg: #171717;
16
+ --ephia-muted: #737373;
17
+ --ephia-border: #e5e5e5;
18
+
19
+ /* Brand mark (apps/ephia-website/public/ephia_logo.svg) */
20
+ --ephia-brand-purple: #723ade;
21
+ --ephia-brand-teal: #00ebd0;
22
+
23
+ --ephia-primary: #723ade;
24
+ --ephia-active: #00ebd0;
25
+ --ephia-soft: #fdf5ed;
26
+ --ephia-lavender: #c8b6ff;
27
+ --ephia-danger: #ef4444;
28
+ --ephia-warning: #f59e0b;
29
+ --ephia-success: #10b981;
30
+
31
+ --ephia-radius-sm: 10px;
32
+ --ephia-radius-md: 16px;
33
+ --ephia-radius-lg: 24px;
34
+
35
+ --ephia-shadow-card: 0 14px 40px rgba(15, 23, 42, 0.08);
36
+
37
+ /* Connection Indicator */
38
+ --ephia-indicator-connected: #22c55e;
39
+ --ephia-indicator-connecting: #f59e0b;
40
+ --ephia-indicator-idle: #d4d4d4;
41
+ --ephia-indicator-warning: #f97316;
42
+ --ephia-indicator-error: #dc2626;
43
+ --ephia-indicator-size: 0.5rem;
44
+
45
+ /* Recorder Button */
46
+ --ephia-btn-record-bg: #2563eb;
47
+ --ephia-btn-record-text: #ffffff;
48
+ --ephia-btn-stop-bg: #dc2626;
49
+ --ephia-btn-stop-text: #ffffff;
50
+ --ephia-btn-disabled-opacity: 0.5;
51
+ --ephia-btn-radius: 0.5rem;
52
+ --ephia-btn-padding-x: 1rem;
53
+ --ephia-btn-padding-y: 0.5rem;
54
+ --ephia-btn-font-size: 0.875rem;
55
+
56
+ /* Animation */
57
+ --ephia-pulse-duration: 2s;
58
+ }
59
+
60
+ /* ── Recorder Button (primitive) ──────────────────── */
61
+ .ephia-recorder-button {
62
+ display: inline-flex;
63
+ align-items: center;
64
+ justify-content: center;
65
+ font-weight: 500;
66
+ border: none;
67
+ cursor: pointer;
68
+ transition: opacity 0.15s ease;
69
+ }
70
+
71
+ .ephia-recorder-button:disabled {
72
+ cursor: not-allowed;
73
+ }
74
+
75
+ /* ── Floating wrapper (fixed position) ───────────── */
76
+ .ephia-transcribe-floating-root {
77
+ position: fixed;
78
+ z-index: 200;
79
+ }
80
+
81
+ /* ── TranscriptionButton — ported from @ephia/transcribe-sdk ── */
82
+
83
+ .ephia-transcribe-root {
84
+ position: relative;
85
+ display: flex;
86
+ flex-direction: column;
87
+ align-items: center;
88
+ --ephia-transcribe-radius: 0.625rem;
89
+ --ephia-transcribe-ink: var(--ephia-brand-purple);
90
+ border: 1px solid transparent;
91
+ border-color: color-mix(in srgb, var(--ephia-transcribe-ink) 40%, transparent);
92
+ border-radius: var(--ephia-transcribe-radius);
93
+ transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
94
+ overflow: visible;
95
+ padding: 8px;
96
+ box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);
97
+ }
98
+
99
+ .ephia-transcribe-radius--sm {
100
+ --ephia-transcribe-radius: 0.375rem;
101
+ }
102
+
103
+ .ephia-transcribe-radius--md {
104
+ --ephia-transcribe-radius: 0.625rem;
105
+ }
106
+
107
+ .ephia-transcribe-radius--lg {
108
+ --ephia-transcribe-radius: 1rem;
109
+ }
110
+
111
+ .ephia-transcribe-radius--full {
112
+ --ephia-transcribe-radius: 9999px;
113
+ /* Pill : un peu plus d'air sur les côtés (standard/minimal inchangés). */
114
+ padding: 8px 13px;
115
+ }
116
+
117
+ .ephia-transcribe-poweredby {
118
+ position: absolute;
119
+ top: 4px;
120
+ right: 4px;
121
+ z-index: 20;
122
+ opacity: 0;
123
+ pointer-events: none;
124
+ transition: opacity 120ms ease;
125
+ /* Conteneur compact — le bouton ne doit pas influencer la hauteur du widget */
126
+ width: 14px;
127
+ height: 14px;
128
+ line-height: 0;
129
+ }
130
+
131
+ .ephia-transcribe-size--S .ephia-transcribe-poweredby {
132
+ top: 2px;
133
+ right: 2px;
134
+ }
135
+
136
+ .ephia-transcribe-root:hover .ephia-transcribe-poweredby {
137
+ opacity: 1;
138
+ pointer-events: auto;
139
+ }
140
+
141
+ .ephia-transcribe-poweredby-btn {
142
+ position: relative;
143
+ box-sizing: border-box;
144
+ width: 14px;
145
+ height: 14px;
146
+ min-width: 0;
147
+ min-height: 0;
148
+ max-width: 14px;
149
+ max-height: 14px;
150
+ flex: 0 0 auto;
151
+ border-radius: 9999px;
152
+ display: inline-flex;
153
+ align-items: center;
154
+ justify-content: center;
155
+ cursor: pointer;
156
+ text-decoration: none;
157
+ user-select: none;
158
+ padding: 0;
159
+ border: 1px solid rgba(0, 0, 0, 0.12);
160
+ background: rgba(255, 255, 255, 0.65);
161
+ color: rgba(0, 0, 0, 0.55);
162
+ backdrop-filter: blur(8px);
163
+ transition: opacity 120ms ease, transform 120ms ease;
164
+ }
165
+
166
+ /* Zone cliquable élargie sans grossir le rendu visuel */
167
+ .ephia-transcribe-poweredby-btn::before {
168
+ content: "";
169
+ position: absolute;
170
+ inset: -8px;
171
+ border-radius: 9999px;
172
+ }
173
+
174
+ .ephia-transcribe-poweredby-icon {
175
+ width: 7px;
176
+ height: 7px;
177
+ flex-shrink: 0;
178
+ stroke: currentColor;
179
+ fill: none;
180
+ stroke-width: 2.5;
181
+ stroke-linecap: round;
182
+ stroke-linejoin: round;
183
+ }
184
+
185
+ .ephia-transcribe-poweredby-btn:focus-visible {
186
+ outline: 2px solid color-mix(in srgb, var(--ephia-brand-purple) 55%, transparent);
187
+ outline-offset: 1px;
188
+ }
189
+
190
+ .ephia-transcribe-size--S .ephia-transcribe-poweredby {
191
+ width: 12px;
192
+ height: 12px;
193
+ }
194
+
195
+ .ephia-transcribe-size--S .ephia-transcribe-poweredby-btn {
196
+ width: 12px;
197
+ height: 12px;
198
+ max-width: 12px;
199
+ max-height: 12px;
200
+ }
201
+
202
+ .ephia-transcribe-size--S .ephia-transcribe-poweredby-icon {
203
+ width: 6px;
204
+ height: 6px;
205
+ stroke-width: 2.75;
206
+ }
207
+
208
+ .ephia-transcribe-size--L .ephia-transcribe-poweredby {
209
+ width: 16px;
210
+ height: 16px;
211
+ }
212
+
213
+ .ephia-transcribe-size--L .ephia-transcribe-poweredby-btn {
214
+ width: 16px;
215
+ height: 16px;
216
+ max-width: 16px;
217
+ max-height: 16px;
218
+ }
219
+
220
+ .ephia-transcribe-size--L .ephia-transcribe-poweredby-icon {
221
+ width: 8px;
222
+ height: 8px;
223
+ }
224
+
225
+ .ephia-transcribe-poweredby-tooltip {
226
+ position: absolute;
227
+ top: 50%;
228
+ left: calc(100% + 4px);
229
+ transform: translateY(-50%) translateY(2px);
230
+ white-space: nowrap;
231
+ padding: 4px 6px;
232
+ border-radius: 8px;
233
+ font-size: 10px;
234
+ font-style: italic;
235
+ letter-spacing: 0.2px;
236
+ border: 1px solid rgba(0, 0, 0, 0.08);
237
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.06);
238
+ backdrop-filter: blur(10px);
239
+ opacity: 0;
240
+ pointer-events: none;
241
+ transition: opacity 120ms ease, transform 120ms ease;
242
+ background: rgba(255, 255, 255, 0.6);
243
+ color: rgba(0, 0, 0, 0.55);
244
+ }
245
+
246
+ .ephia-transcribe-size--S .ephia-transcribe-poweredby-tooltip {
247
+ font-size: 9px;
248
+ padding: 3px 5px;
249
+ }
250
+
251
+ .ephia-transcribe-poweredby:hover .ephia-transcribe-poweredby-btn {
252
+ opacity: 0.95;
253
+ transform: translateY(-0.5px);
254
+ }
255
+
256
+ .ephia-transcribe-poweredby:hover .ephia-transcribe-poweredby-tooltip {
257
+ opacity: 1;
258
+ transform: translateY(-50%) translateY(0);
259
+ }
260
+
261
+ .ephia-transcribe-poweredby--light .ephia-transcribe-poweredby-btn {
262
+ border: 1px solid rgba(255, 255, 255, 0.18);
263
+ background: rgba(30, 30, 30, 0.55);
264
+ color: rgba(255, 255, 255, 0.55);
265
+ }
266
+
267
+ .ephia-transcribe-poweredby--light .ephia-transcribe-poweredby-tooltip {
268
+ border: 1px solid rgba(255, 255, 255, 0.1);
269
+ background: rgba(30, 30, 30, 0.6);
270
+ color: rgba(255, 255, 255, 0.55);
271
+ }
272
+
273
+ .ephia-transcribe-poweredby--dark .ephia-transcribe-poweredby-btn {
274
+ border: 1px solid rgba(0, 0, 0, 0.12);
275
+ background: rgba(255, 255, 255, 0.65);
276
+ color: rgba(0, 0, 0, 0.55);
277
+ }
278
+
279
+ .ephia-transcribe-poweredby--dark .ephia-transcribe-poweredby-tooltip {
280
+ border: 1px solid rgba(0, 0, 0, 0.08);
281
+ background: rgba(255, 255, 255, 0.6);
282
+ color: rgba(0, 0, 0, 0.55);
283
+ }
284
+
285
+ /* theme="light" — bouton fond sombre */
286
+ .ephia-transcribe-root--light {
287
+ background-color: #2e2e2e;
288
+ --ephia-transcribe-ink: #ffffff;
289
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
290
+ }
291
+
292
+ /* theme="dark" — bouton fond clair */
293
+ .ephia-transcribe-root--dark {
294
+ background-color: #fafafa;
295
+ --ephia-transcribe-ink: var(--ephia-brand-purple);
296
+ box-shadow: 0 4px 16px rgba(114, 58, 222, 0.08);
297
+ }
298
+
299
+ .ephia-transcribe-ring {
300
+ position: absolute;
301
+ inset: -2px;
302
+ border-radius: calc(var(--ephia-transcribe-radius) + 2px);
303
+ opacity: 1;
304
+ transition: box-shadow 0.15s ease-out;
305
+ pointer-events: none;
306
+ }
307
+
308
+ .ephia-transcribe-btn {
309
+ flex-shrink: 0;
310
+ position: relative;
311
+ z-index: 10;
312
+ display: flex;
313
+ align-items: center;
314
+ justify-content: center;
315
+ outline: none;
316
+ border: none;
317
+ background: transparent;
318
+ cursor: pointer;
319
+ padding: 0;
320
+ border-radius: var(--ephia-transcribe-radius);
321
+ }
322
+
323
+ .ephia-transcribe-btn-inner {
324
+ display: inline-flex;
325
+ align-items: center;
326
+ justify-content: center;
327
+ gap: 0;
328
+ }
329
+
330
+ .ephia-transcribe-btn-inner--standard {
331
+ gap: 4px;
332
+ width: 100%;
333
+ min-width: 0;
334
+ }
335
+
336
+ .ephia-transcribe-btn-inner--standard .ephia-transcribe-standard-right-swap {
337
+ flex: 1 1 0;
338
+ min-width: 0;
339
+ }
340
+
341
+ /* Traitement : largeur fixée au libellé, pas d'expansion waveform */
342
+ .ephia-transcribe-btn-inner--standard.ephia-transcribe-btn-inner--processing {
343
+ width: auto;
344
+ }
345
+
346
+ .ephia-transcribe-btn-inner--standard .ephia-transcribe-standard-right-swap--processing {
347
+ flex: 0 0 auto;
348
+ width: auto;
349
+ min-width: 0;
350
+ }
351
+
352
+ .ephia-transcribe-btn:disabled {
353
+ opacity: 0.5;
354
+ cursor: not-allowed;
355
+ }
356
+
357
+ .ephia-transcribe-logo-clip {
358
+ display: inline-flex;
359
+ align-items: center;
360
+ justify-content: center;
361
+ overflow: hidden;
362
+ border-radius: inherit;
363
+ mask-image: radial-gradient(white, black);
364
+ -webkit-mask-image: -webkit-radial-gradient(white, black);
365
+ }
366
+
367
+ .ephia-transcribe-logo-slot {
368
+ position: relative;
369
+ transition: width 220ms ease, padding 220ms ease;
370
+ padding: 0;
371
+ }
372
+
373
+ .ephia-transcribe-logo-slot-layer {
374
+ position: absolute;
375
+ inset: 0;
376
+ display: flex;
377
+ align-items: center;
378
+ justify-content: center;
379
+ transition: opacity 180ms ease, transform 220ms ease;
380
+ will-change: opacity, transform;
381
+ }
382
+
383
+ .ephia-transcribe-logo-slot-layer--logo {
384
+ opacity: 1;
385
+ transform: translateY(0);
386
+ }
387
+
388
+ .ephia-transcribe-logo-slot-layer--timer {
389
+ opacity: 0;
390
+ transform: translateY(2px);
391
+ }
392
+
393
+ .ephia-transcribe-logo-slot--recording .ephia-transcribe-logo-slot-layer--logo {
394
+ opacity: 0;
395
+ transform: translateY(-2px);
396
+ }
397
+
398
+ .ephia-transcribe-logo-slot--recording .ephia-transcribe-logo-slot-layer--timer {
399
+ opacity: 1;
400
+ transform: translateY(0);
401
+ }
402
+
403
+ .ephia-transcribe-session-timer {
404
+ font-variant-numeric: tabular-nums;
405
+ letter-spacing: 0.6px;
406
+ font-weight: 600;
407
+ font-size: 0.75rem;
408
+ line-height: 1;
409
+ color: var(--ephia-transcribe-ink);
410
+ }
411
+
412
+ .ephia-transcribe-logo-slot--recording {
413
+ padding: 0 6px;
414
+ }
415
+
416
+ .ephia-transcribe-session-timer--measure {
417
+ position: absolute;
418
+ left: -9999px;
419
+ top: -9999px;
420
+ opacity: 0;
421
+ pointer-events: none;
422
+ white-space: nowrap;
423
+ }
424
+
425
+ .ephia-transcribe-size--S .ephia-transcribe-session-timer {
426
+ font-size: 10px;
427
+ }
428
+
429
+ .ephia-transcribe-size--M .ephia-transcribe-session-timer {
430
+ font-size: 0.8125rem;
431
+ }
432
+
433
+ .ephia-transcribe-size--L .ephia-transcribe-session-timer {
434
+ font-size: 0.875rem;
435
+ }
436
+
437
+ .ephia-transcribe-logo-svg {
438
+ flex-shrink: 0;
439
+ display: block;
440
+ transition: transform 0.2s ease, filter 0.2s ease;
441
+ }
442
+
443
+ .ephia-transcribe-logo-svg--recording {
444
+ transform: scale(1.04);
445
+ filter: drop-shadow(0 0 6px rgba(114, 58, 222, 0.45))
446
+ drop-shadow(0 0 10px rgba(0, 235, 208, 0.35));
447
+ }
448
+
449
+ /* Standard variant: historique de barres d'intensité (fenêtre INTENSITY_SAMPLE_MS) */
450
+ .ephia-transcribe-standard-placeholder {
451
+ flex: 1 1 auto;
452
+ display: flex;
453
+ align-items: center;
454
+ width: 100%;
455
+ min-width: 0;
456
+ height: 28px;
457
+ overflow: hidden;
458
+ }
459
+
460
+ .ephia-transcribe-standard-placeholder__rect {
461
+ flex: 1 1 auto;
462
+ min-width: 0;
463
+ height: 24px;
464
+ box-sizing: border-box;
465
+ overflow: hidden;
466
+ -webkit-mask-image: linear-gradient(
467
+ to right,
468
+ transparent 0%,
469
+ #000 14%,
470
+ #000 72%,
471
+ transparent 100%
472
+ );
473
+ mask-image: linear-gradient(
474
+ to right,
475
+ transparent 0%,
476
+ #000 14%,
477
+ #000 72%,
478
+ transparent 100%
479
+ );
480
+ }
481
+
482
+ .ephia-transcribe-standard-placeholder__viewport {
483
+ width: 100%;
484
+ height: 100%;
485
+ display: flex;
486
+ align-items: center;
487
+ justify-content: flex-end;
488
+ overflow: hidden;
489
+ min-width: 0;
490
+ max-width: 100%;
491
+ }
492
+
493
+ .ephia-transcribe-standard-placeholder__track {
494
+ display: flex;
495
+ flex-direction: row;
496
+ align-items: center;
497
+ justify-content: flex-end;
498
+ gap: 2px;
499
+ height: 100%;
500
+ will-change: transform;
501
+ min-width: 0;
502
+ max-width: 100%;
503
+ }
504
+
505
+ .ephia-transcribe-standard-placeholder__history-bar {
506
+ flex: 0 0 3px;
507
+ width: 3px;
508
+ min-height: 2px;
509
+ border-radius: 1px;
510
+ align-self: center;
511
+ background: var(--ephia-transcribe-intensity-bar, #723ade);
512
+ }
513
+
514
+ .ephia-transcribe-root--light .ephia-transcribe-standard-placeholder__history-bar {
515
+ background: var(--ephia-transcribe-intensity-bar, #723ade);
516
+ }
517
+
518
+ .ephia-transcribe-root--dark .ephia-transcribe-standard-placeholder__history-bar {
519
+ background: var(--ephia-transcribe-intensity-bar, #ffffff);
520
+ }
521
+
522
+ /* Standard variant: swap "Commencer à dicter" ↔ waveform */
523
+ .ephia-transcribe-standard-right-swap {
524
+ position: relative;
525
+ flex: 1 1 0;
526
+ min-width: 0;
527
+ height: 28px;
528
+ display: inline-grid;
529
+ align-items: center;
530
+ justify-items: stretch;
531
+ }
532
+
533
+ .ephia-transcribe-standard-right-layer {
534
+ grid-area: 1 / 1;
535
+ display: inline-flex;
536
+ align-items: center;
537
+ justify-content: center;
538
+ transition: opacity 180ms ease, transform 180ms ease;
539
+ will-change: opacity, transform;
540
+ }
541
+
542
+ .ephia-transcribe-standard-right-layer--prompt {
543
+ font-size: 11px;
544
+ font-weight: 500;
545
+ letter-spacing: 0.2px;
546
+ color: var(--ephia-transcribe-ink);
547
+ opacity: 0.9;
548
+ transform: translateY(0);
549
+ white-space: nowrap;
550
+ padding-right: 6px;
551
+ }
552
+
553
+ .ephia-transcribe-standard-right-layer--waveform {
554
+ opacity: 0;
555
+ visibility: hidden;
556
+ pointer-events: none;
557
+ transform: translateY(2px);
558
+ }
559
+
560
+ .ephia-transcribe-standard-right-swap--prompt .ephia-transcribe-standard-right-layer--waveform {
561
+ visibility: hidden;
562
+ pointer-events: none;
563
+ }
564
+
565
+ .ephia-transcribe-standard-right-swap--waveform .ephia-transcribe-standard-right-layer--prompt {
566
+ opacity: 0;
567
+ visibility: hidden;
568
+ pointer-events: none;
569
+ transform: translateY(-2px);
570
+ }
571
+
572
+ .ephia-transcribe-standard-right-swap--waveform .ephia-transcribe-standard-right-layer--waveform {
573
+ opacity: 1;
574
+ visibility: visible;
575
+ pointer-events: auto;
576
+ transform: translateY(0);
577
+ width: 100%;
578
+ justify-content: stretch;
579
+ }
580
+
581
+ .ephia-transcribe-standard-right-swap--waveform .ephia-transcribe-standard-placeholder {
582
+ /* IMPORTANT:
583
+ La waveform ne doit pas influencer la largeur du bouton.
584
+ Si ce bloc devient flex:1/width:100%, la largeur peut croître avec l'historique (boucle). */
585
+ width: clamp(50px, 9vw, 90px) !important;
586
+ max-width: 90px !important;
587
+ min-width: 0 !important;
588
+ flex: 0 0 auto !important;
589
+ overflow: hidden !important;
590
+ }
591
+
592
+ /* ── Processing layer (finalizing) ───────────────────────────────── */
593
+
594
+ .ephia-transcribe-standard-right-layer--processing {
595
+ font-size: 11px;
596
+ font-weight: 500;
597
+ letter-spacing: 0.2px;
598
+ color: var(--ephia-transcribe-ink);
599
+ opacity: 0;
600
+ visibility: hidden;
601
+ pointer-events: none;
602
+ transform: translateY(2px);
603
+ white-space: nowrap;
604
+ padding-right: 6px;
605
+ }
606
+
607
+ .ephia-transcribe-standard-right-swap--processing .ephia-transcribe-standard-right-layer--prompt {
608
+ opacity: 0;
609
+ visibility: hidden;
610
+ pointer-events: none;
611
+ transform: translateY(-2px);
612
+ }
613
+
614
+ .ephia-transcribe-standard-right-swap--processing .ephia-transcribe-standard-right-layer--waveform {
615
+ display: none;
616
+ }
617
+
618
+ .ephia-transcribe-standard-right-swap--processing .ephia-transcribe-standard-right-layer--processing {
619
+ opacity: 0.9;
620
+ visibility: visible;
621
+ pointer-events: auto;
622
+ transform: translateY(0);
623
+ }
624
+
625
+ /* Priorité sur @ephia/transcribe-sdk/styles.css (chargé après dans la platform) */
626
+ .ephia-transcribe-root .ephia-transcribe-standard-right-swap:not(.ephia-transcribe-standard-right-swap--processing) {
627
+ flex: 1 1 0 !important;
628
+ min-width: 0 !important;
629
+ height: 28px !important;
630
+ justify-items: stretch !important;
631
+ }
632
+
633
+ .ephia-transcribe-root .ephia-transcribe-standard-right-swap--processing {
634
+ flex: 0 0 auto !important;
635
+ width: auto !important;
636
+ height: 28px !important;
637
+ justify-items: center !important;
638
+ }
639
+
640
+ .ephia-transcribe-root .ephia-transcribe-standard-right-layer--prompt {
641
+ color: var(--ephia-transcribe-ink) !important;
642
+ }
643
+
644
+ /* Minimal variant: swap logo ↔ indicateur d'activité (3 barres), sans élargissement */
645
+ .ephia-transcribe-minimal-swap {
646
+ position: relative;
647
+ display: inline-flex;
648
+ align-items: center;
649
+ justify-content: center;
650
+ overflow: hidden;
651
+ border-radius: 0;
652
+ transition: opacity 200ms ease;
653
+ flex-shrink: 0;
654
+ }
655
+
656
+ .ephia-transcribe-minimal-swap-layer {
657
+ position: absolute;
658
+ inset: 0;
659
+ display: flex;
660
+ align-items: center;
661
+ justify-content: center;
662
+ transition: opacity 180ms ease, transform 180ms ease;
663
+ will-change: opacity, transform;
664
+ }
665
+
666
+ .ephia-transcribe-minimal-swap-layer--logo {
667
+ opacity: 1;
668
+ transform: scale(1);
669
+ }
670
+
671
+ .ephia-transcribe-minimal-swap-layer--activity {
672
+ opacity: 0;
673
+ transform: scale(0.88);
674
+ }
675
+
676
+ .ephia-transcribe-minimal-swap--recording .ephia-transcribe-minimal-swap-layer--logo {
677
+ opacity: 0;
678
+ transform: scale(0.88);
679
+ }
680
+
681
+ .ephia-transcribe-minimal-swap--recording .ephia-transcribe-minimal-swap-layer--activity {
682
+ opacity: 1;
683
+ transform: scale(1);
684
+ }
685
+
686
+ /* 3 barres verticales (variant minimal, enregistrement) */
687
+ .ephia-transcribe-minimal-bars {
688
+ display: inline-flex;
689
+ align-items: center;
690
+ justify-content: center;
691
+ gap: var(--ephia-minimal-bar-gap, 3px);
692
+ height: 90%;
693
+ max-height: 100%;
694
+ width: 100%;
695
+ }
696
+
697
+ .ephia-transcribe-minimal-bar {
698
+ display: block;
699
+ flex-shrink: 0;
700
+ align-self: center;
701
+ border-radius: 9999px;
702
+ height: calc(var(--ephia-bar-scale, 0.4) * 100%);
703
+ min-height: 3px;
704
+ max-height: 100%;
705
+ /* Hauteur pilotée par rAF + lerp (pas de balayage CSS min/max). */
706
+ transition: none;
707
+ will-change: height;
708
+ }
709
+
710
+ /* ── EphiaTextarea — overlay Dragon-style ──────────── */
711
+ .ephia-textarea-wrapper {
712
+ position: relative;
713
+ display: block;
714
+ }
715
+
716
+ /*
717
+ * L'overlay affiche TOUT le texte committed + le partial grisé.
718
+ * Le texte de la <textarea> est transparent : seul le caret reste visible.
719
+ * Technique standard (CodeMirror, Monaco, Dragon Medical SDK).
720
+ */
721
+ .ephia-textarea-overlay {
722
+ position: absolute;
723
+ inset: 0;
724
+ overflow: hidden;
725
+ resize: none;
726
+ white-space: pre-wrap;
727
+ word-wrap: break-word;
728
+ color: inherit; /* texte committed affiché normalement */
729
+ pointer-events: none;
730
+ user-select: none;
731
+ z-index: 0;
732
+ }
733
+
734
+ /* Texte interim : grisé + italique, comme Dragon Medical */
735
+ .ephia-textarea-overlay .ephia-text--streaming {
736
+ color: rgba(0, 0, 0, 0.55);
737
+ font-style: italic;
738
+ padding-left: 0.15em;
739
+ }
740
+
741
+ /* Le texte de la textarea est invisible : l'overlay le remplace visuellement */
742
+ .ephia-textarea-input {
743
+ position: relative;
744
+ z-index: 1;
745
+ background: transparent;
746
+ color: transparent;
747
+ caret-color: var(--ephia-fg, #171717);
748
+ }
749
+
750
+ /* Masquer le curseur pendant l'enregistrement — sa position est imprévisible
751
+ quand l'élément n'a pas le focus ou entre deux commits. */
752
+ .ephia-textarea-input[data-ephia-recording="true"] {
753
+ caret-color: transparent;
754
+ }
755
+
756
+ /* ── Spinner ASCII au point d'insertion ────────────── */
757
+ .ephia-cursor {
758
+ font-style: normal;
759
+ color: var(--ephia-primary, #3933d3);
760
+ opacity: 0.85;
761
+ }
762
+
763
+ /* ── A11y : pas d'animations si reduce-motion ─────── */
764
+ @media (prefers-reduced-motion: reduce) {
765
+ .ephia-transcribe-ring,
766
+ .ephia-transcribe-logo-slot-layer,
767
+ .ephia-transcribe-logo-slot,
768
+ .ephia-transcribe-minimal-swap,
769
+ .ephia-transcribe-minimal-swap-layer,
770
+ .ephia-transcribe-standard-right-layer,
771
+ .ephia-transcribe-logo-svg {
772
+ transition: none !important;
773
+ animation: none !important;
774
+ }
775
+ }
776
+ /* ephia-audio React SDK — visual feedback for active target, recording state & text states.
777
+ * Import: `import "ephia-audio/react/ephia-react.css"` (ou via styles.css du package).
778
+ * Tout est dérivé de variables CSS pour être surchargeable.
779
+ *
780
+ * Système de text states :
781
+ * .ephia-text--streaming → texte en cours d'arrivée STT (partial)
782
+ * .ephia-text--committed → transition de validation (streaming → normal)
783
+ * .ephia-text--revised → texte corrigé par le review pipeline
784
+ */
785
+
786
+ :root {
787
+ /* ── Couleurs sémantiques ─────────────────────────────────────────────── */
788
+ --ephia-color-active: #6366f1;
789
+ --ephia-color-recording: #ef4444;
790
+ --ephia-color-streaming: #6366f1;
791
+ --ephia-color-committed: #22c55e;
792
+ --ephia-color-revised: #f59e0b;
793
+
794
+ /* ── Épaisseurs / radius ──────────────────────────────────────────────── */
795
+ --ephia-ring-width: 2px;
796
+ --ephia-ring-radius: 6px;
797
+ --ephia-transition: 180ms ease;
798
+ }
799
+
800
+ /* ═══════════════════════════════════════════════════════════════════════════
801
+ HALOS — états du target (container)
802
+ ═══════════════════════════════════════════════════════════════════════════ */
803
+
804
+ /* ─── Session active : griser les non-cibles pendant une dictée ──────────── */
805
+ /* Cible les descendants directs du body ET les enfants directs des containers
806
+ pour éviter d'affecter des composants imbriqués non liés à Ephia. */
807
+ [data-ephia-session-active="true"] > [data-ephia-target]:not([data-ephia-recording="true"]),
808
+ [data-ephia-session-active="true"] [data-ephia-target]:not([data-ephia-recording="true"]) {
809
+ opacity: 0.55;
810
+ filter: grayscale(0.6);
811
+ transition: opacity 0.2s ease, filter 0.2s ease;
812
+ }
813
+
814
+ /* ─── Target actif : halo discret (uniquement en session active) ────────── */
815
+ body[data-ephia-session-active="true"] [data-ephia-target-active="true"] {
816
+ outline: var(--ephia-ring-width) solid
817
+ color-mix(in srgb, var(--ephia-color-active) 35%, transparent);
818
+ outline-offset: 1px;
819
+ border-radius: var(--ephia-ring-radius);
820
+ transition: outline-color var(--ephia-transition),
821
+ box-shadow var(--ephia-transition);
822
+ }
823
+
824
+ /* ─── Target en enregistrement : border + glow ───────────────────────────── */
825
+ [data-ephia-recording="true"] {
826
+ position: relative;
827
+ outline: none !important;
828
+ border: 2px solid color-mix(in srgb, var(--ephia-color-recording) 55%, transparent);
829
+ border-radius: var(--ephia-ring-radius);
830
+ }
831
+
832
+ [data-ephia-recording="true"]::before {
833
+ content: "";
834
+ position: absolute;
835
+ inset: -3px;
836
+ border-radius: calc(var(--ephia-ring-radius) + 3px);
837
+ pointer-events: none;
838
+ z-index: 50;
839
+ box-shadow: 0 0 0 1px color-mix(in srgb, var(--ephia-color-recording) 30%, transparent),
840
+ 0 0 18px color-mix(in srgb, var(--ephia-color-recording) 25%, transparent);
841
+ animation: ephia-recording-pulse 1.8s ease-in-out infinite;
842
+ }
843
+
844
+ @keyframes ephia-recording-pulse {
845
+ 0%, 100% { opacity: 0.8; transform: scale(1); }
846
+ 50% { opacity: 1; transform: scale(1.01); }
847
+ }
848
+
849
+ /* ─── Indicateur public processing : discret, piloté par EphiaStatusBar ─── */
850
+ [data-ephia-state="processing"] {
851
+ display: inline-flex;
852
+ align-items: center;
853
+ gap: 3px;
854
+ padding: 2px 6px;
855
+ }
856
+
857
+ [data-ephia-state="processing"]::before,
858
+ [data-ephia-state="processing"]::after {
859
+ content: "";
860
+ display: inline-block;
861
+ width: 6px;
862
+ height: 6px;
863
+ border-radius: 999px;
864
+ background: currentColor;
865
+ opacity: 0.4;
866
+ animation: ephia-processing-pulse 1.2s ease-in-out infinite;
867
+ }
868
+
869
+ [data-ephia-state="processing"]::after {
870
+ animation-delay: 0.4s;
871
+ margin-left: 3px;
872
+ }
873
+
874
+ @keyframes ephia-processing-pulse {
875
+ 0%, 80%, 100% { opacity: 0.2; transform: scale(0.85); }
876
+ 40% { opacity: 0.8; transform: scale(1); }
877
+ }
878
+
879
+ /* ═══════════════════════════════════════════════════════════════════════════
880
+ FLASHES — overlays temporaires (position: fixed)
881
+ ═══════════════════════════════════════════════════════════════════════════ */
882
+
883
+ /* ─── Flash sur plage révisée ────────────────────────────────────────────── */
884
+ .ephia-reformat-flash {
885
+ position: fixed;
886
+ pointer-events: none;
887
+ z-index: 100;
888
+ border-radius: 3px;
889
+ background-color: color-mix(in srgb, var(--ephia-color-active) 22%, transparent);
890
+ box-shadow: 0 0 0 1px color-mix(in srgb, var(--ephia-color-active) 40%, transparent);
891
+ animation: ephia-reformat-fade 1.2s ease-out forwards;
892
+ }
893
+
894
+ @keyframes ephia-reformat-fade {
895
+ 0% { opacity: 0; }
896
+ 15% { opacity: 1; }
897
+ 100% { opacity: 0; }
898
+ }
899
+
900
+ /* ─── Flash chunk committed : bleu → vert ────────────────────────────────── */
901
+ .ephia-committed-flash {
902
+ position: fixed;
903
+ pointer-events: none;
904
+ z-index: 100;
905
+ border-radius: 3px;
906
+ background-color: color-mix(in srgb, #3b82f6 22%, transparent);
907
+ box-shadow: 0 0 0 1px color-mix(in srgb, #3b82f6 40%, transparent);
908
+ animation: ephia-committed-fade 1s ease-out forwards;
909
+ }
910
+
911
+ @keyframes ephia-committed-fade {
912
+ 0% { opacity: 0; background-color: color-mix(in srgb, #3b82f6 30%, transparent); }
913
+ 30% { opacity: 1; background-color: color-mix(in srgb, #3b82f6 25%, transparent); }
914
+ 60% { background-color: color-mix(in srgb, #22c55e 20%, transparent); }
915
+ 100% { opacity: 0; background-color: color-mix(in srgb, #22c55e 15%, transparent); }
916
+ }
917
+
918
+ /* ═══════════════════════════════════════════════════════════════════════════
919
+ TEXT STATES — styles appliqués au texte lui-même
920
+ ═══════════════════════════════════════════════════════════════════════════ */
921
+
922
+ /* ─── 1. STREAMING — texte en cours d'arrivée STT ──────────────────────────
923
+ Visuel : fond lavande "respirant" + border-bottom dotted + italique léger.
924
+ Le texte est vivant, il peut encore changer. */
925
+ .ephia-streaming,
926
+ .ephia-text--streaming {
927
+ background-color: color-mix(in srgb, var(--ephia-color-streaming) 8%, transparent);
928
+ border-bottom: 1.5px dotted color-mix(in srgb, var(--ephia-color-streaming) 30%, transparent);
929
+ border-radius: 2px;
930
+ padding: 0 2px;
931
+ margin: 0 -2px;
932
+ font-style: italic;
933
+ animation: ephia-streaming-breathe 2.2s ease-in-out infinite;
934
+ transition: background-color 350ms ease, border-bottom-color 350ms ease, opacity 350ms ease;
935
+ }
936
+
937
+ @keyframes ephia-streaming-breathe {
938
+ 0%, 100% { background-color: color-mix(in srgb, var(--ephia-color-streaming) 6%, transparent); }
939
+ 50% { background-color: color-mix(in srgb, var(--ephia-color-streaming) 11%, transparent); }
940
+ }
941
+
942
+ /* ─── 1b. PLACEHOLDER — texte interim au démarrage (avant premier partial) ─
943
+ Visuel : points de suspension avec un shimmer horizontal qui simule
944
+ une onde traversant le texte en attendant l'arrivée du STT. */
945
+ .ephia-text--placeholder {
946
+ background: linear-gradient(
947
+ 90deg,
948
+ rgba(99, 102, 241, 0.2) 0%,
949
+ rgba(99, 102, 241, 0.7) 40%,
950
+ rgba(99, 102, 241, 0.2) 80%
951
+ );
952
+ background-size: 250% 100%;
953
+ -webkit-background-clip: text;
954
+ -webkit-text-fill-color: transparent;
955
+ background-clip: text;
956
+ animation: ephia-placeholder-shimmer 1.6s ease-in-out infinite;
957
+ font-style: italic;
958
+ }
959
+
960
+ @keyframes ephia-placeholder-shimmer {
961
+ 0% { background-position: 250% 0; }
962
+ 100% { background-position: -250% 0; }
963
+ }
964
+
965
+ /* ─── 2. COMMITTED — transition streaming → normal ─────────────────────────
966
+ Quand le backend valide le texte, le fond lavande glisse vers le vert
967
+ puis disparaît complètement. Feedback de confirmation visuelle. */
968
+ .ephia-committed,
969
+ .ephia-text--committed {
970
+ animation: ephia-committed-settle 1.2s ease-out forwards;
971
+ font-style: normal;
972
+ }
973
+
974
+ @keyframes ephia-committed-settle {
975
+ 0% {
976
+ background-color: color-mix(in srgb, var(--ephia-color-streaming) 8%, transparent);
977
+ border-bottom-color: color-mix(in srgb, var(--ephia-color-streaming) 30%, transparent);
978
+ }
979
+ 25% {
980
+ background-color: color-mix(in srgb, var(--ephia-color-committed) 18%, transparent);
981
+ border-bottom-color: color-mix(in srgb, var(--ephia-color-committed) 45%, transparent);
982
+ }
983
+ 100% {
984
+ background-color: transparent;
985
+ border-bottom-color: transparent;
986
+ }
987
+ }
988
+
989
+ /* ─── 3. REVISED — texte corrigé par le review pipeline ────────────────────
990
+ Visuel : fond ambre + ligne ondulée (style suggestion). S'efface après 2s.
991
+ L'utilisateur remarque que le backend a modifié quelque chose. */
992
+ .ephia-revised,
993
+ .ephia-text--revised {
994
+ background-color: color-mix(in srgb, var(--ephia-color-revised) 14%, transparent);
995
+ border-bottom: 2px wavy color-mix(in srgb, var(--ephia-color-revised) 45%, transparent);
996
+ border-radius: 2px;
997
+ padding: 0 2px;
998
+ margin: 0 -2px;
999
+ animation: ephia-revised-settle 2s ease-out forwards;
1000
+ }
1001
+
1002
+ @keyframes ephia-revised-settle {
1003
+ 0% {
1004
+ background-color: color-mix(in srgb, var(--ephia-color-revised) 22%, transparent);
1005
+ border-bottom-color: color-mix(in srgb, var(--ephia-color-revised) 55%, transparent);
1006
+ }
1007
+ 50% {
1008
+ background-color: color-mix(in srgb, var(--ephia-color-revised) 10%, transparent);
1009
+ border-bottom-color: color-mix(in srgb, var(--ephia-color-revised) 35%, transparent);
1010
+ }
1011
+ 100% {
1012
+ background-color: transparent;
1013
+ border-bottom-color: transparent;
1014
+ }
1015
+ }
1016
+
1017
+ /* ─── 5. ERROR — erreur de transcription ─────────────────────────────────── */
1018
+ .ephia-error {
1019
+ color: #dc2626;
1020
+ text-decoration: line-through;
1021
+ }
1022
+
1023
+ /* ═══════════════════════════════════════════════════════════════════════════
1024
+ UTILITAIRES & A11Y
1025
+ ═══════════════════════════════════════════════════════════════════════════ */
1026
+
1027
+ /* ─── Lock cursor TipTap / ProseMirror ───────────────────────────────────── */
1028
+ [data-ephia-recording="true"] .ProseMirror {
1029
+ caret-color: transparent !important;
1030
+ }
1031
+
1032
+ /* ─── Curseur d'insertion ─────────────────────────────────────────────────── */
1033
+ .ephia-insertion-cursor {
1034
+ display: flex;
1035
+ align-items: center;
1036
+ justify-content: center;
1037
+ }
1038
+
1039
+ @keyframes ephia-pulse {
1040
+ 0%, 100% {
1041
+ transform: translateX(-50%) scale(0.9);
1042
+ opacity: 0.7;
1043
+ }
1044
+ 50% {
1045
+ transform: translateX(-50%) scale(1.15);
1046
+ opacity: 1;
1047
+ }
1048
+ }
1049
+
1050
+ /* ─── A11y : pas d'animations si reduce-motion ────────────────────────────── */
1051
+ @media (prefers-reduced-motion: reduce) {
1052
+ [data-ephia-recording="true"] {
1053
+ animation: none;
1054
+ }
1055
+ [data-ephia-recording="true"]::before {
1056
+ animation: none !important;
1057
+ }
1058
+ .ephia-insertion-cursor > * {
1059
+ animation: none !important;
1060
+ }
1061
+ .ephia-reformat-flash,
1062
+ .ephia-committed-flash {
1063
+ animation: none;
1064
+ opacity: 0.5;
1065
+ }
1066
+ .ephia-streaming,
1067
+ .ephia-text--streaming {
1068
+ animation: none;
1069
+ background-color: color-mix(in srgb, var(--ephia-color-streaming) 8%, transparent);
1070
+ }
1071
+ .ephia-text--placeholder {
1072
+ animation: none;
1073
+ background: transparent;
1074
+ -webkit-text-fill-color: color-mix(in srgb, var(--ephia-color-streaming) 50%, transparent);
1075
+ color: color-mix(in srgb, var(--ephia-color-streaming) 50%, transparent);
1076
+ }
1077
+ .ephia-committed,
1078
+ .ephia-text--committed {
1079
+ animation: none;
1080
+ background-color: transparent;
1081
+ border-bottom-color: transparent;
1082
+ }
1083
+ .ephia-revised,
1084
+ .ephia-text--revised {
1085
+ animation: none;
1086
+ background-color: color-mix(in srgb, var(--ephia-color-revised) 10%, transparent);
1087
+ border-bottom-color: color-mix(in srgb, var(--ephia-color-revised) 40%, transparent);
1088
+ }
1089
+ .ephia-error {
1090
+ color: #dc2626;
1091
+ text-decoration: line-through;
1092
+ }
1093
+ }