quoin-editor 1.0.0 → 1.0.2
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.
- package/CHANGELOG.md +58 -0
- package/dist/ai.cjs +135 -94
- package/dist/ai.cjs.map +1 -1
- package/dist/ai.d.cts +1 -1
- package/dist/ai.d.ts +1 -1
- package/dist/ai.js +62 -21
- package/dist/ai.js.map +1 -1
- package/dist/catalogue.cjs +25 -25
- package/dist/catalogue.d.cts +6 -6
- package/dist/catalogue.d.ts +6 -6
- package/dist/catalogue.js +17 -17
- package/dist/{chunk-ND5XJVK2.cjs → chunk-4SLJNZSM.cjs} +27 -23
- package/dist/chunk-4SLJNZSM.cjs.map +1 -0
- package/dist/{chunk-LCQSG4M6.js → chunk-4XFXB37X.js} +22 -18
- package/dist/chunk-4XFXB37X.js.map +1 -0
- package/dist/{chunk-AEJXZBVR.cjs → chunk-5MAYUGTE.cjs} +110 -103
- package/dist/chunk-5MAYUGTE.cjs.map +1 -0
- package/dist/{chunk-JDPTZUBW.js → chunk-623AOJM2.js} +4 -4
- package/dist/chunk-6NULZPVC.js +374 -0
- package/dist/chunk-6NULZPVC.js.map +1 -0
- package/dist/{chunk-2QCRZ7OL.cjs → chunk-6V6GGBDK.cjs} +12 -11
- package/dist/{chunk-2QCRZ7OL.cjs.map → chunk-6V6GGBDK.cjs.map} +1 -1
- package/dist/{chunk-WXMYDXPN.cjs → chunk-73653CJ4.cjs} +4 -3
- package/dist/chunk-73653CJ4.cjs.map +1 -0
- package/dist/{chunk-47MLKSI2.js → chunk-ADSJ42DO.js} +6 -6
- package/dist/{chunk-PWUEPDA3.cjs → chunk-AUOEM3SF.cjs} +44 -44
- package/dist/{chunk-PWUEPDA3.cjs.map → chunk-AUOEM3SF.cjs.map} +1 -1
- package/dist/{chunk-WQHV3MRE.cjs → chunk-BQANU5BH.cjs} +12 -12
- package/dist/{chunk-WQHV3MRE.cjs.map → chunk-BQANU5BH.cjs.map} +1 -1
- package/dist/{chunk-KXOBZGQO.js → chunk-C23RQVRK.js} +21 -14
- package/dist/chunk-C23RQVRK.js.map +1 -0
- package/dist/{chunk-2VDK27IF.cjs → chunk-CW3AFJBR.cjs} +312 -342
- package/dist/chunk-CW3AFJBR.cjs.map +1 -0
- package/dist/{chunk-MQ2XQ4MX.js → chunk-DUE3HCZD.js} +6 -6
- package/dist/chunk-DUE3HCZD.js.map +1 -0
- package/dist/{chunk-OMOBMXD5.cjs → chunk-EH5KXQXY.cjs} +7 -2
- package/dist/chunk-EH5KXQXY.cjs.map +1 -0
- package/dist/{chunk-ZS2WA357.js → chunk-EPARJI4V.js} +147 -129
- package/dist/chunk-EPARJI4V.js.map +1 -0
- package/dist/{chunk-HZSSEYJS.cjs → chunk-FDRUAL3Z.cjs} +30 -30
- package/dist/{chunk-HZSSEYJS.cjs.map → chunk-FDRUAL3Z.cjs.map} +1 -1
- package/dist/{chunk-T7SW6OWV.js → chunk-IQBMAE3F.js} +5 -5
- package/dist/{chunk-RUDWMKYX.js → chunk-JHQUHFLX.js} +8 -8
- package/dist/{chunk-HELJTLNC.cjs → chunk-KUYZMSMF.cjs} +12 -12
- package/dist/chunk-KUYZMSMF.cjs.map +1 -0
- package/dist/chunk-LE5XMIFJ.cjs +14 -0
- package/dist/chunk-LE5XMIFJ.cjs.map +1 -0
- package/dist/{chunk-EL2OEJQC.cjs → chunk-LLTEGXWZ.cjs} +56 -56
- package/dist/{chunk-EL2OEJQC.cjs.map → chunk-LLTEGXWZ.cjs.map} +1 -1
- package/dist/chunk-MQUCCSWG.js +14 -0
- package/dist/chunk-MQUCCSWG.js.map +1 -0
- package/dist/{chunk-NNDYYEW5.js → chunk-N4FGA3H4.js} +5 -5
- package/dist/{chunk-ZHOSSWUM.js → chunk-NEZJ7ZP5.js} +4 -3
- package/dist/chunk-NEZJ7ZP5.js.map +1 -0
- package/dist/{chunk-XEBWC7ZL.js → chunk-O4Z2WMFF.js} +7 -2
- package/dist/chunk-O4Z2WMFF.js.map +1 -0
- package/dist/{chunk-HJIQT3S5.js → chunk-OBMBSU2S.js} +100 -84
- package/dist/chunk-OBMBSU2S.js.map +1 -0
- package/dist/{chunk-KAVU6T6J.js → chunk-OSHZQR3W.js} +5 -5
- package/dist/{chunk-GTTPVJSD.cjs → chunk-P6KYXFZC.cjs} +265 -247
- package/dist/chunk-P6KYXFZC.cjs.map +1 -0
- package/dist/{chunk-CDELOHDY.cjs → chunk-R2BLBV43.cjs} +23 -23
- package/dist/{chunk-CDELOHDY.cjs.map → chunk-R2BLBV43.cjs.map} +1 -1
- package/dist/{chunk-NOKWNK2O.js → chunk-RJRX34BZ.js} +3 -2
- package/dist/{chunk-ZGZEJCRV.js → chunk-T43RZTUT.js} +166 -196
- package/dist/chunk-T43RZTUT.js.map +1 -0
- package/dist/chunk-TLGX43P6.cjs +374 -0
- package/dist/chunk-TLGX43P6.cjs.map +1 -0
- package/dist/{chunk-POBM7MGR.js → chunk-U3LOLYAU.js} +2 -2
- package/dist/{chunk-RDG4VI6E.cjs → chunk-UEKXVCKC.cjs} +103 -93
- package/dist/chunk-UEKXVCKC.cjs.map +1 -0
- package/dist/{chunk-PMWCQSQX.js → chunk-VCLL5C7J.js} +5 -5
- package/dist/{chunk-SG37UBDO.js → chunk-VRS6WCEK.js} +20 -10
- package/dist/chunk-VRS6WCEK.js.map +1 -0
- package/dist/{chunk-ZC24WB7Y.cjs → chunk-WEYDDIBQ.cjs} +14 -14
- package/dist/{chunk-ZC24WB7Y.cjs.map → chunk-WEYDDIBQ.cjs.map} +1 -1
- package/dist/{chunk-5DNCCN6X.cjs → chunk-X4HDKFW5.cjs} +27 -27
- package/dist/{chunk-5DNCCN6X.cjs.map → chunk-X4HDKFW5.cjs.map} +1 -1
- package/dist/{chunk-SLVDAHHK.cjs → chunk-X4P4TQXH.cjs} +5 -5
- package/dist/{chunk-SLVDAHHK.cjs.map → chunk-X4P4TQXH.cjs.map} +1 -1
- package/dist/{chunk-GLLXHMH2.cjs → chunk-XA3UEJIW.cjs} +3 -3
- package/dist/{chunk-GLLXHMH2.cjs.map → chunk-XA3UEJIW.cjs.map} +1 -1
- package/dist/{chunk-K574XHMM.js → chunk-YBMUEEM5.js} +2 -2
- package/dist/{chunk-PZ4GT5JO.cjs → chunk-ZKVOS57Q.cjs} +316 -300
- package/dist/chunk-ZKVOS57Q.cjs.map +1 -0
- package/dist/comments.cjs +113 -98
- package/dist/comments.cjs.map +1 -1
- package/dist/comments.d.cts +6 -1
- package/dist/comments.d.ts +6 -1
- package/dist/comments.js +32 -17
- package/dist/comments.js.map +1 -1
- package/dist/diff.cjs +44 -40
- package/dist/diff.cjs.map +1 -1
- package/dist/diff.js +17 -13
- package/dist/diff.js.map +1 -1
- package/dist/docx.cjs +60 -40
- package/dist/docx.cjs.map +1 -1
- package/dist/docx.d.cts +6 -0
- package/dist/docx.d.ts +6 -0
- package/dist/docx.js +32 -12
- package/dist/docx.js.map +1 -1
- package/dist/embed.cjs +9 -9
- package/dist/embed.d.cts +1 -1
- package/dist/embed.d.ts +1 -1
- package/dist/embed.js +8 -8
- package/dist/{emoji-extension-DpQ43HjE.d.ts → emoji-extension-CmPlRssm.d.ts} +1 -1
- package/dist/{emoji-extension-C12gDPm6.d.cts → emoji-extension-zJ6F7hW8.d.cts} +1 -1
- package/dist/emoji.cjs +8 -8
- package/dist/emoji.d.cts +3 -3
- package/dist/emoji.d.ts +3 -3
- package/dist/emoji.js +7 -7
- package/dist/engine.cjs +4 -4
- package/dist/engine.d.cts +2 -2
- package/dist/engine.d.ts +2 -2
- package/dist/engine.js +3 -3
- package/dist/{find-extension-_sbZEt3R.d.cts → find-extension-BcRyE9K4.d.cts} +1 -1
- package/dist/{find-extension-DKqLLSuP.d.ts → find-extension-CmFehyul.d.ts} +1 -1
- package/dist/find.cjs +8 -8
- package/dist/find.d.cts +2 -2
- package/dist/find.d.ts +2 -2
- package/dist/find.js +7 -7
- package/dist/headless.cjs +5 -5
- package/dist/headless.d.cts +4 -4
- package/dist/headless.d.ts +4 -4
- package/dist/headless.js +4 -4
- package/dist/{index-CgD72kHB.d.cts → index-CFPmCee4.d.cts} +5 -2
- package/dist/{index-DNYbTK3w.d.ts → index-DiCGV60d.d.ts} +5 -2
- package/dist/index.cjs +7 -7
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +6 -6
- package/dist/math.cjs +10 -10
- package/dist/math.d.cts +1 -1
- package/dist/math.d.ts +1 -1
- package/dist/math.js +9 -9
- package/dist/mention.cjs +10 -10
- package/dist/mention.d.cts +1 -1
- package/dist/mention.d.ts +1 -1
- package/dist/mention.js +9 -9
- package/dist/presence.cjs +98 -104
- package/dist/presence.cjs.map +1 -1
- package/dist/presence.d.cts +2 -2
- package/dist/presence.d.ts +2 -2
- package/dist/presence.js +66 -72
- package/dist/presence.js.map +1 -1
- package/dist/{stats-extension-Bly88uio.d.ts → stats-extension-DisOEbbI.d.ts} +1 -1
- package/dist/{stats-extension-CJPijvUb.d.cts → stats-extension-Dt1lgP3m.d.cts} +1 -1
- package/dist/stats.cjs +9 -9
- package/dist/stats.d.cts +3 -3
- package/dist/stats.d.ts +3 -3
- package/dist/stats.js +8 -8
- package/dist/styles.css +1 -1
- package/dist/{table-extension-Dzf4k6hI.d.ts → table-extension-BTMJnT7J.d.ts} +1 -1
- package/dist/{table-extension-BTLxFy2S.d.cts → table-extension-DxZz3jqp.d.cts} +1 -1
- package/dist/table.cjs +8 -8
- package/dist/table.d.cts +2 -2
- package/dist/table.d.ts +2 -2
- package/dist/table.js +7 -7
- package/dist/{toc-extension-DWm5alVj.d.ts → toc-extension-CR9U-l6j.d.ts} +1 -1
- package/dist/{toc-extension-B3-s6h2x.d.cts → toc-extension-DGHywOBv.d.cts} +1 -1
- package/dist/toc.cjs +8 -8
- package/dist/toc.d.cts +2 -2
- package/dist/toc.d.ts +2 -2
- package/dist/toc.js +7 -7
- package/dist/{types-a1CRBlEh.d.ts → types-C6_QkXzy.d.ts} +1 -9
- package/dist/{types-Cz7XJLaK.d.cts → types-CJ7Gq3GX.d.cts} +1 -9
- package/dist/{view-controller-DSHw0ltQ.d.cts → view-controller-BD1KwYfE.d.cts} +6 -19
- package/dist/{view-controller-UbpzblGl.d.ts → view-controller-y4cb60ue.d.ts} +6 -19
- package/dist/view.cjs +6 -6
- package/dist/view.js +3 -3
- package/dist/yjs.cjs +302 -412
- package/dist/yjs.cjs.map +1 -1
- package/dist/yjs.d.cts +3 -3
- package/dist/yjs.d.ts +3 -3
- package/dist/yjs.js +211 -321
- package/dist/yjs.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-2VDK27IF.cjs.map +0 -1
- package/dist/chunk-AEJXZBVR.cjs.map +0 -1
- package/dist/chunk-E75EHRPA.cjs +0 -175
- package/dist/chunk-E75EHRPA.cjs.map +0 -1
- package/dist/chunk-GTTPVJSD.cjs.map +0 -1
- package/dist/chunk-HELJTLNC.cjs.map +0 -1
- package/dist/chunk-HJIQT3S5.js.map +0 -1
- package/dist/chunk-JEIFJHEI.js +0 -14
- package/dist/chunk-JEIFJHEI.js.map +0 -1
- package/dist/chunk-KXOBZGQO.js.map +0 -1
- package/dist/chunk-LCQSG4M6.js.map +0 -1
- package/dist/chunk-MQ2XQ4MX.js.map +0 -1
- package/dist/chunk-ND5XJVK2.cjs.map +0 -1
- package/dist/chunk-OMOBMXD5.cjs.map +0 -1
- package/dist/chunk-PZ4GT5JO.cjs.map +0 -1
- package/dist/chunk-QAW5NH6S.cjs +0 -14
- package/dist/chunk-QAW5NH6S.cjs.map +0 -1
- package/dist/chunk-RDG4VI6E.cjs.map +0 -1
- package/dist/chunk-SG37UBDO.js.map +0 -1
- package/dist/chunk-TC4DARU2.js +0 -175
- package/dist/chunk-TC4DARU2.js.map +0 -1
- package/dist/chunk-WXMYDXPN.cjs.map +0 -1
- package/dist/chunk-XEBWC7ZL.js.map +0 -1
- package/dist/chunk-ZGZEJCRV.js.map +0 -1
- package/dist/chunk-ZHOSSWUM.js.map +0 -1
- package/dist/chunk-ZS2WA357.js.map +0 -1
- /package/dist/{chunk-JDPTZUBW.js.map → chunk-623AOJM2.js.map} +0 -0
- /package/dist/{chunk-47MLKSI2.js.map → chunk-ADSJ42DO.js.map} +0 -0
- /package/dist/{chunk-T7SW6OWV.js.map → chunk-IQBMAE3F.js.map} +0 -0
- /package/dist/{chunk-RUDWMKYX.js.map → chunk-JHQUHFLX.js.map} +0 -0
- /package/dist/{chunk-NNDYYEW5.js.map → chunk-N4FGA3H4.js.map} +0 -0
- /package/dist/{chunk-KAVU6T6J.js.map → chunk-OSHZQR3W.js.map} +0 -0
- /package/dist/{chunk-NOKWNK2O.js.map → chunk-RJRX34BZ.js.map} +0 -0
- /package/dist/{chunk-POBM7MGR.js.map → chunk-U3LOLYAU.js.map} +0 -0
- /package/dist/{chunk-PMWCQSQX.js.map → chunk-VCLL5C7J.js.map} +0 -0
- /package/dist/{chunk-K574XHMM.js.map → chunk-YBMUEEM5.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,64 @@
|
|
|
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.0.2 (2026-10-27)
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- A task's checkbox and an embed's frame are never left with an empty or blank name. In the canvas, a `task` label that is empty or only white space names the checkbox `Task`, and a `frameTitle` label that returns one titles the frame with its provider's name, or with its page's address when the provider has no name. `toHTML`, `QuoinView` and `QuoinDiff` treat a `taskLabel`, an `emptyTask` label, a `frameTitle` or a provider name of only white space as empty too, rather than writing it as the name.
|
|
9
|
+
- With `quoin-editor/yjs`, a collaborator's change to a block's type reads only that block again from the shared document, rather than the whole document, as does a collaborator's change to the rows or columns of a table, or any other change that only puts in or takes out the start of a region: at 20,000 blocks a type change takes about 8 ms, against about 75 ms before, close to the 5 ms of a collaborator's typing. A block that concurrent edits left out of shape still reads the whole document, as before.
|
|
10
|
+
- With `quoin-editor/yjs`, the caret stays on its character when a collaborator's change reads blocks of the shared document again, such as turning the paragraph above it into a code block: it was put back by the layout from before the change and could jump within its block or into another one.
|
|
11
|
+
- With `quoin-editor/yjs`, a change another writer makes to the `id` in a block's marker in the shared `Y.Text` is read: the editor takes the document as the text now reads, where it kept showing the block under its old id.
|
|
12
|
+
- With `quoin-editor/yjs`, content in the shared `Y.Text` that the `Y.Text` API never writes, such as values put in through `Y.Array` on the same name, counts as one skipped embed per value, rather than being dropped, so the positions the editor holds match those of Yjs.
|
|
13
|
+
|
|
14
|
+
### Known limits
|
|
15
|
+
- In the canvas, a task's text box still takes a `task` label that is empty or only white space as its name.
|
|
16
|
+
|
|
17
|
+
## 1.0.1 (2026-10-26)
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- `toDocx` takes an optional `baseUrl`, the address of the page the document comes from: a link or a file whose address is a relative path, such as `/files/a.pdf` or `notes/a.pdf`, is linked to that address read against it, rather than written as its text. A network path (`//host/x`, `\\host\share`, `/\host`) and a fragment such as `#top` stay text. A base that is not an absolute `http` or `https` address is ignored, a user and password in it are left out of the links, and an empty link stays text.
|
|
21
|
+
- `commentsExtension` takes an optional `initialLayout: 'wide' | 'narrow'`, the layout drawn on the server and in the first render before the canvas is measured, so a server-rendered wide canvas no longer shifts by 236 px as it hydrates. The default stays `'narrow'`.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
- `Transaction` and `ViewController` keep their private state in native private fields, so a proxy around one of them, such as Vue's `reactive` or Valtio's `proxy`, cannot read it and its methods throw: wrap the object that holds them, not these instances.
|
|
25
|
+
- The transaction that moves the caret to where text is dropped has the `drop` origin, rather than `command`.
|
|
26
|
+
- Docs: chains merge the meta of every part, so a key one part set is seen on the whole chain (extensions page).
|
|
27
|
+
- Docs: two clients can hold a table's alignment as no `align` or as `align: [null, null]`, which the editor and `QuoinDiff` treat as equal; a host comparing saved JSON should normalise `align` (collaboration page).
|
|
28
|
+
- Docs: typing into a block whose region another edit took out can bring along text waiting before it, which then shows one block higher on every client (collaboration page).
|
|
29
|
+
- Docs: edits a headless editor makes while unmounted stay local and are not sent when it connects again (collaboration page).
|
|
30
|
+
- Docs: the `onError` of `yjsCollaboration` is the adapter's own, apart from the canvas's; pass both the same handler (collaboration page).
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
- Under a collaboration, `editor.chain().undo().run()` and `editor.chain().redo().run()` undo and redo when the chain holds nothing else, rather than doing nothing. A chain that holds other commands as well still does nothing.
|
|
34
|
+
- A canvas rendered on the server, as with `react-dom/server`, leaves out a code token's `color` that holds what could leave the declaration or load a resource (any of `;` `{` `}` `\` `"` `'` `<` `>` `!`, a `/*`, or `url(`, `image(`, `image-set(` or `expression(`), rather than writing a value such as `red;background-image:url(...)` into the page as it is, and the canvas in the browser leaves out the same values. Other colours, such as `var(--name, #fff)`, `hsl(var(--primary))`, `color-mix()` and `light-dark()`, are drawn by the canvas as in 1.0, and now by `QuoinView` too, which uses the same rule.
|
|
35
|
+
- `toHTML`, `QuoinView` and `QuoinDiff` no longer write an empty accessible name: an empty `taskLabel` names a task with no text `Task` (in `QuoinDiff`, the `emptyTask` label, and `Task` when that is empty too), and a frame whose `frameTitle` is empty is titled with its provider's name, or with its page's address when the provider has no name.
|
|
36
|
+
- `toDocx` writes a link to an `http` or `https` address with no host, such as `http:foo` or `http://?x`, as its text, as it does other targets Word cannot open, rather than as a hyperlink.
|
|
37
|
+
- With presence, a long collaborator name at the end of a line is cut off where the avatars' gutter begins, rather than over the avatars.
|
|
38
|
+
- With presence, a collaborator's caret near the side of a wide table, and its name, are cut off at the table's side as the table's text is, rather than drawn whole past it.
|
|
39
|
+
- With presence, a collaborator's caret, and with `quoin-editor/ai` an answer, in a scroller that sits inside another box that clips what overflows, such as a table in a custom block, is clipped by both, rather than only by the nearest one.
|
|
40
|
+
- With presence, a collaborator's caret in a wide table moves with the table as it scrolls, rather than a frame behind it.
|
|
41
|
+
- With `quoin-editor/ai`, the room kept under a block for its answer no longer adds to the block's bottom padding or to the row gap of a flex or grid canvas under a block another block follows: the answer takes that space first, and the room makes up only the rest. Padding the block paints, with a bottom border or a background, is left to the block.
|
|
42
|
+
- With `quoin-editor/ai`, an answer growing above the part of the page being read no longer pushes that text down in a browser without scroll anchoring, such as Safari, or on a page that turns scroll anchoring off, smooth `scroll-behavior` or not, and in a canvas that scrolls itself.
|
|
43
|
+
- With `quoin-editor/comments`, a thread just resolved or deleted no longer comes back for a moment when your store sets new copies of threads that have not changed before it records the resolution. It comes back once your store, having resolved or removed it, reopens it.
|
|
44
|
+
- With `quoin-editor/comments`, a selection your code sets, as a command or an AI answer does, no longer closes a thread a click opened, "Text removed" included: only the writer's own caret moves close it, an undo, a redo and a drop included.
|
|
45
|
+
- With `quoin-editor/comments`, a thread a click opened closes once the writer moves the caret out of its text, rather than staying open over the lines they go on to type.
|
|
46
|
+
- A canvas drawn inside another canvas's tree owns its own text, menus and popovers: focus in them no longer counts as focus in the outer canvas, which kept the inner canvas's presence, comments and AI from seeing it as their own and let the outer canvas's go on as if focused.
|
|
47
|
+
- With `quoin-editor/yjs`, a row two people add to a table at the same moment, followed by a move of the table and a new column, no longer has one client write cell text at the wrong place, which split it across cells for the others.
|
|
48
|
+
- With `quoin-editor/yjs`, deleting a table while someone adds or removes one of its rows or columns no longer leaves hidden regions in the line after it, over which Enter threw: the cell text shows at the end of that line.
|
|
49
|
+
- Presence on a second editor given a `yjsCollaboration` that already serves one, which stays unconnected, no longer writes to the awareness: unmounting it no longer takes the person and their selection out while the first canvas is still mounted.
|
|
50
|
+
- An editor that disconnects from a `yjsCollaboration` and connects again within a frame, as StrictMode or a quick remount does, keeps what it can undo and redo. A collaboration left disconnected longer lets its undo history go, as in 1.0.
|
|
51
|
+
- `yjsCollaboration` reports to its `onError` a change from the shared document that does not apply to the editor, as the error applying it raised, before the editor takes the document as the shared text reads, rather than recovering in silence.
|
|
52
|
+
- A block that a collaborator's change leaves selected as a whole comes into view with the writer's next change, rather than only once the selection leaves it and comes back.
|
|
53
|
+
- A collaborator's caret move drawn in the same render as a change from your code, such as a command that selects a block below the fold, no longer keeps that change from scrolling it into view. A collaborator's change to the document still scrolls nothing.
|
|
54
|
+
- A table's row or column menu stays open while a collaborator types in its own row or column. It still closes when a change from elsewhere moves that row or column.
|
|
55
|
+
- Two people changing one block's type at the same moment no longer hide the block's text: text left in a region the winning type does not have shows in that type's own region.
|
|
56
|
+
- A caret in a block that a new `blocks` array changes on both sides of it stays on its character, rather than moving by the length of the later change.
|
|
57
|
+
|
|
58
|
+
### Known limits
|
|
59
|
+
- A task's checkbox and an embed's frame in the canvas still get no name when a host's `task` or `frameTitle` label is empty or blank, and a `taskLabel` or `frameTitle` of spaces only gives `toHTML`, `QuoinView` and `QuoinDiff` a blank name.
|
|
60
|
+
- With presence, a collaborator's caret can stay off its character after a style change that reflows the text without resizing its block, until the next change, scroll or resize.
|
|
61
|
+
- With `quoin-editor/yjs`, a table cell whose region concurrent row and column edits took out cannot be selected or typed into until the table is rewritten, as by adding or removing a row or a column. This is as in 1.0.0.
|
|
62
|
+
|
|
5
63
|
## 1.0.0 (2026-10-23)
|
|
6
64
|
|
|
7
65
|
Quoin 1.0 replaces the 0.4 canvas, a card per block with a textarea opened to edit it, by an editor of its own: an engine with no dependency, a view that types into the page in place, and a canvas UI on top. A 0.4 document loads as it is, and the 0.4 `BlockCanvas` props keep their names. [MIGRATION.md](MIGRATION.md) lists what a 0.4 host changes.
|
package/dist/ai.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use client';"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }"use client";
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var _chunkLE5XMIFJcjs = require('./chunk-LE5XMIFJ.cjs');
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
var
|
|
7
|
+
var _chunk73653CJ4cjs = require('./chunk-73653CJ4.cjs');
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
|
@@ -17,14 +17,14 @@ var _chunkWXMYDXPNcjs = require('./chunk-WXMYDXPN.cjs');
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
var
|
|
20
|
+
var _chunkCW3AFJBRcjs = require('./chunk-CW3AFJBR.cjs');
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
var
|
|
27
|
-
require('./chunk-
|
|
26
|
+
var _chunkP6KYXFZCcjs = require('./chunk-P6KYXFZC.cjs');
|
|
27
|
+
require('./chunk-EH5KXQXY.cjs');
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
|
|
@@ -47,14 +47,15 @@ require('./chunk-OMOBMXD5.cjs');
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
var
|
|
51
|
-
require('./chunk-
|
|
50
|
+
var _chunkZKVOS57Qcjs = require('./chunk-ZKVOS57Q.cjs');
|
|
51
|
+
require('./chunk-6V6GGBDK.cjs');
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
|
|
58
|
+
var _chunk4SLJNZSMcjs = require('./chunk-4SLJNZSM.cjs');
|
|
58
59
|
|
|
59
60
|
// src/extensions/ai/streaming-markdown.ts
|
|
60
61
|
var FENCE = /^ {0,3}(`{3,}|~{3,})/;
|
|
@@ -153,30 +154,30 @@ function closeStreaming(markdown) {
|
|
|
153
154
|
// src/extensions/ai/ai-target.ts
|
|
154
155
|
function targetOfBlocks(state, ids) {
|
|
155
156
|
const { doc, schema } = state;
|
|
156
|
-
const first =
|
|
157
|
-
const last =
|
|
157
|
+
const first = _chunk4SLJNZSMcjs.blockById.call(void 0, doc, ids[0]);
|
|
158
|
+
const last = _chunk4SLJNZSMcjs.blockById.call(void 0, doc, ids[ids.length - 1]);
|
|
158
159
|
if (first === void 0 || last === void 0) return null;
|
|
159
|
-
const start =
|
|
160
|
-
const end =
|
|
160
|
+
const start = _chunkZKVOS57Qcjs.primaryRegion.call(void 0, schema, first);
|
|
161
|
+
const end = _chunkZKVOS57Qcjs.primaryRegion.call(void 0, schema, last);
|
|
161
162
|
if (start === null || end === null) return null;
|
|
162
163
|
const from = { block: first.id, region: start, offset: 0 };
|
|
163
|
-
return { from, to: { block: last.id, region: end, offset:
|
|
164
|
+
return { from, to: { block: last.id, region: end, offset: _chunkZKVOS57Qcjs.regionLength.call(void 0, last, end) }, selected: ids.length > 1 };
|
|
164
165
|
}
|
|
165
166
|
function targetOfSelection(state) {
|
|
166
167
|
const { doc, selection } = state;
|
|
167
168
|
if (selection === null) return null;
|
|
168
|
-
if (selection.kind !== "text") return targetOfBlocks(state,
|
|
169
|
-
const { from, to } =
|
|
170
|
-
if (
|
|
169
|
+
if (selection.kind !== "text") return targetOfBlocks(state, _chunkZKVOS57Qcjs.selectedBlockIds.call(void 0, doc, selection));
|
|
170
|
+
const { from, to } = _chunkZKVOS57Qcjs.orderedRange.call(void 0, doc, selection.anchor, selection.head);
|
|
171
|
+
if (_chunkZKVOS57Qcjs.comparePos.call(void 0, doc, from, to) !== 0) return { from, to, selected: true };
|
|
171
172
|
const block = doc.blocks[doc.index.get(from.block)];
|
|
172
173
|
const whole = { block: from.block, region: from.region };
|
|
173
|
-
return { from: { ...whole, offset: 0 }, to: { ...whole, offset:
|
|
174
|
+
return { from: { ...whole, offset: 0 }, to: { ...whole, offset: _chunkZKVOS57Qcjs.regionLength.call(void 0, block, from.region) }, selected: false };
|
|
174
175
|
}
|
|
175
176
|
function nearestKept(tr, target) {
|
|
176
177
|
const { doc } = tr.before;
|
|
177
178
|
const first = _nullishCoalesce(doc.index.get(target.from.block), () => ( 0));
|
|
178
179
|
const last = _nullishCoalesce(doc.index.get(target.to.block), () => ( first));
|
|
179
|
-
const kept = (block) => block &&
|
|
180
|
+
const kept = (block) => block && _chunk4SLJNZSMcjs.blockById.call(void 0, tr.doc, block.id);
|
|
180
181
|
for (let step = 1; first - step >= 0 || last + step < doc.blocks.length; step++) {
|
|
181
182
|
const found = _nullishCoalesce(kept(doc.blocks[first - step]), () => ( kept(doc.blocks[last + step])));
|
|
182
183
|
if (found) return found;
|
|
@@ -185,10 +186,10 @@ function nearestKept(tr, target) {
|
|
|
185
186
|
}
|
|
186
187
|
function edgeOf(tr, target) {
|
|
187
188
|
const point = tr.mapPos(target.from, -1);
|
|
188
|
-
const block = _nullishCoalesce(
|
|
189
|
+
const block = _nullishCoalesce(_chunk4SLJNZSMcjs.blockById.call(void 0, tr.doc, point.block), () => ( nearestKept(tr, target)));
|
|
189
190
|
if (block === void 0) return null;
|
|
190
|
-
const region = _nullishCoalesce(
|
|
191
|
-
const length = region === "" ? 0 :
|
|
191
|
+
const region = _nullishCoalesce(_chunkZKVOS57Qcjs.primaryRegion.call(void 0, tr.before.schema, block), () => ( ""));
|
|
192
|
+
const length = region === "" ? 0 : _chunkZKVOS57Qcjs.regionLength.call(void 0, block, region);
|
|
192
193
|
const offset = block.id === point.block ? Math.min(point.offset, length) : length;
|
|
193
194
|
const after = offset > 0 || length === 0;
|
|
194
195
|
const at = { block: block.id, region, offset: after ? length : 0 };
|
|
@@ -200,29 +201,29 @@ function mapTarget(tr, target) {
|
|
|
200
201
|
const from = tr.mapPos(target.from, inside);
|
|
201
202
|
const mapped2 = tr.mapPos(target.to, -inside);
|
|
202
203
|
if (!tr.doc.index.has(from.block) || !tr.doc.index.has(mapped2.block)) return edgeOf(tr, target);
|
|
203
|
-
const to =
|
|
204
|
+
const to = _chunkZKVOS57Qcjs.comparePos.call(void 0, tr.doc, from, mapped2) > 0 ? from : mapped2;
|
|
204
205
|
return from === target.from && to === target.to ? target : { ...target, from, to };
|
|
205
206
|
}
|
|
206
|
-
var targetGone = (doc, target) => target.lost !== void 0 || target.selected &&
|
|
207
|
+
var targetGone = (doc, target) => target.lost !== void 0 || target.selected && _chunkZKVOS57Qcjs.comparePos.call(void 0, doc, target.from, target.to) === 0;
|
|
207
208
|
function targetRanges(state, { from, to }) {
|
|
208
209
|
const { doc, schema } = state;
|
|
209
210
|
const start = doc.index.get(from.block);
|
|
210
211
|
const end = doc.index.get(to.block);
|
|
211
212
|
return doc.blocks.slice(start, end + 1).flatMap((block, i) => {
|
|
212
|
-
const region = i === 0 ? from.region : block.id === to.block ? to.region :
|
|
213
|
+
const region = i === 0 ? from.region : block.id === to.block ? to.region : _chunkZKVOS57Qcjs.primaryRegion.call(void 0, schema, block);
|
|
213
214
|
if (region === null || !Object.hasOwn(block.regions, region)) return [];
|
|
214
215
|
const left = i === 0 ? from.offset : 0;
|
|
215
|
-
const right = block.id === to.block ? to.offset :
|
|
216
|
+
const right = block.id === to.block ? to.offset : _chunkZKVOS57Qcjs.regionLength.call(void 0, block, region);
|
|
216
217
|
return right > left ? [{ block: block.id, region, from: left, to: right }] : [];
|
|
217
218
|
});
|
|
218
219
|
}
|
|
219
220
|
|
|
220
221
|
// src/extensions/ai/ai-plugin.ts
|
|
221
|
-
var aiKey =
|
|
222
|
+
var aiKey = _chunkZKVOS57Qcjs.createPluginKey.call(void 0, "ai");
|
|
222
223
|
var AI = "ai";
|
|
223
224
|
var IDLE = { target: null, menu: null, ghost: null, held: [] };
|
|
224
|
-
var aiState = (state) =>
|
|
225
|
-
var aiTransaction = (state, act2) => new (0,
|
|
225
|
+
var aiState = (state) => _chunkZKVOS57Qcjs.getPluginState.call(void 0, state, aiKey);
|
|
226
|
+
var aiTransaction = (state, act2) => new (0, _chunkZKVOS57Qcjs.Transaction)(state).setMeta(AI, act2).setMeta("addToHistory", false);
|
|
226
227
|
function written(ghost, key, phase, output) {
|
|
227
228
|
return ghost.key !== key ? ghost : { ...ghost, phase, output, shown: closeStreaming(output) };
|
|
228
229
|
}
|
|
@@ -254,8 +255,8 @@ function caretBlock(state) {
|
|
|
254
255
|
return _optionalChain([selection, 'optionalAccess', _11 => _11.kind]) === "text" ? selection.head.block : null;
|
|
255
256
|
}
|
|
256
257
|
function hasText(state, id) {
|
|
257
|
-
const block =
|
|
258
|
-
return block !== void 0 && Object.keys(block.regions).some((region) =>
|
|
258
|
+
const block = _chunk4SLJNZSMcjs.blockById.call(void 0, state.doc, id);
|
|
259
|
+
return block !== void 0 && Object.keys(block.regions).some((region) => _chunkZKVOS57Qcjs.regionLength.call(void 0, block, region) > 0);
|
|
259
260
|
}
|
|
260
261
|
function heldText(tr, value, before, after) {
|
|
261
262
|
if (!tr.docChanged) return value.held;
|
|
@@ -356,13 +357,13 @@ function aroundSelection(text, start, end, limit = CONTEXT_LIMIT) {
|
|
|
356
357
|
}
|
|
357
358
|
var WHITESPACE_ENTITY = /(?<!\\)&#(32|9);/g;
|
|
358
359
|
var plain = (markdown) => markdown.replace(WHITESPACE_ENTITY, (_, code) => code === "32" ? " " : " ");
|
|
359
|
-
var markdownOf = (state, blocks) =>
|
|
360
|
+
var markdownOf = (state, blocks) => _chunkZKVOS57Qcjs.toMarkdown.call(void 0, blocks, { schema: state.schema, comments: false });
|
|
360
361
|
function rangeMarkdown(state, from, to) {
|
|
361
|
-
const slice =
|
|
362
|
+
const slice = _chunkZKVOS57Qcjs.sliceFromSelection.call(void 0, { ...state, selection: _chunkZKVOS57Qcjs.textSelection.call(void 0, from, to) }, _chunk4SLJNZSMcjs.createBlockCodec.call(void 0, state.schema));
|
|
362
363
|
return slice === null ? "" : markdownOf(state, slice.blocks).replace(/\n$/, "");
|
|
363
364
|
}
|
|
364
365
|
function documentContext(state, target, selection) {
|
|
365
|
-
const blocks =
|
|
366
|
+
const blocks = _chunk4SLJNZSMcjs.createBlockCodec.call(void 0, state.schema).blocksFromDoc(state.doc);
|
|
366
367
|
const full = plain(markdownOf(state, blocks));
|
|
367
368
|
if (full.length <= CONTEXT_LIMIT) return full;
|
|
368
369
|
const index = _nullishCoalesce(state.doc.index.get(target.from.block), () => ( 0));
|
|
@@ -423,7 +424,7 @@ async function read(editor, link, run, stream, request) {
|
|
|
423
424
|
} catch (error) {
|
|
424
425
|
if (!live()) return;
|
|
425
426
|
end("error");
|
|
426
|
-
link.report(
|
|
427
|
+
link.report(_chunkCW3AFJBRcjs.toError.call(void 0, error));
|
|
427
428
|
}
|
|
428
429
|
}
|
|
429
430
|
function startStream(editor, link, stream, request, replace) {
|
|
@@ -598,11 +599,11 @@ function resolveAiLabels(overrides) {
|
|
|
598
599
|
var openOnSpace = (state, dispatch) => {
|
|
599
600
|
const { selection, doc } = state;
|
|
600
601
|
const ai = aiState(state);
|
|
601
|
-
if (_optionalChain([selection, 'optionalAccess', _26 => _26.kind]) !== "text" || !
|
|
602
|
+
if (_optionalChain([selection, 'optionalAccess', _26 => _26.kind]) !== "text" || !_chunkZKVOS57Qcjs.isCollapsed.call(void 0, selection) || ai.menu !== null || ai.ghost !== null) return false;
|
|
602
603
|
const { head } = selection;
|
|
603
604
|
const block = doc.blocks[doc.index.get(head.block)];
|
|
604
605
|
const paragraph = block.type === "MARKDOWN" && block.attrs.kind === "paragraph";
|
|
605
|
-
if (!paragraph ||
|
|
606
|
+
if (!paragraph || _chunkZKVOS57Qcjs.regionLength.call(void 0, block, head.region) > 0 || ai.held.includes(block.id)) return false;
|
|
606
607
|
_optionalChain([dispatch, 'optionalCall', _27 => _27(aiTransaction(state, { type: "open", target: { from: head, to: head, selected: false }, fromSpace: true }))]);
|
|
607
608
|
return true;
|
|
608
609
|
};
|
|
@@ -614,15 +615,15 @@ var _react = require('react');
|
|
|
614
615
|
|
|
615
616
|
|
|
616
617
|
// src/extensions/ai/ai-accept.ts
|
|
617
|
-
var isParagraph = (block) => _optionalChain([block, 'optionalAccess', _28 => _28.type]) === "MARKDOWN" &&
|
|
618
|
+
var isParagraph = (block) => _optionalChain([block, 'optionalAccess', _28 => _28.type]) === "MARKDOWN" && _chunk4SLJNZSMcjs.parseLine.call(void 0, String(_nullishCoalesce(block.data.markdown, () => ( "")))).kind === "paragraph";
|
|
618
619
|
var DISCARD = { type: "discard" };
|
|
619
620
|
function insertBeside(tr, schema, target, pieces, makeId) {
|
|
620
621
|
const index = tr.doc.index.get(target.to.block);
|
|
621
|
-
const at = _optionalChain([target, 'access', _29 => _29.lost, 'optionalAccess', _30 => _30.after]) ?
|
|
622
|
-
const fresh =
|
|
623
|
-
tr.step(
|
|
624
|
-
|
|
625
|
-
tr.setSelection(
|
|
622
|
+
const at = _optionalChain([target, 'access', _29 => _29.lost, 'optionalAccess', _30 => _30.after]) ? _chunkZKVOS57Qcjs.descendantsEnd.call(void 0, tr.doc, index) : index;
|
|
623
|
+
const fresh = _chunkZKVOS57Qcjs.freshBlocks.call(void 0, pieces, tr.doc.blocks[index].indent, makeId);
|
|
624
|
+
tr.step(_chunkZKVOS57Qcjs.insertBlocksStep.call(void 0, at, fresh));
|
|
625
|
+
_chunkZKVOS57Qcjs.capIndents.call(void 0, tr, at, at + fresh.length);
|
|
626
|
+
tr.setSelection(_chunkZKVOS57Qcjs.endOf.call(void 0, tr.doc, schema, fresh[fresh.length - 1].id));
|
|
626
627
|
}
|
|
627
628
|
function acceptAnswer(editor) {
|
|
628
629
|
const { state } = editor;
|
|
@@ -630,7 +631,7 @@ function acceptAnswer(editor) {
|
|
|
630
631
|
if (target === null || ghost === null || ghost.phase === "streaming" || ghost.phase === "error") return null;
|
|
631
632
|
const { codec } = editor;
|
|
632
633
|
const makeId = codec.context.newId;
|
|
633
|
-
const blocks =
|
|
634
|
+
const blocks = _chunk4SLJNZSMcjs.fromMarkdown.call(void 0, ghost.output, { newId: makeId });
|
|
634
635
|
const pieces = blocks.flatMap((block) => codec.toEngineBlocks(block));
|
|
635
636
|
const tr = editor.createTransaction().setOrigin("ai").setMeta(AI, DISCARD);
|
|
636
637
|
const gone = targetGone(state.doc, target);
|
|
@@ -640,12 +641,12 @@ function acceptAnswer(editor) {
|
|
|
640
641
|
const { schema } = state;
|
|
641
642
|
const open = { openStart: isParagraph(blocks[0]), openEnd: isParagraph(blocks[blocks.length - 1]) };
|
|
642
643
|
if (ghost.replace || gone) {
|
|
643
|
-
const caret = ghost.replace && !gone ?
|
|
644
|
-
|
|
644
|
+
const caret = ghost.replace && !gone ? _chunkZKVOS57Qcjs.deleteRange.call(void 0, tr, schema, target.from, target.to, makeId) : target.to;
|
|
645
|
+
_chunkZKVOS57Qcjs.pasteAtCaret.call(void 0, tr, schema, caret, open, pieces, makeId);
|
|
645
646
|
} else {
|
|
646
647
|
const block = tr.doc.blocks[tr.doc.index.get(target.to.block)];
|
|
647
|
-
const end = { ...target.to, offset:
|
|
648
|
-
|
|
648
|
+
const end = { ...target.to, offset: _chunkZKVOS57Qcjs.regionLength.call(void 0, block, target.to.region) };
|
|
649
|
+
_chunkZKVOS57Qcjs.pasteAtCaret.call(void 0, tr, schema, end, { openStart: false, openEnd: false }, pieces, makeId);
|
|
649
650
|
}
|
|
650
651
|
}
|
|
651
652
|
editor.dispatch(tr);
|
|
@@ -663,12 +664,12 @@ function AiBar({ env, ghost, gone, barId }) {
|
|
|
663
664
|
const [next, setNext] = _react.useState.call(void 0, "");
|
|
664
665
|
const { phase } = ghost;
|
|
665
666
|
const focusPrimary = () => _optionalChain([content, 'access', _31 => _31.current, 'optionalAccess', _32 => _32.querySelector, 'call', _33 => _33(`[${PRIMARY}]`), 'optionalAccess', _34 => _34.focus, 'call', _35 => _35()]);
|
|
666
|
-
|
|
667
|
+
_chunkP6KYXFZCcjs.useIsomorphicLayoutEffect.call(void 0, () => {
|
|
667
668
|
const active = _optionalChain([content, 'access', _36 => _36.current, 'optionalAccess', _37 => _37.ownerDocument, 'access', _38 => _38.activeElement]);
|
|
668
669
|
inside.current = active == null || active === active.ownerDocument.body;
|
|
669
670
|
if (inside.current) focusPrimary();
|
|
670
671
|
}, []);
|
|
671
|
-
|
|
672
|
+
_chunkP6KYXFZCcjs.useIsomorphicLayoutEffect.call(void 0, () => {
|
|
672
673
|
const active = _optionalChain([content, 'access', _39 => _39.current, 'optionalAccess', _40 => _40.ownerDocument, 'access', _41 => _41.activeElement]);
|
|
673
674
|
if (inside.current && !_optionalChain([content, 'access', _42 => _42.current, 'optionalAccess', _43 => _43.contains, 'call', _44 => _44(_nullishCoalesce(active, () => ( null)))])) focusPrimary();
|
|
674
675
|
}, [phase, ghost.key]);
|
|
@@ -716,21 +717,21 @@ function AiBar({ env, ghost, gone, barId }) {
|
|
|
716
717
|
children: [
|
|
717
718
|
phase === "streaming" && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
718
719
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-sm text-muted-foreground", children: labels.writing }),
|
|
719
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
720
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkCW3AFJBRcjs.Button, { ...primary, size: "sm", variant: "outline", onClick: () => stop(editor), children: labels.stop })
|
|
720
721
|
] }),
|
|
721
722
|
phase === "error" && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
722
723
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { role: "alert", className: "text-sm text-destructive", children: labels.failed }),
|
|
723
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
724
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
724
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkCW3AFJBRcjs.Button, { ...primary, size: "sm", onClick: () => retry(editor, stream), children: labels.retry }),
|
|
725
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkCW3AFJBRcjs.Button, { size: "sm", variant: "ghost", onClick: onDiscard, children: labels.discard })
|
|
725
726
|
] }),
|
|
726
727
|
(phase === "done" || phase === "stopped") && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
727
728
|
phase === "stopped" && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-sm text-muted-foreground", children: labels.stopped }),
|
|
728
729
|
gone && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "w-full text-xs text-muted-foreground", children: labels.targetGone }),
|
|
729
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
730
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
731
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
730
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkCW3AFJBRcjs.Button, { ...primary, size: "sm", onClick: onAccept, children: labels.accept }),
|
|
731
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkCW3AFJBRcjs.Button, { size: "sm", variant: "ghost", onClick: onDiscard, children: labels.discard }),
|
|
732
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkCW3AFJBRcjs.Button, { size: "sm", variant: "ghost", onClick: () => retry(editor, stream), children: labels.retry }),
|
|
732
733
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "form", { className: "w-full", onSubmit: onFollowUp, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
733
|
-
|
|
734
|
+
_chunkCW3AFJBRcjs.Input,
|
|
734
735
|
{
|
|
735
736
|
"aria-label": labels.followUp,
|
|
736
737
|
placeholder: labels.followUp,
|
|
@@ -754,8 +755,8 @@ function AiBar({ env, ghost, gone, barId }) {
|
|
|
754
755
|
var reader = null;
|
|
755
756
|
function readerOf() {
|
|
756
757
|
if (reader === null) {
|
|
757
|
-
const schema =
|
|
758
|
-
reader = { schema, codec:
|
|
758
|
+
const schema = _chunk4SLJNZSMcjs.createSchema.call(void 0, );
|
|
759
|
+
reader = { schema, codec: _chunk4SLJNZSMcjs.createBlockCodec.call(void 0, schema) };
|
|
759
760
|
}
|
|
760
761
|
return reader;
|
|
761
762
|
}
|
|
@@ -763,16 +764,16 @@ function fillGhost(ghost, markdown) {
|
|
|
763
764
|
const doc = ghost.ownerDocument;
|
|
764
765
|
const { schema, codec } = readerOf();
|
|
765
766
|
const drawn2 = [];
|
|
766
|
-
for (const block of
|
|
767
|
+
for (const block of _chunk4SLJNZSMcjs.fromMarkdown.call(void 0, markdown)) {
|
|
767
768
|
for (const piece of codec.toEngineBlocks(block)) {
|
|
768
769
|
const element = doc.createElement("div");
|
|
769
770
|
element.className = "quoin-ai-ghost-block";
|
|
770
771
|
element.dataset.kind = String(_nullishCoalesce(piece.attrs.kind, () => ( piece.type.toLowerCase())));
|
|
771
772
|
if (piece.attrs.level !== void 0) element.dataset.level = String(piece.attrs.level);
|
|
772
773
|
if (piece.indent > 0) element.style.setProperty("--quoin-ai-indent", String(piece.indent));
|
|
773
|
-
const region =
|
|
774
|
-
if (region === null) element.textContent =
|
|
775
|
-
else
|
|
774
|
+
const region = _chunkZKVOS57Qcjs.primaryRegion.call(void 0, schema, piece);
|
|
775
|
+
if (region === null) element.textContent = _chunkZKVOS57Qcjs.toPlainText.call(void 0, [codec.toQuoin(piece)], { schema });
|
|
776
|
+
else _chunkP6KYXFZCcjs.renderInline.call(void 0, element, piece.regions[region], []);
|
|
776
777
|
drawn2.push(element);
|
|
777
778
|
}
|
|
778
779
|
}
|
|
@@ -781,6 +782,45 @@ function fillGhost(ghost, markdown) {
|
|
|
781
782
|
|
|
782
783
|
// src/extensions/ai/use-ghost-place.ts
|
|
783
784
|
|
|
785
|
+
|
|
786
|
+
// src/extensions/ai/keep-reader-still.ts
|
|
787
|
+
function scrollerOf(root) {
|
|
788
|
+
for (let element = root; element !== null; element = element.parentElement) {
|
|
789
|
+
if (/auto|scroll/.test(getComputedStyle(element).overflowY) && element.scrollHeight > element.clientHeight) {
|
|
790
|
+
return element;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
return _nullishCoalesce(root.ownerDocument.scrollingElement, () => ( null));
|
|
794
|
+
}
|
|
795
|
+
function keepReaderStill(root, shell, change) {
|
|
796
|
+
const scroller = scrollerOf(root);
|
|
797
|
+
if (scroller === null) return change();
|
|
798
|
+
const view = scroller === root.ownerDocument.scrollingElement ? 0 : scroller.getBoundingClientRect().top;
|
|
799
|
+
if (shell.getBoundingClientRect().bottom > view) return change();
|
|
800
|
+
const next = shell.nextElementSibling;
|
|
801
|
+
const mark = () => next === null ? root.getBoundingClientRect().bottom : next.getBoundingClientRect().top;
|
|
802
|
+
const before = mark();
|
|
803
|
+
change();
|
|
804
|
+
const moved = mark() - before;
|
|
805
|
+
if (moved) scroller.scrollBy({ top: moved, behavior: "instant" });
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// src/extensions/ai/space-below.ts
|
|
809
|
+
function rowGap(shell) {
|
|
810
|
+
const parent = shell.parentElement;
|
|
811
|
+
if (parent === null || !_optionalChain([shell, 'access', _49 => _49.nextElementSibling, 'optionalAccess', _50 => _50.hasAttribute, 'call', _51 => _51("data-block-id")])) return 0;
|
|
812
|
+
const { display, flexDirection, rowGap: gap } = getComputedStyle(parent);
|
|
813
|
+
const column = display.includes("grid") || display.includes("flex") && flexDirection.startsWith("column");
|
|
814
|
+
return column ? parseFloat(gap) || 0 : 0;
|
|
815
|
+
}
|
|
816
|
+
function spaceBelow(shell) {
|
|
817
|
+
const own = getComputedStyle(shell);
|
|
818
|
+
const drawn2 = parseFloat(own.borderBottomWidth) || own.backgroundColor + own.backgroundImage !== "rgba(0, 0, 0, 0)none";
|
|
819
|
+
const padding = drawn2 ? 0 : parseFloat(own.paddingBottom) || 0;
|
|
820
|
+
return { padding, gap: rowGap(shell) };
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// src/extensions/ai/use-ghost-place.ts
|
|
784
824
|
var ROOM2 = "--quoin-ai-room";
|
|
785
825
|
var GAP = 4;
|
|
786
826
|
function place(controller, root, layer, block, region) {
|
|
@@ -795,27 +835,28 @@ function place(controller, root, layer, block, region) {
|
|
|
795
835
|
const scale = origin.width / root.offsetWidth || 1;
|
|
796
836
|
const below = shell.getBoundingClientRect();
|
|
797
837
|
const text = host.getBoundingClientRect();
|
|
838
|
+
const { padding, gap } = spaceBelow(shell);
|
|
798
839
|
const left = (text.left - origin.left) / scale - root.clientLeft + root.scrollLeft;
|
|
799
|
-
const top = (below.bottom - origin.top) / scale - root.clientTop + root.scrollTop + GAP;
|
|
840
|
+
const top = (below.bottom - origin.top) / scale - padding - root.clientTop + root.scrollTop + GAP;
|
|
800
841
|
layer.style.transform = `translate(${left}px, ${top}px)`;
|
|
801
842
|
layer.style.width = `${text.width / scale}px`;
|
|
802
|
-
clipAside(layer, text,
|
|
803
|
-
|
|
843
|
+
clipAside(layer, text, _chunkLE5XMIFJcjs.clipBoxes.call(void 0, host, root), scale);
|
|
844
|
+
const room = Math.max(0, layer.offsetHeight + 2 * GAP - padding - gap);
|
|
845
|
+
keepReaderStill(root, shell, () => root.style.setProperty(ROOM2, `${room}px`));
|
|
804
846
|
}
|
|
805
|
-
function clipAside(layer, text,
|
|
806
|
-
const
|
|
807
|
-
const
|
|
808
|
-
const end = area === void 0 ? 0 : Math.max(0, text.right - area.right) / scale;
|
|
847
|
+
function clipAside(layer, text, areas, scale) {
|
|
848
|
+
const start = Math.max(0, ...areas.map((area) => area.left - text.left)) / scale;
|
|
849
|
+
const end = Math.max(0, ...areas.map((area) => text.right - area.right)) / scale;
|
|
809
850
|
layer.style.clipPath = start > 0 || end > 0 ? `inset(0 ${end}px 0 ${start}px)` : "";
|
|
810
851
|
if (start + end >= text.width / scale && text.width > 0) layer.style.visibility = "hidden";
|
|
811
852
|
}
|
|
812
853
|
function useGhostPlace(controller, layer, block, region) {
|
|
813
854
|
const placeNow = _react.useRef.call(void 0, () => {
|
|
814
855
|
});
|
|
815
|
-
|
|
856
|
+
_chunkP6KYXFZCcjs.useIsomorphicLayoutEffect.call(void 0, () => {
|
|
816
857
|
const root = controller.root;
|
|
817
858
|
const element = layer.current;
|
|
818
|
-
const view = _optionalChain([root, 'optionalAccess',
|
|
859
|
+
const view = _optionalChain([root, 'optionalAccess', _52 => _52.ownerDocument, 'access', _53 => _53.defaultView]);
|
|
819
860
|
if (!root || !element || !view) return void 0;
|
|
820
861
|
const update = () => place(controller, root, element, block, region);
|
|
821
862
|
let frame = 0;
|
|
@@ -833,7 +874,7 @@ function useGhostPlace(controller, layer, block, region) {
|
|
|
833
874
|
const unsubscribe = controller.editor.subscribe(later);
|
|
834
875
|
const fonts = root.ownerDocument.fonts;
|
|
835
876
|
root.addEventListener("scroll", later, { capture: true, passive: true });
|
|
836
|
-
_optionalChain([fonts, 'optionalAccess',
|
|
877
|
+
_optionalChain([fonts, 'optionalAccess', _54 => _54.addEventListener, 'call', _55 => _55("loadingdone", later)]);
|
|
837
878
|
return () => {
|
|
838
879
|
placeNow.current = () => {
|
|
839
880
|
};
|
|
@@ -841,7 +882,7 @@ function useGhostPlace(controller, layer, block, region) {
|
|
|
841
882
|
observer.disconnect();
|
|
842
883
|
unsubscribe();
|
|
843
884
|
root.removeEventListener("scroll", later, { capture: true });
|
|
844
|
-
_optionalChain([fonts, 'optionalAccess',
|
|
885
|
+
_optionalChain([fonts, 'optionalAccess', _56 => _56.removeEventListener, 'call', _57 => _57("loadingdone", later)]);
|
|
845
886
|
root.style.removeProperty(ROOM2);
|
|
846
887
|
};
|
|
847
888
|
}, [controller, layer, block, region]);
|
|
@@ -854,7 +895,7 @@ function AiGhost({ env, ghost, target, gone, barId }) {
|
|
|
854
895
|
const layer = _react.useRef.call(void 0, null);
|
|
855
896
|
const drawn2 = _react.useRef.call(void 0, null);
|
|
856
897
|
const place2 = useGhostPlace(env.controller, layer, target.to.block, target.to.region);
|
|
857
|
-
|
|
898
|
+
_chunkP6KYXFZCcjs.useIsomorphicLayoutEffect.call(void 0, () => {
|
|
858
899
|
const element = drawn2.current;
|
|
859
900
|
if (element === null) return;
|
|
860
901
|
element.setAttribute("inert", "");
|
|
@@ -874,7 +915,7 @@ function AiGhost({ env, ghost, target, gone, barId }) {
|
|
|
874
915
|
|
|
875
916
|
function useTargetAnchor(controller, target) {
|
|
876
917
|
const latest = _react.useRef.call(void 0, target);
|
|
877
|
-
|
|
918
|
+
_chunkP6KYXFZCcjs.useIsomorphicLayoutEffect.call(void 0, () => {
|
|
878
919
|
latest.current = target;
|
|
879
920
|
});
|
|
880
921
|
return _react.useMemo.call(void 0, () => {
|
|
@@ -883,8 +924,8 @@ function useTargetAnchor(controller, target) {
|
|
|
883
924
|
return { block: to.block, region: to.region, from: to.offset, to: to.offset };
|
|
884
925
|
};
|
|
885
926
|
return {
|
|
886
|
-
current:
|
|
887
|
-
() =>
|
|
927
|
+
current: _chunkCW3AFJBRcjs.virtualAnchor.call(void 0,
|
|
928
|
+
() => _chunkCW3AFJBRcjs.rangeRect.call(void 0, controller, end()),
|
|
888
929
|
() => controller.registry.get(end().block, end().region)
|
|
889
930
|
)
|
|
890
931
|
};
|
|
@@ -941,7 +982,7 @@ function AiMenu({ env, target, fromSpace }) {
|
|
|
941
982
|
};
|
|
942
983
|
const fieldLabel = asking === "tone" ? labels.tonePrompt : asking === "translate" ? labels.translatePrompt : labels.prompt;
|
|
943
984
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
944
|
-
|
|
985
|
+
_chunkCW3AFJBRcjs.Popover,
|
|
945
986
|
{
|
|
946
987
|
open: true,
|
|
947
988
|
modal: false,
|
|
@@ -949,9 +990,9 @@ function AiMenu({ env, target, fromSpace }) {
|
|
|
949
990
|
if (!next) closeMenu(controller.editor);
|
|
950
991
|
},
|
|
951
992
|
children: [
|
|
952
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
993
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkCW3AFJBRcjs.PopoverAnchor, { virtualRef: anchor }),
|
|
953
994
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
954
|
-
|
|
995
|
+
_chunkCW3AFJBRcjs.PopoverContent,
|
|
955
996
|
{
|
|
956
997
|
"aria-label": labels.askAi,
|
|
957
998
|
side: "bottom",
|
|
@@ -959,7 +1000,7 @@ function AiMenu({ env, target, fromSpace }) {
|
|
|
959
1000
|
className: "w-80 p-1",
|
|
960
1001
|
onOpenAutoFocus: (event) => {
|
|
961
1002
|
event.preventDefault();
|
|
962
|
-
_optionalChain([field, 'access',
|
|
1003
|
+
_optionalChain([field, 'access', _58 => _58.current, 'optionalAccess', _59 => _59.focus, 'call', _60 => _60()]);
|
|
963
1004
|
},
|
|
964
1005
|
onCloseAutoFocus: (event) => event.preventDefault(),
|
|
965
1006
|
onEscapeKeyDown: (event) => {
|
|
@@ -968,7 +1009,7 @@ function AiMenu({ env, target, fromSpace }) {
|
|
|
968
1009
|
},
|
|
969
1010
|
children: [
|
|
970
1011
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
971
|
-
|
|
1012
|
+
_chunkCW3AFJBRcjs.Input,
|
|
972
1013
|
{
|
|
973
1014
|
ref: field,
|
|
974
1015
|
role: "combobox",
|
|
@@ -992,7 +1033,7 @@ function AiMenu({ env, target, fromSpace }) {
|
|
|
992
1033
|
id: optionId(index),
|
|
993
1034
|
role: "option",
|
|
994
1035
|
"aria-selected": index === current,
|
|
995
|
-
className:
|
|
1036
|
+
className: _chunkCW3AFJBRcjs.cn.call(void 0, _chunkCW3AFJBRcjs.MENU_ITEM, index === current && "bg-accent text-accent-foreground"),
|
|
996
1037
|
onMouseDown: (event) => event.preventDefault(),
|
|
997
1038
|
onClick: () => choose(choice),
|
|
998
1039
|
children: choice.label
|
|
@@ -1013,17 +1054,17 @@ var goneOf = (state) => {
|
|
|
1013
1054
|
const { target, ghost } = aiState(state);
|
|
1014
1055
|
return target !== null && ghost !== null && targetGone(state.doc, target);
|
|
1015
1056
|
};
|
|
1016
|
-
var plainText = (markdown) =>
|
|
1057
|
+
var plainText = (markdown) => _chunkZKVOS57Qcjs.toPlainText.call(void 0, _chunk4SLJNZSMcjs.fromMarkdown.call(void 0, markdown));
|
|
1017
1058
|
function AiOverlay({ controller, stream, labels }) {
|
|
1018
|
-
const { onError } =
|
|
1059
|
+
const { onError } = _chunkCW3AFJBRcjs.useQuoin.call(void 0, );
|
|
1019
1060
|
const link = linkOf(controller.editor);
|
|
1020
|
-
|
|
1061
|
+
_chunkP6KYXFZCcjs.useIsomorphicLayoutEffect.call(void 0, () => {
|
|
1021
1062
|
link.report = onError;
|
|
1022
1063
|
link.ended = (phase, output) => controller.announce((phase === "done" ? labels.written : labels.halted)(plainText(output)));
|
|
1023
1064
|
}, [link, onError, controller, labels]);
|
|
1024
|
-
const value =
|
|
1025
|
-
const gone =
|
|
1026
|
-
const readOnly =
|
|
1065
|
+
const value = _chunkP6KYXFZCcjs.useEditorState.call(void 0, controller, aiState);
|
|
1066
|
+
const gone = _chunkP6KYXFZCcjs.useEditorState.call(void 0, controller, goneOf);
|
|
1067
|
+
const readOnly = _chunkP6KYXFZCcjs.useReadOnly.call(void 0, controller);
|
|
1027
1068
|
const env = _react.useMemo.call(void 0, () => ({ controller, stream, labels }), [controller, stream, labels]);
|
|
1028
1069
|
const barId = _react.useId.call(void 0, );
|
|
1029
1070
|
const showing = value.ghost !== null;
|
|
@@ -1031,8 +1072,8 @@ function AiOverlay({ controller, stream, labels }) {
|
|
|
1031
1072
|
if (!showing) return;
|
|
1032
1073
|
const toBar = (event) => {
|
|
1033
1074
|
if (event.key !== "Escape" || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return false;
|
|
1034
|
-
const bar = _optionalChain([controller, 'access',
|
|
1035
|
-
_optionalChain([bar, 'optionalAccess',
|
|
1075
|
+
const bar = _optionalChain([controller, 'access', _61 => _61.root, 'optionalAccess', _62 => _62.ownerDocument, 'access', _63 => _63.getElementById, 'call', _64 => _64(barId)]);
|
|
1076
|
+
_optionalChain([bar, 'optionalAccess', _65 => _65.querySelector, 'call', _66 => _66(`[${PRIMARY}]`), 'optionalAccess', _67 => _67.focus, 'call', _68 => _68()]);
|
|
1036
1077
|
return bar != null;
|
|
1037
1078
|
};
|
|
1038
1079
|
controller.keyInterceptors.add(toBar);
|
|
@@ -1042,7 +1083,7 @@ function AiOverlay({ controller, stream, labels }) {
|
|
|
1042
1083
|
const root = controller.root;
|
|
1043
1084
|
if (!root) return;
|
|
1044
1085
|
let away = true;
|
|
1045
|
-
return
|
|
1086
|
+
return _chunk73653CJ4cjs.watchCanvasFocus.call(void 0, controller, root.ownerDocument, (inside, node) => {
|
|
1046
1087
|
if (!inside) away = true;
|
|
1047
1088
|
else if (root.contains(node)) {
|
|
1048
1089
|
if (away) noteFocus(controller.editor);
|
|
@@ -1081,7 +1122,7 @@ function aiExtension(options) {
|
|
|
1081
1122
|
blockMenuItems: [aiBlockMenuItem(labels)],
|
|
1082
1123
|
overlays: [Overlay]
|
|
1083
1124
|
};
|
|
1084
|
-
return
|
|
1125
|
+
return _chunkZKVOS57Qcjs.defineExtension.call(void 0, {
|
|
1085
1126
|
name: "ai",
|
|
1086
1127
|
plugins: [aiPlugin],
|
|
1087
1128
|
decorations: aiDecorations,
|
|
@@ -1092,7 +1133,7 @@ function aiExtension(options) {
|
|
|
1092
1133
|
if (link.mounts++ === 0) link.stopWatching = watchGhost(editor, link);
|
|
1093
1134
|
return () => {
|
|
1094
1135
|
if (--link.mounts > 0) return;
|
|
1095
|
-
_optionalChain([link, 'access',
|
|
1136
|
+
_optionalChain([link, 'access', _69 => _69.stopWatching, 'optionalCall', _70 => _70()]);
|
|
1096
1137
|
link.stopWatching = null;
|
|
1097
1138
|
endRun(link);
|
|
1098
1139
|
};
|