quoin-editor 1.7.0 → 1.8.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 (257) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +25 -10
  3. package/dist/ai-labels-BXX32H9a.d.cts +53 -0
  4. package/dist/ai-labels-BXX32H9a.d.ts +53 -0
  5. package/dist/ai.cjs +84 -86
  6. package/dist/ai.d.cts +4 -53
  7. package/dist/ai.d.ts +4 -53
  8. package/dist/ai.js +8 -10
  9. package/dist/ai.js.map +1 -1
  10. package/dist/catalogue.cjs +27 -26
  11. package/dist/catalogue.d.cts +10 -1
  12. package/dist/catalogue.d.ts +10 -1
  13. package/dist/catalogue.js +18 -17
  14. package/dist/catalogue.js.map +1 -1
  15. package/dist/{chunk-QR2NIDUK.cjs → chunk-4MCYQHFL.cjs} +2 -2
  16. package/dist/{chunk-YEFPH27F.cjs → chunk-53CNYRVQ.cjs} +92 -94
  17. package/dist/{chunk-FNJOPGMN.cjs → chunk-53FJFYBR.cjs} +115 -117
  18. package/dist/{chunk-OJ4O6HWC.js → chunk-5OZX2LV4.js} +10 -12
  19. package/dist/chunk-5OZX2LV4.js.map +1 -0
  20. package/dist/{chunk-OS2O6IKX.js → chunk-5RBMZGKV.js} +4 -4
  21. package/dist/{chunk-OS2O6IKX.js.map → chunk-5RBMZGKV.js.map} +1 -1
  22. package/dist/{chunk-4T4TJ2QS.cjs → chunk-7E43TURF.cjs} +7 -7
  23. package/dist/{chunk-I722LBD3.cjs → chunk-7X3T7VXV.cjs} +32 -34
  24. package/dist/{chunk-BJTOLCRR.js → chunk-7Y4B7NHO.js} +2 -2
  25. package/dist/chunk-7Y4B7NHO.js.map +1 -0
  26. package/dist/{chunk-MZXW2HXP.cjs → chunk-B3OBM6I4.cjs} +25 -27
  27. package/dist/{chunk-6T5YGCZ5.cjs → chunk-BA5WXBGE.cjs} +290 -293
  28. package/dist/{chunk-CSKKSXCE.js → chunk-CSPCDVCT.js} +3 -3
  29. package/dist/{chunk-CSKKSXCE.js.map → chunk-CSPCDVCT.js.map} +1 -1
  30. package/dist/{chunk-R7MYG522.js → chunk-D4L5FINK.js} +3 -3
  31. package/dist/{chunk-R7MYG522.js.map → chunk-D4L5FINK.js.map} +1 -1
  32. package/dist/{chunk-GHNQKJMD.js → chunk-DBOZFNWU.js} +3 -3
  33. package/dist/{chunk-GHNQKJMD.js.map → chunk-DBOZFNWU.js.map} +1 -1
  34. package/dist/{chunk-BM2GRZNM.cjs → chunk-DJU25PFC.cjs} +11 -11
  35. package/dist/chunk-DLQCTGC3.js +10 -0
  36. package/dist/chunk-DLQCTGC3.js.map +1 -0
  37. package/dist/{chunk-7GU25Z6L.js → chunk-DXUDBPG5.js} +9 -11
  38. package/dist/chunk-DXUDBPG5.js.map +1 -0
  39. package/dist/{chunk-KDK2ATMB.cjs → chunk-EZWJRBQT.cjs} +51 -53
  40. package/dist/{chunk-PWUFFHAQ.js → chunk-EZX37R5D.js} +8 -10
  41. package/dist/chunk-EZX37R5D.js.map +1 -0
  42. package/dist/{chunk-VZZPCYA3.cjs → chunk-FK7EBZPX.cjs} +226 -226
  43. package/dist/{chunk-BXSWZEA5.js → chunk-GPTJYWGS.js} +3 -3
  44. package/dist/{chunk-BXSWZEA5.js.map → chunk-GPTJYWGS.js.map} +1 -1
  45. package/dist/{chunk-H3C3MJW5.cjs → chunk-LM3DHROX.cjs} +2 -2
  46. package/dist/{chunk-AD3NY7NA.js → chunk-LSI4RMTY.js} +3 -3
  47. package/dist/{chunk-AD3NY7NA.js.map → chunk-LSI4RMTY.js.map} +1 -1
  48. package/dist/{chunk-LKZJ2TDU.cjs → chunk-MMS4D4MK.cjs} +10 -13
  49. package/dist/{chunk-PLUMKN4B.js → chunk-MRHDPXQ7.js} +8 -10
  50. package/dist/chunk-MRHDPXQ7.js.map +1 -0
  51. package/dist/{chunk-XFEDJEJF.js → chunk-MSWA5ZZM.js} +4 -4
  52. package/dist/{chunk-XFEDJEJF.js.map → chunk-MSWA5ZZM.js.map} +1 -1
  53. package/dist/{chunk-RQXJKJ5S.js → chunk-NCDMQCZR.js} +7 -10
  54. package/dist/chunk-NCDMQCZR.js.map +1 -0
  55. package/dist/{chunk-QJ34MOFV.cjs → chunk-P6HC7RUN.cjs} +120 -120
  56. package/dist/{chunk-UZS3TW75.js → chunk-PHQMHXNG.js} +9 -11
  57. package/dist/chunk-PHQMHXNG.js.map +1 -0
  58. package/dist/{chunk-WN7BZDNH.cjs → chunk-PURYVDC3.cjs} +13 -13
  59. package/dist/{chunk-JY5VUHKN.cjs → chunk-PVFSJXYQ.cjs} +28 -30
  60. package/dist/{chunk-QOIVLYMW.js → chunk-SP4F5F6V.js} +5 -5
  61. package/dist/{chunk-QOIVLYMW.js.map → chunk-SP4F5F6V.js.map} +1 -1
  62. package/dist/{chunk-BF7733XS.cjs → chunk-TQHFGSTT.cjs} +57 -59
  63. package/dist/{chunk-G25FMFEZ.js → chunk-UDBLZILE.js} +4 -4
  64. package/dist/{chunk-G25FMFEZ.js.map → chunk-UDBLZILE.js.map} +1 -1
  65. package/dist/{chunk-5JEQZIRI.js → chunk-VM567D6O.js} +8 -10
  66. package/dist/chunk-VM567D6O.js.map +1 -0
  67. package/dist/{chunk-MNA7MBCH.js → chunk-VWYPO6SX.js} +8 -10
  68. package/dist/chunk-VWYPO6SX.js.map +1 -0
  69. package/dist/{chunk-IJKGF6SC.js → chunk-WOGCD7JN.js} +8 -10
  70. package/dist/chunk-WOGCD7JN.js.map +1 -0
  71. package/dist/{chunk-AZF7B2AE.cjs → chunk-XKYNINUX.cjs} +11 -11
  72. package/dist/{chunk-HSLOPJVT.js → chunk-Y4EJ6UBV.js} +7 -10
  73. package/dist/chunk-Y4EJ6UBV.js.map +1 -0
  74. package/dist/{chunk-XZ4NC3Q7.cjs → chunk-YEDPBYO5.cjs} +4 -4
  75. package/dist/{chunk-STKGRXSA.cjs → chunk-YVPZDTVZ.cjs} +15 -17
  76. package/dist/chunk-ZWTIRGY5.cjs +10 -0
  77. package/dist/comments-labels-DWqsSz8G.d.cts +30 -0
  78. package/dist/comments-labels-DWqsSz8G.d.ts +30 -0
  79. package/dist/comments.cjs +63 -65
  80. package/dist/comments.d.cts +4 -30
  81. package/dist/comments.d.ts +4 -30
  82. package/dist/comments.js +9 -11
  83. package/dist/comments.js.map +1 -1
  84. package/dist/{diagram-extension-Da7hp-tv.d.ts → diagram-extension-22x_njXB.d.ts} +4 -12
  85. package/dist/{diagram-extension-D573COJv.d.cts → diagram-extension-C_FSkoj9.d.cts} +4 -12
  86. package/dist/diagram-labels-Bi_jEZ8_.d.cts +11 -0
  87. package/dist/diagram-labels-Bi_jEZ8_.d.ts +11 -0
  88. package/dist/diagram.cjs +10 -9
  89. package/dist/diagram.d.cts +3 -2
  90. package/dist/diagram.d.ts +3 -2
  91. package/dist/diagram.js +7 -6
  92. package/dist/diff-labels-DWwg7N0n.d.cts +27 -0
  93. package/dist/diff-labels-DWwg7N0n.d.ts +27 -0
  94. package/dist/diff.cjs +27 -29
  95. package/dist/diff.d.cts +4 -27
  96. package/dist/diff.d.ts +4 -27
  97. package/dist/diff.js +5 -7
  98. package/dist/diff.js.map +1 -1
  99. package/dist/docx.cjs +30 -30
  100. package/dist/docx.js +3 -3
  101. package/dist/embed-labels-BiVa9HYl.d.cts +23 -0
  102. package/dist/embed-labels-BiVa9HYl.d.ts +23 -0
  103. package/dist/embed.cjs +10 -9
  104. package/dist/embed.d.cts +4 -23
  105. package/dist/embed.d.ts +4 -23
  106. package/dist/embed.js +8 -7
  107. package/dist/emoji-extension-C832oGeT.d.ts +16 -0
  108. package/dist/emoji-extension-Dk5IQLZf.d.cts +16 -0
  109. package/dist/{emoji-extension-gRKnbW8G.d.ts → emoji-labels-YwOms7ZJ.d.cts} +2 -15
  110. package/dist/{emoji-extension-C3o_Y5uC.d.cts → emoji-labels-YwOms7ZJ.d.ts} +2 -15
  111. package/dist/emoji.cjs +10 -9
  112. package/dist/emoji.d.cts +4 -3
  113. package/dist/emoji.d.ts +4 -3
  114. package/dist/emoji.js +7 -6
  115. package/dist/engine.cjs +58 -58
  116. package/dist/engine.d.cts +8 -7
  117. package/dist/engine.d.ts +8 -7
  118. package/dist/engine.js +3 -3
  119. package/dist/find-extension-BfruAPLi.d.cts +15 -0
  120. package/dist/find-extension-DmK9_N81.d.ts +15 -0
  121. package/dist/{find-extension-B_Ctrs6T.d.ts → find-labels-C4vs8Qe9.d.cts} +2 -14
  122. package/dist/{find-extension-BGGsJMu4.d.cts → find-labels-C4vs8Qe9.d.ts} +2 -14
  123. package/dist/find.cjs +11 -10
  124. package/dist/find.d.cts +2 -1
  125. package/dist/find.d.ts +2 -1
  126. package/dist/find.js +7 -6
  127. package/dist/headless.cjs +74 -74
  128. package/dist/headless.d.cts +38 -8
  129. package/dist/headless.d.ts +38 -8
  130. package/dist/headless.js +4 -4
  131. package/dist/highlight.cjs +7 -6
  132. package/dist/highlight.js +6 -5
  133. package/dist/highlight.js.map +1 -1
  134. package/dist/{index-CsG7OQoq.d.cts → index-3fYvwqUZ.d.cts} +1 -1
  135. package/dist/{index-h1llgZM-.d.ts → index-CgxnzCO5.d.ts} +1 -1
  136. package/dist/index.cjs +87 -86
  137. package/dist/index.d.cts +18 -185
  138. package/dist/index.d.ts +18 -185
  139. package/dist/index.js +6 -5
  140. package/dist/labels-BhRJYo46.d.ts +176 -0
  141. package/dist/labels-CiCxj2jP.d.cts +176 -0
  142. package/dist/locale-ar.cjs +565 -0
  143. package/dist/locale-ar.d.cts +63 -0
  144. package/dist/locale-ar.d.ts +63 -0
  145. package/dist/locale-ar.js +551 -0
  146. package/dist/locale-ar.js.map +1 -0
  147. package/dist/locale-de.cjs +510 -0
  148. package/dist/locale-de.d.cts +63 -0
  149. package/dist/locale-de.d.ts +63 -0
  150. package/dist/locale-de.js +496 -0
  151. package/dist/locale-de.js.map +1 -0
  152. package/dist/locale-es.cjs +510 -0
  153. package/dist/locale-es.d.cts +63 -0
  154. package/dist/locale-es.d.ts +63 -0
  155. package/dist/locale-es.js +496 -0
  156. package/dist/locale-es.js.map +1 -0
  157. package/dist/locale-fr.cjs +510 -0
  158. package/dist/locale-fr.d.cts +63 -0
  159. package/dist/locale-fr.d.ts +63 -0
  160. package/dist/locale-fr.js +496 -0
  161. package/dist/locale-fr.js.map +1 -0
  162. package/dist/locale-ja.cjs +505 -0
  163. package/dist/locale-ja.d.cts +63 -0
  164. package/dist/locale-ja.d.ts +63 -0
  165. package/dist/locale-ja.js +491 -0
  166. package/dist/locale-ja.js.map +1 -0
  167. package/dist/locale-pt.cjs +511 -0
  168. package/dist/locale-pt.d.cts +63 -0
  169. package/dist/locale-pt.d.ts +63 -0
  170. package/dist/locale-pt.js +497 -0
  171. package/dist/locale-pt.js.map +1 -0
  172. package/dist/locale-zh.cjs +505 -0
  173. package/dist/locale-zh.d.cts +63 -0
  174. package/dist/locale-zh.d.ts +63 -0
  175. package/dist/locale-zh.js +491 -0
  176. package/dist/locale-zh.js.map +1 -0
  177. package/dist/math-labels-DOf5KFxp.d.cts +24 -0
  178. package/dist/math-labels-DOf5KFxp.d.ts +24 -0
  179. package/dist/math.cjs +11 -10
  180. package/dist/math.d.cts +4 -24
  181. package/dist/math.d.ts +4 -24
  182. package/dist/math.js +9 -8
  183. package/dist/mention-labels-DIQy8LRT.d.cts +15 -0
  184. package/dist/mention-labels-DIQy8LRT.d.ts +15 -0
  185. package/dist/mention.cjs +11 -10
  186. package/dist/mention.d.cts +4 -15
  187. package/dist/mention.d.ts +4 -15
  188. package/dist/mention.js +9 -8
  189. package/dist/{view-controller-p-2x3J7Y.d.ts → node-reveal-BnFcnHhz.d.ts} +14 -14
  190. package/dist/{view-controller-BfVSNZYZ.d.cts → node-reveal-DY6bimv3.d.cts} +14 -14
  191. package/dist/palette-BrX-hEd-.d.cts +4 -0
  192. package/dist/palette-BrX-hEd-.d.ts +4 -0
  193. package/dist/presence-labels-DXVJLifl.d.cts +15 -0
  194. package/dist/presence-labels-DXVJLifl.d.ts +15 -0
  195. package/dist/presence.cjs +40 -42
  196. package/dist/presence.d.cts +4 -15
  197. package/dist/presence.d.ts +4 -15
  198. package/dist/presence.js +9 -11
  199. package/dist/presence.js.map +1 -1
  200. package/dist/source-labels-CeQDXsYZ.d.cts +9 -0
  201. package/dist/source-labels-CeQDXsYZ.d.ts +9 -0
  202. package/dist/source.cjs +11 -14
  203. package/dist/source.d.cts +5 -10
  204. package/dist/source.d.ts +5 -10
  205. package/dist/source.js +7 -10
  206. package/dist/source.js.map +1 -1
  207. package/dist/{stats-extension-oD6D1cYy.d.ts → stats-extension-D5HySF0a.d.cts} +4 -16
  208. package/dist/{stats-extension-XqOTvRaq.d.cts → stats-extension-Dmw0Ujxe.d.ts} +4 -16
  209. package/dist/stats-labels-W8gep51s.d.cts +15 -0
  210. package/dist/stats-labels-W8gep51s.d.ts +15 -0
  211. package/dist/stats.cjs +11 -10
  212. package/dist/stats.d.cts +3 -2
  213. package/dist/stats.d.ts +3 -2
  214. package/dist/stats.js +8 -7
  215. package/dist/table-extension-7LkLNK6Q.d.cts +15 -0
  216. package/dist/table-extension-DEpV0DnP.d.ts +15 -0
  217. package/dist/{table-extension-v6VB59bF.d.ts → table-labels-Dc-MzU3j.d.cts} +2 -14
  218. package/dist/{table-extension-BP47hHoz.d.cts → table-labels-Dc-MzU3j.d.ts} +2 -14
  219. package/dist/table.cjs +26 -25
  220. package/dist/table.d.cts +2 -1
  221. package/dist/table.d.ts +2 -1
  222. package/dist/table.js +7 -6
  223. package/dist/{to-html-1oD-CDle.d.cts → to-html-C19PbIzh.d.cts} +1 -0
  224. package/dist/{to-html-CLRBCX0I.d.ts → to-html-CogwrQer.d.ts} +1 -0
  225. package/dist/{to-markdown-CFPMbbiX.d.cts → to-markdown-C2w7Dzg8.d.cts} +1 -0
  226. package/dist/{to-markdown-BVOzhgVM.d.ts → to-markdown-DC1EUye5.d.ts} +1 -0
  227. package/dist/toc-extension-Dvh37bWQ.d.cts +15 -0
  228. package/dist/toc-extension-SVckrFmb.d.ts +15 -0
  229. package/dist/toc-labels-B2apPfXG.d.cts +14 -0
  230. package/dist/toc-labels-B2apPfXG.d.ts +14 -0
  231. package/dist/toc.cjs +10 -9
  232. package/dist/toc.d.cts +3 -2
  233. package/dist/toc.d.ts +3 -2
  234. package/dist/toc.js +7 -6
  235. package/dist/{types-DXF9p4qV.d.cts → types-Bzhq5AXj.d.cts} +1 -0
  236. package/dist/{types-BjLcmV00.d.ts → types-DMuuDBVh.d.ts} +1 -0
  237. package/dist/view.cjs +4 -4
  238. package/dist/view.js +2 -2
  239. package/dist/yjs.cjs +121 -121
  240. package/dist/yjs.d.cts +2 -2
  241. package/dist/yjs.d.ts +2 -2
  242. package/dist/yjs.js +5 -5
  243. package/package.json +71 -1
  244. package/dist/chunk-5JEQZIRI.js.map +0 -1
  245. package/dist/chunk-7GU25Z6L.js.map +0 -1
  246. package/dist/chunk-BJTOLCRR.js.map +0 -1
  247. package/dist/chunk-HSLOPJVT.js.map +0 -1
  248. package/dist/chunk-IJKGF6SC.js.map +0 -1
  249. package/dist/chunk-MNA7MBCH.js.map +0 -1
  250. package/dist/chunk-OJ4O6HWC.js.map +0 -1
  251. package/dist/chunk-PLUMKN4B.js.map +0 -1
  252. package/dist/chunk-PWUFFHAQ.js.map +0 -1
  253. package/dist/chunk-RQXJKJ5S.js.map +0 -1
  254. package/dist/chunk-UZS3TW75.js.map +0 -1
  255. package/dist/toc-extension-CI6jNOYl.d.cts +0 -26
  256. package/dist/toc-extension-CTHWjNJC.d.ts +0 -26
  257. /package/dist/{chunk-WZA5S5EJ.cjs → chunk-I7X7B3WD.cjs} +0 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  Every release of `quoin-editor` is listed here, newest first, in the spirit of [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). From 1.0.0 the package follows [semantic versioning](https://semver.org/): a breaking change waits for the next major version. Until then, Quoin was 0.x and a minor version could contain breaking changes; those known at release were marked "Breaking". The 0.x sections are kept as they were released, "Docs" headings included. Moving from 0.4 to 1.0 is covered step by step in [MIGRATION.md](MIGRATION.md). Review finding ids such as F-17 are those of the 0.4.0 review in [#73](https://github.com/AmineSeboui/quoin-editor/issues/73).
4
4
 
5
+ ## 1.8.0 (2026-11-05)
6
+
7
+ ### Added
8
+ - Seven locale packs, one entry each, carrying every string the editor shows or speaks in that language: `quoin-editor/locale-ar` (Arabic), `quoin-editor/locale-de` (German), `quoin-editor/locale-es` (Spanish), `quoin-editor/locale-fr` (French), `quoin-editor/locale-ja` (Japanese), `quoin-editor/locale-pt` (Portuguese) and `quoin-editor/locale-zh` (Simplified Chinese). A pack covers the canvas and all fourteen feature label sets, so `frLabels` goes to the canvas and `frTableLabels` to the table, and it is data alone: no React, no side effects, about 5.5 KB gzip each, downloaded only by a host that imports one. A label that carries a count, a date, a number or a file size is written as its language writes it, through `Intl`, so Arabic gets its six plural forms and Japanese its one. Nothing is detected: the host picks the language, and a key a pack leaves out still falls back to English.
9
+ - `quoin-editor/headless` exports the types its own signatures already named, so a host can write them down rather than reach them through `Parameters` and `ReturnType`: `Announcer` with `Spoken`, `Engagement`, `FocusTarget`, `HostRegistry` with `HostEntry`, and `NodeReveal`, all reached through `ViewController`; `CaretPoint`, which `caretFromPoint` returns; and `BlockShellProps` and `SelectionSinkProps`, taken by the two components named after them. They reach `quoin-editor` too, which re-exports the headless entry. Nothing new runs and no type changes shape.
10
+
11
+ ### Changed
12
+ - The exports a host meets first carry a documentation comment, so an editor explains them on hover: `BlockCanvas`, `EditorView`, `useQuoinEditor`, `createEditor`, `toMarkdown`, `toHTML`, `toPlainText`, `fromMarkdown`, `PALETTE` and `englishLabels`. Nothing about their behaviour changes.
13
+ - `BlockShell`, `SelectionSink`, `defaultRenderers`, `VoidBlockFallback` and `isCrossSelection` are marked deprecated, in an editor and on the headless reference, and they go in 2.0.0. All five are internal to the view layer and a host has no use for them: `EditorView` draws the first two itself and a second copy of either takes the registration with the controller from the first, a renderer of your own draws a block's text with `EditableRegion` rather than reaching for `defaultRenderers`, a block shell already falls back to `VoidBlockFallback` when no renderer draws a block's type, and a host reads `anchor` and `head` of the public `Selection` to tell a selection that spans blocks or regions from one held inside a single region. `BlockRenderProps` and `BlockRenderer` stay public, so a renderer table still types. Every name is still exported and still does what it did.
14
+ - The comparison table weighs Quoin against Lexical and Plate as well, rechecked against each project's own pages and dated 2026-11-05. Lexical keeps an editor state it writes as JSON, draws no interface of its own, and ships neither comments nor translations: none of its forty packages covers either, and the comment plugin a reader finds is playground code. Plate keeps Slate JSON, converts markdown both ways, collaborates over Yjs and marks comments on text, and is MIT with a paid set of components behind its Notion-like template. A localization row goes in beside them: BlockNote ships 24 locales, Editor.js takes a dictionary the host writes, and Tiptap, Lexical and Plate ship none, since their interface strings live in the code a host copies into its own project. The comparison page reads in a wider column than the guide, because seven columns in a line of prose left each one too narrow to read.
15
+
5
16
  ## 1.7.0 (2026-11-04)
6
17
 
7
18
  ### Added
package/README.md CHANGED
@@ -69,23 +69,25 @@ What `onChange` hands you, and what you store:
69
69
  | `quoin-editor/comments` | Comment threads anchored to text, kept in your store |
70
70
  | `quoin-editor/ai` | Your model's answers streamed into the document; Quoin never calls a model |
71
71
  | `quoin-editor/view`, `/diff`, `/docx` | Server rendering with no hooks, a diff of two versions, a Word export |
72
+ | `quoin-editor/locale-ar`, `/locale-de`, `/locale-es`, `/locale-fr`, `/locale-ja`, `/locale-pt`, `/locale-zh` | Every string the editor shows, in that language |
72
73
  | `quoin-editor/headless`, `/engine` | The editor without the canvas UI, and the engine alone with no React |
73
74
 
74
75
  ## Why Quoin
75
76
 
76
77
  Each competitor cell links to the page that shows it; the Tiptap UI cell also draws on the review in [#73](https://github.com/AmineSeboui/quoin-editor/issues/73) (2026-10-12).
77
78
 
78
- | Feature | Quoin 1.7 | Tiptap 3 | BlockNote | Editor.js |
79
- | --- | --- | --- | --- | --- |
80
- | Stored document | Block array, markdown per text block | [JSON, or HTML][tt-store] | [Block JSON][bn-formats] | [JSON][ej-repo] |
81
- | Markdown | The storage format | [`@tiptap/markdown`, an early release][tt-md] | [Export and import, documented as lossy][bn-formats] | [Community plugin][ej-awesome] |
82
- | Notion-style UI | Included, MIT | [MIT menus and drag handle][tt-review]; [Notion-like template on a paid plan][tt-notion] | [Included][bn-home] | [Toolbox and inline toolbar][ej-repo] |
83
- | Licence | MIT | [MIT core, paid Pro extensions][tt-repo] | [MPL-2.0, `xl-*` packages GPL-3.0 or commercial][bn-repo] | [Apache-2.0][ej-repo] |
84
- | Real-time collaboration | Yjs, opt-in entry | [Yjs][tt-collab] | [Yjs][bn-collab] | [On its roadmap, in progress][ej-repo] |
85
- | Comments | Opt-in, your store | [Paid plan][tt-comments] | [Built in, on Yjs or a thread store][bn-comments] | [Community plugin][ej-awesome] |
86
- | Framework | React | [React, Vue, Svelte, plain JavaScript and more][tt-install] | [React, or vanilla JS with your own UI][bn-vanilla] | [Plain JavaScript][ej-start] |
79
+ | Feature | Quoin 1.8 | Tiptap 3 | BlockNote | Editor.js | Lexical | Plate |
80
+ | --- | --- | --- | --- | --- | --- | --- |
81
+ | Stored document | Block array, markdown per text block | [JSON, or HTML][tt-store] | [Block JSON][bn-formats] | [JSON][ej-repo] | [Editor state, as JSON][lx-intro] | [Slate JSON][pl-docs] |
82
+ | Markdown | The storage format | [`@tiptap/markdown`, an early release][tt-md] | [Export and import, documented as lossy][bn-formats] | [Community plugin][ej-awesome] | [`@lexical/markdown`, in and out][lx-md] | [`@platejs/markdown`, in and out][pl-md] |
83
+ | Notion-style UI | Included, MIT | [MIT menus and drag handle][tt-review]; [Notion-like template on a paid plan][tt-notion] | [Included][bn-home] | [Toolbox and inline toolbar][ej-repo] | [None; you draw it][lx-intro] | [MIT components][pl-repo]; [Notion-like set on a paid licence][pl-pro] |
84
+ | Licence | MIT | [MIT core, paid Pro extensions][tt-repo] | [MPL-2.0, `xl-*` packages GPL-3.0 or commercial][bn-repo] | [Apache-2.0][ej-repo] | [MIT][lx-repo] | [MIT, with paid Plus components][pl-pro] |
85
+ | Real-time collaboration | Yjs, opt-in entry | [Yjs][tt-collab] | [Yjs][bn-collab] | [On its roadmap, in progress][ej-repo] | [Yjs][lx-collab] | [Yjs][pl-yjs] |
86
+ | Comments | Opt-in, your store | [Paid plan][tt-comments] | [Built in, on Yjs or a thread store][bn-comments] | [Community plugin][ej-awesome] | [None shipped; a playground example][lx-repo] | [`@platejs/comment`, as text marks][pl-comment] |
87
+ | Localization | Seven packs, every string | [None shipped; the UI is yours][tt-ui] | [24 locales built in][bn-i18n] | [An i18n API; you bring the words][ej-i18n] | [None shipped; it draws no UI][lx-repo] | [None shipped; the UI is yours][pl-docs] |
88
+ | Framework | React | [React, Vue, Svelte, plain JavaScript and more][tt-install] | [React, or vanilla JS with your own UI][bn-vanilla] | [Plain JavaScript][ej-start] | [Any, with React bindings][lx-repo] | [React][pl-repo] |
87
89
 
88
- Sources, checked on 2026-11-03: Tiptap [persistence][tt-store], [markdown][tt-md], [Notion-like template][tt-notion], [repository][tt-repo], [collaboration][tt-collab], [comments][tt-comments], [install][tt-install]; BlockNote [home][bn-home], [formats][bn-formats], [repository][bn-repo], [collaboration][bn-collab], [comments][bn-comments], [getting started][bn-start], [vanilla JS][bn-vanilla]; Editor.js [home][ej-home], [repository][ej-repo], [getting started][ej-start], [awesome-editorjs][ej-awesome].
90
+ Sources, checked on 2026-11-05: Tiptap [persistence][tt-store], [markdown][tt-md], [Notion-like template][tt-notion], [repository][tt-repo], [collaboration][tt-collab], [comments][tt-comments], [install][tt-install], [UI components][tt-ui]; BlockNote [home][bn-home], [formats][bn-formats], [repository][bn-repo], [collaboration][bn-collab], [comments][bn-comments], [getting started][bn-start], [vanilla JS][bn-vanilla], [localization][bn-i18n]; Editor.js [home][ej-home], [repository][ej-repo], [getting started][ej-start], [awesome-editorjs][ej-awesome], [i18n][ej-i18n]; Lexical [docs][lx-intro], [repository][lx-repo], [markdown][lx-md], [collaboration][lx-collab]; Plate [docs][pl-docs], [repository][pl-repo], [markdown][pl-md], [collaboration][pl-yjs], [comments][pl-comment], [Plate Plus][pl-pro].
89
91
 
90
92
  [tt-store]: https://tiptap.dev/docs/editor/core-concepts/persistence
91
93
  [tt-md]: https://tiptap.dev/docs/editor/markdown
@@ -106,6 +108,19 @@ Sources, checked on 2026-11-03: Tiptap [persistence][tt-store], [markdown][tt-md
106
108
  [ej-repo]: https://github.com/codex-team/editor.js
107
109
  [ej-start]: https://editorjs.io/getting-started
108
110
  [ej-awesome]: https://github.com/editor-js/awesome-editorjs
111
+ [tt-ui]: https://tiptap.dev/docs/ui-components/components/overview
112
+ [bn-i18n]: https://www.blocknotejs.org/docs/features/localization
113
+ [ej-i18n]: https://editorjs.io/i18n/
114
+ [lx-intro]: https://lexical.dev/docs/intro
115
+ [lx-repo]: https://github.com/facebook/lexical
116
+ [lx-md]: https://lexical.dev/docs/packages/lexical-markdown
117
+ [lx-collab]: https://lexical.dev/docs/collaboration/react
118
+ [pl-docs]: https://platejs.org/docs
119
+ [pl-repo]: https://github.com/udecode/plate
120
+ [pl-md]: https://platejs.org/docs/markdown
121
+ [pl-yjs]: https://platejs.org/docs/yjs
122
+ [pl-comment]: https://platejs.org/docs/comment
123
+ [pl-pro]: https://pro.platejs.org
109
124
 
110
125
  ## Learn more
111
126
 
@@ -0,0 +1,53 @@
1
+ type AiAction = 'continue' | 'improve' | 'fix' | 'shorten' | 'lengthen' | 'simplify' | 'tone' | 'translate' | 'summarize' | 'explain' | 'custom';
2
+ /** What the host is asked: `selection` and `context` are markdown, `language` the canvas's `lang` when it has one. */
3
+ type AiRequest = {
4
+ action: AiAction;
5
+ prompt?: string;
6
+ selection: string;
7
+ context: string;
8
+ language?: string;
9
+ };
10
+ /** The host's model, answering a request as markdown in chunks, and stopping when `signal` aborts. */
11
+ type AiStream = (request: AiRequest, signal: AbortSignal) => AsyncIterable<string>;
12
+
13
+ /** Every string AI actions show or speak. */
14
+ type AiLabels = {
15
+ /** The toolbar button, block menu item and slash command that open the AI menu. */
16
+ askAi: string;
17
+ /** What the slash command for the AI menu does. */
18
+ askAiDescription: string;
19
+ /** The name of each action in the AI menu; `custom` names the slash command group's own entry. */
20
+ actions: Record<AiAction, string>;
21
+ /** The name of the list of actions. */
22
+ menu: string;
23
+ /** The field the writer types a request in. */
24
+ prompt: string;
25
+ /** The field once Change tone is chosen. */
26
+ tonePrompt: string;
27
+ /** The field once Translate is chosen. */
28
+ translatePrompt: string;
29
+ /** The choice that sends what was typed as a request of its own. */
30
+ ask(prompt: string): string;
31
+ /** The name of the bar under the answer. */
32
+ response: string;
33
+ writing: string;
34
+ stopped: string;
35
+ stop: string;
36
+ accept: string;
37
+ discard: string;
38
+ retry: string;
39
+ /** The field for a further request about the answer. */
40
+ followUp: string;
41
+ /** Shown when the host's answer failed. */
42
+ failed: string;
43
+ /** Said once an answer is complete, given its text. */
44
+ written(text: string): string;
45
+ /** Said once an answer is stopped, given what it wrote. */
46
+ halted(text: string): string;
47
+ /** Shown when the text the answer was for was deleted meanwhile. */
48
+ targetGone: string;
49
+ };
50
+ declare const englishAiLabels: AiLabels;
51
+
52
+ export { englishAiLabels as e };
53
+ export type { AiStream as A, AiLabels as a, AiAction as b, AiRequest as c };
@@ -0,0 +1,53 @@
1
+ type AiAction = 'continue' | 'improve' | 'fix' | 'shorten' | 'lengthen' | 'simplify' | 'tone' | 'translate' | 'summarize' | 'explain' | 'custom';
2
+ /** What the host is asked: `selection` and `context` are markdown, `language` the canvas's `lang` when it has one. */
3
+ type AiRequest = {
4
+ action: AiAction;
5
+ prompt?: string;
6
+ selection: string;
7
+ context: string;
8
+ language?: string;
9
+ };
10
+ /** The host's model, answering a request as markdown in chunks, and stopping when `signal` aborts. */
11
+ type AiStream = (request: AiRequest, signal: AbortSignal) => AsyncIterable<string>;
12
+
13
+ /** Every string AI actions show or speak. */
14
+ type AiLabels = {
15
+ /** The toolbar button, block menu item and slash command that open the AI menu. */
16
+ askAi: string;
17
+ /** What the slash command for the AI menu does. */
18
+ askAiDescription: string;
19
+ /** The name of each action in the AI menu; `custom` names the slash command group's own entry. */
20
+ actions: Record<AiAction, string>;
21
+ /** The name of the list of actions. */
22
+ menu: string;
23
+ /** The field the writer types a request in. */
24
+ prompt: string;
25
+ /** The field once Change tone is chosen. */
26
+ tonePrompt: string;
27
+ /** The field once Translate is chosen. */
28
+ translatePrompt: string;
29
+ /** The choice that sends what was typed as a request of its own. */
30
+ ask(prompt: string): string;
31
+ /** The name of the bar under the answer. */
32
+ response: string;
33
+ writing: string;
34
+ stopped: string;
35
+ stop: string;
36
+ accept: string;
37
+ discard: string;
38
+ retry: string;
39
+ /** The field for a further request about the answer. */
40
+ followUp: string;
41
+ /** Shown when the host's answer failed. */
42
+ failed: string;
43
+ /** Said once an answer is complete, given its text. */
44
+ written(text: string): string;
45
+ /** Said once an answer is stopped, given what it wrote. */
46
+ halted(text: string): string;
47
+ /** Shown when the text the answer was for was deleted meanwhile. */
48
+ targetGone: string;
49
+ };
50
+ declare const englishAiLabels: AiLabels;
51
+
52
+ export { englishAiLabels as e };
53
+ export type { AiStream as A, AiLabels as a, AiAction as b, AiRequest as c };
package/dist/ai.cjs CHANGED
@@ -2,13 +2,14 @@
2
2
  'use strict';
3
3
 
4
4
  var chunkLE5XMIFJ_cjs = require('./chunk-LE5XMIFJ.cjs');
5
- var chunkQR2NIDUK_cjs = require('./chunk-QR2NIDUK.cjs');
6
- var chunk6T5YGCZ5_cjs = require('./chunk-6T5YGCZ5.cjs');
7
- var chunkQJ34MOFV_cjs = require('./chunk-QJ34MOFV.cjs');
5
+ var chunk4MCYQHFL_cjs = require('./chunk-4MCYQHFL.cjs');
6
+ var chunkBA5WXBGE_cjs = require('./chunk-BA5WXBGE.cjs');
7
+ var chunkP6HC7RUN_cjs = require('./chunk-P6HC7RUN.cjs');
8
+ var chunkZWTIRGY5_cjs = require('./chunk-ZWTIRGY5.cjs');
8
9
  require('./chunk-A6PGQVTR.cjs');
9
- var chunkVZZPCYA3_cjs = require('./chunk-VZZPCYA3.cjs');
10
- require('./chunk-BM2GRZNM.cjs');
11
- var chunkWZA5S5EJ_cjs = require('./chunk-WZA5S5EJ.cjs');
10
+ var chunkFK7EBZPX_cjs = require('./chunk-FK7EBZPX.cjs');
11
+ require('./chunk-DJU25PFC.cjs');
12
+ var chunkI7X7B3WD_cjs = require('./chunk-I7X7B3WD.cjs');
12
13
  var react = require('react');
13
14
  var jsxRuntime = require('react/jsx-runtime');
14
15
 
@@ -109,30 +110,30 @@ function closeStreaming(markdown) {
109
110
  // src/extensions/ai/ai-target.ts
110
111
  function targetOfBlocks(state, ids) {
111
112
  const { doc, schema } = state;
112
- const first = chunkWZA5S5EJ_cjs.blockById(doc, ids[0]);
113
- const last = chunkWZA5S5EJ_cjs.blockById(doc, ids[ids.length - 1]);
113
+ const first = chunkI7X7B3WD_cjs.blockById(doc, ids[0]);
114
+ const last = chunkI7X7B3WD_cjs.blockById(doc, ids[ids.length - 1]);
114
115
  if (first === void 0 || last === void 0) return null;
115
- const start = chunkVZZPCYA3_cjs.primaryRegion(schema, first);
116
- const end = chunkVZZPCYA3_cjs.primaryRegion(schema, last);
116
+ const start = chunkFK7EBZPX_cjs.primaryRegion(schema, first);
117
+ const end = chunkFK7EBZPX_cjs.primaryRegion(schema, last);
117
118
  if (start === null || end === null) return null;
118
119
  const from = { block: first.id, region: start, offset: 0 };
119
- return { from, to: { block: last.id, region: end, offset: chunkVZZPCYA3_cjs.regionLength(last, end) }, selected: ids.length > 1 };
120
+ return { from, to: { block: last.id, region: end, offset: chunkFK7EBZPX_cjs.regionLength(last, end) }, selected: ids.length > 1 };
120
121
  }
121
122
  function targetOfSelection(state) {
122
123
  const { doc, selection } = state;
123
124
  if (selection === null) return null;
124
- if (selection.kind !== "text") return targetOfBlocks(state, chunkVZZPCYA3_cjs.selectedBlockIds(doc, selection));
125
- const { from, to } = chunkVZZPCYA3_cjs.orderedRange(doc, selection.anchor, selection.head);
126
- if (chunkVZZPCYA3_cjs.comparePos(doc, from, to) !== 0) return { from, to, selected: true };
125
+ if (selection.kind !== "text") return targetOfBlocks(state, chunkFK7EBZPX_cjs.selectedBlockIds(doc, selection));
126
+ const { from, to } = chunkFK7EBZPX_cjs.orderedRange(doc, selection.anchor, selection.head);
127
+ if (chunkFK7EBZPX_cjs.comparePos(doc, from, to) !== 0) return { from, to, selected: true };
127
128
  const block = doc.blocks[doc.index.get(from.block)];
128
129
  const whole = { block: from.block, region: from.region };
129
- return { from: { ...whole, offset: 0 }, to: { ...whole, offset: chunkVZZPCYA3_cjs.regionLength(block, from.region) }, selected: false };
130
+ return { from: { ...whole, offset: 0 }, to: { ...whole, offset: chunkFK7EBZPX_cjs.regionLength(block, from.region) }, selected: false };
130
131
  }
131
132
  function nearestKept(tr, target) {
132
133
  const { doc } = tr.before;
133
134
  const first = doc.index.get(target.from.block) ?? 0;
134
135
  const last = doc.index.get(target.to.block) ?? first;
135
- const kept = (block) => block && chunkWZA5S5EJ_cjs.blockById(tr.doc, block.id);
136
+ const kept = (block) => block && chunkI7X7B3WD_cjs.blockById(tr.doc, block.id);
136
137
  for (let step = 1; first - step >= 0 || last + step < doc.blocks.length; step++) {
137
138
  const found = kept(doc.blocks[first - step]) ?? kept(doc.blocks[last + step]);
138
139
  if (found) return found;
@@ -141,10 +142,10 @@ function nearestKept(tr, target) {
141
142
  }
142
143
  function edgeOf(tr, target) {
143
144
  const point = tr.mapPos(target.from, -1);
144
- const block = chunkWZA5S5EJ_cjs.blockById(tr.doc, point.block) ?? nearestKept(tr, target);
145
+ const block = chunkI7X7B3WD_cjs.blockById(tr.doc, point.block) ?? nearestKept(tr, target);
145
146
  if (block === void 0) return null;
146
- const region = chunkVZZPCYA3_cjs.primaryRegion(tr.before.schema, block) ?? "";
147
- const length = region === "" ? 0 : chunkVZZPCYA3_cjs.regionLength(block, region);
147
+ const region = chunkFK7EBZPX_cjs.primaryRegion(tr.before.schema, block) ?? "";
148
+ const length = region === "" ? 0 : chunkFK7EBZPX_cjs.regionLength(block, region);
148
149
  const offset = block.id === point.block ? Math.min(point.offset, length) : length;
149
150
  const after = offset > 0 || length === 0;
150
151
  const at = { block: block.id, region, offset: after ? length : 0 };
@@ -156,29 +157,29 @@ function mapTarget(tr, target) {
156
157
  const from = tr.mapPos(target.from, inside);
157
158
  const mapped2 = tr.mapPos(target.to, -inside);
158
159
  if (!tr.doc.index.has(from.block) || !tr.doc.index.has(mapped2.block)) return edgeOf(tr, target);
159
- const to = chunkVZZPCYA3_cjs.comparePos(tr.doc, from, mapped2) > 0 ? from : mapped2;
160
+ const to = chunkFK7EBZPX_cjs.comparePos(tr.doc, from, mapped2) > 0 ? from : mapped2;
160
161
  return from === target.from && to === target.to ? target : { ...target, from, to };
161
162
  }
162
- var targetGone = (doc, target) => target.lost !== void 0 || target.selected && chunkVZZPCYA3_cjs.comparePos(doc, target.from, target.to) === 0;
163
+ var targetGone = (doc, target) => target.lost !== void 0 || target.selected && chunkFK7EBZPX_cjs.comparePos(doc, target.from, target.to) === 0;
163
164
  function targetRanges(state, { from, to }) {
164
165
  const { doc, schema } = state;
165
166
  const start = doc.index.get(from.block);
166
167
  const end = doc.index.get(to.block);
167
168
  return doc.blocks.slice(start, end + 1).flatMap((block, i) => {
168
- const region = i === 0 ? from.region : block.id === to.block ? to.region : chunkVZZPCYA3_cjs.primaryRegion(schema, block);
169
+ const region = i === 0 ? from.region : block.id === to.block ? to.region : chunkFK7EBZPX_cjs.primaryRegion(schema, block);
169
170
  if (region === null || !Object.hasOwn(block.regions, region)) return [];
170
171
  const left = i === 0 ? from.offset : 0;
171
- const right = block.id === to.block ? to.offset : chunkVZZPCYA3_cjs.regionLength(block, region);
172
+ const right = block.id === to.block ? to.offset : chunkFK7EBZPX_cjs.regionLength(block, region);
172
173
  return right > left ? [{ block: block.id, region, from: left, to: right }] : [];
173
174
  });
174
175
  }
175
176
 
176
177
  // src/extensions/ai/ai-plugin.ts
177
- var aiKey = chunkVZZPCYA3_cjs.createPluginKey("ai");
178
+ var aiKey = chunkFK7EBZPX_cjs.createPluginKey("ai");
178
179
  var AI = "ai";
179
180
  var IDLE = { target: null, menu: null, ghost: null, held: [] };
180
- var aiState = (state) => chunkVZZPCYA3_cjs.getPluginState(state, aiKey);
181
- var aiTransaction = (state, act2) => new chunkVZZPCYA3_cjs.Transaction(state).setMeta(AI, act2).setMeta("addToHistory", false);
181
+ var aiState = (state) => chunkFK7EBZPX_cjs.getPluginState(state, aiKey);
182
+ var aiTransaction = (state, act2) => new chunkFK7EBZPX_cjs.Transaction(state).setMeta(AI, act2).setMeta("addToHistory", false);
182
183
  function written(ghost, key, phase, output) {
183
184
  return ghost.key !== key ? ghost : { ...ghost, phase, output, shown: closeStreaming(output) };
184
185
  }
@@ -210,8 +211,8 @@ function caretBlock(state) {
210
211
  return selection?.kind === "text" ? selection.head.block : null;
211
212
  }
212
213
  function hasText(state, id) {
213
- const block = chunkWZA5S5EJ_cjs.blockById(state.doc, id);
214
- return block !== void 0 && Object.keys(block.regions).some((region) => chunkVZZPCYA3_cjs.regionLength(block, region) > 0);
214
+ const block = chunkI7X7B3WD_cjs.blockById(state.doc, id);
215
+ return block !== void 0 && Object.keys(block.regions).some((region) => chunkFK7EBZPX_cjs.regionLength(block, region) > 0);
215
216
  }
216
217
  function heldText(tr, value, before, after) {
217
218
  if (!tr.docChanged) return value.held;
@@ -309,13 +310,13 @@ function aroundSelection(text, start, end, limit = CONTEXT_LIMIT) {
309
310
  }
310
311
  var WHITESPACE_ENTITY = /(?<!\\)&#(32|9);/g;
311
312
  var plain = (markdown) => markdown.replace(WHITESPACE_ENTITY, (_, code) => code === "32" ? " " : " ");
312
- var markdownOf = (state, blocks) => chunkVZZPCYA3_cjs.toMarkdown(blocks, { schema: state.schema, comments: false });
313
+ var markdownOf = (state, blocks) => chunkFK7EBZPX_cjs.toMarkdown(blocks, { schema: state.schema, comments: false });
313
314
  function rangeMarkdown(state, from, to) {
314
- const slice = chunkVZZPCYA3_cjs.sliceFromSelection({ ...state, selection: chunkVZZPCYA3_cjs.textSelection(from, to) }, chunkWZA5S5EJ_cjs.createBlockCodec(state.schema));
315
+ const slice = chunkFK7EBZPX_cjs.sliceFromSelection({ ...state, selection: chunkFK7EBZPX_cjs.textSelection(from, to) }, chunkI7X7B3WD_cjs.createBlockCodec(state.schema));
315
316
  return slice === null ? "" : markdownOf(state, slice.blocks).replace(/\n$/, "");
316
317
  }
317
318
  function documentContext(state, target, selection) {
318
- const blocks = chunkWZA5S5EJ_cjs.createBlockCodec(state.schema).blocksFromDoc(state.doc);
319
+ const blocks = chunkI7X7B3WD_cjs.createBlockCodec(state.schema).blocksFromDoc(state.doc);
319
320
  const full = plain(markdownOf(state, blocks));
320
321
  if (full.length <= CONTEXT_LIMIT) return full;
321
322
  const index = state.doc.index.get(target.from.block) ?? 0;
@@ -376,7 +377,7 @@ async function read(editor, link, run, stream, request) {
376
377
  } catch (error) {
377
378
  if (!live()) return;
378
379
  end("error");
379
- link.report(chunk6T5YGCZ5_cjs.toError(error));
380
+ link.report(chunkBA5WXBGE_cjs.toError(error));
380
381
  }
381
382
  }
382
383
  function startStream(editor, link, stream, request, replace) {
@@ -473,7 +474,7 @@ function aiSlashItems(labels, stream) {
473
474
  title: labels.askAi,
474
475
  description: labels.askAiDescription,
475
476
  group: "ai",
476
- icon: chunk6T5YGCZ5_cjs.Sparkles,
477
+ icon: chunkBA5WXBGE_cjs.Sparkles,
477
478
  keywords: KEYWORDS,
478
479
  run: ({ controller }) => void openFor(controller)
479
480
  },
@@ -481,7 +482,7 @@ function aiSlashItems(labels, stream) {
481
482
  id: "ai-continue",
482
483
  title: labels.actions.continue,
483
484
  group: "ai",
484
- icon: chunk6T5YGCZ5_cjs.PenLine,
485
+ icon: chunkBA5WXBGE_cjs.PenLine,
485
486
  keywords: [...KEYWORDS, "continue"],
486
487
  run: ({ controller }) => {
487
488
  if (openFor(controller)) askFrom({ controller, stream}, "continue");
@@ -492,14 +493,14 @@ function aiSlashItems(labels, stream) {
492
493
  var aiBubbleItem = (labels) => ({
493
494
  id: "ai",
494
495
  title: labels.askAi,
495
- icon: chunk6T5YGCZ5_cjs.Sparkles,
496
+ icon: chunkBA5WXBGE_cjs.Sparkles,
496
497
  available: (state) => aiState(state).ghost === null,
497
498
  run: (controller) => void openFor(controller)
498
499
  });
499
500
  var aiBlockMenuItem = (labels) => ({
500
501
  id: "ai",
501
502
  label: labels.askAi,
502
- icon: chunk6T5YGCZ5_cjs.Sparkles,
503
+ icon: chunkBA5WXBGE_cjs.Sparkles,
503
504
  available: (state, blockIds) => targetOfBlocks(state, blockIds) !== null,
504
505
  run: (controller, blockIds) => {
505
506
  const target = targetOfBlocks(controller.editor.state, blockIds);
@@ -542,34 +543,31 @@ var englishAiLabels = {
542
543
  halted: (text) => `Stopped. AI response so far: ${text}`,
543
544
  targetGone: "The text this was for was removed, so Accept inserts it where that text was"
544
545
  };
545
- function resolveAiLabels(overrides) {
546
- const given = Object.entries(overrides ?? {}).filter(([, value]) => value !== void 0);
547
- return given.length === 0 ? englishAiLabels : { ...englishAiLabels, ...Object.fromEntries(given) };
548
- }
546
+ var resolveAiLabels = (overrides) => chunkZWTIRGY5_cjs.mergeLabels(englishAiLabels, overrides);
549
547
 
550
548
  // src/extensions/ai/ai-space.ts
551
549
  var openOnSpace = (state, dispatch) => {
552
550
  const { selection, doc } = state;
553
551
  const ai = aiState(state);
554
- if (selection?.kind !== "text" || !chunkVZZPCYA3_cjs.isCollapsed(selection) || ai.menu !== null || ai.ghost !== null) return false;
552
+ if (selection?.kind !== "text" || !chunkFK7EBZPX_cjs.isCollapsed(selection) || ai.menu !== null || ai.ghost !== null) return false;
555
553
  const { head } = selection;
556
554
  const block = doc.blocks[doc.index.get(head.block)];
557
555
  const paragraph = block.type === "MARKDOWN" && block.attrs.kind === "paragraph";
558
- if (!paragraph || chunkVZZPCYA3_cjs.regionLength(block, head.region) > 0 || ai.held.includes(block.id)) return false;
556
+ if (!paragraph || chunkFK7EBZPX_cjs.regionLength(block, head.region) > 0 || ai.held.includes(block.id)) return false;
559
557
  dispatch?.(aiTransaction(state, { type: "open", target: { from: head, to: head, selected: false }, fromSpace: true }));
560
558
  return true;
561
559
  };
562
560
 
563
561
  // src/extensions/ai/ai-accept.ts
564
- var isParagraph = (block) => block?.type === "MARKDOWN" && chunkWZA5S5EJ_cjs.parseLine(String(block.data.markdown ?? "")).kind === "paragraph";
562
+ var isParagraph = (block) => block?.type === "MARKDOWN" && chunkI7X7B3WD_cjs.parseLine(String(block.data.markdown ?? "")).kind === "paragraph";
565
563
  var DISCARD = { type: "discard" };
566
564
  function insertBeside(tr, schema, target, pieces, makeId) {
567
565
  const index = tr.doc.index.get(target.to.block);
568
- const at = target.lost?.after ? chunkVZZPCYA3_cjs.descendantsEnd(tr.doc, index) : index;
569
- const fresh = chunkVZZPCYA3_cjs.freshBlocks(pieces, tr.doc.blocks[index].indent, makeId);
570
- tr.step(chunkVZZPCYA3_cjs.insertBlocksStep(at, fresh));
571
- chunkVZZPCYA3_cjs.capIndents(tr, at, at + fresh.length);
572
- tr.setSelection(chunkVZZPCYA3_cjs.endOf(tr.doc, schema, fresh[fresh.length - 1].id));
566
+ const at = target.lost?.after ? chunkFK7EBZPX_cjs.descendantsEnd(tr.doc, index) : index;
567
+ const fresh = chunkFK7EBZPX_cjs.freshBlocks(pieces, tr.doc.blocks[index].indent, makeId);
568
+ tr.step(chunkFK7EBZPX_cjs.insertBlocksStep(at, fresh));
569
+ chunkFK7EBZPX_cjs.capIndents(tr, at, at + fresh.length);
570
+ tr.setSelection(chunkFK7EBZPX_cjs.endOf(tr.doc, schema, fresh[fresh.length - 1].id));
573
571
  }
574
572
  function acceptAnswer(editor) {
575
573
  const { state } = editor;
@@ -577,7 +575,7 @@ function acceptAnswer(editor) {
577
575
  if (target === null || ghost === null || ghost.phase === "streaming" || ghost.phase === "error") return null;
578
576
  const { codec } = editor;
579
577
  const makeId = codec.context.newId;
580
- const blocks = chunkWZA5S5EJ_cjs.fromMarkdown(ghost.output, { newId: makeId });
578
+ const blocks = chunkI7X7B3WD_cjs.fromMarkdown(ghost.output, { newId: makeId });
581
579
  const pieces = blocks.flatMap((block) => codec.toEngineBlocks(block));
582
580
  const tr = editor.createTransaction().setOrigin("ai").setMeta(AI, DISCARD);
583
581
  const gone = targetGone(state.doc, target);
@@ -587,12 +585,12 @@ function acceptAnswer(editor) {
587
585
  const { schema } = state;
588
586
  const open = { openStart: isParagraph(blocks[0]), openEnd: isParagraph(blocks[blocks.length - 1]) };
589
587
  if (ghost.replace || gone) {
590
- const caret = ghost.replace && !gone ? chunkVZZPCYA3_cjs.deleteRange(tr, schema, target.from, target.to, makeId) : target.to;
591
- chunkVZZPCYA3_cjs.pasteAtCaret(tr, schema, caret, open, pieces, makeId);
588
+ const caret = ghost.replace && !gone ? chunkFK7EBZPX_cjs.deleteRange(tr, schema, target.from, target.to, makeId) : target.to;
589
+ chunkFK7EBZPX_cjs.pasteAtCaret(tr, schema, caret, open, pieces, makeId);
592
590
  } else {
593
591
  const block = tr.doc.blocks[tr.doc.index.get(target.to.block)];
594
- const end = { ...target.to, offset: chunkVZZPCYA3_cjs.regionLength(block, target.to.region) };
595
- chunkVZZPCYA3_cjs.pasteAtCaret(tr, schema, end, { openStart: false, openEnd: false }, pieces, makeId);
592
+ const end = { ...target.to, offset: chunkFK7EBZPX_cjs.regionLength(block, target.to.region) };
593
+ chunkFK7EBZPX_cjs.pasteAtCaret(tr, schema, end, { openStart: false, openEnd: false }, pieces, makeId);
596
594
  }
597
595
  }
598
596
  editor.dispatch(tr);
@@ -607,12 +605,12 @@ function AiBar({ env, ghost, gone, barId }) {
607
605
  const [next, setNext] = react.useState("");
608
606
  const { phase } = ghost;
609
607
  const focusPrimary = () => content.current?.querySelector(`[${PRIMARY}]`)?.focus();
610
- chunkQJ34MOFV_cjs.useIsomorphicLayoutEffect(() => {
608
+ chunkP6HC7RUN_cjs.useIsomorphicLayoutEffect(() => {
611
609
  const active = content.current?.ownerDocument.activeElement;
612
610
  inside.current = active == null || active === active.ownerDocument.body;
613
611
  if (inside.current) focusPrimary();
614
612
  }, []);
615
- chunkQJ34MOFV_cjs.useIsomorphicLayoutEffect(() => {
613
+ chunkP6HC7RUN_cjs.useIsomorphicLayoutEffect(() => {
616
614
  const active = content.current?.ownerDocument.activeElement;
617
615
  if (inside.current && !content.current?.contains(active ?? null)) focusPrimary();
618
616
  }, [phase, ghost.key]);
@@ -660,21 +658,21 @@ function AiBar({ env, ghost, gone, barId }) {
660
658
  children: [
661
659
  phase === "streaming" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
662
660
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: labels.writing }),
663
- /* @__PURE__ */ jsxRuntime.jsx(chunk6T5YGCZ5_cjs.Button, { ...primary, size: "sm", variant: "outline", onClick: () => stop(editor), children: labels.stop })
661
+ /* @__PURE__ */ jsxRuntime.jsx(chunkBA5WXBGE_cjs.Button, { ...primary, size: "sm", variant: "outline", onClick: () => stop(editor), children: labels.stop })
664
662
  ] }),
665
663
  phase === "error" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
666
664
  /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: "text-sm text-destructive", children: labels.failed }),
667
- /* @__PURE__ */ jsxRuntime.jsx(chunk6T5YGCZ5_cjs.Button, { ...primary, size: "sm", onClick: () => retry(editor, stream), children: labels.retry }),
668
- /* @__PURE__ */ jsxRuntime.jsx(chunk6T5YGCZ5_cjs.Button, { size: "sm", variant: "ghost", onClick: onDiscard, children: labels.discard })
665
+ /* @__PURE__ */ jsxRuntime.jsx(chunkBA5WXBGE_cjs.Button, { ...primary, size: "sm", onClick: () => retry(editor, stream), children: labels.retry }),
666
+ /* @__PURE__ */ jsxRuntime.jsx(chunkBA5WXBGE_cjs.Button, { size: "sm", variant: "ghost", onClick: onDiscard, children: labels.discard })
669
667
  ] }),
670
668
  (phase === "done" || phase === "stopped") && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
671
669
  phase === "stopped" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: labels.stopped }),
672
670
  gone && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "w-full text-xs text-muted-foreground", children: labels.targetGone }),
673
- /* @__PURE__ */ jsxRuntime.jsx(chunk6T5YGCZ5_cjs.Button, { ...primary, size: "sm", onClick: onAccept, children: labels.accept }),
674
- /* @__PURE__ */ jsxRuntime.jsx(chunk6T5YGCZ5_cjs.Button, { size: "sm", variant: "ghost", onClick: onDiscard, children: labels.discard }),
675
- /* @__PURE__ */ jsxRuntime.jsx(chunk6T5YGCZ5_cjs.Button, { size: "sm", variant: "ghost", onClick: () => retry(editor, stream), children: labels.retry }),
671
+ /* @__PURE__ */ jsxRuntime.jsx(chunkBA5WXBGE_cjs.Button, { ...primary, size: "sm", onClick: onAccept, children: labels.accept }),
672
+ /* @__PURE__ */ jsxRuntime.jsx(chunkBA5WXBGE_cjs.Button, { size: "sm", variant: "ghost", onClick: onDiscard, children: labels.discard }),
673
+ /* @__PURE__ */ jsxRuntime.jsx(chunkBA5WXBGE_cjs.Button, { size: "sm", variant: "ghost", onClick: () => retry(editor, stream), children: labels.retry }),
676
674
  /* @__PURE__ */ jsxRuntime.jsx("form", { className: "w-full", onSubmit: onFollowUp, children: /* @__PURE__ */ jsxRuntime.jsx(
677
- chunk6T5YGCZ5_cjs.Input,
675
+ chunkBA5WXBGE_cjs.Input,
678
676
  {
679
677
  "aria-label": labels.followUp,
680
678
  placeholder: labels.followUp,
@@ -695,8 +693,8 @@ function AiBar({ env, ghost, gone, barId }) {
695
693
  var reader = null;
696
694
  function readerOf() {
697
695
  if (reader === null) {
698
- const schema = chunkWZA5S5EJ_cjs.createSchema();
699
- reader = { schema, codec: chunkWZA5S5EJ_cjs.createBlockCodec(schema) };
696
+ const schema = chunkI7X7B3WD_cjs.createSchema();
697
+ reader = { schema, codec: chunkI7X7B3WD_cjs.createBlockCodec(schema) };
700
698
  }
701
699
  return reader;
702
700
  }
@@ -704,16 +702,16 @@ function fillGhost(ghost, markdown) {
704
702
  const doc = ghost.ownerDocument;
705
703
  const { schema, codec } = readerOf();
706
704
  const drawn2 = [];
707
- for (const block of chunkWZA5S5EJ_cjs.fromMarkdown(markdown)) {
705
+ for (const block of chunkI7X7B3WD_cjs.fromMarkdown(markdown)) {
708
706
  for (const piece of codec.toEngineBlocks(block)) {
709
707
  const element = doc.createElement("div");
710
708
  element.className = "quoin-ai-ghost-block";
711
709
  element.dataset.kind = String(piece.attrs.kind ?? piece.type.toLowerCase());
712
710
  if (piece.attrs.level !== void 0) element.dataset.level = String(piece.attrs.level);
713
711
  if (piece.indent > 0) element.style.setProperty("--quoin-ai-indent", String(piece.indent));
714
- const region = chunkVZZPCYA3_cjs.primaryRegion(schema, piece);
715
- if (region === null) element.textContent = chunkVZZPCYA3_cjs.toPlainText([codec.toQuoin(piece)], { schema });
716
- else chunkQJ34MOFV_cjs.renderInline(element, piece.regions[region], []);
712
+ const region = chunkFK7EBZPX_cjs.primaryRegion(schema, piece);
713
+ if (region === null) element.textContent = chunkFK7EBZPX_cjs.toPlainText([codec.toQuoin(piece)], { schema });
714
+ else chunkP6HC7RUN_cjs.renderInline(element, piece.regions[region], []);
717
715
  drawn2.push(element);
718
716
  }
719
717
  }
@@ -790,7 +788,7 @@ function clipAside(layer, text, areas, scale) {
790
788
  function useGhostPlace(controller, layer, block, region) {
791
789
  const placeNow = react.useRef(() => {
792
790
  });
793
- chunkQJ34MOFV_cjs.useIsomorphicLayoutEffect(() => {
791
+ chunkP6HC7RUN_cjs.useIsomorphicLayoutEffect(() => {
794
792
  const root = controller.root;
795
793
  const element = layer.current;
796
794
  const view = root?.ownerDocument.defaultView;
@@ -829,7 +827,7 @@ function AiGhost({ env, ghost, target, gone, barId }) {
829
827
  const layer = react.useRef(null);
830
828
  const drawn2 = react.useRef(null);
831
829
  const place2 = useGhostPlace(env.controller, layer, target.to.block, target.to.region);
832
- chunkQJ34MOFV_cjs.useIsomorphicLayoutEffect(() => {
830
+ chunkP6HC7RUN_cjs.useIsomorphicLayoutEffect(() => {
833
831
  const element = drawn2.current;
834
832
  if (element === null) return;
835
833
  element.setAttribute("inert", "");
@@ -843,7 +841,7 @@ function AiGhost({ env, ghost, target, gone, barId }) {
843
841
  }
844
842
  function useTargetAnchor(controller, target) {
845
843
  const latest = react.useRef(target);
846
- chunkQJ34MOFV_cjs.useIsomorphicLayoutEffect(() => {
844
+ chunkP6HC7RUN_cjs.useIsomorphicLayoutEffect(() => {
847
845
  latest.current = target;
848
846
  });
849
847
  return react.useMemo(() => {
@@ -852,8 +850,8 @@ function useTargetAnchor(controller, target) {
852
850
  return { block: to.block, region: to.region, from: to.offset, to: to.offset };
853
851
  };
854
852
  return {
855
- current: chunk6T5YGCZ5_cjs.virtualAnchor(
856
- () => chunk6T5YGCZ5_cjs.rangeRect(controller, end()),
853
+ current: chunkBA5WXBGE_cjs.virtualAnchor(
854
+ () => chunkBA5WXBGE_cjs.rangeRect(controller, end()),
857
855
  () => controller.registry.get(end().block, end().region)
858
856
  )
859
857
  };
@@ -907,7 +905,7 @@ function AiMenu({ env, target, fromSpace }) {
907
905
  };
908
906
  const fieldLabel = asking === "tone" ? labels.tonePrompt : asking === "translate" ? labels.translatePrompt : labels.prompt;
909
907
  return /* @__PURE__ */ jsxRuntime.jsxs(
910
- chunk6T5YGCZ5_cjs.Popover,
908
+ chunkBA5WXBGE_cjs.Popover,
911
909
  {
912
910
  open: true,
913
911
  modal: false,
@@ -915,9 +913,9 @@ function AiMenu({ env, target, fromSpace }) {
915
913
  if (!next) closeMenu(controller.editor);
916
914
  },
917
915
  children: [
918
- /* @__PURE__ */ jsxRuntime.jsx(chunk6T5YGCZ5_cjs.PopoverAnchor, { virtualRef: anchor }),
916
+ /* @__PURE__ */ jsxRuntime.jsx(chunkBA5WXBGE_cjs.PopoverAnchor, { virtualRef: anchor }),
919
917
  /* @__PURE__ */ jsxRuntime.jsxs(
920
- chunk6T5YGCZ5_cjs.PopoverContent,
918
+ chunkBA5WXBGE_cjs.PopoverContent,
921
919
  {
922
920
  "aria-label": labels.askAi,
923
921
  side: "bottom",
@@ -934,7 +932,7 @@ function AiMenu({ env, target, fromSpace }) {
934
932
  },
935
933
  children: [
936
934
  /* @__PURE__ */ jsxRuntime.jsx(
937
- chunk6T5YGCZ5_cjs.Input,
935
+ chunkBA5WXBGE_cjs.Input,
938
936
  {
939
937
  ref: field,
940
938
  role: "combobox",
@@ -958,7 +956,7 @@ function AiMenu({ env, target, fromSpace }) {
958
956
  id: optionId(index),
959
957
  role: "option",
960
958
  "aria-selected": index === current,
961
- className: chunk6T5YGCZ5_cjs.cn(chunk6T5YGCZ5_cjs.MENU_ITEM, index === current && "bg-accent text-accent-foreground"),
959
+ className: chunkBA5WXBGE_cjs.cn(chunkBA5WXBGE_cjs.MENU_ITEM, index === current && "bg-accent text-accent-foreground"),
962
960
  onMouseDown: (event) => event.preventDefault(),
963
961
  onClick: () => choose(choice),
964
962
  children: choice.label
@@ -976,17 +974,17 @@ var goneOf = (state) => {
976
974
  const { target, ghost } = aiState(state);
977
975
  return target !== null && ghost !== null && targetGone(state.doc, target);
978
976
  };
979
- var plainText = (markdown) => chunkVZZPCYA3_cjs.toPlainText(chunkWZA5S5EJ_cjs.fromMarkdown(markdown));
977
+ var plainText = (markdown) => chunkFK7EBZPX_cjs.toPlainText(chunkI7X7B3WD_cjs.fromMarkdown(markdown));
980
978
  function AiOverlay({ controller, stream, labels }) {
981
- const { onError } = chunk6T5YGCZ5_cjs.useQuoin();
979
+ const { onError } = chunkBA5WXBGE_cjs.useQuoin();
982
980
  const link = linkOf(controller.editor);
983
- chunkQJ34MOFV_cjs.useIsomorphicLayoutEffect(() => {
981
+ chunkP6HC7RUN_cjs.useIsomorphicLayoutEffect(() => {
984
982
  link.report = onError;
985
983
  link.ended = (phase, output) => controller.announce((phase === "done" ? labels.written : labels.halted)(plainText(output)));
986
984
  }, [link, onError, controller, labels]);
987
- const value = chunkQJ34MOFV_cjs.useEditorState(controller, aiState);
988
- const gone = chunkQJ34MOFV_cjs.useEditorState(controller, goneOf);
989
- const readOnly = chunkQJ34MOFV_cjs.useReadOnly(controller);
985
+ const value = chunkP6HC7RUN_cjs.useEditorState(controller, aiState);
986
+ const gone = chunkP6HC7RUN_cjs.useEditorState(controller, goneOf);
987
+ const readOnly = chunkP6HC7RUN_cjs.useReadOnly(controller);
990
988
  const env = react.useMemo(() => ({ controller, stream, labels }), [controller, stream, labels]);
991
989
  const barId = react.useId();
992
990
  const showing = value.ghost !== null;
@@ -1005,7 +1003,7 @@ function AiOverlay({ controller, stream, labels }) {
1005
1003
  const root = controller.root;
1006
1004
  if (!root) return;
1007
1005
  let away = true;
1008
- return chunkQR2NIDUK_cjs.watchCanvasFocus(controller, root.ownerDocument, (inside, node) => {
1006
+ return chunk4MCYQHFL_cjs.watchCanvasFocus(controller, root.ownerDocument, (inside, node) => {
1009
1007
  if (!inside) away = true;
1010
1008
  else if (root.contains(node)) {
1011
1009
  if (away) noteFocus(controller.editor);
@@ -1041,7 +1039,7 @@ function aiExtension(options) {
1041
1039
  blockMenuItems: [aiBlockMenuItem(labels)],
1042
1040
  overlays: [Overlay]
1043
1041
  };
1044
- return chunkVZZPCYA3_cjs.defineExtension({
1042
+ return chunkFK7EBZPX_cjs.defineExtension({
1045
1043
  name: "ai",
1046
1044
  plugins: [aiPlugin],
1047
1045
  decorations: aiDecorations,