@mieweb/ui 0.6.1 → 0.7.0-dev.178

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 (450) hide show
  1. package/README.md +90 -53
  2. package/dist/CsvBlock-2A5VEDD3.cjs +15 -0
  3. package/dist/CsvBlock-2A5VEDD3.cjs.map +1 -0
  4. package/dist/CsvBlock-D6PWAJ5X.js +6 -0
  5. package/dist/CsvBlock-D6PWAJ5X.js.map +1 -0
  6. package/dist/HtmlPreviewBlock-PEHTJ6C3.js +6 -0
  7. package/dist/HtmlPreviewBlock-PEHTJ6C3.js.map +1 -0
  8. package/dist/HtmlPreviewBlock-SYXCV5RH.cjs +15 -0
  9. package/dist/HtmlPreviewBlock-SYXCV5RH.cjs.map +1 -0
  10. package/dist/MedicationEditor-Bp4NLy7y.d.cts +352 -0
  11. package/dist/MedicationEditor-Bp4NLy7y.d.ts +352 -0
  12. package/dist/MermaidBlock-7NK3AVK2.js +6 -0
  13. package/dist/MermaidBlock-7NK3AVK2.js.map +1 -0
  14. package/dist/MermaidBlock-AFXNP5NK.cjs +15 -0
  15. package/dist/MermaidBlock-AFXNP5NK.cjs.map +1 -0
  16. package/dist/SurveyBlock-6KRNKFH5.cjs +15 -0
  17. package/dist/SurveyBlock-6KRNKFH5.cjs.map +1 -0
  18. package/dist/SurveyBlock-NNDGXGG4.js +6 -0
  19. package/dist/SurveyBlock-NNDGXGG4.js.map +1 -0
  20. package/dist/ag-grid.cjs +6 -6
  21. package/dist/ag-grid.cjs.map +1 -1
  22. package/dist/ag-grid.js +2 -2
  23. package/dist/ag-grid.js.map +1 -1
  24. package/dist/brands/index.cjs +17 -17
  25. package/dist/brands/index.js +4 -4
  26. package/dist/chunk-372IIHUA.cjs +2017 -0
  27. package/dist/chunk-372IIHUA.cjs.map +1 -0
  28. package/dist/{chunk-2DS3RJ2D.js → chunk-3TERETXH.js} +112 -3
  29. package/dist/chunk-3TERETXH.js.map +1 -0
  30. package/dist/chunk-44CK4CT2.cjs +336 -0
  31. package/dist/chunk-44CK4CT2.cjs.map +1 -0
  32. package/dist/{chunk-FSBFQBNE.cjs → chunk-4GXECTPS.cjs} +2 -2
  33. package/dist/{chunk-FSBFQBNE.cjs.map → chunk-4GXECTPS.cjs.map} +1 -1
  34. package/dist/chunk-4LVPQ44A.js +228 -0
  35. package/dist/chunk-4LVPQ44A.js.map +1 -0
  36. package/dist/{chunk-6555OYRU.js → chunk-5BQE6BEL.js} +2 -2
  37. package/dist/{chunk-6555OYRU.js.map → chunk-5BQE6BEL.js.map} +1 -1
  38. package/dist/chunk-62ZOA6PX.js +31 -0
  39. package/dist/chunk-62ZOA6PX.js.map +1 -0
  40. package/dist/chunk-63KVQHGQ.cjs +1802 -0
  41. package/dist/chunk-63KVQHGQ.cjs.map +1 -0
  42. package/dist/chunk-6AHYYWVB.js +78 -0
  43. package/dist/chunk-6AHYYWVB.js.map +1 -0
  44. package/dist/chunk-6I7IDZ4A.js +51 -0
  45. package/dist/chunk-6I7IDZ4A.js.map +1 -0
  46. package/dist/chunk-6ZYE6JUK.cjs +80 -0
  47. package/dist/chunk-6ZYE6JUK.cjs.map +1 -0
  48. package/dist/chunk-7QXG5BBB.cjs +164 -0
  49. package/dist/chunk-7QXG5BBB.cjs.map +1 -0
  50. package/dist/chunk-7WMSUXOQ.js +182 -0
  51. package/dist/chunk-7WMSUXOQ.js.map +1 -0
  52. package/dist/chunk-7XUN3OQT.js +3 -0
  53. package/dist/{chunk-V2DF2GUE.js.map → chunk-7XUN3OQT.js.map} +1 -1
  54. package/dist/chunk-7ZVTLTJF.js +109 -0
  55. package/dist/chunk-7ZVTLTJF.js.map +1 -0
  56. package/dist/chunk-AES2BKEW.cjs +229 -0
  57. package/dist/chunk-AES2BKEW.cjs.map +1 -0
  58. package/dist/chunk-AFKKJEV5.cjs +72 -0
  59. package/dist/chunk-AFKKJEV5.cjs.map +1 -0
  60. package/dist/chunk-AT732HC6.cjs +4 -0
  61. package/dist/{chunk-74K3RRU7.cjs.map → chunk-AT732HC6.cjs.map} +1 -1
  62. package/dist/chunk-BB4WQ4LV.cjs +137 -0
  63. package/dist/chunk-BB4WQ4LV.cjs.map +1 -0
  64. package/dist/chunk-BIT27PPD.js +49 -0
  65. package/dist/chunk-BIT27PPD.js.map +1 -0
  66. package/dist/chunk-BLPCYP4D.cjs +537 -0
  67. package/dist/chunk-BLPCYP4D.cjs.map +1 -0
  68. package/dist/chunk-BT3ZQJGT.cjs +130 -0
  69. package/dist/chunk-BT3ZQJGT.cjs.map +1 -0
  70. package/dist/chunk-BX6BTDTR.js +34 -0
  71. package/dist/chunk-BX6BTDTR.js.map +1 -0
  72. package/dist/chunk-C2544GKZ.js +313 -0
  73. package/dist/chunk-C2544GKZ.js.map +1 -0
  74. package/dist/chunk-C7RVKV25.cjs +25 -0
  75. package/dist/chunk-C7RVKV25.cjs.map +1 -0
  76. package/dist/chunk-CKD554XR.cjs +100 -0
  77. package/dist/chunk-CKD554XR.cjs.map +1 -0
  78. package/dist/chunk-CVC4NWFA.cjs +154 -0
  79. package/dist/chunk-CVC4NWFA.cjs.map +1 -0
  80. package/dist/{chunk-K3IVCUPP.js → chunk-CYMZ2QS5.js} +2 -2
  81. package/dist/{chunk-K3IVCUPP.js.map → chunk-CYMZ2QS5.js.map} +1 -1
  82. package/dist/{chunk-FRT6RQND.cjs → chunk-DAPZPT5N.cjs} +92 -56
  83. package/dist/chunk-DAPZPT5N.cjs.map +1 -0
  84. package/dist/chunk-DHKYGVP7.js +3 -0
  85. package/dist/{chunk-LEMY57MI.js.map → chunk-DHKYGVP7.js.map} +1 -1
  86. package/dist/chunk-DLNJLCNO.cjs +101 -0
  87. package/dist/chunk-DLNJLCNO.cjs.map +1 -0
  88. package/dist/chunk-DUPR6FEL.js +43 -0
  89. package/dist/chunk-DUPR6FEL.js.map +1 -0
  90. package/dist/{chunk-4FALCEJI.cjs → chunk-EL2RSQTJ.cjs} +2 -2
  91. package/dist/{chunk-4FALCEJI.cjs.map → chunk-EL2RSQTJ.cjs.map} +1 -1
  92. package/dist/chunk-ERLPVXOV.js +22 -0
  93. package/dist/chunk-ERLPVXOV.js.map +1 -0
  94. package/dist/chunk-FIWKHLWY.js +197 -0
  95. package/dist/chunk-FIWKHLWY.js.map +1 -0
  96. package/dist/chunk-FJH7P5WG.js +513 -0
  97. package/dist/chunk-FJH7P5WG.js.map +1 -0
  98. package/dist/chunk-FSGEDDF2.cjs +33 -0
  99. package/dist/chunk-FSGEDDF2.cjs.map +1 -0
  100. package/dist/chunk-GLXTVCHN.cjs +4 -0
  101. package/dist/{chunk-4ZU53GNR.cjs.map → chunk-GLXTVCHN.cjs.map} +1 -1
  102. package/dist/chunk-HRS6W74P.js +1568 -0
  103. package/dist/chunk-HRS6W74P.js.map +1 -0
  104. package/dist/chunk-I2W24RHW.js +196 -0
  105. package/dist/chunk-I2W24RHW.js.map +1 -0
  106. package/dist/chunk-I6CY5C6A.js +12 -0
  107. package/dist/chunk-I6CY5C6A.js.map +1 -0
  108. package/dist/chunk-IQ4KQJAO.js +3 -0
  109. package/dist/chunk-IQ4KQJAO.js.map +1 -0
  110. package/dist/chunk-IWDQPSJN.cjs +1452 -0
  111. package/dist/chunk-IWDQPSJN.cjs.map +1 -0
  112. package/dist/chunk-J7KWQJOB.js +14 -0
  113. package/dist/chunk-J7KWQJOB.js.map +1 -0
  114. package/dist/{chunk-R25H4N4Z.js → chunk-J7Z7467B.js} +92 -56
  115. package/dist/chunk-J7Z7467B.js.map +1 -0
  116. package/dist/{chunk-NINI4WVS.cjs → chunk-JC6Y3LGF.cjs} +297 -84
  117. package/dist/chunk-JC6Y3LGF.cjs.map +1 -0
  118. package/dist/chunk-JCNPVK74.cjs +347 -0
  119. package/dist/chunk-JCNPVK74.cjs.map +1 -0
  120. package/dist/chunk-JFLC7SHM.cjs +35 -0
  121. package/dist/chunk-JFLC7SHM.cjs.map +1 -0
  122. package/dist/chunk-JHYFHEHS.js +1967 -0
  123. package/dist/chunk-JHYFHEHS.js.map +1 -0
  124. package/dist/{chunk-IBZXDX4L.cjs → chunk-JQEY53SH.cjs} +112 -3
  125. package/dist/chunk-JQEY53SH.cjs.map +1 -0
  126. package/dist/chunk-JTBQNOSL.cjs +1605 -0
  127. package/dist/chunk-JTBQNOSL.cjs.map +1 -0
  128. package/dist/chunk-JYZW2PZS.cjs +166 -0
  129. package/dist/chunk-JYZW2PZS.cjs.map +1 -0
  130. package/dist/chunk-KBEKQMVY.cjs +198 -0
  131. package/dist/chunk-KBEKQMVY.cjs.map +1 -0
  132. package/dist/chunk-KJ5BSGEO.cjs +258 -0
  133. package/dist/chunk-KJ5BSGEO.cjs.map +1 -0
  134. package/dist/{chunk-DNPRRYPQ.js → chunk-KL2S36FM.js} +2 -2
  135. package/dist/chunk-KL2S36FM.js.map +1 -0
  136. package/dist/chunk-LIH3IZYK.js +689 -0
  137. package/dist/chunk-LIH3IZYK.js.map +1 -0
  138. package/dist/chunk-MAMY5HTT.cjs +1177 -0
  139. package/dist/chunk-MAMY5HTT.cjs.map +1 -0
  140. package/dist/{chunk-WFS6R2F5.js → chunk-MKWGKASO.js} +114 -101
  141. package/dist/chunk-MKWGKASO.js.map +1 -0
  142. package/dist/{chunk-CV4CVGLO.cjs → chunk-MZPF2QGM.cjs} +95 -74
  143. package/dist/chunk-MZPF2QGM.cjs.map +1 -0
  144. package/dist/chunk-NQME7MCK.js +1416 -0
  145. package/dist/chunk-NQME7MCK.js.map +1 -0
  146. package/dist/chunk-NTYGCBCL.js +543 -0
  147. package/dist/chunk-NTYGCBCL.js.map +1 -0
  148. package/dist/chunk-OF7NTZ2I.js +3 -0
  149. package/dist/chunk-OF7NTZ2I.js.map +1 -0
  150. package/dist/chunk-OXYFTCRW.cjs +57 -0
  151. package/dist/chunk-OXYFTCRW.cjs.map +1 -0
  152. package/dist/chunk-QA6ZDHFP.js +117 -0
  153. package/dist/chunk-QA6ZDHFP.js.map +1 -0
  154. package/dist/chunk-RHNPILZQ.cjs +4 -0
  155. package/dist/chunk-RHNPILZQ.cjs.map +1 -0
  156. package/dist/chunk-RHYIFIT5.js +98 -0
  157. package/dist/chunk-RHYIFIT5.js.map +1 -0
  158. package/dist/chunk-RI56YPED.cjs +201 -0
  159. package/dist/chunk-RI56YPED.cjs.map +1 -0
  160. package/dist/chunk-RISZWWEQ.js +3 -0
  161. package/dist/chunk-RISZWWEQ.js.map +1 -0
  162. package/dist/chunk-S6Z3KDEM.js +140 -0
  163. package/dist/chunk-S6Z3KDEM.js.map +1 -0
  164. package/dist/chunk-S7JQ3FV5.cjs +16 -0
  165. package/dist/chunk-S7JQ3FV5.cjs.map +1 -0
  166. package/dist/{chunk-OXGUG57G.cjs → chunk-SNEQXSC5.cjs} +2 -2
  167. package/dist/{chunk-OXGUG57G.cjs.map → chunk-SNEQXSC5.cjs.map} +1 -1
  168. package/dist/chunk-SQHMGT4W.js +164 -0
  169. package/dist/chunk-SQHMGT4W.js.map +1 -0
  170. package/dist/{chunk-JHPFOG2N.cjs → chunk-ST2RE5UB.cjs} +30 -18
  171. package/dist/chunk-ST2RE5UB.cjs.map +1 -0
  172. package/dist/{chunk-QVJBUWJQ.js → chunk-STPK44AU.js} +46 -12
  173. package/dist/chunk-STPK44AU.js.map +1 -0
  174. package/dist/chunk-T3C4RNJ4.js +106 -0
  175. package/dist/chunk-T3C4RNJ4.js.map +1 -0
  176. package/dist/chunk-T3RWXOCM.js +148 -0
  177. package/dist/chunk-T3RWXOCM.js.map +1 -0
  178. package/dist/{chunk-DCER2QQB.js → chunk-T42BCIUA.js} +2 -2
  179. package/dist/{chunk-DCER2QQB.js.map → chunk-T42BCIUA.js.map} +1 -1
  180. package/dist/{chunk-OYKS2JBB.cjs → chunk-TN33E2VN.cjs} +2 -2
  181. package/dist/chunk-TN33E2VN.cjs.map +1 -0
  182. package/dist/{chunk-K7IGBNZA.cjs → chunk-TR2OJKQ4.cjs} +46 -12
  183. package/dist/chunk-TR2OJKQ4.cjs.map +1 -0
  184. package/dist/chunk-UFYXU7KV.js +135 -0
  185. package/dist/chunk-UFYXU7KV.js.map +1 -0
  186. package/dist/chunk-UM6NA7HL.js +1145 -0
  187. package/dist/chunk-UM6NA7HL.js.map +1 -0
  188. package/dist/chunk-UO3KEUYK.cjs +220 -0
  189. package/dist/chunk-UO3KEUYK.cjs.map +1 -0
  190. package/dist/chunk-UOOH356R.cjs +66 -0
  191. package/dist/chunk-UOOH356R.cjs.map +1 -0
  192. package/dist/{chunk-4ZU53GNR.cjs → chunk-UTYUT5YY.cjs} +62 -2
  193. package/dist/chunk-UTYUT5YY.cjs.map +1 -0
  194. package/dist/chunk-UVSODK6V.cjs +53 -0
  195. package/dist/chunk-UVSODK6V.cjs.map +1 -0
  196. package/dist/chunk-V25Z5GR6.cjs +184 -0
  197. package/dist/chunk-V25Z5GR6.cjs.map +1 -0
  198. package/dist/chunk-VBWRH4UW.cjs +4 -0
  199. package/dist/chunk-VBWRH4UW.cjs.map +1 -0
  200. package/dist/chunk-VJOVJTFP.cjs +370 -0
  201. package/dist/chunk-VJOVJTFP.cjs.map +1 -0
  202. package/dist/{chunk-7BARESXI.cjs → chunk-VPNURIAY.cjs} +114 -101
  203. package/dist/chunk-VPNURIAY.cjs.map +1 -0
  204. package/dist/{chunk-I56VBDSC.js → chunk-XDFDVF2O.js} +30 -18
  205. package/dist/chunk-XDFDVF2O.js.map +1 -0
  206. package/dist/chunk-XL7TTKHP.js +327 -0
  207. package/dist/chunk-XL7TTKHP.js.map +1 -0
  208. package/dist/chunk-Y2S6QMKU.js +78 -0
  209. package/dist/chunk-Y2S6QMKU.js.map +1 -0
  210. package/dist/chunk-Y4ESBDQ5.cjs +552 -0
  211. package/dist/chunk-Y4ESBDQ5.cjs.map +1 -0
  212. package/dist/chunk-Y5KEFV5O.js +192 -0
  213. package/dist/chunk-Y5KEFV5O.js.map +1 -0
  214. package/dist/chunk-YBLIDESO.js +365 -0
  215. package/dist/chunk-YBLIDESO.js.map +1 -0
  216. package/dist/chunk-YVLP3ATI.js +1776 -0
  217. package/dist/chunk-YVLP3ATI.js.map +1 -0
  218. package/dist/chunk-YVPC65ZJ.cjs +134 -0
  219. package/dist/chunk-YVPC65ZJ.cjs.map +1 -0
  220. package/dist/{chunk-MPOWJVV4.js → chunk-YZT62IMX.js} +95 -74
  221. package/dist/chunk-YZT62IMX.js.map +1 -0
  222. package/dist/chunk-ZAIEQWRA.js +207 -0
  223. package/dist/chunk-ZAIEQWRA.js.map +1 -0
  224. package/dist/chunk-ZDEYV373.cjs +139 -0
  225. package/dist/chunk-ZDEYV373.cjs.map +1 -0
  226. package/dist/{chunk-Q7NBJFEB.js → chunk-ZGSC2CB2.js} +2 -2
  227. package/dist/{chunk-Q7NBJFEB.js.map → chunk-ZGSC2CB2.js.map} +1 -1
  228. package/dist/{chunk-3M4P2KMG.cjs → chunk-ZHBANUBI.cjs} +2 -2
  229. package/dist/{chunk-3M4P2KMG.cjs.map → chunk-ZHBANUBI.cjs.map} +1 -1
  230. package/dist/components/AlertDialog/index.cjs +18 -0
  231. package/dist/components/AlertDialog/index.cjs.map +1 -0
  232. package/dist/components/AlertDialog/index.d.cts +56 -0
  233. package/dist/components/AlertDialog/index.d.ts +56 -0
  234. package/dist/components/AlertDialog/index.js +9 -0
  235. package/dist/components/AlertDialog/index.js.map +1 -0
  236. package/dist/components/AudioRecorder/index.d.cts +1 -1
  237. package/dist/components/AudioRecorder/index.d.ts +1 -1
  238. package/dist/components/Autocomplete/index.cjs +16 -0
  239. package/dist/components/Autocomplete/index.cjs.map +1 -0
  240. package/dist/components/Autocomplete/index.d.cts +77 -0
  241. package/dist/components/Autocomplete/index.d.ts +77 -0
  242. package/dist/components/Autocomplete/index.js +7 -0
  243. package/dist/components/Autocomplete/index.js.map +1 -0
  244. package/dist/components/Badge/index.cjs +4 -3
  245. package/dist/components/Badge/index.js +2 -1
  246. package/dist/components/Card/index.cjs +16 -15
  247. package/dist/components/Card/index.js +2 -1
  248. package/dist/components/Collapsible/index.cjs +21 -0
  249. package/dist/components/Collapsible/index.cjs.map +1 -0
  250. package/dist/components/Collapsible/index.d.cts +33 -0
  251. package/dist/components/Collapsible/index.d.ts +33 -0
  252. package/dist/components/Collapsible/index.js +4 -0
  253. package/dist/components/Collapsible/index.js.map +1 -0
  254. package/dist/components/CountryCodeDropdown/index.cjs +6 -5
  255. package/dist/components/CountryCodeDropdown/index.js +3 -2
  256. package/dist/components/DateInput/index.cjs +2 -2
  257. package/dist/components/DateInput/index.d.cts +8 -2
  258. package/dist/components/DateInput/index.d.ts +8 -2
  259. package/dist/components/DateInput/index.js +1 -1
  260. package/dist/components/Dropdown/index.cjs +9 -8
  261. package/dist/components/Dropdown/index.d.cts +2 -1
  262. package/dist/components/Dropdown/index.d.ts +2 -1
  263. package/dist/components/Dropdown/index.js +3 -2
  264. package/dist/components/FloatingWindow/index.cjs +18 -0
  265. package/dist/components/FloatingWindow/index.cjs.map +1 -0
  266. package/dist/components/FloatingWindow/index.d.cts +64 -0
  267. package/dist/components/FloatingWindow/index.d.ts +64 -0
  268. package/dist/components/FloatingWindow/index.js +5 -0
  269. package/dist/components/FloatingWindow/index.js.map +1 -0
  270. package/dist/components/Label/index.cjs +17 -0
  271. package/dist/components/Label/index.cjs.map +1 -0
  272. package/dist/components/Label/index.d.cts +23 -0
  273. package/dist/components/Label/index.d.ts +23 -0
  274. package/dist/components/Label/index.js +4 -0
  275. package/dist/components/Label/index.js.map +1 -0
  276. package/dist/components/Markdown/index.cjs +51 -0
  277. package/dist/components/Markdown/index.cjs.map +1 -0
  278. package/dist/components/Markdown/index.css +331 -0
  279. package/dist/components/Markdown/index.css.map +1 -0
  280. package/dist/components/Markdown/index.d.cts +82 -0
  281. package/dist/components/Markdown/index.d.ts +82 -0
  282. package/dist/components/Markdown/index.js +10 -0
  283. package/dist/components/Markdown/index.js.map +1 -0
  284. package/dist/components/Markdown/styles.css +354 -0
  285. package/dist/components/MediaEditor/index.cjs +44 -0
  286. package/dist/components/MediaEditor/index.cjs.map +1 -0
  287. package/dist/components/MediaEditor/index.d.cts +151 -0
  288. package/dist/components/MediaEditor/index.d.ts +151 -0
  289. package/dist/components/MediaEditor/index.js +19 -0
  290. package/dist/components/MediaEditor/index.js.map +1 -0
  291. package/dist/components/MediaPlayer/index.cjs +24 -0
  292. package/dist/components/MediaPlayer/index.cjs.map +1 -0
  293. package/dist/components/MediaPlayer/index.d.cts +71 -0
  294. package/dist/components/MediaPlayer/index.d.ts +71 -0
  295. package/dist/components/MediaPlayer/index.js +7 -0
  296. package/dist/components/MediaPlayer/index.js.map +1 -0
  297. package/dist/components/RecordButton/index.cjs +4 -4
  298. package/dist/components/RecordButton/index.js +1 -1
  299. package/dist/components/RichTextEditor/index.cjs +33 -0
  300. package/dist/components/RichTextEditor/index.cjs.map +1 -0
  301. package/dist/components/RichTextEditor/index.d.cts +62 -0
  302. package/dist/components/RichTextEditor/index.d.ts +62 -0
  303. package/dist/components/RichTextEditor/index.js +12 -0
  304. package/dist/components/RichTextEditor/index.js.map +1 -0
  305. package/dist/components/ScrollArea/index.cjs +17 -0
  306. package/dist/components/ScrollArea/index.cjs.map +1 -0
  307. package/dist/components/ScrollArea/index.d.cts +22 -0
  308. package/dist/components/ScrollArea/index.d.ts +22 -0
  309. package/dist/components/ScrollArea/index.js +4 -0
  310. package/dist/components/ScrollArea/index.js.map +1 -0
  311. package/dist/components/Select/index.cjs +5 -3
  312. package/dist/components/Select/index.js +3 -1
  313. package/dist/components/Separator/index.cjs +17 -0
  314. package/dist/components/Separator/index.cjs.map +1 -0
  315. package/dist/components/Separator/index.d.cts +27 -0
  316. package/dist/components/Separator/index.d.ts +27 -0
  317. package/dist/components/Separator/index.js +4 -0
  318. package/dist/components/Separator/index.js.map +1 -0
  319. package/dist/components/Sheet/index.cjs +48 -0
  320. package/dist/components/Sheet/index.cjs.map +1 -0
  321. package/dist/components/Sheet/index.d.cts +62 -0
  322. package/dist/components/Sheet/index.d.ts +62 -0
  323. package/dist/components/Sheet/index.js +7 -0
  324. package/dist/components/Sheet/index.js.map +1 -0
  325. package/dist/components/SuperChat/index.cjs +1373 -0
  326. package/dist/components/SuperChat/index.cjs.map +1 -0
  327. package/dist/components/SuperChat/index.d.cts +189 -0
  328. package/dist/components/SuperChat/index.d.ts +189 -0
  329. package/dist/components/SuperChat/index.js +1336 -0
  330. package/dist/components/SuperChat/index.js.map +1 -0
  331. package/dist/components/SuperChat/plugins/index.cjs +1292 -0
  332. package/dist/components/SuperChat/plugins/index.cjs.map +1 -0
  333. package/dist/components/SuperChat/plugins/index.d.cts +233 -0
  334. package/dist/components/SuperChat/plugins/index.d.ts +233 -0
  335. package/dist/components/SuperChat/plugins/index.js +1252 -0
  336. package/dist/components/SuperChat/plugins/index.js.map +1 -0
  337. package/dist/components/Table/index.cjs +9 -9
  338. package/dist/components/Table/index.d.cts +4 -0
  339. package/dist/components/Table/index.d.ts +4 -0
  340. package/dist/components/Table/index.js +1 -1
  341. package/dist/components/ThemeProvider/index.cjs +7 -7
  342. package/dist/components/ThemeProvider/index.js +1 -1
  343. package/dist/components/Toggle/index.cjs +17 -0
  344. package/dist/components/Toggle/index.cjs.map +1 -0
  345. package/dist/components/Toggle/index.d.cts +28 -0
  346. package/dist/components/Toggle/index.d.ts +28 -0
  347. package/dist/components/Toggle/index.js +4 -0
  348. package/dist/components/Toggle/index.js.map +1 -0
  349. package/dist/components/TranscriptView/index.cjs +21 -0
  350. package/dist/components/TranscriptView/index.cjs.map +1 -0
  351. package/dist/components/TranscriptView/index.d.cts +55 -0
  352. package/dist/components/TranscriptView/index.d.ts +55 -0
  353. package/dist/components/TranscriptView/index.js +4 -0
  354. package/dist/components/TranscriptView/index.js.map +1 -0
  355. package/dist/datavis.cjs +268 -227
  356. package/dist/datavis.cjs.map +1 -1
  357. package/dist/datavis.d.cts +83 -9
  358. package/dist/datavis.d.ts +83 -9
  359. package/dist/datavis.js +235 -229
  360. package/dist/datavis.js.map +1 -1
  361. package/dist/esheet.cjs +184 -0
  362. package/dist/esheet.cjs.map +1 -1
  363. package/dist/esheet.d.cts +117 -0
  364. package/dist/esheet.d.ts +117 -0
  365. package/dist/esheet.js +163 -0
  366. package/dist/esheet.js.map +1 -1
  367. package/dist/hey-buddy-CLUVAY2X.cjs +1133 -0
  368. package/dist/hey-buddy-CLUVAY2X.cjs.map +1 -0
  369. package/dist/hey-buddy-NMSWZ4TN.js +1131 -0
  370. package/dist/hey-buddy-NMSWZ4TN.js.map +1 -0
  371. package/dist/hooks/index.cjs +76 -16
  372. package/dist/hooks/index.d.cts +151 -2
  373. package/dist/hooks/index.d.ts +151 -2
  374. package/dist/hooks/index.js +6 -2
  375. package/dist/html-MdxqSzjD.d.cts +13 -0
  376. package/dist/html-MdxqSzjD.d.ts +13 -0
  377. package/dist/index.cjs +10394 -6565
  378. package/dist/index.cjs.map +1 -1
  379. package/dist/index.css +331 -0
  380. package/dist/index.css.map +1 -0
  381. package/dist/index.d.cts +2020 -249
  382. package/dist/index.d.ts +2020 -249
  383. package/dist/index.js +9223 -5969
  384. package/dist/index.js.map +1 -1
  385. package/dist/kerebron.cjs +119 -0
  386. package/dist/kerebron.cjs.map +1 -0
  387. package/dist/kerebron.css +11 -0
  388. package/dist/kerebron.d.cts +23 -0
  389. package/dist/kerebron.d.ts +23 -0
  390. package/dist/kerebron.js +116 -0
  391. package/dist/kerebron.js.map +1 -0
  392. package/dist/nitroTableGrid-3J2GY6CK.cjs +47 -0
  393. package/dist/nitroTableGrid-3J2GY6CK.cjs.map +1 -0
  394. package/dist/nitroTableGrid-SIMT7635.js +25 -0
  395. package/dist/nitroTableGrid-SIMT7635.js.map +1 -0
  396. package/dist/orderRows-BUJYgSc7.d.cts +221 -0
  397. package/dist/orderRows-BUJYgSc7.d.ts +221 -0
  398. package/dist/speaker-verify-5GTWAN5Y.cjs +357 -0
  399. package/dist/speaker-verify-5GTWAN5Y.cjs.map +1 -0
  400. package/dist/speaker-verify-R67P433H.js +355 -0
  401. package/dist/speaker-verify-R67P433H.js.map +1 -0
  402. package/dist/styles/init.css +5 -0
  403. package/dist/styles.css +1 -1
  404. package/dist/tailwind-preset.cjs +4 -4
  405. package/dist/tailwind-preset.js +1 -1
  406. package/dist/transcript-oaWqY3yr.d.cts +81 -0
  407. package/dist/transcript-oaWqY3yr.d.ts +81 -0
  408. package/dist/types-BYnLjEFg.d.cts +248 -0
  409. package/dist/types-BYnLjEFg.d.ts +248 -0
  410. package/dist/types-CBh8Zlje.d.cts +240 -0
  411. package/dist/types-CyCwYBlr.d.ts +240 -0
  412. package/dist/useAnchoredPosition-nXdSFKHf.d.cts +65 -0
  413. package/dist/useAnchoredPosition-nXdSFKHf.d.ts +65 -0
  414. package/dist/useTranscriptEdits-BUylQFg0.d.cts +168 -0
  415. package/dist/useTranscriptEdits-CWYnjbGP.d.ts +168 -0
  416. package/dist/utils/index.cjs +27 -18
  417. package/dist/utils/index.d.cts +1 -0
  418. package/dist/utils/index.d.ts +1 -0
  419. package/dist/utils/index.js +3 -2
  420. package/package.json +180 -55
  421. package/dist/chunk-2DS3RJ2D.js.map +0 -1
  422. package/dist/chunk-74K3RRU7.cjs +0 -4
  423. package/dist/chunk-7BARESXI.cjs.map +0 -1
  424. package/dist/chunk-CV4CVGLO.cjs.map +0 -1
  425. package/dist/chunk-DNPRRYPQ.js.map +0 -1
  426. package/dist/chunk-FRT6RQND.cjs.map +0 -1
  427. package/dist/chunk-HYEJLKXI.js +0 -476
  428. package/dist/chunk-HYEJLKXI.js.map +0 -1
  429. package/dist/chunk-I56VBDSC.js.map +0 -1
  430. package/dist/chunk-IBZXDX4L.cjs.map +0 -1
  431. package/dist/chunk-IKMR2ADM.cjs +0 -25
  432. package/dist/chunk-IKMR2ADM.cjs.map +0 -1
  433. package/dist/chunk-JHPFOG2N.cjs.map +0 -1
  434. package/dist/chunk-K7IGBNZA.cjs.map +0 -1
  435. package/dist/chunk-LEMY57MI.js +0 -3
  436. package/dist/chunk-MPOWJVV4.js.map +0 -1
  437. package/dist/chunk-NINI4WVS.cjs.map +0 -1
  438. package/dist/chunk-OKBR6PX4.js +0 -23
  439. package/dist/chunk-OKBR6PX4.js.map +0 -1
  440. package/dist/chunk-OYKS2JBB.cjs.map +0 -1
  441. package/dist/chunk-QVJBUWJQ.js.map +0 -1
  442. package/dist/chunk-R25H4N4Z.js.map +0 -1
  443. package/dist/chunk-V2DF2GUE.js +0 -3
  444. package/dist/chunk-WFS6R2F5.js.map +0 -1
  445. package/dist/ozwell.cjs +0 -385
  446. package/dist/ozwell.cjs.map +0 -1
  447. package/dist/ozwell.d.cts +0 -15
  448. package/dist/ozwell.d.ts +0 -15
  449. package/dist/ozwell.js +0 -360
  450. package/dist/ozwell.js.map +0 -1
