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
|
@@ -39,9 +39,9 @@ export class Composition {
|
|
|
39
39
|
*/
|
|
40
40
|
targetless = false;
|
|
41
41
|
/**
|
|
42
|
-
* The start target is a range across texts
|
|
43
|
-
* atom
|
|
44
|
-
* the
|
|
42
|
+
* The start target is a range across texts inside one block (across an
|
|
43
|
+
* inline atom): its first write merges the rest into the host text. A range
|
|
44
|
+
* across blocks is replaced at the start (`#rangeless`).
|
|
45
45
|
*/
|
|
46
46
|
#across = false;
|
|
47
47
|
/** That rest, shown in the host once the IME wrote its first preview. */
|
|
@@ -108,18 +108,18 @@ export class Composition {
|
|
|
108
108
|
this.#item = null;
|
|
109
109
|
this.marks = insertionMarks(this.edytor, intentSnapshot(this.edytor, 'insertCompositionText'));
|
|
110
110
|
const shown = selection.value.kind;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
111
|
+
// A range across blocks is replaced now, as a block selection is: the
|
|
112
|
+
// IME then writes at a parked caret in one text, never its own delete
|
|
113
|
+
// across blocks (Firefox's joins the blocks' elements and removes the
|
|
114
|
+
// render's anchors between them before the change renders).
|
|
115
|
+
const blocks = shown === 'text' && !!startText && !!endText && startText.parent !== endText.parent;
|
|
116
|
+
this.#across = shown === 'text' && startText !== endText && !blocks;
|
|
117
117
|
// With no target, the caret the browser parked for the IME.
|
|
118
118
|
const parked = targetless && dom?.anchorNode;
|
|
119
119
|
const point = parked ? this.edytor.ports.surface.pointAt(parked, dom.anchorOffset) : null;
|
|
120
120
|
const at = targetless
|
|
121
121
|
? point && { text: point.text, from: point.offset, to: point.offset }
|
|
122
|
-
: shown === 'blocks' || shown === 'atom'
|
|
122
|
+
: shown === 'blocks' || shown === 'atom' || blocks
|
|
123
123
|
? this.#rangeless()
|
|
124
124
|
: startText && {
|
|
125
125
|
text: startText,
|
|
@@ -217,7 +217,7 @@ export class Composition {
|
|
|
217
217
|
const [merged, host] = [this.#merged, this.host];
|
|
218
218
|
const cell = merged && host && this.edytor.cells?.get(host.parent.id);
|
|
219
219
|
if (cell)
|
|
220
|
-
this.edytor.pin.merge(cell, merged
|
|
220
|
+
this.edytor.pin.merge(cell, merged);
|
|
221
221
|
this.#merged = null;
|
|
222
222
|
return 'live';
|
|
223
223
|
}
|
|
@@ -286,13 +286,17 @@ export class Composition {
|
|
|
286
286
|
}
|
|
287
287
|
/**
|
|
288
288
|
* A block or atom selection shows no DOM range, so the IME has no caret
|
|
289
|
-
* of its own and would write at the editable's start
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
289
|
+
* of its own and would write at the editable's start; over a text range
|
|
290
|
+
* across blocks the IME's own replacement would join the blocks' elements
|
|
291
|
+
* and remove the render's anchors between them before the change renders
|
|
292
|
+
* (Firefox, `ime.range.blocks`). Its start target is replaced now (blocks:
|
|
293
|
+
* `flow.slot`, one empty block in their place; an atom: removed; a range:
|
|
294
|
+
* its blocks merged around the caret), the change rendered and the DOM
|
|
295
|
+
* caret parked where the preview goes before the IME writes
|
|
296
|
+
* (`projector.park`, the DOM-selection writer). When the replacement is
|
|
294
297
|
* refused, the IME writes where Escape leaves for (`selectedBlocksExit`;
|
|
295
|
-
* an atom: before it) and the tail restores it: nothing is
|
|
298
|
+
* an atom or a range: before it) and the tail restores it: nothing is
|
|
299
|
+
* written.
|
|
296
300
|
*/
|
|
297
301
|
#rangeless() {
|
|
298
302
|
const { edytor } = this;
|
|
@@ -15,28 +15,15 @@
|
|
|
15
15
|
import { Block } from '../block/block.svelte.js';
|
|
16
16
|
import { InlineBlock } from '../block/inlineBlock.svelte.js';
|
|
17
17
|
import { Text } from '../text/text.svelte.js';
|
|
18
|
-
import type {
|
|
18
|
+
import type { Destination, DocChange } from '../crdt/index.js';
|
|
19
|
+
import { type Part } from '../crdt/parts.js';
|
|
19
20
|
import type { Edytor } from '../edytor.svelte.js';
|
|
20
21
|
import type { JSONText } from '../utils/json.js';
|
|
21
22
|
export type TextRunItem = {
|
|
22
23
|
text: string;
|
|
23
24
|
marks?: Record<string, unknown>;
|
|
24
25
|
};
|
|
25
|
-
|
|
26
|
-
export type ContentPart = {
|
|
27
|
-
kind: 'text';
|
|
28
|
-
start: number;
|
|
29
|
-
items: TextRunItem[];
|
|
30
|
-
length: number;
|
|
31
|
-
} | {
|
|
32
|
-
kind: 'inline';
|
|
33
|
-
start: number;
|
|
34
|
-
item: ContentItem & {
|
|
35
|
-
kind: 'inline';
|
|
36
|
-
};
|
|
37
|
-
};
|
|
38
|
-
/** Segments and atoms, alternating, starting and ending with a segment. */
|
|
39
|
-
export declare const contentParts: (items: readonly ContentItem[]) => ContentPart[];
|
|
26
|
+
export type { Part as ContentPart } from '../crdt/parts.js';
|
|
40
27
|
/**
|
|
41
28
|
* A text segment's items as `JSONText` runs (`text.value`): empty items
|
|
42
29
|
* dropped, adjacent runs with the same marks joined, each run's marks a
|
|
@@ -65,7 +52,7 @@ export declare class Handles {
|
|
|
65
52
|
* Block `id`'s content parts — from its runs, which the index keeps per
|
|
66
53
|
* block and reads-your-writes (a write elsewhere re-derives nothing here).
|
|
67
54
|
*/
|
|
68
|
-
parts: (id: string) =>
|
|
55
|
+
parts: (id: string) => readonly Part[];
|
|
69
56
|
/** Forget the handles of the blocks a commit removed. */
|
|
70
57
|
prune: (change: DocChange) => void;
|
|
71
58
|
clear: () => void;
|
package/dist/session/handles.js
CHANGED
|
@@ -15,29 +15,7 @@
|
|
|
15
15
|
import { Block } from '../block/block.svelte.js';
|
|
16
16
|
import { InlineBlock } from '../block/inlineBlock.svelte.js';
|
|
17
17
|
import { Text } from '../text/text.svelte.js';
|
|
18
|
-
|
|
19
|
-
export const contentParts = (items) => {
|
|
20
|
-
const parts = [];
|
|
21
|
-
let segment = [];
|
|
22
|
-
let start = 0;
|
|
23
|
-
let at = 0;
|
|
24
|
-
const close = () => {
|
|
25
|
-
parts.push({ kind: 'text', start, items: segment, length: at - start });
|
|
26
|
-
segment = [];
|
|
27
|
-
};
|
|
28
|
-
for (const item of items) {
|
|
29
|
-
if (item.kind === 'text') {
|
|
30
|
-
segment.push({ text: item.text, ...(item.marks ? { marks: item.marks } : {}) });
|
|
31
|
-
at += item.text.length;
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
close();
|
|
35
|
-
parts.push({ kind: 'inline', start: at, item });
|
|
36
|
-
start = ++at;
|
|
37
|
-
}
|
|
38
|
-
close();
|
|
39
|
-
return parts;
|
|
40
|
-
};
|
|
18
|
+
import { partsOf } from '../crdt/parts.js';
|
|
41
19
|
/**
|
|
42
20
|
* A text segment's items as `JSONText` runs (`text.value`): empty items
|
|
43
21
|
* dropped, adjacent runs with the same marks joined, each run's marks a
|
|
@@ -58,7 +36,7 @@ export const segmentJSON = (items) => {
|
|
|
58
36
|
}
|
|
59
37
|
return out;
|
|
60
38
|
};
|
|
61
|
-
const ROOT =
|
|
39
|
+
const ROOT = partsOf([]);
|
|
62
40
|
/** The destination right after `block` among its siblings (`after: false`, right before it). */
|
|
63
41
|
export const siblingSlot = (block, after = true) => {
|
|
64
42
|
const parent = block.parent;
|
|
@@ -73,7 +51,7 @@ export class Handles {
|
|
|
73
51
|
#blocks = new Map();
|
|
74
52
|
#texts = new Map();
|
|
75
53
|
#atoms = new Map();
|
|
76
|
-
/** Each block's parts
|
|
54
|
+
/** Each block's parts as last read (a dead block's: what it last showed). */
|
|
77
55
|
#parts = new Map();
|
|
78
56
|
constructor(edytor) {
|
|
79
57
|
this.edytor = edytor;
|
|
@@ -119,24 +97,20 @@ export class Handles {
|
|
|
119
97
|
parts = (id) => {
|
|
120
98
|
if (id === 'root')
|
|
121
99
|
return ROOT;
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
const runs = facade.runs(id);
|
|
125
|
-
if (memo?.runs === runs)
|
|
126
|
-
return memo.parts;
|
|
100
|
+
const parts = partsOf(this.edytor.facade.runs(id));
|
|
101
|
+
const last = this.#parts.get(id);
|
|
127
102
|
// A dead block keeps what it last showed (a write's intent is minted from it).
|
|
128
|
-
if (
|
|
129
|
-
return
|
|
130
|
-
|
|
131
|
-
this.#parts.set(id, { runs, parts });
|
|
103
|
+
if (last && last !== parts && !this.edytor.facade.isVisibleBlock(id))
|
|
104
|
+
return last;
|
|
105
|
+
this.#parts.set(id, parts);
|
|
132
106
|
return parts;
|
|
133
107
|
};
|
|
134
108
|
/** Forget the handles of the blocks a commit removed. */
|
|
135
109
|
prune = (change) => {
|
|
136
110
|
for (const id of change.removed) {
|
|
137
|
-
for (const part of this.#parts.get(id)
|
|
138
|
-
if (part.kind === 'inline' && this.#atoms.get(part.
|
|
139
|
-
this.#atoms.delete(part.
|
|
111
|
+
for (const part of this.#parts.get(id) ?? [])
|
|
112
|
+
if (part.kind === 'inline' && this.#atoms.get(part.id)?.blockId === id)
|
|
113
|
+
this.#atoms.delete(part.id);
|
|
140
114
|
this.#blocks.delete(id);
|
|
141
115
|
this.#texts.delete(id);
|
|
142
116
|
this.#parts.delete(id);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { getNextGraphemeEnd, getNextWordEndOffset, getPreviousGraphemeStart, getPreviousWordStartOffset } from '../text/wordBoundary.js';
|
|
2
2
|
import { getSelectedBlocksInDocumentOrder, shownText } from '../selection/replaceSelection.js';
|
|
3
|
-
import { atomSelection, segmentsOf } from './selection.js';
|
|
3
|
+
import { atomSelection, pointOf, segmentsOf } from './selection.js';
|
|
4
4
|
import { untargeted } from './attempt.js';
|
|
5
5
|
import { siblingSlot } from './handles.js';
|
|
6
|
+
import { partsOf } from '../crdt/parts.js';
|
|
6
7
|
/**
|
|
7
8
|
* With no target (`untargeted`: the value is `none`, no gesture placed the
|
|
8
9
|
* host's caret), a navigation key does nothing (Notion): it is claimed, so
|
|
@@ -16,9 +17,8 @@ export const stayWithoutTarget = (edytor, prevent) => {
|
|
|
16
17
|
/** An inline atom in a block's string. */
|
|
17
18
|
const ATOM = '';
|
|
18
19
|
/** A block's content as one string, each inline atom one `ATOM`. */
|
|
19
|
-
const stringOf = (edytor, block) => edytor.facade
|
|
20
|
-
.
|
|
21
|
-
.map((item) => (item.kind === 'text' ? item.text : ATOM))
|
|
20
|
+
const stringOf = (edytor, block) => partsOf(edytor.facade.runs(block))
|
|
21
|
+
.map((part) => (part.kind === 'text' ? part.text : ATOM))
|
|
22
22
|
.join('');
|
|
23
23
|
/** A block's stop at its edge in `dir` (its end going forward, its start going backward). */
|
|
24
24
|
const edge = (edytor, block, dir) => ({
|
|
@@ -68,9 +68,7 @@ const select = (edytor, anchor, focus) => {
|
|
|
68
68
|
: null;
|
|
69
69
|
if (atom?.kind === 'inline')
|
|
70
70
|
return selection.select(atomSelection(start.block, atom.id, order > 0 ? 'after' : 'before'));
|
|
71
|
-
|
|
72
|
-
if (from && to)
|
|
73
|
-
selection.selectText(from.text, from.offset, to.text, to.offset, order > 0);
|
|
71
|
+
selection.selectAt(start, end, order > 0);
|
|
74
72
|
};
|
|
75
73
|
/**
|
|
76
74
|
* One apply step for `unit` in `dir` (`left`/`right`: visual). Answers whether
|
|
@@ -94,8 +92,7 @@ export const move = (edytor, unit, key, extend) => {
|
|
|
94
92
|
const first = blocks.map((block) => shownText(block, 'first')).find(Boolean);
|
|
95
93
|
const last = blocks.map((block) => shownText(block, 'last')).findLast(Boolean);
|
|
96
94
|
if (first && last) {
|
|
97
|
-
start =
|
|
98
|
-
end = { block: last.blockId, offset: last.segStart + last.length };
|
|
95
|
+
[start, end] = [pointOf(first, 0), pointOf(last, last.length)];
|
|
99
96
|
}
|
|
100
97
|
// Voids alone show no line: the blocks stay selected, except for the
|
|
101
98
|
// document keys, whose target is the document's edge (`select` moves
|
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
* The view's `commit(value, cause)` (public: `select(value)`) is the only writer; the
|
|
14
14
|
* presence payload is `serialize(value)`.
|
|
15
15
|
*/
|
|
16
|
-
import type { BlockId,
|
|
16
|
+
import type { BlockId, ContentRun } from '../crdt/index.js';
|
|
17
17
|
import type { DocAnchor, EdytorDoc } from '../crdt/edytor-doc.js';
|
|
18
|
+
import type { Text } from '../text/text.svelte.js';
|
|
18
19
|
export type Marks = Record<string, unknown>;
|
|
19
20
|
export type SelectionValue = {
|
|
20
21
|
readonly kind: 'none';
|
|
@@ -45,7 +46,9 @@ export type SelectionPoint = {
|
|
|
45
46
|
block: BlockId;
|
|
46
47
|
offset: number;
|
|
47
48
|
};
|
|
48
|
-
|
|
49
|
+
/** A point inside a text segment (its block's `ordinal`-th) as a block-offset point. */
|
|
50
|
+
export declare const pointOf: (text: Text, offset: number) => SelectionPoint;
|
|
51
|
+
type TextRun = Extract<ContentRun, {
|
|
49
52
|
kind: 'text';
|
|
50
53
|
}>;
|
|
51
54
|
/** One content part of a block: a text segment (between atoms, possibly empty) or an inline atom. */
|
|
@@ -55,7 +58,7 @@ export type SelectionSegment = {
|
|
|
55
58
|
segOrd: number;
|
|
56
59
|
start: number;
|
|
57
60
|
end: number;
|
|
58
|
-
runs: TextRun[];
|
|
61
|
+
runs: readonly TextRun[];
|
|
59
62
|
} | {
|
|
60
63
|
block: BlockId;
|
|
61
64
|
kind: 'inline';
|
|
@@ -88,7 +91,7 @@ export type SelectionProjection = {
|
|
|
88
91
|
readonly content: string;
|
|
89
92
|
};
|
|
90
93
|
/** The document reads a projection needs (the facade satisfies it). */
|
|
91
|
-
export type ProjectionDoc = Pick<EdytorDoc, 'version' | 'resolveAnchor' | 'compare' | 'order' | '
|
|
94
|
+
export type ProjectionDoc = Pick<EdytorDoc, 'version' | 'resolveAnchor' | 'compare' | 'order' | 'runs' | 'ancestorsOf' | 'isIsland' | 'isVoid' | 'isVisibleBlock'>;
|
|
92
95
|
/** A block's content parts; always text first and last, one text segment between two atoms. */
|
|
93
96
|
export declare const segmentsOf: (doc: ProjectionDoc, block: BlockId) => SelectionSegment[];
|
|
94
97
|
/** The projection of `value` at the document's current version, memoized per (value, version). */
|
|
@@ -97,7 +100,10 @@ export declare const project: (value: SelectionValue, doc: ProjectionDoc) => Sel
|
|
|
97
100
|
export declare const sameEnds: (a: SelectionProjection, b: SelectionProjection) => boolean;
|
|
98
101
|
/** Why a selection was written. */
|
|
99
102
|
export type SelectCause = 'dom' | 'model' | 'repair' | 'history';
|
|
100
|
-
/**
|
|
103
|
+
/**
|
|
104
|
+
* Value equality: kind, anchors (item + side), ids, pending marks. A value
|
|
105
|
+
* is plain JSON (anchors, ids, marks), so it is the JSON equality.
|
|
106
|
+
*/
|
|
101
107
|
export declare const sameValue: (a: SelectionValue, b: SelectionValue) => boolean;
|
|
102
108
|
/**
|
|
103
109
|
* The presence payload: anchors only. A text range publishes its
|
|
@@ -1,36 +1,16 @@
|
|
|
1
|
+
import { partsOf, segmentAt } from '../crdt/parts.js';
|
|
1
2
|
import { jsonEquals } from '../utils/json.js';
|
|
2
3
|
export const noSelection = Object.freeze({ kind: 'none' });
|
|
3
4
|
export const textSelection = (anchor, focus = anchor, pending) => Object.freeze({ kind: 'text', anchor, focus, ...(pending ? { pending } : {}) });
|
|
4
5
|
export const atomSelection = (blockId, atomId, from = 'before') => Object.freeze({ kind: 'atom', blockId, atomId, from });
|
|
5
6
|
export const blockSelection = (ids) => Object.freeze({ kind: 'blocks', ids: Object.freeze([...ids]) });
|
|
7
|
+
/** A point inside a text segment (its block's `ordinal`-th) as a block-offset point. */
|
|
8
|
+
export const pointOf = (text, offset) => ({
|
|
9
|
+
block: text.blockId,
|
|
10
|
+
offset: text.segStart + offset
|
|
11
|
+
});
|
|
6
12
|
/** A block's content parts; always text first and last, one text segment between two atoms. */
|
|
7
|
-
export const segmentsOf = (doc, block) => {
|
|
8
|
-
const out = [];
|
|
9
|
-
let text = { block, kind: 'text', segOrd: 0, start: 0, end: 0, runs: [] };
|
|
10
|
-
for (const item of doc.contentItems(block)) {
|
|
11
|
-
if (item.kind === 'text') {
|
|
12
|
-
text.runs.push(item);
|
|
13
|
-
text.end += item.text.length;
|
|
14
|
-
continue;
|
|
15
|
-
}
|
|
16
|
-
out.push(text, { block, kind: 'inline', id: item.id, start: text.end, end: text.end + 1 });
|
|
17
|
-
text = {
|
|
18
|
-
block,
|
|
19
|
-
kind: 'text',
|
|
20
|
-
segOrd: text.segOrd + 1,
|
|
21
|
-
start: text.end + 1,
|
|
22
|
-
end: text.end + 1,
|
|
23
|
-
runs: []
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
out.push(text);
|
|
27
|
-
return out;
|
|
28
|
-
};
|
|
29
|
-
/** The text segment displaying `offset`: the first one ending at or after it (a seam belongs to the left text). */
|
|
30
|
-
const textSegmentAt = (segments, offset) => {
|
|
31
|
-
const texts = segments.filter((s) => s.kind === 'text');
|
|
32
|
-
return texts.find((s) => offset <= s.end) ?? texts[texts.length - 1];
|
|
33
|
-
};
|
|
13
|
+
export const segmentsOf = (doc, block) => partsOf(doc.runs(block)).map((part) => ({ ...part, block }));
|
|
34
14
|
const EMPTY = {
|
|
35
15
|
start: null,
|
|
36
16
|
end: null,
|
|
@@ -95,8 +75,8 @@ const projectRange = (doc, kind, anchor, focus, atomic) => {
|
|
|
95
75
|
const partsOf = new Map(blocks.map((id) => [id, segmentsOf(doc, id)]));
|
|
96
76
|
const startSegments = partsOf.get(start.block) ?? segmentsOf(doc, start.block);
|
|
97
77
|
const endSegments = partsOf.get(end.block) ?? segmentsOf(doc, end.block);
|
|
98
|
-
const first =
|
|
99
|
-
const last =
|
|
78
|
+
const first = segmentAt(startSegments, start.offset);
|
|
79
|
+
const last = segmentAt(endSegments, end.offset);
|
|
100
80
|
const all = blocks.flatMap((id) => partsOf.get(id));
|
|
101
81
|
const from = all.indexOf(first);
|
|
102
82
|
const to = all.indexOf(last);
|
|
@@ -191,25 +171,11 @@ export const sameEnds = (a, b) => [a.start, a.end].every((p, i) => {
|
|
|
191
171
|
const q = i ? b.end : b.start;
|
|
192
172
|
return p?.block === q?.block && p?.offset === q?.offset;
|
|
193
173
|
});
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const
|
|
199
|
-
/** Value equality: kind, anchors (item + side), ids, pending marks. */
|
|
200
|
-
export const sameValue = (a, b) => {
|
|
201
|
-
if (a === b)
|
|
202
|
-
return true;
|
|
203
|
-
if (a.kind === 'text' && b.kind === 'text')
|
|
204
|
-
return (sameAnchor(a.anchor, b.anchor) &&
|
|
205
|
-
sameAnchor(a.focus, b.focus) &&
|
|
206
|
-
sameMarks(a.pending, b.pending));
|
|
207
|
-
if (a.kind === 'blocks' && b.kind === 'blocks')
|
|
208
|
-
return a.ids.length === b.ids.length && a.ids.every((id, i) => id === b.ids[i]);
|
|
209
|
-
if (a.kind === 'atom' && b.kind === 'atom')
|
|
210
|
-
return a.blockId === b.blockId && a.atomId === b.atomId && a.from === b.from;
|
|
211
|
-
return a.kind === b.kind && a.kind === 'none';
|
|
212
|
-
};
|
|
174
|
+
/**
|
|
175
|
+
* Value equality: kind, anchors (item + side), ids, pending marks. A value
|
|
176
|
+
* is plain JSON (anchors, ids, marks), so it is the JSON equality.
|
|
177
|
+
*/
|
|
178
|
+
export const sameValue = jsonEquals;
|
|
213
179
|
/**
|
|
214
180
|
* A text value's anchors as (start, end). A range an edit collapsed has no
|
|
215
181
|
* order left; its anchors keep the roles they were minted with (a start
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BlockId, DocAnchor, OpResult } from '../crdt/index.js';
|
|
2
|
+
import type { SuggestedChange } from '../crdt/doc/suggest.js';
|
|
2
3
|
import type { Edytor } from '../edytor.svelte.js';
|
|
3
4
|
import { type JSONBlock } from '../utils/json.js';
|
|
4
5
|
/** A text range, by its anchors (a text selection's). */
|
|
@@ -48,12 +49,15 @@ export type SuggestionsAt = {
|
|
|
48
49
|
};
|
|
49
50
|
/** The block's `data-edytor-suggestion-replaced` (`empty`: folded, the preview stands in its place). */
|
|
50
51
|
export declare const replacedMark: ({ replaced, inPlace }: SuggestionsAt) => "" | "empty" | null;
|
|
52
|
+
/** A view-local suggestion: shown in this view only, until it is accepted. */
|
|
51
53
|
export declare class Suggestion {
|
|
52
54
|
#private;
|
|
53
55
|
readonly id: string;
|
|
54
56
|
/** The position, resolved: `replace: 'selection'` is the selection's blocks or text range. */
|
|
55
57
|
readonly at: ResolvedAt;
|
|
56
58
|
readonly label: string | undefined;
|
|
59
|
+
/** View-local: a {@link SharedSuggestion} is in the document. */
|
|
60
|
+
readonly shared: false;
|
|
57
61
|
constructor(layer: Suggestions, at: ResolvedAt, kind: string, options: SuggestionOptions, status: SuggestionStatus);
|
|
58
62
|
/** The proposed blocks (reactive). */
|
|
59
63
|
get content(): readonly JSONBlock[];
|
|
@@ -71,25 +75,50 @@ export declare class Suggestion {
|
|
|
71
75
|
append: (text: string) => void;
|
|
72
76
|
/** The stream ended: `status` is `ready`. */
|
|
73
77
|
done: () => void;
|
|
74
|
-
/**
|
|
78
|
+
/** Accept it: one command, one undo step, placing it in the document (see {@link Suggestions}). */
|
|
75
79
|
accept: () => OpResult;
|
|
76
|
-
/** Drop it
|
|
80
|
+
/** Drop it (writes nothing). */
|
|
77
81
|
discard: () => void;
|
|
78
82
|
/** Empty it, stream again, and ask the app for another answer (`onRetry`). */
|
|
79
83
|
retry: () => void;
|
|
80
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* A shared suggestion (`createSharedSuggestionsPlugin`): in the document,
|
|
87
|
+
* so every collaborator sees it with its author and reviews it. Its
|
|
88
|
+
* `accept()` and `reject()` are one command each (`acceptSuggestion`,
|
|
89
|
+
* `rejectSuggestion`) of a view that may decide; `discard()` rejects it
|
|
90
|
+
* (its author's withdrawal too).
|
|
91
|
+
*/
|
|
92
|
+
export interface SharedSuggestion extends Omit<Suggestion, 'shared' | 'at'> {
|
|
93
|
+
readonly shared: true;
|
|
94
|
+
/** Its position as this view added it; `null` for one another view made (its `changes` say where it is). */
|
|
95
|
+
readonly at: ResolvedAt | null;
|
|
96
|
+
/** Its author (an actor id). */
|
|
97
|
+
readonly by: string | undefined;
|
|
98
|
+
/** Who wrote it for its author (`'ai'`): a reviewer sees "Ada (AI)". */
|
|
99
|
+
readonly via: string | undefined;
|
|
100
|
+
/** Its pending changes, in document order (reactive). */
|
|
101
|
+
readonly changes: readonly SuggestedChange[];
|
|
102
|
+
/** This view may accept or reject it. */
|
|
103
|
+
readonly canDecide: boolean;
|
|
104
|
+
/** Its author is this view's actor (it may reject it). */
|
|
105
|
+
readonly mine: boolean;
|
|
106
|
+
/** Reject its changes: one command (`rejectSuggestion`). */
|
|
107
|
+
reject(): OpResult;
|
|
108
|
+
}
|
|
109
|
+
/** A suggestion of the list: view-local, or shared (with the shared suggestions plugin). */
|
|
110
|
+
export type AnySuggestion = Suggestion | SharedSuggestion;
|
|
81
111
|
/** The view's suggestions (`edytor.suggestions`). */
|
|
82
112
|
export declare class Suggestions {
|
|
83
113
|
#private;
|
|
84
|
-
|
|
85
|
-
/** Bumped by every change of a suggestion: the surface's passes re-run. */
|
|
114
|
+
readonly edytor: Edytor;
|
|
115
|
+
/** Bumped by every change of a suggestion (and of the document's): the surface's passes re-run. */
|
|
86
116
|
revision: number;
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
/** The newest one: what the keys act on. */
|
|
117
|
+
/** Every suggestion, view-local and shared, oldest first (reactive). */
|
|
118
|
+
get list(): readonly AnySuggestion[];
|
|
119
|
+
/** The newest view-local one: what the keys act on. */
|
|
91
120
|
get latest(): Suggestion | undefined;
|
|
92
|
-
get: (id: string) =>
|
|
121
|
+
get: (id: string) => AnySuggestion | undefined;
|
|
93
122
|
/**
|
|
94
123
|
* Propose `content` at `at`. Throws when the position names no shown
|
|
95
124
|
* block, a block that holds no children (`inside` a void or a code line),
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Suggestions (site `editor/suggestions`): content
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
2
|
+
* Suggestions (site `editor/suggestions`): content proposed at a position —
|
|
3
|
+
* AI output, a completion, a collaborator's edit to review — in one list,
|
|
4
|
+
* each one view-local or shared (`shared`).
|
|
5
|
+
*
|
|
6
|
+
* - **View-local** (the default): shown only in this view until accepted.
|
|
7
|
+
* Session state: nothing reaches the document, its history, presence,
|
|
8
|
+
* `onChange` or another view before `accept()`, one command
|
|
9
|
+
* (`acceptSuggestion`: admission, hooks, one transaction, one undo step)
|
|
10
|
+
* placing the content as a paste places it (`flow.place`, `flow.slot`,
|
|
11
|
+
* `flow.inline`). Anchored by block ids (a text range by its anchors): it
|
|
12
|
+
* survives selection changes and edits elsewhere, and is dropped when its
|
|
13
|
+
* block dies.
|
|
14
|
+
* - **Shared** (with the shared suggestions plugin,
|
|
15
|
+
* `plugins/suggestions/shared.svelte.ts`, `sugg.*`): in the document, so
|
|
16
|
+
* every replica sees it and its author is known. The plugin adds them and
|
|
17
|
+
* lists them here (`shared`, the port it sets).
|
|
18
|
+
*
|
|
19
|
+
* The layer owns the list; the surface renders it.
|
|
11
20
|
*/
|
|
12
21
|
import { DEV } from 'esm-env';
|
|
13
22
|
import { REFUSED } from '../crdt/doc/plan.js';
|
|
@@ -34,11 +43,14 @@ const sideOf = (at) => {
|
|
|
34
43
|
const side = SIDES.find((side) => side in at);
|
|
35
44
|
return side && [side, at[side]];
|
|
36
45
|
};
|
|
46
|
+
/** A view-local suggestion: shown in this view only, until it is accepted. */
|
|
37
47
|
export class Suggestion {
|
|
38
48
|
id;
|
|
39
49
|
/** The position, resolved: `replace: 'selection'` is the selection's blocks or text range. */
|
|
40
50
|
at;
|
|
41
51
|
label;
|
|
52
|
+
/** View-local: a {@link SharedSuggestion} is in the document. */
|
|
53
|
+
shared = false;
|
|
42
54
|
#content = $state.raw([]);
|
|
43
55
|
#status = $state('ready');
|
|
44
56
|
#layer;
|
|
@@ -109,9 +121,9 @@ export class Suggestion {
|
|
|
109
121
|
if (this.live)
|
|
110
122
|
this.#status = 'ready';
|
|
111
123
|
};
|
|
112
|
-
/**
|
|
124
|
+
/** Accept it: one command, one undo step, placing it in the document (see {@link Suggestions}). */
|
|
113
125
|
accept = () => this.#layer.accept(this);
|
|
114
|
-
/** Drop it
|
|
126
|
+
/** Drop it (writes nothing). */
|
|
115
127
|
discard = () => this.#layer.drop(this);
|
|
116
128
|
/** Empty it, stream again, and ask the app for another answer (`onRetry`). */
|
|
117
129
|
retry = () => {
|
|
@@ -127,27 +139,33 @@ export class Suggestion {
|
|
|
127
139
|
export class Suggestions {
|
|
128
140
|
edytor;
|
|
129
141
|
#list = $state.raw([]);
|
|
130
|
-
/** Bumped by every change of a suggestion: the surface's passes re-run. */
|
|
142
|
+
/** Bumped by every change of a suggestion (and of the document's): the surface's passes re-run. */
|
|
131
143
|
revision = $state(0);
|
|
144
|
+
/**
|
|
145
|
+
* @internal The shared suggestions plugin's part (`createSharedSuggestionsPlugin`):
|
|
146
|
+
* the list with the shared ones after this view's own.
|
|
147
|
+
*/
|
|
148
|
+
shared;
|
|
149
|
+
/** @internal */
|
|
132
150
|
constructor(edytor) {
|
|
133
151
|
this.edytor = edytor;
|
|
134
152
|
}
|
|
135
|
-
/** Every
|
|
153
|
+
/** Every suggestion, view-local and shared, oldest first (reactive). */
|
|
136
154
|
get list() {
|
|
137
|
-
return this.#list;
|
|
155
|
+
return this.shared?.(this.#list) ?? this.#list;
|
|
138
156
|
}
|
|
139
|
-
/** The newest one: what the keys act on. */
|
|
157
|
+
/** The newest view-local one: what the keys act on. */
|
|
140
158
|
get latest() {
|
|
141
159
|
return this.#list.at(-1);
|
|
142
160
|
}
|
|
143
|
-
get = (id) => this
|
|
161
|
+
get = (id) => this.list.find((s) => s.id === id);
|
|
144
162
|
/**
|
|
145
163
|
* Propose `content` at `at`. Throws when the position names no shown
|
|
146
164
|
* block, a block that holds no children (`inside` a void or a code line),
|
|
147
165
|
* or no selection (`replace: 'selection'` with none).
|
|
148
166
|
*/
|
|
149
167
|
add = (at, content, options = {}) => {
|
|
150
|
-
const resolved = this
|
|
168
|
+
const resolved = this.resolve(at);
|
|
151
169
|
const { facade, document } = this.edytor;
|
|
152
170
|
const anchor = this.#blocks(resolved)[0];
|
|
153
171
|
const parent = 'inside' in resolved ? anchor : (facade.parentOf(anchor) ?? null);
|
|
@@ -327,7 +345,8 @@ export class Suggestions {
|
|
|
327
345
|
const blocks = this.#blocks(at);
|
|
328
346
|
return blocks.length > 0 && blocks.every((id) => this.edytor.facade.isVisibleBlock(id));
|
|
329
347
|
};
|
|
330
|
-
|
|
348
|
+
/** @internal `at` resolved: a selection read, a position checked (throws when it names nothing shown). */
|
|
349
|
+
resolve = (at) => {
|
|
331
350
|
const { edytor } = this;
|
|
332
351
|
const fail = (why) => {
|
|
333
352
|
throw new RangeError(`[edytor] suggestions.add: ${why}`);
|
|
@@ -42,6 +42,11 @@ export declare const ownedOf: (edytor: Edytor, element: Element, entry: {
|
|
|
42
42
|
block: string;
|
|
43
43
|
name?: string;
|
|
44
44
|
owned?: () => Owned;
|
|
45
|
+
handle?: {
|
|
46
|
+
id: string;
|
|
47
|
+
type?: string;
|
|
48
|
+
isEmpty?: boolean;
|
|
49
|
+
};
|
|
45
50
|
} | null) => Owned | null;
|
|
46
51
|
/** Heal `element` against `table`; answers whether anything was written. */
|
|
47
52
|
export declare const heal: (element: HTMLElement, table: Owned) => boolean;
|
|
@@ -63,7 +63,7 @@ export const ownedOf = (edytor, element, entry) => {
|
|
|
63
63
|
style: { display: 'none' }
|
|
64
64
|
};
|
|
65
65
|
if (entry.kind === 'atom') {
|
|
66
|
-
const atom =
|
|
66
|
+
const atom = entry.handle;
|
|
67
67
|
return {
|
|
68
68
|
strict: true,
|
|
69
69
|
owned: {
|
|
@@ -74,7 +74,7 @@ export const ownedOf = (edytor, element, entry) => {
|
|
|
74
74
|
style: {}
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
|
-
const text =
|
|
77
|
+
const text = entry.handle;
|
|
78
78
|
const voided = insideVoid(edytor, block);
|
|
79
79
|
return {
|
|
80
80
|
strict: true,
|