edytor 3.0.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/block/block.svelte.js +6 -21
- package/dist/block/inlineBlock.svelte.js +4 -6
- package/dist/clipboard/moveText.js +6 -8
- package/dist/cloudflare/DocumentRoom.d.ts +23 -12
- package/dist/cloudflare/DocumentRoom.js +10 -6
- package/dist/cloudflare/index.d.ts +1 -1
- package/dist/cloudflare/room/access.d.ts +8 -3
- package/dist/cloudflare/room/access.js +44 -20
- package/dist/cloudflare/room/admission.js +28 -5
- package/dist/cloudflare/room/comments.js +28 -1
- package/dist/cloudflare/room/context.d.ts +139 -129
- package/dist/cloudflare/room/context.js +11 -4
- package/dist/cloudflare/room/forged.d.ts +14 -0
- package/dist/cloudflare/room/forged.js +30 -2
- package/dist/cloudflare/room/frames.d.ts +4 -0
- package/dist/cloudflare/room/frames.js +5 -1
- package/dist/cloudflare/room/purge.d.ts +6 -3
- package/dist/cloudflare/room/purge.js +45 -19
- package/dist/cloudflare/room/replicas.d.ts +4 -2
- package/dist/cloudflare/room/replicas.js +8 -3
- package/dist/cloudflare/room/suggest.d.ts +7 -0
- package/dist/cloudflare/room/suggest.js +143 -0
- package/dist/cloudflare/room/updates.d.ts +3 -4
- package/dist/cloudflare/room/updates.js +6 -8
- package/dist/cloudflare/routeDocumentSocket.d.ts +10 -1
- package/dist/cloudflare/routeDocumentSocket.js +5 -3
- package/dist/components/Content.svelte +9 -1
- package/dist/crdt/anchors.js +3 -2
- package/dist/crdt/attribution/attribution.d.ts +34 -12
- package/dist/crdt/attribution/attribution.js +59 -21
- package/dist/crdt/attribution/block.d.ts +3 -3
- package/dist/crdt/attribution/block.js +7 -8
- package/dist/crdt/attribution/index.d.ts +1 -1
- package/dist/crdt/attribution/index.js +1 -1
- package/dist/crdt/data.d.ts +62 -31
- package/dist/crdt/data.js +211 -103
- package/dist/crdt/doc/capability.js +9 -13
- package/dist/crdt/doc/delete.d.ts +1 -1
- package/dist/crdt/doc/delete.js +13 -30
- package/dist/crdt/doc/funnel.d.ts +1 -1
- package/dist/crdt/doc/funnel.js +34 -26
- package/dist/crdt/doc/gate.d.ts +3 -3
- package/dist/crdt/doc/history.d.ts +31 -9
- package/dist/crdt/doc/history.js +204 -73
- package/dist/crdt/doc/layout.js +16 -27
- package/dist/crdt/doc/meta.js +17 -23
- package/dist/crdt/doc/moves.d.ts +1 -1
- package/dist/crdt/doc/moves.js +52 -32
- package/dist/crdt/doc/reads.d.ts +2 -2
- package/dist/crdt/doc/reads.js +20 -30
- package/dist/crdt/doc/seed.js +2 -2
- package/dist/crdt/doc/split-merge.js +12 -15
- package/dist/crdt/doc/steps.d.ts +25 -13
- package/dist/crdt/doc/steps.js +55 -125
- package/dist/crdt/doc/suggest.d.ts +167 -0
- package/dist/crdt/doc/suggest.js +579 -0
- package/dist/crdt/doc/types.d.ts +21 -9
- package/dist/crdt/document.d.ts +26 -6
- package/dist/crdt/document.js +40 -15
- package/dist/crdt/edytor-doc.d.ts +410 -390
- package/dist/crdt/edytor-doc.js +143 -95
- package/dist/crdt/engine-api.d.ts +39 -1
- package/dist/crdt/engine.d.ts +2 -2
- package/dist/crdt/engine.js +4 -5
- package/dist/crdt/flow.d.ts +2 -2
- package/dist/crdt/flow.js +29 -37
- package/dist/crdt/index.d.ts +134 -127
- package/dist/crdt/index.js +4 -1
- package/dist/crdt/operations.d.ts +12 -3
- package/dist/crdt/parts.d.ts +43 -0
- package/dist/crdt/parts.js +44 -0
- package/dist/crdt/placement/display.d.ts +6 -31
- package/dist/crdt/placement/display.js +41 -146
- package/dist/crdt/placement/keys.d.ts +63 -0
- package/dist/crdt/placement/keys.js +95 -0
- package/dist/crdt/placement/model.d.ts +119 -118
- package/dist/crdt/placement/model.js +242 -142
- package/dist/crdt/placement/resolve.d.ts +123 -35
- package/dist/crdt/placement/resolve.js +188 -153
- package/dist/crdt/placement/slots.d.ts +134 -0
- package/dist/crdt/placement/slots.js +152 -0
- package/dist/crdt/protocol.d.ts +1 -1
- package/dist/crdt/protocol.js +1 -1
- package/dist/crdt/protocols/auth.d.ts +11 -1
- package/dist/crdt/protocols/auth.js +17 -1
- package/dist/crdt/protocols/comments.d.ts +16 -1
- package/dist/crdt/protocols/comments.js +9 -1
- package/dist/crdt/providers/index.d.ts +6 -4
- package/dist/crdt/providers/index.js +3 -1
- package/dist/crdt/providers/room.d.ts +16 -2
- package/dist/crdt/providers/room.js +64 -17
- package/dist/crdt/providers/websocket.d.ts +6 -4
- package/dist/crdt/providers/websocket.js +14 -7
- package/dist/crdt/purge.d.ts +24 -9
- package/dist/crdt/purge.js +266 -126
- package/dist/crdt/restore.d.ts +4 -3
- package/dist/crdt/restore.js +27 -34
- package/dist/crdt/schema.d.ts +30 -21
- package/dist/crdt/schema.js +39 -23
- package/dist/crdt/storage.d.ts +1 -1
- package/dist/crdt/storage.js +1 -1
- package/dist/crdt/structs.d.ts +17 -15
- package/dist/crdt/structs.js +10 -22
- package/dist/crdt/text/index/cache.js +4 -0
- package/dist/crdt/text/index/claims.js +13 -23
- package/dist/crdt/text/index/fold.d.ts +2 -3
- package/dist/crdt/text/index/fold.js +194 -44
- package/dist/crdt/text/index/layout.d.ts +2 -4
- package/dist/crdt/text/index/layout.js +14 -30
- package/dist/crdt/text/index/placement.d.ts +26 -18
- package/dist/crdt/text/index/placement.js +420 -157
- package/dist/crdt/text/index/records.d.ts +3 -3
- package/dist/crdt/text/index/records.js +24 -31
- package/dist/crdt/text/index/shared.d.ts +11 -3
- package/dist/crdt/text/index/shared.js +29 -17
- package/dist/crdt/text/index/state.d.ts +21 -17
- package/dist/crdt/text/index/state.js +15 -17
- package/dist/crdt/text/index/streams.d.ts +10 -5
- package/dist/crdt/text/index/streams.js +78 -27
- package/dist/crdt/text/items.d.ts +10 -3
- package/dist/crdt/text/items.js +7 -0
- package/dist/crdt/text/model.d.ts +87 -62
- package/dist/crdt/text/model.js +91 -169
- package/dist/crdt/text/ranges.d.ts +1 -1
- package/dist/crdt/text/ranges.js +3 -3
- package/dist/crdt/text/rows.d.ts +13 -7
- package/dist/crdt/text/rows.js +24 -7
- package/dist/crdt/text/runs.d.ts +51 -42
- package/dist/crdt/text/runs.js +24 -7
- package/dist/crdt/text/streams.d.ts +26 -13
- package/dist/crdt/text/streams.js +41 -14
- package/dist/crdt/vendor/yjs/UPSTREAM.md +651 -21
- package/dist/crdt/vendor/yjs/dts/global.d.ts +2 -0
- package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
- package/dist/crdt/vendor/yjs/dts/structs/AbstractStruct.d.ts +7 -0
- package/dist/crdt/vendor/yjs/dts/structs/Item.d.ts +197 -10
- package/dist/crdt/vendor/yjs/dts/utils/Doc.d.ts +6 -3
- package/dist/crdt/vendor/yjs/dts/utils/StructStore.d.ts +5 -18
- package/dist/crdt/vendor/yjs/dts/utils/Transaction.d.ts +15 -1
- package/dist/crdt/vendor/yjs/dts/utils/UpdateDecoder.d.ts +14 -6
- package/dist/crdt/vendor/yjs/dts/utils/UpdateEncoder.d.ts +33 -6
- package/dist/crdt/vendor/yjs/dts/utils/ids.d.ts +1 -65
- package/dist/crdt/vendor/yjs/dts/utils/marks.d.ts +6 -1
- package/dist/crdt/vendor/yjs/dts/ynode.d.ts +54 -7
- package/dist/crdt/vendor/yjs/global.d.ts +2 -0
- package/dist/crdt/vendor/yjs/src/index.js +1 -1
- package/dist/crdt/vendor/yjs/src/structs/AbstractStruct.js +10 -0
- package/dist/crdt/vendor/yjs/src/structs/Item.js +649 -51
- package/dist/crdt/vendor/yjs/src/utils/BlockSet.js +18 -9
- package/dist/crdt/vendor/yjs/src/utils/Doc.js +5 -3
- package/dist/crdt/vendor/yjs/src/utils/StructStore.js +5 -0
- package/dist/crdt/vendor/yjs/src/utils/Transaction.js +36 -13
- package/dist/crdt/vendor/yjs/src/utils/UpdateDecoder.js +88 -18
- package/dist/crdt/vendor/yjs/src/utils/UpdateEncoder.js +119 -32
- package/dist/crdt/vendor/yjs/src/utils/encoding.js +13 -1
- package/dist/crdt/vendor/yjs/src/utils/ids.js +7 -7
- package/dist/crdt/vendor/yjs/src/utils/marks.js +193 -8
- package/dist/crdt/vendor/yjs/src/utils/redo.js +1 -1
- package/dist/crdt/vendor/yjs/src/utils/transaction-helpers.js +1 -1
- package/dist/crdt/vendor/yjs/src/utils/updates.js +3 -3
- package/dist/crdt/vendor/yjs/src/ynode.js +199 -12
- package/dist/edytor.svelte.d.ts +1 -1
- package/dist/edytor.svelte.js +103 -75
- package/dist/events/beforeInputCommands.d.ts +2 -2
- package/dist/events/beforeInputCommands.js +2 -2
- package/dist/events/nativeInteractiveControl.d.ts +11 -1
- package/dist/events/nativeInteractiveControl.js +41 -1
- package/dist/events/onBeforeInput.js +3 -3
- package/dist/events/onFocus.js +9 -11
- package/dist/events/onPageHide.d.ts +3 -0
- package/dist/events/onPageHide.js +16 -0
- package/dist/export/forms.d.ts +50 -50
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1 -1
- package/dist/labels.d.ts +8 -0
- package/dist/labels.js +6 -1
- package/dist/plugins/comments/CommentsController.svelte.d.ts +5 -1
- package/dist/plugins/comments/CommentsController.svelte.js +14 -4
- package/dist/plugins/find/search.js +2 -2
- package/dist/plugins/index.d.ts +1 -0
- package/dist/plugins/index.js +1 -0
- package/dist/plugins/suggestions/SuggestionBars.svelte +40 -23
- package/dist/plugins/suggestions/SuggestionBars.svelte.d.ts +3 -1
- package/dist/plugins/suggestions/SuggestionRuns.svelte +77 -0
- package/dist/plugins/suggestions/SuggestionRuns.svelte.d.ts +9 -0
- package/dist/plugins/suggestions/shared.svelte.d.ts +58 -0
- package/dist/plugins/suggestions/shared.svelte.js +421 -0
- package/dist/plugins/suggestions/suggestionsPlugin.d.ts +15 -4
- package/dist/selection/domSelection.d.ts +2 -0
- package/dist/selection/domSelection.js +6 -2
- package/dist/selection/pointer.svelte.js +75 -48
- package/dist/selection/replaceSelection.js +2 -6
- package/dist/selection/selection.svelte.d.ts +3 -3
- package/dist/selection/selection.svelte.js +39 -32
- package/dist/selection/selection.utils.d.ts +14 -0
- package/dist/selection/selection.utils.js +28 -3
- package/dist/session/commands.js +21 -11
- package/dist/session/composition.svelte.d.ts +5 -3
- package/dist/session/composition.svelte.js +21 -17
- package/dist/session/handles.d.ts +4 -17
- package/dist/session/handles.js +11 -37
- package/dist/session/navigation.js +6 -9
- package/dist/session/selection.d.ts +11 -5
- package/dist/session/selection.js +14 -48
- package/dist/session/suggestions.svelte.d.ts +38 -9
- package/dist/session/suggestions.svelte.js +37 -18
- package/dist/session/virtual.d.ts +1 -1
- package/dist/surface/attributes.d.ts +5 -0
- package/dist/surface/attributes.js +2 -2
- package/dist/surface/cells.d.ts +2 -26
- package/dist/surface/cells.js +2 -33
- package/dist/surface/observer.svelte.d.ts +6 -1
- package/dist/surface/observer.svelte.js +130 -83
- package/dist/surface/pin.svelte.d.ts +9 -9
- package/dist/surface/pin.svelte.js +15 -22
- package/dist/surface/projector.svelte.d.ts +15 -3
- package/dist/surface/projector.svelte.js +67 -22
- package/dist/text/text.svelte.js +14 -19
- package/dist/themes/notion.css +29 -0
- package/package.json +4 -2
- package/dist/crdt/placement/rank.d.ts +0 -87
- package/dist/crdt/placement/rank.js +0 -332
- package/dist/crdt/placement/source.d.ts +0 -43
- package/dist/crdt/placement/source.js +0 -66
- package/dist/crdt/text/delete-index.d.ts +0 -129
- package/dist/crdt/text/delete-index.js +0 -365
- package/dist/crdt/text/deletes.d.ts +0 -76
- package/dist/crdt/text/deletes.js +0 -362
- package/dist/crdt/text/index/anchored.d.ts +0 -17
- package/dist/crdt/text/index/anchored.js +0 -197
|
@@ -34,10 +34,12 @@
|
|
|
34
34
|
* the render epoch and the gesture serial: echo, render drift (displayed
|
|
35
35
|
* again), composition, foreign write or intent (adopted). Two named, counted,
|
|
36
36
|
* time-bounded browser rules are the only signatures:
|
|
37
|
-
* the Android post-delete snap-back and the IME post-commit jump.
|
|
37
|
+
* the Android post-delete snap-back and the IME post-commit jump. A render
|
|
38
|
+
* that moved the DOM selection with no event to say so is drift too, read
|
|
39
|
+
* from the observer's records (`rendered`, `sel.drift.unreported`).
|
|
38
40
|
*/
|
|
39
41
|
import { untrack } from 'svelte';
|
|
40
|
-
import { clearDomSelection, createDomRange, getActiveElement, getDomSelection, getDomSelectionSnapshot, scrollCaretIntoView } from '../selection/domSelection.js';
|
|
42
|
+
import { clearDomSelection, createDomRange, focusIn, getActiveElement, getDomSelection, getDomSelectionSnapshot, scrollCaretIntoView } from '../selection/domSelection.js';
|
|
41
43
|
import { getMarkEdgeSide, getYIndex, isTextBoundSelectionPoint } from '../selection/selection.utils.js';
|
|
42
44
|
import { project, sameEnds } from '../session/selection.js';
|
|
43
45
|
import { isAndroidChromeBrowser } from '../events/events.utils.js';
|
|
@@ -88,6 +90,8 @@ export class Projector {
|
|
|
88
90
|
#opening = false;
|
|
89
91
|
/** The gesture serial at the last observation (a display or an adopted `selectionchange`). */
|
|
90
92
|
#serial = -1;
|
|
93
|
+
/** Where the DOM selection was at that observation (`#note`; null: no range). */
|
|
94
|
+
#at = null;
|
|
91
95
|
/** The named rules' evidence: the last model-owned delete and the last IME commit. */
|
|
92
96
|
#deleted = null;
|
|
93
97
|
#committed = null;
|
|
@@ -112,9 +116,7 @@ export class Projector {
|
|
|
112
116
|
pre = () => {
|
|
113
117
|
void this.#deps();
|
|
114
118
|
untrack(() => {
|
|
115
|
-
|
|
116
|
-
const active = node && getActiveElement(node);
|
|
117
|
-
this.#noted = active && node.contains(active) ? active : null;
|
|
119
|
+
this.#noted = focusIn(this.edytor.node);
|
|
118
120
|
});
|
|
119
121
|
};
|
|
120
122
|
/** Root post-flush `$effect`: every DOM write of the flush has landed. */
|
|
@@ -183,14 +185,51 @@ export class Projector {
|
|
|
183
185
|
* removal re-rendering the text under a range) runs now. The flag is no
|
|
184
186
|
* reactive source, so no later flush is owed: without this, the range the
|
|
185
187
|
* render moved (Firefox keeps it where the removed element stood) would be
|
|
186
|
-
* adopted as the gesture's when its `selectionchange` lands.
|
|
187
|
-
* calls it too once it processed DOM records: a display that waits gets a
|
|
188
|
-
* pass.
|
|
188
|
+
* adopted as the gesture's when its `selectionchange` lands.
|
|
189
189
|
*/
|
|
190
190
|
inputHandled = () => {
|
|
191
191
|
if (this.#pending)
|
|
192
192
|
this.edytor.surface.update();
|
|
193
193
|
};
|
|
194
|
+
/**
|
|
195
|
+
* The observer compared the records of a DOM change (a render, a browser
|
|
196
|
+
* edit): a display that waits on them gets a pass (`inputHandled`), and a
|
|
197
|
+
* DOM selection they moved off the last observation with no gesture since
|
|
198
|
+
* is render drift no event reports (`sel.drift.unreported`). A render no
|
|
199
|
+
* commit asked for (a kind's decoration reading state of its own: a code
|
|
200
|
+
* grammar that lands re-tokenizes its lines) replaced the text node the
|
|
201
|
+
* caret was in; the browser left the caret where that node stood (the
|
|
202
|
+
* line's start) and Chromium and WebKit send no `selectionchange` for it,
|
|
203
|
+
* while one that comes would read as foreign (no flush ran the projector
|
|
204
|
+
* since). The value is displayed again now, in the render's own task:
|
|
205
|
+
* before a key, or its `beforeinput` declaring that caret, can read it.
|
|
206
|
+
*/
|
|
207
|
+
rendered = () => {
|
|
208
|
+
if (this.#unreported())
|
|
209
|
+
this.edytor.selection.display();
|
|
210
|
+
else
|
|
211
|
+
this.inputHandled();
|
|
212
|
+
};
|
|
213
|
+
/**
|
|
214
|
+
* `rendered`'s drift: a text value, no display waiting, no gesture, drag
|
|
215
|
+
* or composition since the last observation, and the DOM selection in the
|
|
216
|
+
* host no longer where that observation found it.
|
|
217
|
+
*/
|
|
218
|
+
#unreported = () => {
|
|
219
|
+
const { edytor } = this;
|
|
220
|
+
const { selection } = edytor;
|
|
221
|
+
const at = this.#at;
|
|
222
|
+
const node = edytor.node;
|
|
223
|
+
if (!at || !node || selection.value.kind !== 'text' || edytor.intentSerial !== this.#serial)
|
|
224
|
+
return false;
|
|
225
|
+
if (selection.request !== this.#request || selection.dragging || edytor.composition.live)
|
|
226
|
+
return false;
|
|
227
|
+
const dom = getDomSelection(node);
|
|
228
|
+
if (!dom || !isOwnPoint(node, dom.anchorNode))
|
|
229
|
+
return false;
|
|
230
|
+
return !(samePoint(at.anchor, dom.anchorNode, dom.anchorOffset) &&
|
|
231
|
+
samePoint(at.focus, dom.focusNode, dom.focusOffset));
|
|
232
|
+
};
|
|
194
233
|
/**
|
|
195
234
|
* Classify a `selectionchange` by comparing the DOM selection with the
|
|
196
235
|
* last display, the render epoch and the gesture serial:
|
|
@@ -283,9 +322,19 @@ export class Projector {
|
|
|
283
322
|
/** The DOM selection is observed now (adopted, or ignored by the adopter's own rules). */
|
|
284
323
|
#observed = (observation) => {
|
|
285
324
|
this.#seen = this.#flushes;
|
|
286
|
-
this.#
|
|
325
|
+
this.#note();
|
|
287
326
|
return observation;
|
|
288
327
|
};
|
|
328
|
+
/** An observation: its gesture serial, and where the DOM selection is (`#at`). */
|
|
329
|
+
#note = () => {
|
|
330
|
+
const { edytor } = this;
|
|
331
|
+
this.#serial = edytor.intentSerial;
|
|
332
|
+
const dom = edytor.node && getDomSelection(edytor.node);
|
|
333
|
+
this.#at =
|
|
334
|
+
dom?.anchorNode && dom.focusNode
|
|
335
|
+
? { anchor: [dom.anchorNode, dom.anchorOffset], focus: [dom.focusNode, dom.focusOffset] }
|
|
336
|
+
: null;
|
|
337
|
+
};
|
|
289
338
|
/** The DOM selection is our last display unchanged, or a text-bound range that shows the value. */
|
|
290
339
|
#echoes = (dom) => {
|
|
291
340
|
const displayed = this.#displayed;
|
|
@@ -344,7 +393,7 @@ export class Projector {
|
|
|
344
393
|
observe = () => {
|
|
345
394
|
this.#parking = false;
|
|
346
395
|
this.#seen = this.#flushes;
|
|
347
|
-
this.#
|
|
396
|
+
this.#note();
|
|
348
397
|
this.#displayed = null;
|
|
349
398
|
};
|
|
350
399
|
/** Answers whether the pass completed (false: wait for a later flush). */
|
|
@@ -379,8 +428,7 @@ export class Projector {
|
|
|
379
428
|
// whose `selectionchange` can land after that key counted as a
|
|
380
429
|
// gesture) leaves any caret the browser parks later its own
|
|
381
430
|
// (`#parking`, `placed`), whatever key comes before it.
|
|
382
|
-
|
|
383
|
-
if (requested && value.kind !== 'blocks' && active && node.contains(active))
|
|
431
|
+
if (requested && value.kind !== 'blocks' && focusIn(node))
|
|
384
432
|
this.#parking = true;
|
|
385
433
|
const inside = !!dom?.anchorNode && node.contains(dom.anchorNode);
|
|
386
434
|
const clears = value.kind === 'none' ? requested && inside : inside || dom?.rangeCount === 0;
|
|
@@ -388,7 +436,7 @@ export class Projector {
|
|
|
388
436
|
if (inside)
|
|
389
437
|
clearDomSelection(node);
|
|
390
438
|
this.#displayed = null;
|
|
391
|
-
this.#
|
|
439
|
+
this.#note();
|
|
392
440
|
}
|
|
393
441
|
return true;
|
|
394
442
|
}
|
|
@@ -408,15 +456,11 @@ export class Projector {
|
|
|
408
456
|
if (!this.#shows(dom, range, isReversed)) {
|
|
409
457
|
if (!this.#ours(requested))
|
|
410
458
|
return true;
|
|
411
|
-
|
|
412
|
-
if (!(active && node.contains(active))) {
|
|
459
|
+
if (!focusIn(node)) {
|
|
413
460
|
// Focus first (focusing a host can move the selection): the editing
|
|
414
461
|
// host, never a focusable element inside the text such as a link.
|
|
415
|
-
edytor.expectInternalFocus();
|
|
416
462
|
const element = start[0] instanceof HTMLElement ? start[0] : start[0].parentElement;
|
|
417
|
-
(element?.closest('[contenteditable="true"]') ?? node)
|
|
418
|
-
preventScroll: true
|
|
419
|
-
});
|
|
463
|
+
edytor.expectInternalFocus(element?.closest('[contenteditable="true"]') ?? node);
|
|
420
464
|
}
|
|
421
465
|
this.#write(getDomSelection(node) ?? dom, points, isCollapsed);
|
|
422
466
|
if (selection.scrollOnDisplay)
|
|
@@ -425,7 +469,7 @@ export class Projector {
|
|
|
425
469
|
selection.scrollOnDisplay = false;
|
|
426
470
|
this.#displayed = { ...points, epoch: selection.epoch };
|
|
427
471
|
this.#seen = this.#flushes;
|
|
428
|
-
this.#
|
|
472
|
+
this.#note();
|
|
429
473
|
selection.observed({
|
|
430
474
|
edge: isCollapsed ? getMarkEdgeSide(startText, start[0], yStart) : undefined
|
|
431
475
|
});
|
|
@@ -500,10 +544,11 @@ export class Projector {
|
|
|
500
544
|
#ours = (requested) => {
|
|
501
545
|
const node = this.edytor.node;
|
|
502
546
|
const active = getActiveElement(node);
|
|
547
|
+
const inside = focusIn(node);
|
|
503
548
|
// A form control or a foreign editable in a kind's chrome (a field bound to
|
|
504
549
|
// `block.data`) owns its focus and its selection: never written over.
|
|
505
|
-
if (
|
|
506
|
-
return !isKindControl(node,
|
|
550
|
+
if (inside)
|
|
551
|
+
return !isKindControl(node, inside);
|
|
507
552
|
const document = node.ownerDocument;
|
|
508
553
|
if (active && active !== document.body && active !== document.documentElement)
|
|
509
554
|
return false;
|
package/dist/text/text.svelte.js
CHANGED
|
@@ -27,28 +27,26 @@ export class Text {
|
|
|
27
27
|
get parent() {
|
|
28
28
|
return this.edytor.idToBlock.block(this.blockId);
|
|
29
29
|
}
|
|
30
|
-
/** This segment in its block's parts
|
|
31
|
-
get #
|
|
30
|
+
/** This segment in its block's parts. */
|
|
31
|
+
get #parts() {
|
|
32
32
|
if ($effect.tracking())
|
|
33
33
|
this.edytor.cells?.get(this.blockId);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return { part, index };
|
|
39
|
-
}
|
|
40
|
-
return { index: -1 };
|
|
34
|
+
return this.edytor.idToBlock.parts(this.blockId);
|
|
35
|
+
}
|
|
36
|
+
get #part() {
|
|
37
|
+
return this.#parts.find((part) => part.kind === 'text' && part.segOrd === this.ordinal);
|
|
41
38
|
}
|
|
42
39
|
/** Position among the block's content parts. */
|
|
43
40
|
get index() {
|
|
44
|
-
|
|
41
|
+
const parts = this.#parts;
|
|
42
|
+
return parts.indexOf(this.#part);
|
|
45
43
|
}
|
|
46
44
|
get value() {
|
|
47
|
-
return segmentJSON(this.#
|
|
45
|
+
return segmentJSON(this.#part?.runs ?? []);
|
|
48
46
|
}
|
|
49
47
|
/** The segment's text. */
|
|
50
48
|
get stringContent() {
|
|
51
|
-
return this.#
|
|
49
|
+
return this.#part?.text ?? '';
|
|
52
50
|
}
|
|
53
51
|
get isEmpty() {
|
|
54
52
|
return this.length === 0;
|
|
@@ -57,15 +55,15 @@ export class Text {
|
|
|
57
55
|
return this.stringContent.endsWith('\n');
|
|
58
56
|
}
|
|
59
57
|
get length() {
|
|
60
|
-
return this
|
|
58
|
+
return this.stringContent.length;
|
|
61
59
|
}
|
|
62
60
|
/** Display offset of this segment inside its block's content (atoms count 1). */
|
|
63
61
|
get segStart() {
|
|
64
|
-
return this.#
|
|
62
|
+
return this.#part?.start ?? 0;
|
|
65
63
|
}
|
|
66
64
|
/** The segment is shown by a live block. */
|
|
67
65
|
get isInDocument() {
|
|
68
|
-
return this.parent.isInTree && this.#
|
|
66
|
+
return this.parent.isInTree && this.#part !== undefined;
|
|
69
67
|
}
|
|
70
68
|
/**
|
|
71
69
|
* Re-create this block's text elements from its cell — the repair of a
|
|
@@ -101,9 +99,8 @@ export class Text {
|
|
|
101
99
|
/** @internal */
|
|
102
100
|
attach = (node) => {
|
|
103
101
|
this.node = node;
|
|
104
|
-
this.edytor.nodeToText.set(node, this);
|
|
105
102
|
// Registered, it takes the attribute table's attributes (`surface/attributes.ts`).
|
|
106
|
-
const release = this.edytor.surface.register(node, 'text', this.parent.id);
|
|
103
|
+
const release = this.edytor.surface.register(node, 'text', this.parent.id, undefined, undefined, this);
|
|
107
104
|
const pluginDestroy = cleanups(this.edytor.plugins, (plugin) => plugin.onTextAttached?.({ node, text: this }));
|
|
108
105
|
// A display that waited for a mounted destination runs in the next pass.
|
|
109
106
|
this.edytor.projector?.mounted(this);
|
|
@@ -111,8 +108,6 @@ export class Text {
|
|
|
111
108
|
destroy: () => {
|
|
112
109
|
if (this.node === node)
|
|
113
110
|
this.node = undefined;
|
|
114
|
-
if (this.edytor.nodeToText.get(node) === this)
|
|
115
|
-
this.edytor.nodeToText.delete(node);
|
|
116
111
|
release();
|
|
117
112
|
pluginDestroy.forEach((destroy) => destroy());
|
|
118
113
|
}
|
package/dist/themes/notion.css
CHANGED
|
@@ -807,6 +807,35 @@
|
|
|
807
807
|
color: var(--notion-placeholder);
|
|
808
808
|
}
|
|
809
809
|
|
|
810
|
+
/*
|
|
811
|
+
* Shared suggestions: a block a suggestion made or proposes to delete
|
|
812
|
+
* (`data-edytor-suggested`), and its runs in the overlay, in its author's
|
|
813
|
+
* colour (`--edytor-range-color`).
|
|
814
|
+
*/
|
|
815
|
+
.edytor-notion [data-edytor-suggested='inserted'],
|
|
816
|
+
.edytor-notion [data-edytor-suggested='split'] {
|
|
817
|
+
box-shadow: inset 2px 0 0 var(--edytor-suggestion-inserted, rgb(68 131 97));
|
|
818
|
+
background: var(--edytor-suggestion-inserted-background, rgba(68, 131, 97, 0.07));
|
|
819
|
+
}
|
|
820
|
+
.edytor-notion [data-edytor-suggested='removed'] {
|
|
821
|
+
background: var(--edytor-suggestion-removed);
|
|
822
|
+
}
|
|
823
|
+
.edytor-notion [data-edytor-suggested='removed'] [data-edytor-text] {
|
|
824
|
+
text-decoration: line-through;
|
|
825
|
+
text-decoration-color: var(--edytor-suggestion-removed-line, rgba(235, 87, 87, 0.6));
|
|
826
|
+
}
|
|
827
|
+
:is(.edytor-notion, .edytor-notion + [data-edytor-overlay]) [data-edytor-suggestion-inserted] {
|
|
828
|
+
border-bottom: 2px solid var(--edytor-range-color, rgb(68 131 97));
|
|
829
|
+
background: color-mix(in srgb, var(--edytor-range-color, rgb(68 131 97)) 10%, transparent);
|
|
830
|
+
}
|
|
831
|
+
:is(.edytor-notion, .edytor-notion + [data-edytor-overlay]) [data-edytor-suggestion-deleted] {
|
|
832
|
+
background: linear-gradient(
|
|
833
|
+
transparent calc(50% - 1px),
|
|
834
|
+
var(--edytor-range-color, rgb(235 87 87)) calc(50% - 1px) calc(50% + 1px),
|
|
835
|
+
transparent calc(50% + 1px)
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
|
|
810
839
|
/* ── Page (a subpage link) and table of contents ────────────────────── */
|
|
811
840
|
.edytor-notion [data-edytor-type='page'] {
|
|
812
841
|
margin: 1px 0;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "edytor",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "5.0.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/beynar/edytor.git"
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"check:deps": "node scripts/deps.mjs",
|
|
24
24
|
"check:docs": "node scripts/check-docs.mjs",
|
|
25
25
|
"check:api": "node scripts/api-report.mjs --check",
|
|
26
|
+
"check:size": "node scripts/bundle-size.mjs --budget bundle-budget.json --top 0",
|
|
26
27
|
"api:report": "node scripts/api-report.mjs",
|
|
27
28
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
28
29
|
"lint": "prettier --check . && eslint .",
|
|
@@ -50,6 +51,7 @@
|
|
|
50
51
|
"bench:browser": "node bench/browser.js",
|
|
51
52
|
"bench:runs": "BENCH_RUNS=1 vitest --config ./vitest.crdt.config.ts --run src/tests/crdt/runs/runs.bench.test.ts",
|
|
52
53
|
"size": "node scripts/bundle-size.mjs --by dir",
|
|
54
|
+
"bundle:attribution": "node scripts/bundle-attribution.mjs",
|
|
53
55
|
"registry:build": "shadcn-svelte registry build ./registry.json --output site/public/r",
|
|
54
56
|
"check:registry": "node scripts/check-registry.mjs",
|
|
55
57
|
"test:registry-install": "sh tests/registry-install/run.sh",
|
|
@@ -163,7 +165,7 @@
|
|
|
163
165
|
}
|
|
164
166
|
},
|
|
165
167
|
"dependencies": {
|
|
166
|
-
"@tanstack/highlight": "^
|
|
168
|
+
"@tanstack/highlight": "^1.1.0",
|
|
167
169
|
"@tanstack/markdown": "1.0.0",
|
|
168
170
|
"esm-env": "^1.2.2",
|
|
169
171
|
"lib0-v14": "npm:lib0@1.0.0-rc.32"
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rank keys for the placement model — a Logoot-style dense ordering.
|
|
3
|
-
*
|
|
4
|
-
* A rank is a non-empty sequence of *segments* `(v, t)`:
|
|
5
|
-
*
|
|
6
|
-
* - `v` — a signed digit in `[VMIN, VMAX]` (±2^40). There is always room
|
|
7
|
-
* between two distinct digits (`rv - lv > 1`); when there is not, the
|
|
8
|
-
* generator descends one level instead of renumbering.
|
|
9
|
-
* - `t` — the allocator's `doc.clientID` (uint53), the *tiebreak* that makes
|
|
10
|
-
* concurrent allocations of the same digit path distinct, or none
|
|
11
|
-
* (`Infinity`: sorts above every client) on a segment that only leads to
|
|
12
|
-
* deeper ones. The last segment always carries one (schema
|
|
13
|
-
* generation 5): a rank's own client sits on its last segment, and a
|
|
14
|
-
* copied segment keeps its tie only where the order needs it (between two
|
|
15
|
-
* keys that differ only by their ties).
|
|
16
|
-
*
|
|
17
|
-
* Encoding is variable-length and order-preserving, so the persisted rank
|
|
18
|
-
* is a plain string and the comparator is `a < b` (CRDT study 2026-10;
|
|
19
|
-
* up to generation 4 every segment was 16 characters, 9 of them a tie):
|
|
20
|
-
*
|
|
21
|
-
* - `v` → a length character (`'H'` for zero, `'H' + k` for a positive
|
|
22
|
-
* digit of `k` base-64 places, `'H' - k` for a negative one, its places
|
|
23
|
-
* complemented) and its places — small digits are short;
|
|
24
|
-
* - a tie → `'!'`, then `t` as a digit (`'!'` sorts below every length
|
|
25
|
-
* character, so a tied segment sorts before the same digit leading on);
|
|
26
|
-
* - rank = segments concatenated; a prefix sorts before its extension, as
|
|
27
|
-
* string `<` does.
|
|
28
|
-
*
|
|
29
|
-
* Allocation (`rankBetween(left, right, clientId)`):
|
|
30
|
-
*
|
|
31
|
-
* - gap at level `i` (`rv - lv > 1`): emit `lv + 1 + floor(rand·(rv-lv-1))`,
|
|
32
|
-
* own tie — random-in-gap keeps sequential inserts shallow and concurrent
|
|
33
|
-
* same-gap inserts deterministic-distinct.
|
|
34
|
-
* - adjacent or equal digits (`rv - lv ≤ 1`): the left digit or the right
|
|
35
|
-
* one with the allocator's own tie when that tie sorts between theirs;
|
|
36
|
-
* else copy the left segment and descend — untied when that already sorts
|
|
37
|
-
* below the right bound (the result is then above left and below right,
|
|
38
|
-
* and the next level is free), tied otherwise.
|
|
39
|
-
* - left exhausted/open: emit `rSeg.v - 1` (boundary-minus). Sequential
|
|
40
|
-
* prepends stay at depth 1; the bound can only underflow after ~2^40
|
|
41
|
-
* consecutive boundary pushes — documented unreachable, guarded by a
|
|
42
|
-
* descriptive throw.
|
|
43
|
-
*
|
|
44
|
-
* No floating point, no global renumbering, no wall clocks.
|
|
45
|
-
*/
|
|
46
|
-
/** Digit range. VMIN = -2^40, VMAX = 2^40 - 1. */
|
|
47
|
-
export declare const RANK_VMIN: number;
|
|
48
|
-
export declare const RANK_VMAX: number;
|
|
49
|
-
export type RankSeg = {
|
|
50
|
-
v: number;
|
|
51
|
-
t: number;
|
|
52
|
-
};
|
|
53
|
-
/** Thrown when rank generation would need a digit below VMIN (unreachable). */
|
|
54
|
-
export declare class RankSpaceExhausted extends Error {
|
|
55
|
-
constructor();
|
|
56
|
-
}
|
|
57
|
-
/** Decode a rank string into its segment array (`t: Infinity` where a segment has no tie). */
|
|
58
|
-
export declare const decodeRank: (rank: string) => RankSeg[];
|
|
59
|
-
/** Encode segments back to the persisted string form. */
|
|
60
|
-
export declare const encodeRank: (segs: readonly RankSeg[]) => string;
|
|
61
|
-
/**
|
|
62
|
-
* Allocate a rank strictly between `left` and `right` (either may be
|
|
63
|
-
* `undefined` for the open end of a sibling list). `clientId` is the
|
|
64
|
-
* allocating doc's clientID; `rand` supplies the in-gap pick (defaults to
|
|
65
|
-
* `Math.random`, tests inject a seeded stream).
|
|
66
|
-
*
|
|
67
|
-
* Guaranteed: `left < result < right` for any `left < right`, including
|
|
68
|
-
* equal-prefix/concurrent-collision inputs — the tiebreak dimension makes a
|
|
69
|
-
* strictly-between key always exist (modulo the documented VMIN bound).
|
|
70
|
-
*/
|
|
71
|
-
export declare const rankBetween: (left: string | undefined, right: string | undefined, clientId: number, rand?: () => number, compact?: boolean) => string;
|
|
72
|
-
/** The base of the run band: a run member's digit lies just above it. */
|
|
73
|
-
export declare const RANK_RUN: number;
|
|
74
|
-
/**
|
|
75
|
-
* A rank in `(left, right)` for an insert right after `left` — one rule for
|
|
76
|
-
* array items (`crdt/data.ts`) and inserted blocks (`order.insert.run`).
|
|
77
|
-
* When `client` made `left`, the rank is in the client's run after it: `left`
|
|
78
|
-
* itself as the prefix, then one segment of the run band — or, when `left`
|
|
79
|
-
* is already a member of the client's run, that run's prefix and a member
|
|
80
|
-
* between `left` and the next one. So what one client inserts after its own
|
|
81
|
-
* items stays together whatever a peer inserts in that gap meanwhile: a
|
|
82
|
-
* peer's rank there is either no extension of the run's prefix (it sorts
|
|
83
|
-
* after the whole run) or one whose next digit is above the band (YATA's
|
|
84
|
-
* origin rule, as `Y.Array` keeps an insert after its origin). Elsewhere, or
|
|
85
|
-
* when the run rank would not sort below `right`, a plain {@link rankBetween}.
|
|
86
|
-
*/
|
|
87
|
-
export declare const rankAfter: (left: string | undefined, right: string | undefined, client: number, rand?: () => number) => string;
|