@@ -0,0 +1,552 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+
5
+ // src/hooks/useTranscriptEdits.ts
6
+ var DEFAULT_FILLER_WORDS = [
7
+ "um",
8
+ "uh",
9
+ "umm",
10
+ "uhh",
11
+ "uh-huh",
12
+ "mm-hmm",
13
+ "hmm",
14
+ "hm",
15
+ "er",
16
+ "err",
17
+ "ah",
18
+ "ahh",
19
+ "eh",
20
+ "oh",
21
+ "ooh",
22
+ "like",
23
+ "you know",
24
+ "i mean",
25
+ "so",
26
+ "well",
27
+ "actually",
28
+ "basically",
29
+ "literally",
30
+ "right",
31
+ "okay",
32
+ "ok"
33
+ ];
34
+ var DEFAULT_MIN_SILENCE_MS = 400;
35
+ var DEFAULT_NL_SILENCE_MS = 1500;
36
+ var SILENCE_ANALYSIS_THRESHOLDS = [0.3, 0.4, 0.5, 0.75, 1, 1.5, 2];
37
+ function buildPlaybackSegments(editedWords) {
38
+ const activeWords = editedWords.filter((w) => !w.deleted);
39
+ if (activeWords.length === 0) return [];
40
+ const segments = [];
41
+ let currentSegment = null;
42
+ let lastOriginalIndex = -2;
43
+ let lastWasInserted = false;
44
+ for (let i = 0; i < editedWords.length; i++) {
45
+ const ew = editedWords[i];
46
+ if (ew.deleted) continue;
47
+ const isConsecutive = !ew.inserted && !lastWasInserted && ew.originalIndex === lastOriginalIndex + 1;
48
+ if (currentSegment && isConsecutive) {
49
+ currentSegment.endMs = ew.word.endMs;
50
+ currentSegment.editedIndices.push(i);
51
+ } else {
52
+ if (currentSegment) {
53
+ segments.push(currentSegment);
54
+ }
55
+ currentSegment = {
56
+ startMs: ew.word.startMs,
57
+ endMs: ew.word.endMs,
58
+ editedIndices: [i]
59
+ };
60
+ }
61
+ lastOriginalIndex = ew.originalIndex;
62
+ lastWasInserted = ew.inserted ?? false;
63
+ }
64
+ if (currentSegment) {
65
+ segments.push(currentSegment);
66
+ }
67
+ return segments;
68
+ }
69
+ function getSpeedAtIndex(wordIndex, speedMarkers, defaultSpeed) {
70
+ const sortedMarkers = [...speedMarkers].sort(
71
+ (a, b) => a.wordIndex - b.wordIndex
72
+ );
73
+ let effectiveSpeed = defaultSpeed;
74
+ for (const marker of sortedMarkers) {
75
+ if (marker.wordIndex <= wordIndex) {
76
+ effectiveSpeed = marker.speed;
77
+ } else {
78
+ break;
79
+ }
80
+ }
81
+ return effectiveSpeed;
82
+ }
83
+ function insertSilences(words, minSilenceMs = DEFAULT_MIN_SILENCE_MS, nlSilenceMs = DEFAULT_NL_SILENCE_MS) {
84
+ if (words.length === 0) return [];
85
+ const result = [];
86
+ for (let i = 0; i < words.length; i++) {
87
+ const word = words[i];
88
+ if (i === 0 && word.startMs > minSilenceMs) {
89
+ const isNewline = word.startMs >= nlSilenceMs;
90
+ result.push({
91
+ text: `[${(word.startMs / 1e3).toFixed(1)}s]`,
92
+ startMs: 0,
93
+ endMs: word.startMs,
94
+ wordType: isNewline ? "silence-newline" : "silence"
95
+ });
96
+ }
97
+ result.push({ ...word, wordType: word.wordType ?? "word" });
98
+ if (i < words.length - 1) {
99
+ const nextWord = words[i + 1];
100
+ const gapMs = nextWord.startMs - word.endMs;
101
+ if (gapMs >= minSilenceMs) {
102
+ const isNewline = gapMs >= nlSilenceMs;
103
+ result.push({
104
+ text: `[${(gapMs / 1e3).toFixed(1)}s]`,
105
+ startMs: word.endMs,
106
+ endMs: nextWord.startMs,
107
+ wordType: isNewline ? "silence-newline" : "silence"
108
+ });
109
+ }
110
+ }
111
+ }
112
+ return result;
113
+ }
114
+ function initEditableWords(transcript, minSilenceMs = DEFAULT_MIN_SILENCE_MS, nlSilenceMs = DEFAULT_NL_SILENCE_MS) {
115
+ const wordsWithSilences = insertSilences(
116
+ transcript.words,
117
+ minSilenceMs,
118
+ nlSilenceMs
119
+ );
120
+ return wordsWithSilences.map((word, index) => ({
121
+ originalIndex: index,
122
+ word,
123
+ deleted: false
124
+ }));
125
+ }
126
+ function normalizeWordText(text) {
127
+ return text.toLowerCase().replace(/[.,!?;:]/g, "");
128
+ }
129
+ function useTranscriptEdits(options) {
130
+ const {
131
+ transcript,
132
+ initialEditedWords,
133
+ initialUndoStack,
134
+ onChange,
135
+ minSilenceMs: initialMinSilenceMs = DEFAULT_MIN_SILENCE_MS,
136
+ nlSilenceMs: initialNlSilenceMs = DEFAULT_NL_SILENCE_MS
137
+ } = options;
138
+ const initializedFromSaved = react.useRef(false);
139
+ const [editedWords, setEditedWords] = react.useState(
140
+ () => initialEditedWords || initEditableWords(transcript, initialMinSilenceMs, initialNlSilenceMs)
141
+ );
142
+ const [undoStack, setUndoStack] = react.useState(
143
+ initialUndoStack || []
144
+ );
145
+ const [clipboard, setClipboard] = react.useState(null);
146
+ const [hasEdits, setHasEdits] = react.useState(() => {
147
+ if (initialEditedWords) {
148
+ return initialEditedWords.some((ew) => ew.deleted || ew.inserted);
149
+ }
150
+ return false;
151
+ });
152
+ const [minSilenceMs, setMinSilenceMs] = react.useState(initialMinSilenceMs);
153
+ const [nlSilenceMs, setNlSilenceMs] = react.useState(initialNlSilenceMs);
154
+ const [defaultSpeed, setDefaultSpeed] = react.useState(1);
155
+ const [speedMarkers, setSpeedMarkers] = react.useState([]);
156
+ const pushUndo = react.useCallback(() => {
157
+ setUndoStack((prev) => [...prev, editedWords]);
158
+ }, [editedWords]);
159
+ const prevTranscriptRef = react.useRef(transcript);
160
+ react.useEffect(() => {
161
+ if (prevTranscriptRef.current === transcript) {
162
+ return;
163
+ }
164
+ prevTranscriptRef.current = transcript;
165
+ setEditedWords(initEditableWords(transcript, minSilenceMs, nlSilenceMs));
166
+ setUndoStack([]);
167
+ setHasEdits(false);
168
+ setClipboard(null);
169
+ initializedFromSaved.current = false;
170
+ }, [transcript, minSilenceMs, nlSilenceMs]);
171
+ react.useEffect(() => {
172
+ if (!initializedFromSaved.current && initialEditedWords) {
173
+ initializedFromSaved.current = true;
174
+ return;
175
+ }
176
+ initializedFromSaved.current = true;
177
+ if (hasEdits || undoStack.length > 0) {
178
+ onChange?.(editedWords, undoStack);
179
+ }
180
+ }, [editedWords, undoStack, hasEdits, onChange, initialEditedWords]);
181
+ const undo = react.useCallback(() => {
182
+ if (undoStack.length === 0) return;
183
+ const previousState = undoStack[undoStack.length - 1];
184
+ setUndoStack((prev) => prev.slice(0, -1));
185
+ setEditedWords(previousState);
186
+ const isOriginal = previousState.every((ew, i) => ew.originalIndex === i && !ew.deleted) && previousState.length === transcript.words.length;
187
+ setHasEdits(!isOriginal);
188
+ }, [undoStack, transcript.words.length]);
189
+ const toggleWordDeleted = react.useCallback(
190
+ (index) => {
191
+ const ew = editedWords[index];
192
+ if (!ew) return;
193
+ pushUndo();
194
+ const newEditedWords = [...editedWords];
195
+ newEditedWords[index] = { ...ew, deleted: !ew.deleted };
196
+ setEditedWords(newEditedWords);
197
+ setHasEdits(true);
198
+ },
199
+ [editedWords, pushUndo]
200
+ );
201
+ const deleteRange = react.useCallback(
202
+ (indices) => {
203
+ if (indices.length === 0) return;
204
+ const anchor = editedWords[indices[0]];
205
+ if (!anchor) return;
206
+ pushUndo();
207
+ const targetDeletedState = !anchor.deleted;
208
+ setEditedWords((prev) => {
209
+ const updated = [...prev];
210
+ for (const i of indices) {
211
+ if (updated[i]) {
212
+ updated[i] = { ...updated[i], deleted: targetDeletedState };
213
+ }
214
+ }
215
+ return updated;
216
+ });
217
+ setHasEdits(true);
218
+ },
219
+ [editedWords, pushUndo]
220
+ );
221
+ const cut = react.useCallback(
222
+ (indices) => {
223
+ if (indices.length === 0) return;
224
+ const cutWords = indices.map((i) => editedWords[i]).filter((ew) => ew !== void 0);
225
+ if (cutWords.length === 0) return;
226
+ pushUndo();
227
+ setClipboard({ words: cutWords, operation: "cut" });
228
+ setEditedWords((prev) => {
229
+ const updated = [...prev];
230
+ for (const i of indices) {
231
+ if (updated[i]) {
232
+ updated[i] = { ...updated[i], deleted: true };
233
+ }
234
+ }
235
+ return updated;
236
+ });
237
+ setHasEdits(true);
238
+ },
239
+ [editedWords, pushUndo]
240
+ );
241
+ const copy = react.useCallback(
242
+ (indices) => {
243
+ const selectedWords = indices.map((i) => editedWords[i]).filter((ew) => ew !== void 0 && !ew.deleted);
244
+ if (selectedWords.length === 0) return;
245
+ setClipboard({ words: selectedWords, operation: "copy" });
246
+ },
247
+ [editedWords]
248
+ );
249
+ const paste = react.useCallback(
250
+ (atIndex, before = true) => {
251
+ if (!clipboard) return null;
252
+ pushUndo();
253
+ const insertIndex = before ? atIndex : atIndex + 1;
254
+ setEditedWords((prev) => {
255
+ const updated = [...prev];
256
+ const wordsToInsert = clipboard.words.map((w) => ({
257
+ ...w,
258
+ deleted: false,
259
+ inserted: true
260
+ }));
261
+ updated.splice(insertIndex, 0, ...wordsToInsert);
262
+ return updated;
263
+ });
264
+ setHasEdits(true);
265
+ return insertIndex;
266
+ },
267
+ [clipboard, pushUndo]
268
+ );
269
+ const setWordText = react.useCallback(
270
+ (index, text) => {
271
+ const ew = editedWords[index];
272
+ if (!ew || text === ew.word.text) return;
273
+ pushUndo();
274
+ const newEditedWords = [...editedWords];
275
+ newEditedWords[index] = {
276
+ ...ew,
277
+ word: { ...ew.word, text }
278
+ };
279
+ setEditedWords(newEditedWords);
280
+ setHasEdits(true);
281
+ },
282
+ [editedWords, pushUndo]
283
+ );
284
+ const splitSilence = react.useCallback(
285
+ (index, durationsSec) => {
286
+ const ew = editedWords[index];
287
+ if (!ew || ew.word.wordType !== "silence") return;
288
+ pushUndo();
289
+ const silenceStart = ew.word.startMs;
290
+ const newSilences = [];
291
+ let currentMs = silenceStart;
292
+ for (let i = 0; i < durationsSec.length; i++) {
293
+ const durationMs = Math.round(durationsSec[i] * 1e3);
294
+ const endMs = currentMs + durationMs;
295
+ newSilences.push({
296
+ originalIndex: -1,
297
+ // Negative indicates a split/inserted silence
298
+ word: {
299
+ text: `[${durationsSec[i].toFixed(1)}s]`,
300
+ startMs: currentMs,
301
+ endMs,
302
+ wordType: "silence"
303
+ },
304
+ deleted: false,
305
+ inserted: true
306
+ });
307
+ currentMs = endMs;
308
+ }
309
+ const newEditedWords = [...editedWords];
310
+ newEditedWords.splice(index, 1, ...newSilences);
311
+ setEditedWords(newEditedWords);
312
+ setHasEdits(true);
313
+ },
314
+ [editedWords, pushUndo]
315
+ );
316
+ const deleteWord = react.useCallback(
317
+ (index) => {
318
+ toggleWordDeleted(index);
319
+ },
320
+ [toggleWordDeleted]
321
+ );
322
+ const replaceEditedWords = react.useCallback(
323
+ (next) => {
324
+ pushUndo();
325
+ setEditedWords(next);
326
+ setHasEdits(true);
327
+ },
328
+ [pushUndo]
329
+ );
330
+ const removeFillers = react.useCallback(
331
+ (fillerWords, removeSilenceAboveSec) => {
332
+ const fillerSet = new Set(fillerWords.map((f) => f.toLowerCase()));
333
+ const silenceThresholdMs = removeSilenceAboveSec !== null ? removeSilenceAboveSec * 1e3 : null;
334
+ const newEditedWords = editedWords.map((ew) => {
335
+ if (ew.deleted) return ew;
336
+ if ((ew.word.wordType === "silence" || ew.word.wordType === "silence-newline") && silenceThresholdMs !== null) {
337
+ const durationMs = ew.word.endMs - ew.word.startMs;
338
+ if (durationMs > silenceThresholdMs) {
339
+ return { ...ew, deleted: true };
340
+ }
341
+ return ew;
342
+ }
343
+ if (ew.word.wordType !== "silence" && ew.word.wordType !== "silence-newline") {
344
+ const wordText = normalizeWordText(ew.word.text);
345
+ if (fillerSet.has(wordText)) {
346
+ return { ...ew, deleted: true };
347
+ }
348
+ }
349
+ return ew;
350
+ });
351
+ const deletedCount = newEditedWords.filter(
352
+ (ew, i) => ew.deleted && !editedWords[i].deleted
353
+ ).length;
354
+ if (deletedCount > 0) {
355
+ pushUndo();
356
+ setEditedWords(newEditedWords);
357
+ setHasEdits(true);
358
+ }
359
+ },
360
+ [editedWords, pushUndo]
361
+ );
362
+ const setSilenceThresholds = react.useCallback(
363
+ (newMinSilenceMs, newNlSilenceMs) => {
364
+ setMinSilenceMs(newMinSilenceMs);
365
+ setNlSilenceMs(newNlSilenceMs);
366
+ const wordDeletedStatus = /* @__PURE__ */ new Map();
367
+ editedWords.forEach((ew) => {
368
+ if (ew.word.wordType !== "silence" && ew.word.wordType !== "silence-newline" && ew.originalIndex >= 0) {
369
+ wordDeletedStatus.set(ew.originalIndex, ew.deleted);
370
+ }
371
+ });
372
+ const newEditedWords = initEditableWords(
373
+ transcript,
374
+ newMinSilenceMs,
375
+ newNlSilenceMs
376
+ );
377
+ let wordIdx = 0;
378
+ const restoredWords = newEditedWords.map((ew) => {
379
+ if (ew.word.wordType !== "silence" && ew.word.wordType !== "silence-newline") {
380
+ const wasDeleted = wordDeletedStatus.get(wordIdx) ?? false;
381
+ wordIdx++;
382
+ return { ...ew, deleted: wasDeleted };
383
+ }
384
+ return ew;
385
+ });
386
+ setEditedWords(restoredWords);
387
+ },
388
+ [editedWords, transcript]
389
+ );
390
+ const getSpeedMarkerAtIndex = react.useCallback(
391
+ (wordIndex) => {
392
+ return speedMarkers.find((m) => m.wordIndex === wordIndex);
393
+ },
394
+ [speedMarkers]
395
+ );
396
+ const toggleSpeedMarker = react.useCallback(
397
+ (wordIndex, speed) => {
398
+ setSpeedMarkers((prev) => {
399
+ const existingIdx = prev.findIndex((m) => m.wordIndex === wordIndex);
400
+ if (existingIdx >= 0) {
401
+ if (prev[existingIdx].speed === speed) {
402
+ return prev.filter((_, i) => i !== existingIdx);
403
+ }
404
+ const updated = [...prev];
405
+ updated[existingIdx] = { wordIndex, speed };
406
+ return updated;
407
+ }
408
+ return [...prev, { wordIndex, speed }].sort(
409
+ (a, b) => a.wordIndex - b.wordIndex
410
+ );
411
+ });
412
+ setHasEdits(true);
413
+ },
414
+ []
415
+ );
416
+ const removeSpeedMarker = react.useCallback(
417
+ (wordIndex) => {
418
+ const filtered = speedMarkers.filter((m) => m.wordIndex !== wordIndex);
419
+ if (filtered.length !== speedMarkers.length) {
420
+ setSpeedMarkers(filtered);
421
+ setHasEdits(true);
422
+ }
423
+ },
424
+ [speedMarkers]
425
+ );
426
+ const getSpeedAtIndexBound = react.useCallback(
427
+ (index) => {
428
+ return getSpeedAtIndex(index, speedMarkers, defaultSpeed);
429
+ },
430
+ [speedMarkers, defaultSpeed]
431
+ );
432
+ const playbackSegments = react.useMemo(
433
+ () => buildPlaybackSegments(editedWords),
434
+ [editedWords]
435
+ );
436
+ const stats = react.useMemo(() => {
437
+ const activeWordCount = editedWords.filter(
438
+ (ew) => !ew.deleted && ew.word.wordType !== "silence"
439
+ ).length;
440
+ const activeSilenceCount = editedWords.filter(
441
+ (ew) => !ew.deleted && ew.word.wordType === "silence"
442
+ ).length;
443
+ const deletedWordCount = editedWords.filter(
444
+ (ew) => ew.deleted && ew.word.wordType !== "silence"
445
+ ).length;
446
+ const deletedSilenceCount = editedWords.filter(
447
+ (ew) => ew.deleted && ew.word.wordType === "silence"
448
+ ).length;
449
+ const editedDurationMs = editedWords.reduce((sum, ew, index) => {
450
+ if (ew.deleted) return sum;
451
+ const wordDuration = ew.word.endMs - ew.word.startMs;
452
+ const speed = getSpeedAtIndex(index, speedMarkers, defaultSpeed);
453
+ return sum + wordDuration / speed;
454
+ }, 0);
455
+ return {
456
+ activeWordCount,
457
+ activeSilenceCount,
458
+ deletedWordCount,
459
+ deletedSilenceCount,
460
+ editedDurationMs
461
+ };
462
+ }, [editedWords, speedMarkers, defaultSpeed]);
463
+ const fillerAnalysis = react.useMemo(() => {
464
+ const matchCounts = /* @__PURE__ */ new Map();
465
+ for (const ew of editedWords) {
466
+ if (ew.deleted || ew.word.wordType === "silence") continue;
467
+ const wordText = normalizeWordText(ew.word.text);
468
+ for (const filler of DEFAULT_FILLER_WORDS) {
469
+ if (wordText === filler) {
470
+ matchCounts.set(filler, (matchCounts.get(filler) || 0) + 1);
471
+ }
472
+ }
473
+ matchCounts.set(wordText, (matchCounts.get(wordText) || 0) + 1);
474
+ }
475
+ const durations = /* @__PURE__ */ new Map();
476
+ for (const ew of editedWords) {
477
+ if (ew.deleted || ew.word.wordType === "silence" || ew.word.wordType === "silence-newline")
478
+ continue;
479
+ const wordText = normalizeWordText(ew.word.text);
480
+ const wordDuration = ew.word.endMs - ew.word.startMs;
481
+ for (const filler of DEFAULT_FILLER_WORDS) {
482
+ if (wordText === filler) {
483
+ durations.set(filler, (durations.get(filler) || 0) + wordDuration);
484
+ }
485
+ }
486
+ durations.set(wordText, (durations.get(wordText) || 0) + wordDuration);
487
+ }
488
+ const silenceCounts = SILENCE_ANALYSIS_THRESHOLDS.map((threshold) => {
489
+ const thresholdMs = threshold * 1e3;
490
+ let count = 0;
491
+ let durationMs = 0;
492
+ for (const ew of editedWords) {
493
+ if (ew.deleted) continue;
494
+ if (ew.word.wordType !== "silence" && ew.word.wordType !== "silence-newline")
495
+ continue;
496
+ const silenceDuration = ew.word.endMs - ew.word.startMs;
497
+ if (silenceDuration > thresholdMs) {
498
+ count++;
499
+ durationMs += silenceDuration;
500
+ }
501
+ }
502
+ return { threshold, count, durationMs };
503
+ });
504
+ return {
505
+ matchCounts,
506
+ durations,
507
+ silenceCounts,
508
+ defaultFillerWords: DEFAULT_FILLER_WORDS
509
+ };
510
+ }, [editedWords]);
511
+ return {
512
+ editedWords,
513
+ undoStack,
514
+ clipboard,
515
+ hasEdits,
516
+ pushUndo,
517
+ undo,
518
+ toggleWordDeleted,
519
+ deleteRange,
520
+ cut,
521
+ copy,
522
+ paste,
523
+ setWordText,
524
+ splitSilence,
525
+ deleteWord,
526
+ replaceEditedWords,
527
+ removeFillers,
528
+ setSilenceThresholds,
529
+ silenceThresholds: { minSilenceMs, nlSilenceMs },
530
+ speedMarkers,
531
+ defaultSpeed,
532
+ setDefaultSpeed,
533
+ toggleSpeedMarker,
534
+ removeSpeedMarker,
535
+ getSpeedAtIndex: getSpeedAtIndexBound,
536
+ getSpeedMarkerAtIndex,
537
+ playbackSegments,
538
+ stats,
539
+ fillerAnalysis
540
+ };
541
+ }
542
+
543
+ exports.DEFAULT_FILLER_WORDS = DEFAULT_FILLER_WORDS;
544
+ exports.DEFAULT_MIN_SILENCE_MS = DEFAULT_MIN_SILENCE_MS;
545
+ exports.DEFAULT_NL_SILENCE_MS = DEFAULT_NL_SILENCE_MS;
546
+ exports.buildPlaybackSegments = buildPlaybackSegments;
547
+ exports.getSpeedAtIndex = getSpeedAtIndex;
548
+ exports.initEditableWords = initEditableWords;
549
+ exports.insertSilences = insertSilences;
550
+ exports.useTranscriptEdits = useTranscriptEdits;
551
+ //# sourceMappingURL=chunk-Y4ESBDQ5.cjs.map
552
+ //# sourceMappingURL=chunk-Y4ESBDQ5.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/useTranscriptEdits.ts"],"names":["useRef","useState","useCallback","useEffect","useMemo"],"mappings":"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","file":"chunk-Y4ESBDQ5.cjs","sourcesContent":["/**\n * useTranscriptEdits — headless transcript-editing state hook.\n *\n * Extracted from pulseclip's TranscriptViewer for @mieweb/ui. Holds the\n * edited word timeline (deletions, insertions, text edits, silence\n * splitting), a single-level undo stack, an internal clipboard, speed\n * markers, and derived playback/statistics data.\n *\n * Controlled + persistence-agnostic: no DOM, no fetches. Selection lives in\n * the UI layer — mutations take explicit indices. State flows out via\n * `onChange` and back in via `initialEditedWords` / `initialUndoStack`.\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type {\n Transcript,\n TranscriptWord,\n EditableWord,\n PlaybackSegment,\n PlaybackSpeed,\n SpeedMarker,\n} from '../components/TranscriptView/transcript';\n\n/** Default filler words offered for removal */\nexport const DEFAULT_FILLER_WORDS = [\n 'um',\n 'uh',\n 'umm',\n 'uhh',\n 'uh-huh',\n 'mm-hmm',\n 'hmm',\n 'hm',\n 'er',\n 'err',\n 'ah',\n 'ahh',\n 'eh',\n 'oh',\n 'ooh',\n 'like',\n 'you know',\n 'i mean',\n 'so',\n 'well',\n 'actually',\n 'basically',\n 'literally',\n 'right',\n 'okay',\n 'ok',\n];\n\n/** Default minimum silence duration in milliseconds to detect and insert */\nexport const DEFAULT_MIN_SILENCE_MS = 400;\n\n/** Default threshold for \"newline\" silences - longer pauses that indicate paragraph breaks */\nexport const DEFAULT_NL_SILENCE_MS = 1500;\n\n/** Silence thresholds (seconds) reported by the filler analysis */\nconst SILENCE_ANALYSIS_THRESHOLDS = [0.3, 0.4, 0.5, 0.75, 1.0, 1.5, 2.0];\n\n/** Clipboard data for cut/copy/paste operations */\nexport interface TranscriptClipboard {\n words: EditableWord[];\n operation: 'cut' | 'copy';\n}\n\n/** Count and total duration of silences above a given threshold (seconds) */\nexport interface SilenceThresholdCount {\n threshold: number;\n count: number;\n durationMs: number;\n}\n\n/**\n * Builds playback segments from the edited word list.\n * Consecutive words in original order are merged into single segments.\n * Inserted (pasted) words each get their own segment since they may be duplicates.\n */\nexport function buildPlaybackSegments(\n editedWords: EditableWord[]\n): PlaybackSegment[] {\n const activeWords = editedWords.filter((w) => !w.deleted);\n if (activeWords.length === 0) return [];\n\n const segments: PlaybackSegment[] = [];\n let currentSegment: PlaybackSegment | null = null;\n let lastOriginalIndex = -2; // Use -2 so first word always starts new segment\n let lastWasInserted = false;\n\n for (let i = 0; i < editedWords.length; i++) {\n const ew = editedWords[i];\n if (ew.deleted) continue;\n\n // Check if this word is consecutive to the previous in the original\n // Inserted words always start a new segment (they might be duplicates)\n const isConsecutive =\n !ew.inserted &&\n !lastWasInserted &&\n ew.originalIndex === lastOriginalIndex + 1;\n\n if (currentSegment && isConsecutive) {\n // Extend current segment\n currentSegment.endMs = ew.word.endMs;\n currentSegment.editedIndices.push(i);\n } else {\n // Start new segment\n if (currentSegment) {\n segments.push(currentSegment);\n }\n currentSegment = {\n startMs: ew.word.startMs,\n endMs: ew.word.endMs,\n editedIndices: [i],\n };\n }\n\n lastOriginalIndex = ew.originalIndex;\n lastWasInserted = ew.inserted ?? false;\n }\n\n if (currentSegment) {\n segments.push(currentSegment);\n }\n\n return segments;\n}\n\n/**\n * Get the effective playback speed at a given word index.\n * Finds the most recent speed marker at or before the word index,\n * or returns the default speed if no marker applies.\n */\nexport function getSpeedAtIndex(\n wordIndex: number,\n speedMarkers: SpeedMarker[],\n defaultSpeed: PlaybackSpeed\n): PlaybackSpeed {\n // Sort markers by word index (should already be sorted, but be safe)\n const sortedMarkers = [...speedMarkers].sort(\n (a, b) => a.wordIndex - b.wordIndex\n );\n\n // Find the last marker at or before this word index\n let effectiveSpeed = defaultSpeed;\n for (const marker of sortedMarkers) {\n if (marker.wordIndex <= wordIndex) {\n effectiveSpeed = marker.speed;\n } else {\n break;\n }\n }\n return effectiveSpeed;\n}\n\n/**\n * Detect silences between words and return a new array with silence pseudo-words inserted.\n * Silences are detected as gaps between the endMs of one word and startMs of the next.\n * Silences >= nlSilenceMs are marked as 'silence-newline' for visual line breaks.\n */\nexport function insertSilences(\n words: TranscriptWord[],\n minSilenceMs: number = DEFAULT_MIN_SILENCE_MS,\n nlSilenceMs: number = DEFAULT_NL_SILENCE_MS\n): TranscriptWord[] {\n if (words.length === 0) return [];\n\n const result: TranscriptWord[] = [];\n\n for (let i = 0; i < words.length; i++) {\n const word = words[i];\n\n // Check for silence before the first word (from 0ms)\n if (i === 0 && word.startMs > minSilenceMs) {\n const isNewline = word.startMs >= nlSilenceMs;\n result.push({\n text: `[${(word.startMs / 1000).toFixed(1)}s]`,\n startMs: 0,\n endMs: word.startMs,\n wordType: isNewline ? 'silence-newline' : 'silence',\n });\n }\n\n // Add the word (with default wordType)\n result.push({ ...word, wordType: word.wordType ?? 'word' });\n\n // Check for silence after this word (gap before next word)\n if (i < words.length - 1) {\n const nextWord = words[i + 1];\n const gapMs = nextWord.startMs - word.endMs;\n\n if (gapMs >= minSilenceMs) {\n const isNewline = gapMs >= nlSilenceMs;\n result.push({\n text: `[${(gapMs / 1000).toFixed(1)}s]`,\n startMs: word.endMs,\n endMs: nextWord.startMs,\n wordType: isNewline ? 'silence-newline' : 'silence',\n });\n }\n }\n }\n\n return result;\n}\n\n/** Initialize editable words from transcript, inserting detected silences */\nexport function initEditableWords(\n transcript: Transcript,\n minSilenceMs: number = DEFAULT_MIN_SILENCE_MS,\n nlSilenceMs: number = DEFAULT_NL_SILENCE_MS\n): EditableWord[] {\n // First insert silences between the original words\n const wordsWithSilences = insertSilences(\n transcript.words,\n minSilenceMs,\n nlSilenceMs\n );\n\n return wordsWithSilences.map((word, index) => ({\n originalIndex: index,\n word,\n deleted: false,\n }));\n}\n\n/** Strip trailing punctuation and lowercase for filler-word matching */\nfunction normalizeWordText(text: string): string {\n return text.toLowerCase().replace(/[.,!?;:]/g, '');\n}\n\nexport interface UseTranscriptEditsOptions {\n /** The source transcript (canonical, immutable) */\n transcript: Transcript;\n /** Initial edited words (from saved edits) */\n initialEditedWords?: EditableWord[];\n /** Initial undo stack (from saved edits) */\n initialUndoStack?: EditableWord[][];\n /** Fired after every mutation (for persistence) */\n onChange?: (editedWords: EditableWord[], undoStack: EditableWord[][]) => void;\n /** Minimum silence duration (ms) to detect and insert. Default 400 */\n minSilenceMs?: number;\n /** Threshold (ms) for \"newline\" silences (paragraph breaks). Default 1500 */\n nlSilenceMs?: number;\n}\n\nexport interface TranscriptEditStats {\n /** Non-deleted spoken words (silences excluded) */\n activeWordCount: number;\n /** Non-deleted silence entries */\n activeSilenceCount: number;\n /** Deleted spoken words */\n deletedWordCount: number;\n /** Deleted silence entries */\n deletedSilenceCount: number;\n /** Total duration of non-deleted entries, adjusted for speed markers */\n editedDurationMs: number;\n}\n\nexport interface FillerAnalysis {\n /** Occurrence count per normalized word (fillers and every active word) */\n matchCounts: Map<string, number>;\n /** Total duration (ms) per normalized word */\n durations: Map<string, number>;\n /** Count + total duration of active silences above each analysis threshold */\n silenceCounts: SilenceThresholdCount[];\n /** The built-in filler word list */\n defaultFillerWords: string[];\n}\n\nexport interface UseTranscriptEditsResult {\n // -- State --\n /** The edited timeline (words + silence pseudo-words) */\n editedWords: EditableWord[];\n /** Undo snapshots, oldest first */\n undoStack: EditableWord[][];\n /** Internal clipboard from the last cut/copy, or null */\n clipboard: TranscriptClipboard | null;\n /** Whether any edit has been made (deletions, insertions, text edits, speed markers) */\n hasEdits: boolean;\n\n // -- Undo --\n /** Push the current editedWords onto the undo stack (call before external batch mutations) */\n pushUndo: () => void;\n /** Restore the most recent undo snapshot */\n undo: () => void;\n\n // -- Word mutations (selection-agnostic: pass explicit indices) --\n /** Toggle the deleted flag on a single word */\n toggleWordDeleted: (index: number) => void;\n /**\n * Toggle the deleted state of a contiguous range. The first index acts as\n * the anchor: all words are set to the opposite of the anchor's state\n * (delete all if anchor active, restore all if anchor deleted).\n */\n deleteRange: (indices: number[]) => void;\n /** Cut words at the given indices into the clipboard (marks them deleted) */\n cut: (indices: number[]) => void;\n /** Copy non-deleted words at the given indices into the clipboard */\n copy: (indices: number[]) => void;\n /**\n * Paste the clipboard at a word index. `before` (default true) inserts\n * before the word at `atIndex`; false inserts after it. Returns the index\n * where the first pasted word landed, or null if the clipboard was empty.\n */\n paste: (atIndex: number, before?: boolean) => number | null;\n /** Replace a word's text (no-op if unchanged) */\n setWordText: (index: number, text: string) => void;\n /** Split a silence at the given index into segments with the given durations (seconds) */\n splitSilence: (index: number, durationsSec: number[]) => void;\n /** Toggle the word at the index deleted (editor \"delete\" action) */\n deleteWord: (index: number) => void;\n /** Replace the entire edit state from an external source (e.g. an editable script view); undo-safe */\n replaceEditedWords: (next: EditableWord[]) => void;\n /** Mark matching filler words (and silences above the given seconds threshold) deleted */\n removeFillers: (\n fillerWords: string[],\n removeSilenceAboveSec: number | null\n ) => void;\n\n // -- Silence thresholds --\n /** Rebuild silence detection with new thresholds, preserving word deletions */\n setSilenceThresholds: (minMs: number, nlMs: number) => void;\n /** Current silence detection thresholds (ms) */\n silenceThresholds: { minSilenceMs: number; nlSilenceMs: number };\n\n // -- Playback speed --\n /** Speed markers, sorted by word index */\n speedMarkers: SpeedMarker[];\n /** Default playback speed when no marker applies */\n defaultSpeed: PlaybackSpeed;\n /** Set the default playback speed */\n setDefaultSpeed: (speed: PlaybackSpeed) => void;\n /** Add/update/remove a speed marker (re-selecting the same speed removes it) */\n toggleSpeedMarker: (index: number, speed: PlaybackSpeed) => void;\n /** Remove the speed marker at a word index, if any */\n removeSpeedMarker: (index: number) => void;\n /** Effective playback speed at a word index */\n getSpeedAtIndex: (index: number) => PlaybackSpeed;\n /** The speed marker at a word index, if any */\n getSpeedMarkerAtIndex: (index: number) => SpeedMarker | undefined;\n\n // -- Derived --\n /** Contiguous playback segments built from the edited timeline */\n playbackSegments: PlaybackSegment[];\n /** Word/silence counts and speed-adjusted edited duration */\n stats: TranscriptEditStats;\n /** Filler word and silence analysis for removal UIs */\n fillerAnalysis: FillerAnalysis;\n}\n\n/**\n * Headless transcript-editing hook. See module docs for scope.\n *\n * Undo semantics: each mutation snapshots the full `editedWords` array onto\n * a single-level stack before applying; `undo` pops one snapshot. Speed\n * markers and silence-threshold changes are NOT part of the undo stack\n * (matching the source component).\n */\nexport function useTranscriptEdits(\n options: UseTranscriptEditsOptions\n): UseTranscriptEditsResult {\n const {\n transcript,\n initialEditedWords,\n initialUndoStack,\n onChange,\n minSilenceMs: initialMinSilenceMs = DEFAULT_MIN_SILENCE_MS,\n nlSilenceMs: initialNlSilenceMs = DEFAULT_NL_SILENCE_MS,\n } = options;\n\n // Track if we've initialized from saved state (skip first onChange notification)\n const initializedFromSaved = useRef(false);\n\n const [editedWords, setEditedWords] = useState<EditableWord[]>(\n () =>\n initialEditedWords ||\n initEditableWords(transcript, initialMinSilenceMs, initialNlSilenceMs)\n );\n const [undoStack, setUndoStack] = useState<EditableWord[][]>(\n initialUndoStack || []\n );\n const [clipboard, setClipboard] = useState<TranscriptClipboard | null>(null);\n const [hasEdits, setHasEdits] = useState(() => {\n // Check if initial state has edits\n if (initialEditedWords) {\n return initialEditedWords.some((ew) => ew.deleted || ew.inserted);\n }\n return false;\n });\n const [minSilenceMs, setMinSilenceMs] = useState(initialMinSilenceMs);\n const [nlSilenceMs, setNlSilenceMs] = useState(initialNlSilenceMs);\n const [defaultSpeed, setDefaultSpeed] = useState<PlaybackSpeed>(1);\n const [speedMarkers, setSpeedMarkers] = useState<SpeedMarker[]>([]);\n\n // Helper to save current state to undo stack before making changes\n const pushUndo = useCallback(() => {\n setUndoStack((prev) => [...prev, editedWords]);\n }, [editedWords]);\n\n // Track previous transcript to detect changes\n const prevTranscriptRef = useRef(transcript);\n\n // Reset edited words when transcript changes (but not on initial load with saved edits)\n useEffect(() => {\n if (prevTranscriptRef.current === transcript) {\n return;\n }\n prevTranscriptRef.current = transcript;\n\n // Transcript changed - reset to fresh state (use current silence thresholds)\n setEditedWords(initEditableWords(transcript, minSilenceMs, nlSilenceMs));\n setUndoStack([]);\n setHasEdits(false);\n setClipboard(null);\n initializedFromSaved.current = false;\n }, [transcript, minSilenceMs, nlSilenceMs]);\n\n // Notify consumer when editor state changes (for persistence)\n useEffect(() => {\n // Skip the initial render if we initialized from saved state\n if (!initializedFromSaved.current && initialEditedWords) {\n initializedFromSaved.current = true;\n return;\n }\n initializedFromSaved.current = true;\n\n // Only notify if there are edits to persist\n if (hasEdits || undoStack.length > 0) {\n onChange?.(editedWords, undoStack);\n }\n }, [editedWords, undoStack, hasEdits, onChange, initialEditedWords]);\n\n // Undo last edit\n const undo = useCallback(() => {\n if (undoStack.length === 0) return;\n const previousState = undoStack[undoStack.length - 1];\n setUndoStack((prev) => prev.slice(0, -1));\n setEditedWords(previousState);\n const isOriginal =\n previousState.every((ew, i) => ew.originalIndex === i && !ew.deleted) &&\n previousState.length === transcript.words.length;\n setHasEdits(!isOriginal);\n }, [undoStack, transcript.words.length]);\n\n // Toggle deleted state on a single word\n const toggleWordDeleted = useCallback(\n (index: number) => {\n const ew = editedWords[index];\n if (!ew) return;\n\n pushUndo();\n const newEditedWords = [...editedWords];\n newEditedWords[index] = { ...ew, deleted: !ew.deleted };\n setEditedWords(newEditedWords);\n setHasEdits(true);\n },\n [editedWords, pushUndo]\n );\n\n // Toggle deleted state for a range; the first index acts as the anchor\n const deleteRange = useCallback(\n (indices: number[]) => {\n if (indices.length === 0) return;\n const anchor = editedWords[indices[0]];\n if (!anchor) return;\n\n pushUndo();\n // The anchor word's state determines the action for all words:\n // if anchor is not deleted, delete all; if anchor is deleted, restore all\n const targetDeletedState = !anchor.deleted;\n setEditedWords((prev) => {\n const updated = [...prev];\n for (const i of indices) {\n if (updated[i]) {\n updated[i] = { ...updated[i], deleted: targetDeletedState };\n }\n }\n return updated;\n });\n setHasEdits(true);\n },\n [editedWords, pushUndo]\n );\n\n // Cut words at the given indices into the clipboard\n const cut = useCallback(\n (indices: number[]) => {\n if (indices.length === 0) return;\n const cutWords = indices\n .map((i) => editedWords[i])\n .filter((ew): ew is EditableWord => ew !== undefined);\n if (cutWords.length === 0) return;\n\n pushUndo();\n setClipboard({ words: cutWords, operation: 'cut' });\n setEditedWords((prev) => {\n const updated = [...prev];\n for (const i of indices) {\n if (updated[i]) {\n updated[i] = { ...updated[i], deleted: true };\n }\n }\n return updated;\n });\n setHasEdits(true);\n },\n [editedWords, pushUndo]\n );\n\n // Copy non-deleted words at the given indices into the clipboard (no mutation)\n const copy = useCallback(\n (indices: number[]) => {\n const selectedWords = indices\n .map((i) => editedWords[i])\n .filter((ew): ew is EditableWord => ew !== undefined && !ew.deleted);\n if (selectedWords.length === 0) return;\n setClipboard({ words: selectedWords, operation: 'copy' });\n },\n [editedWords]\n );\n\n // Paste from clipboard at a word index\n const paste = useCallback(\n (atIndex: number, before: boolean = true): number | null => {\n if (!clipboard) return null;\n pushUndo();\n const insertIndex = before ? atIndex : atIndex + 1;\n setEditedWords((prev) => {\n const updated = [...prev];\n const wordsToInsert = clipboard.words.map((w) => ({\n ...w,\n deleted: false,\n inserted: true,\n }));\n updated.splice(insertIndex, 0, ...wordsToInsert);\n return updated;\n });\n setHasEdits(true);\n return insertIndex;\n },\n [clipboard, pushUndo]\n );\n\n // Replace a word's text\n const setWordText = useCallback(\n (index: number, text: string) => {\n const ew = editedWords[index];\n if (!ew || text === ew.word.text) return;\n\n pushUndo();\n const newEditedWords = [...editedWords];\n newEditedWords[index] = {\n ...ew,\n word: { ...ew.word, text },\n };\n setEditedWords(newEditedWords);\n setHasEdits(true);\n },\n [editedWords, pushUndo]\n );\n\n // Split a silence into multiple segments\n const splitSilence = useCallback(\n (index: number, durationsSec: number[]) => {\n const ew = editedWords[index];\n if (!ew || ew.word.wordType !== 'silence') return;\n\n pushUndo();\n\n // Create new silence segments from the durations\n const silenceStart = ew.word.startMs;\n const newSilences: EditableWord[] = [];\n let currentMs = silenceStart;\n\n for (let i = 0; i < durationsSec.length; i++) {\n const durationMs = Math.round(durationsSec[i] * 1000);\n const endMs = currentMs + durationMs;\n\n newSilences.push({\n originalIndex: -1, // Negative indicates a split/inserted silence\n word: {\n text: `[${durationsSec[i].toFixed(1)}s]`,\n startMs: currentMs,\n endMs: endMs,\n wordType: 'silence',\n },\n deleted: false,\n inserted: true,\n });\n\n currentMs = endMs;\n }\n\n // Replace the original silence with the new segments\n const newEditedWords = [...editedWords];\n newEditedWords.splice(index, 1, ...newSilences);\n setEditedWords(newEditedWords);\n setHasEdits(true);\n },\n [editedWords, pushUndo]\n );\n\n // Editor \"delete\" action: toggles the word's deleted state\n const deleteWord = useCallback(\n (index: number) => {\n toggleWordDeleted(index);\n },\n [toggleWordDeleted]\n );\n\n // Replace the entire edit state from an external source (e.g. the editable\n // script/YAML panel). Undo-safe: the previous state is pushed first.\n const replaceEditedWords = useCallback(\n (next: EditableWord[]) => {\n pushUndo();\n setEditedWords(next);\n setHasEdits(true);\n },\n [pushUndo]\n );\n\n // Mark filler words (and optionally silences above threshold) deleted\n const removeFillers = useCallback(\n (fillerWords: string[], removeSilenceAboveSec: number | null) => {\n const fillerSet = new Set(fillerWords.map((f) => f.toLowerCase()));\n const silenceThresholdMs =\n removeSilenceAboveSec !== null ? removeSilenceAboveSec * 1000 : null;\n\n const newEditedWords = editedWords.map((ew) => {\n if (ew.deleted) return ew;\n\n // Check if it's a silence that should be removed\n if (\n (ew.word.wordType === 'silence' ||\n ew.word.wordType === 'silence-newline') &&\n silenceThresholdMs !== null\n ) {\n const durationMs = ew.word.endMs - ew.word.startMs;\n if (durationMs > silenceThresholdMs) {\n return { ...ew, deleted: true };\n }\n return ew;\n }\n\n // Check if it's a filler word that should be removed\n if (\n ew.word.wordType !== 'silence' &&\n ew.word.wordType !== 'silence-newline'\n ) {\n const wordText = normalizeWordText(ew.word.text);\n if (fillerSet.has(wordText)) {\n return { ...ew, deleted: true };\n }\n }\n\n return ew;\n });\n\n const deletedCount = newEditedWords.filter(\n (ew, i) => ew.deleted && !editedWords[i].deleted\n ).length;\n\n if (deletedCount > 0) {\n pushUndo();\n setEditedWords(newEditedWords);\n setHasEdits(true);\n }\n },\n [editedWords, pushUndo]\n );\n\n // Apply new silence thresholds, rebuilding silences while preserving word deletions\n const setSilenceThresholds = useCallback(\n (newMinSilenceMs: number, newNlSilenceMs: number) => {\n setMinSilenceMs(newMinSilenceMs);\n setNlSilenceMs(newNlSilenceMs);\n // Rebuild the editable words with new silence detection\n // Preserve deleted status for actual words (not silences)\n const wordDeletedStatus = new Map<number, boolean>();\n editedWords.forEach((ew) => {\n if (\n ew.word.wordType !== 'silence' &&\n ew.word.wordType !== 'silence-newline' &&\n ew.originalIndex >= 0\n ) {\n // Map original word index to deleted status\n wordDeletedStatus.set(ew.originalIndex, ew.deleted);\n }\n });\n\n // Re-initialize with new thresholds\n const newEditedWords = initEditableWords(\n transcript,\n newMinSilenceMs,\n newNlSilenceMs\n );\n\n // Restore deleted status for words\n let wordIdx = 0;\n const restoredWords = newEditedWords.map((ew) => {\n if (\n ew.word.wordType !== 'silence' &&\n ew.word.wordType !== 'silence-newline'\n ) {\n const wasDeleted = wordDeletedStatus.get(wordIdx) ?? false;\n wordIdx++;\n return { ...ew, deleted: wasDeleted };\n }\n return ew;\n });\n\n setEditedWords(restoredWords);\n },\n [editedWords, transcript]\n );\n\n // Get the speed marker at a specific word index (if any)\n const getSpeedMarkerAtIndex = useCallback(\n (wordIndex: number): SpeedMarker | undefined => {\n return speedMarkers.find((m) => m.wordIndex === wordIndex);\n },\n [speedMarkers]\n );\n\n // Toggle a speed marker at a word index\n const toggleSpeedMarker = useCallback(\n (wordIndex: number, speed: PlaybackSpeed) => {\n setSpeedMarkers((prev) => {\n const existingIdx = prev.findIndex((m) => m.wordIndex === wordIndex);\n if (existingIdx >= 0) {\n // If marker exists with same speed, remove it\n if (prev[existingIdx].speed === speed) {\n return prev.filter((_, i) => i !== existingIdx);\n }\n // If marker exists with different speed, update it\n const updated = [...prev];\n updated[existingIdx] = { wordIndex, speed };\n return updated;\n }\n // Add new marker\n return [...prev, { wordIndex, speed }].sort(\n (a, b) => a.wordIndex - b.wordIndex\n );\n });\n setHasEdits(true);\n },\n []\n );\n\n // Remove a speed marker at a word index\n const removeSpeedMarker = useCallback(\n (wordIndex: number) => {\n const filtered = speedMarkers.filter((m) => m.wordIndex !== wordIndex);\n if (filtered.length !== speedMarkers.length) {\n setSpeedMarkers(filtered);\n setHasEdits(true);\n }\n },\n [speedMarkers]\n );\n\n // Effective playback speed at a word index (bound to current markers + default)\n const getSpeedAtIndexBound = useCallback(\n (index: number): PlaybackSpeed => {\n return getSpeedAtIndex(index, speedMarkers, defaultSpeed);\n },\n [speedMarkers, defaultSpeed]\n );\n\n // -- Derived data --\n\n const playbackSegments = useMemo(\n () => buildPlaybackSegments(editedWords),\n [editedWords]\n );\n\n const stats = useMemo<TranscriptEditStats>(() => {\n // Count active (non-deleted) words (excluding silences for word count)\n const activeWordCount = editedWords.filter(\n (ew) => !ew.deleted && ew.word.wordType !== 'silence'\n ).length;\n const activeSilenceCount = editedWords.filter(\n (ew) => !ew.deleted && ew.word.wordType === 'silence'\n ).length;\n const deletedWordCount = editedWords.filter(\n (ew) => ew.deleted && ew.word.wordType !== 'silence'\n ).length;\n const deletedSilenceCount = editedWords.filter(\n (ew) => ew.deleted && ew.word.wordType === 'silence'\n ).length;\n\n // Calculate edited duration (sum of non-deleted words/silences, adjusted for speed markers)\n const editedDurationMs = editedWords.reduce((sum, ew, index) => {\n if (ew.deleted) return sum;\n const wordDuration = ew.word.endMs - ew.word.startMs;\n const speed = getSpeedAtIndex(index, speedMarkers, defaultSpeed);\n // Duration is divided by speed (2x speed = half the time)\n return sum + wordDuration / speed;\n }, 0);\n\n return {\n activeWordCount,\n activeSilenceCount,\n deletedWordCount,\n deletedSilenceCount,\n editedDurationMs,\n };\n }, [editedWords, speedMarkers, defaultSpeed]);\n\n const fillerAnalysis = useMemo<FillerAnalysis>(() => {\n // Occurrence counts per filler (and every active word, for custom fillers)\n const matchCounts = new Map<string, number>();\n for (const ew of editedWords) {\n if (ew.deleted || ew.word.wordType === 'silence') continue;\n const wordText = normalizeWordText(ew.word.text);\n for (const filler of DEFAULT_FILLER_WORDS) {\n if (wordText === filler) {\n matchCounts.set(filler, (matchCounts.get(filler) || 0) + 1);\n }\n }\n // Also check for the exact word in case it's a custom filler\n matchCounts.set(wordText, (matchCounts.get(wordText) || 0) + 1);\n }\n\n // Total duration per filler word type\n const durations = new Map<string, number>();\n for (const ew of editedWords) {\n if (\n ew.deleted ||\n ew.word.wordType === 'silence' ||\n ew.word.wordType === 'silence-newline'\n )\n continue;\n const wordText = normalizeWordText(ew.word.text);\n const wordDuration = ew.word.endMs - ew.word.startMs;\n for (const filler of DEFAULT_FILLER_WORDS) {\n if (wordText === filler) {\n durations.set(filler, (durations.get(filler) || 0) + wordDuration);\n }\n }\n // Also track duration for the exact word in case it's a custom filler\n durations.set(wordText, (durations.get(wordText) || 0) + wordDuration);\n }\n\n // Silence counts and durations at various thresholds\n const silenceCounts = SILENCE_ANALYSIS_THRESHOLDS.map((threshold) => {\n const thresholdMs = threshold * 1000;\n let count = 0;\n let durationMs = 0;\n for (const ew of editedWords) {\n if (ew.deleted) continue;\n if (\n ew.word.wordType !== 'silence' &&\n ew.word.wordType !== 'silence-newline'\n )\n continue;\n const silenceDuration = ew.word.endMs - ew.word.startMs;\n if (silenceDuration > thresholdMs) {\n count++;\n durationMs += silenceDuration;\n }\n }\n return { threshold, count, durationMs };\n });\n\n return {\n matchCounts,\n durations,\n silenceCounts,\n defaultFillerWords: DEFAULT_FILLER_WORDS,\n };\n }, [editedWords]);\n\n return {\n editedWords,\n undoStack,\n clipboard,\n hasEdits,\n pushUndo,\n undo,\n toggleWordDeleted,\n deleteRange,\n cut,\n copy,\n paste,\n setWordText,\n splitSilence,\n deleteWord,\n replaceEditedWords,\n removeFillers,\n setSilenceThresholds,\n silenceThresholds: { minSilenceMs, nlSilenceMs },\n speedMarkers,\n defaultSpeed,\n setDefaultSpeed,\n toggleSpeedMarker,\n removeSpeedMarker,\n getSpeedAtIndex: getSpeedAtIndexBound,\n getSpeedMarkerAtIndex,\n playbackSegments,\n stats,\n fillerAnalysis,\n };\n}\n"]}