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
package/dist/surface/cells.d.ts
CHANGED
|
@@ -39,37 +39,14 @@ export type Cells = {
|
|
|
39
39
|
* reports (`onPatch` is told which cells each report replaced).
|
|
40
40
|
*/
|
|
41
41
|
export declare const createCells: (source: CellSource, onPatch?: (patched: Patched, before: Replaced) => void) => Cells;
|
|
42
|
-
export
|
|
43
|
-
type
|
|
44
|
-
kind: 'text';
|
|
45
|
-
}>;
|
|
46
|
-
/** The text between two inline atoms (or a content edge), keyed causally. */
|
|
47
|
-
export type Segment = {
|
|
48
|
-
readonly kind: 'text';
|
|
49
|
-
/** The id of the atom before this segment, or {@link START}. */
|
|
50
|
-
readonly key: string;
|
|
51
|
-
readonly text: string;
|
|
52
|
-
readonly runs: readonly TextRun[];
|
|
53
|
-
};
|
|
42
|
+
export { START, partsOf, type Segment, type AtomPart, type Part } from '../crdt/parts.js';
|
|
43
|
+
import { type Segment } from '../crdt/parts.js';
|
|
54
44
|
/** A suggestion's block as a cell (`components/Suggestion`): rendered like one, never registered. */
|
|
55
45
|
export type PreviewCell = Omit<Cell, 'childIds'> & {
|
|
56
46
|
readonly children: readonly PreviewCell[];
|
|
57
47
|
};
|
|
58
48
|
/** `blocks` as preview cells, keyed by position under `key` (stable while a stream grows). */
|
|
59
49
|
export declare const previewCells: (blocks: readonly JSONBlock[], key: string) => PreviewCell[];
|
|
60
|
-
export type AtomPart = {
|
|
61
|
-
readonly kind: 'inline';
|
|
62
|
-
readonly key: string;
|
|
63
|
-
readonly id: string;
|
|
64
|
-
readonly type: string;
|
|
65
|
-
readonly data: Data;
|
|
66
|
-
};
|
|
67
|
-
export type Part = Segment | AtomPart;
|
|
68
|
-
/**
|
|
69
|
-
* A cell's content as segments and atoms, alternating, starting and ending
|
|
70
|
-
* with a segment (an empty content is one empty `start` segment).
|
|
71
|
-
*/
|
|
72
|
-
export declare const partsOf: (runs: readonly ContentRun[]) => readonly Part[];
|
|
73
50
|
/** Whether a view renders mark `name` (its record has a tag or a snippet): an unknown mark renders its text only. */
|
|
74
51
|
export declare const rendersMark: (marks: ReadonlyMap<string, {
|
|
75
52
|
tag?: unknown;
|
|
@@ -105,4 +82,3 @@ export declare const segmentDeltas: (cell: Cell, segment: Segment, transform?: T
|
|
|
105
82
|
* composition is in it.
|
|
106
83
|
*/
|
|
107
84
|
export declare const placeholderOf: (cell: Cell, composing: boolean) => boolean;
|
|
108
|
-
export {};
|
package/dist/surface/cells.js
CHANGED
|
@@ -125,7 +125,8 @@ export const createCells = (source, onPatch) => {
|
|
|
125
125
|
};
|
|
126
126
|
};
|
|
127
127
|
// ── segments and render deltas ──────────────────────────────────────────
|
|
128
|
-
export
|
|
128
|
+
export { START, partsOf } from '../crdt/parts.js';
|
|
129
|
+
import { partsOf } from '../crdt/parts.js';
|
|
129
130
|
/** `blocks` as preview cells, keyed by position under `key` (stable while a stream grows). */
|
|
130
131
|
export const previewCells = (blocks, key) => blocks.map((block, index) => {
|
|
131
132
|
const id = `${key}.${index}`;
|
|
@@ -139,38 +140,6 @@ export const previewCells = (blocks, key) => blocks.map((block, index) => {
|
|
|
139
140
|
children: Object.freeze(previewCells(block.children ?? [], id))
|
|
140
141
|
});
|
|
141
142
|
});
|
|
142
|
-
const partsMemo = new WeakMap();
|
|
143
|
-
/**
|
|
144
|
-
* A cell's content as segments and atoms, alternating, starting and ending
|
|
145
|
-
* with a segment (an empty content is one empty `start` segment).
|
|
146
|
-
*/
|
|
147
|
-
export const partsOf = (runs) => {
|
|
148
|
-
const memo = partsMemo.get(runs);
|
|
149
|
-
if (memo)
|
|
150
|
-
return memo;
|
|
151
|
-
const parts = [];
|
|
152
|
-
let key = START;
|
|
153
|
-
let segment = [];
|
|
154
|
-
const close = () => parts.push(Object.freeze({
|
|
155
|
-
kind: 'text',
|
|
156
|
-
key,
|
|
157
|
-
text: segment.map((run) => run.text).join(''),
|
|
158
|
-
runs: Object.freeze(segment)
|
|
159
|
-
}));
|
|
160
|
-
for (const run of runs) {
|
|
161
|
-
if (run.kind === 'text') {
|
|
162
|
-
segment.push(run);
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
close();
|
|
166
|
-
parts.push(Object.freeze({ kind: 'inline', key: run.id, id: run.id, type: run.type, data: run.data }));
|
|
167
|
-
key = run.id;
|
|
168
|
-
segment = [];
|
|
169
|
-
}
|
|
170
|
-
close();
|
|
171
|
-
partsMemo.set(runs, Object.freeze(parts));
|
|
172
|
-
return parts;
|
|
173
|
-
};
|
|
174
143
|
/** Whether a view renders mark `name` (its record has a tag or a snippet): an unknown mark renders its text only. */
|
|
175
144
|
export const rendersMark = (marks, name) => {
|
|
176
145
|
const mark = marks.get(name);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Edytor } from '../edytor.svelte.js';
|
|
2
2
|
import type { Text } from '../text/text.svelte.js';
|
|
3
|
+
import type { InlineBlock } from '../block/inlineBlock.svelte.js';
|
|
3
4
|
import { type Kind, type Owned } from './attributes.js';
|
|
4
5
|
import { type Patched, type Replaced } from './cells.js';
|
|
5
6
|
/**
|
|
@@ -29,7 +30,7 @@ export declare class SurfaceObserver {
|
|
|
29
30
|
* release. A text element takes the table's attributes now (its id, its
|
|
30
31
|
* editing host inside a void).
|
|
31
32
|
*/
|
|
32
|
-
register: (node: Element, kind: Kind, block: string, name?: string, owned?: () => Owned) => () => void;
|
|
33
|
+
register: (node: Element, kind: Kind, block: string, name?: string, owned?: () => Owned, handle?: Text | InlineBlock) => () => void;
|
|
33
34
|
/** The render anchor (`Edytor.svelte`): a registered child of the strict root. */
|
|
34
35
|
anchor: (node: HTMLElement) => {
|
|
35
36
|
destroy: () => void;
|
|
@@ -53,6 +54,10 @@ export declare class SurfaceObserver {
|
|
|
53
54
|
flush: () => Promise<void>;
|
|
54
55
|
pre: () => void;
|
|
55
56
|
post: () => void;
|
|
57
|
+
/** The handle a registered element of `kind` renders (`null`: another element). */
|
|
58
|
+
handleOf: <K extends "text" | "atom">(element: Node | null | undefined, kind: K) => (K extends "text" ? Text : InlineBlock) | null;
|
|
59
|
+
/** The block `node` is in (its closest registered element's), none in a preview or outside the host. */
|
|
60
|
+
blockOf: (node: Node | null) => import("../index.js").Block | null;
|
|
56
61
|
}
|
|
57
62
|
/**
|
|
58
63
|
* Adopt what the browser made of `text` — the only adopter: one
|
|
@@ -18,8 +18,11 @@
|
|
|
18
18
|
* - The pre pass (root `$effect.pre`, before the flush's writes) inverts a
|
|
19
19
|
* record that removed registered elements (its added nodes go, its removed
|
|
20
20
|
* nodes return at their recorded siblings, before Svelte reconciles around
|
|
21
|
-
* them) and
|
|
22
|
-
* the
|
|
21
|
+
* them), and one that removed only the render's anchors from a block list
|
|
22
|
+
* when the editor did not write it (`dom.anchors.foreign`: the records taken
|
|
23
|
+
* from the pre pass to the end of a compare pass are the editor's own), and
|
|
24
|
+
* snapshots a content the flush re-renders whose DOM holds an edit the
|
|
25
|
+
* flush would overwrite.
|
|
23
26
|
* - The compare pass (root `$effect`, after every DOM write of the flush)
|
|
24
27
|
* removes identity clones, nodes inside a content's run and the root's
|
|
25
28
|
* foreign children, heals owned attributes (bounded), and compares each
|
|
@@ -187,8 +190,12 @@ export class SurfaceObserver {
|
|
|
187
190
|
#heals = 0;
|
|
188
191
|
/** The pass wrote the host itself: a render (the projector displays after it). */
|
|
189
192
|
#wrote = false;
|
|
190
|
-
/** Records arrived since the last pass (
|
|
193
|
+
/** Records arrived since the last pass (the projector reads what they did: `rendered`). */
|
|
191
194
|
#fresh = false;
|
|
195
|
+
/** The flush writes the host: from the pre pass to the end of its compare pass. */
|
|
196
|
+
#rendering = false;
|
|
197
|
+
/** Passes under way (a pass's own repairs; an adoption's synchronous flush nests one). */
|
|
198
|
+
#passes = 0;
|
|
192
199
|
#window = null;
|
|
193
200
|
constructor(edytor) {
|
|
194
201
|
this.edytor = edytor;
|
|
@@ -196,11 +203,13 @@ export class SurfaceObserver {
|
|
|
196
203
|
/** Observe `root` (the host): records name what to compare. */
|
|
197
204
|
attach = (root) => {
|
|
198
205
|
const mo = (this.#mo = new MutationObserver((records) => {
|
|
199
|
-
records
|
|
206
|
+
for (const record of records)
|
|
207
|
+
this.#intake(record, this.#own());
|
|
200
208
|
this.signal();
|
|
201
209
|
}));
|
|
202
210
|
for (const child of root.childNodes)
|
|
203
211
|
this.#rooted.add(child);
|
|
212
|
+
this.#rendering = false;
|
|
204
213
|
mo.observe(root, {
|
|
205
214
|
attributes: true,
|
|
206
215
|
attributeOldValue: true,
|
|
@@ -220,8 +229,8 @@ export class SurfaceObserver {
|
|
|
220
229
|
* release. A text element takes the table's attributes now (its id, its
|
|
221
230
|
* editing host inside a void).
|
|
222
231
|
*/
|
|
223
|
-
register = (node, kind, block, name, owned) => {
|
|
224
|
-
const entry = { kind, block, name, owned };
|
|
232
|
+
register = (node, kind, block, name, owned, handle) => {
|
|
233
|
+
const entry = { kind, block, name, owned, handle };
|
|
225
234
|
this.#registry.set(node, entry);
|
|
226
235
|
if (kind === 'text')
|
|
227
236
|
heal(node, ownedOf(this.edytor, node, entry));
|
|
@@ -289,50 +298,71 @@ export class SurfaceObserver {
|
|
|
289
298
|
if (!this.edytor.readonly)
|
|
290
299
|
this.#restore();
|
|
291
300
|
this.#snapshot();
|
|
301
|
+
// From here to the end of the compare pass, what the host records is the flush's.
|
|
302
|
+
this.#rendering = true;
|
|
292
303
|
});
|
|
293
304
|
};
|
|
294
305
|
post = () => {
|
|
295
306
|
this.#deps();
|
|
296
|
-
untrack(() =>
|
|
307
|
+
untrack(() => {
|
|
308
|
+
void this.#pass(true);
|
|
309
|
+
this.#rendering = false;
|
|
310
|
+
});
|
|
297
311
|
};
|
|
298
312
|
#deps = () => {
|
|
299
313
|
const { edytor } = this;
|
|
300
314
|
void [this.epoch, this.records, edytor.readonly, edytor.composition.phase];
|
|
301
315
|
void [edytor.selection.value, edytor.suggestions.revision];
|
|
302
316
|
};
|
|
317
|
+
/**
|
|
318
|
+
* The editor writes the host now: the flush (from the pre pass to the end
|
|
319
|
+
* of its compare pass) or a pass's own repairs. A record taken then is the
|
|
320
|
+
* render's (`dom.anchors.foreign`); every other one is the browser's, a
|
|
321
|
+
* page script's or an extension's.
|
|
322
|
+
*/
|
|
323
|
+
#own = () => this.#rendering || this.#passes > 0;
|
|
303
324
|
#take = () => {
|
|
304
325
|
const records = this.#mo?.takeRecords() ?? [];
|
|
305
|
-
|
|
326
|
+
const own = this.#own();
|
|
327
|
+
for (const record of records)
|
|
328
|
+
this.#intake(record, own);
|
|
306
329
|
return records.length > 0;
|
|
307
330
|
};
|
|
308
|
-
/** The
|
|
309
|
-
#
|
|
331
|
+
/** The closest registered element holding `node` in the host: its entry (`null`: the host). */
|
|
332
|
+
#near = (node) => {
|
|
310
333
|
for (let at = node; at; at = at.parentNode) {
|
|
311
334
|
if (at === this.edytor.node)
|
|
312
335
|
return null;
|
|
313
|
-
const entry =
|
|
314
|
-
// What happens in a preview is the view's own render: nothing to compare.
|
|
336
|
+
const entry = this.#registry.get(at);
|
|
315
337
|
if (entry)
|
|
316
|
-
return entry
|
|
338
|
+
return entry;
|
|
317
339
|
}
|
|
318
|
-
return undefined;
|
|
319
340
|
};
|
|
320
|
-
/** The
|
|
321
|
-
#
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
341
|
+
/** The block an element belongs to: its closest registered element's (`null`: the root). */
|
|
342
|
+
#owner = (node) => {
|
|
343
|
+
const entry = this.#near(node);
|
|
344
|
+
// What happens in a preview is the view's own render: nothing to compare.
|
|
345
|
+
return (entry && (entry.kind === 'preview' ? undefined : entry.kind === 'anchor' ? null : entry.block));
|
|
346
|
+
};
|
|
347
|
+
/** The handle a registered element of `kind` renders (`null`: another element). */
|
|
348
|
+
handleOf = (element, kind) => {
|
|
349
|
+
const entry = element && this.#registry.get(element);
|
|
350
|
+
return (entry?.kind === kind ? entry.handle : null);
|
|
351
|
+
};
|
|
352
|
+
/** The block `node` is in (its closest registered element's), none in a preview or outside the host. */
|
|
353
|
+
blockOf = (node) => {
|
|
354
|
+
const entry = this.#near(node);
|
|
355
|
+
return entry && entry.kind !== 'preview' && entry.kind !== 'anchor'
|
|
356
|
+
? (this.edytor.idToBlock.get(entry.block) ?? null)
|
|
357
|
+
: null;
|
|
328
358
|
};
|
|
329
359
|
#registered = (node) => node instanceof Element && this.#registry.has(node);
|
|
330
360
|
/** `node` is, or holds, an element the core still renders. */
|
|
331
361
|
#holds = (node) => this.#registered(node) ||
|
|
332
362
|
(node instanceof Element &&
|
|
333
363
|
[...node.querySelectorAll('*')].some((child) => this.#registry.has(child)));
|
|
334
|
-
/** A record names elements to compare (never a verdict). */
|
|
335
|
-
#intake = (record) => {
|
|
364
|
+
/** A record names elements to compare (never a verdict); `own`: the editor wrote it. */
|
|
365
|
+
#intake = (record, own) => {
|
|
336
366
|
this.#fresh = true;
|
|
337
367
|
const id = this.#owner(record.target);
|
|
338
368
|
if (id === undefined)
|
|
@@ -355,7 +385,7 @@ export class SurfaceObserver {
|
|
|
355
385
|
return;
|
|
356
386
|
const added = [...record.addedNodes];
|
|
357
387
|
const { target: parent, nextSibling: next, previousSibling: prev } = record;
|
|
358
|
-
this.#removed.push({ parent, nodes: removed, added, next, prev });
|
|
388
|
+
this.#removed.push({ parent, nodes: removed, added, next, prev, foreign: !own });
|
|
359
389
|
// Wrapper replacement: the browser replaced a text element with its own nodes.
|
|
360
390
|
const [text] = removed;
|
|
361
391
|
if (removed.length === 1 &&
|
|
@@ -394,21 +424,33 @@ export class SurfaceObserver {
|
|
|
394
424
|
* browser removed them one record each (Firefox): an anchor-only record is
|
|
395
425
|
* inverted in a parent that gets a registered element back, else the
|
|
396
426
|
* element's recorded siblings are gone and it lands outside Svelte's block.
|
|
427
|
+
* A foreign range across blocks (`dom.anchors.foreign`: a script's
|
|
428
|
+
* `Range.deleteContents()`) takes the render's anchors between the blocks
|
|
429
|
+
* from their list (the root, a block's children) and those after its start
|
|
430
|
+
* and before its end inside the blocks it enters, and leaves the elements:
|
|
431
|
+
* Svelte's next removal of either block would start at a detached anchor
|
|
432
|
+
* and leave its element, a child added to one would find no anchor to
|
|
433
|
+
* render before. When a foreign record took anchors from a block list,
|
|
434
|
+
* every foreign anchor-only record of the batch is inverted. The render's
|
|
435
|
+
* own removals (an item leaving the list) are never undone.
|
|
397
436
|
*/
|
|
398
437
|
#restore = () => {
|
|
399
438
|
const root = this.edytor.node;
|
|
400
439
|
const removals = this.#removed.splice(0).reverse();
|
|
401
440
|
const held = ({ parent, nodes }) => nodes.some((node) => node.parentNode !== parent && this.#holds(node));
|
|
402
441
|
const parents = new Set(removals.filter(held).map(({ parent }) => parent));
|
|
442
|
+
const crossed = removals.some((removal) => this.#lost(removal) && this.#list(removal.parent));
|
|
403
443
|
for (const removal of removals) {
|
|
404
444
|
const { parent, nodes, added, next, prev } = removal;
|
|
405
445
|
if (!root?.contains(parent))
|
|
406
446
|
continue;
|
|
407
|
-
|
|
447
|
+
const anchors = parents.has(parent) && nodes.every(isAnchor);
|
|
448
|
+
if (!held(removal) && !anchors && !(crossed && this.#lost(removal)))
|
|
408
449
|
continue;
|
|
409
450
|
// A browser-owned attempt's host: its change is adopted whatever the
|
|
410
451
|
// browser did to its structure, which the re-render then restores.
|
|
411
|
-
const
|
|
452
|
+
const near = this.#near(parent);
|
|
453
|
+
const inside = near?.kind === 'text' && near.handle;
|
|
412
454
|
if (inside && this.edytor.attempts.on(inside))
|
|
413
455
|
continue;
|
|
414
456
|
for (const node of added)
|
|
@@ -428,6 +470,17 @@ export class SurfaceObserver {
|
|
|
428
470
|
}
|
|
429
471
|
this.#replaced.clear();
|
|
430
472
|
};
|
|
473
|
+
/**
|
|
474
|
+
* A foreign record took only the render's anchors, and they are gone from
|
|
475
|
+
* the page (not moved): they can return beside a recorded sibling still
|
|
476
|
+
* there (never at the parent's end).
|
|
477
|
+
*/
|
|
478
|
+
#lost = ({ parent, nodes, next, prev, foreign }) => foreign &&
|
|
479
|
+
nodes.every((node) => isAnchor(node) && !node.isConnected) &&
|
|
480
|
+
(next?.parentNode === parent || prev?.parentNode === parent);
|
|
481
|
+
/** A block list: the root, or a parent of block elements (a block's children). */
|
|
482
|
+
#list = (parent) => parent === this.edytor.node ||
|
|
483
|
+
[...parent.childNodes].some((child) => child instanceof Element && this.#registry.get(child)?.kind === 'block');
|
|
431
484
|
/**
|
|
432
485
|
* Where an edit made at `at` of the base lands now: through the anchor
|
|
433
486
|
* minted at it (it follows its text through a re-key, split, move or
|
|
@@ -479,6 +532,19 @@ export class SurfaceObserver {
|
|
|
479
532
|
* `$effect`); otherwise contents waiting for a flush are left to it.
|
|
480
533
|
*/
|
|
481
534
|
#pass = (flushed) => {
|
|
535
|
+
this.#passes++;
|
|
536
|
+
try {
|
|
537
|
+
return this.#compare(flushed);
|
|
538
|
+
}
|
|
539
|
+
finally {
|
|
540
|
+
// What the flush and this pass wrote: the render's records, compared
|
|
541
|
+
// at the next pass (as the observer's callback would have them).
|
|
542
|
+
if (this.#take())
|
|
543
|
+
queueMicrotask(this.signal);
|
|
544
|
+
this.#passes--;
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
#compare = (flushed) => {
|
|
482
548
|
const { edytor } = this;
|
|
483
549
|
const cells = edytor.cells;
|
|
484
550
|
if (!cells || !edytor.node?.isConnected || edytor.destroyed)
|
|
@@ -534,7 +600,7 @@ export class SurfaceObserver {
|
|
|
534
600
|
}
|
|
535
601
|
if (this.#fresh) {
|
|
536
602
|
this.#fresh = false;
|
|
537
|
-
void settle(work).then(edytor.projector.
|
|
603
|
+
void settle(work).then(edytor.projector.rendered);
|
|
538
604
|
}
|
|
539
605
|
return work;
|
|
540
606
|
};
|
|
@@ -620,31 +686,11 @@ export class SurfaceObserver {
|
|
|
620
686
|
#invert = (id) => {
|
|
621
687
|
if (id === this.#live())
|
|
622
688
|
return;
|
|
623
|
-
if (this.#shape(id
|
|
689
|
+
if (this.#shape(id, true))
|
|
624
690
|
this.edytor.cells?.remount(id);
|
|
625
691
|
else
|
|
626
692
|
this.update();
|
|
627
693
|
};
|
|
628
|
-
/** Write each render delta's text back into its node; false when a node is not there. */
|
|
629
|
-
#rewrite = (id) => {
|
|
630
|
-
const writes = [];
|
|
631
|
-
for (const text of this.#texts(id)) {
|
|
632
|
-
const deltas = this.edytor.deltasOf(text);
|
|
633
|
-
const nodes = this.#nodes(text.node);
|
|
634
|
-
const shown = deltas.length ? deltas : [{ text: '\u200B' }];
|
|
635
|
-
for (const [i, delta] of shown.entries()) {
|
|
636
|
-
const node = nodes[i];
|
|
637
|
-
const leaf = node?.nodeType === Node.TEXT_NODE ? node : node && textLeaf(node);
|
|
638
|
-
if (!leaf)
|
|
639
|
-
return false;
|
|
640
|
-
if (leaf.data !== delta.text)
|
|
641
|
-
writes.push([leaf, delta.text]);
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
for (const [leaf, value] of writes)
|
|
645
|
-
leaf.data = value;
|
|
646
|
-
return true;
|
|
647
|
-
};
|
|
648
694
|
/** A text element's children that stand for render deltas (Svelte's anchors and the newline marker aside). */
|
|
649
695
|
#nodes = (element) => [...element.childNodes].filter((node) => !isAnchor(node) &&
|
|
650
696
|
!(node instanceof Element && node.hasAttribute('data-edytor-trailing-newline')));
|
|
@@ -655,7 +701,7 @@ export class SurfaceObserver {
|
|
|
655
701
|
*/
|
|
656
702
|
#adoptContent = (id) => {
|
|
657
703
|
const { edytor } = this;
|
|
658
|
-
let broken =
|
|
704
|
+
let broken = this.#shape(id);
|
|
659
705
|
const caret = getCollapsedDomTextSelection(edytor);
|
|
660
706
|
const work = [];
|
|
661
707
|
for (const text of this.#texts(id)) {
|
|
@@ -688,7 +734,7 @@ export class SurfaceObserver {
|
|
|
688
734
|
const value = now.slice(0, local) + insert + now.slice(local + remove);
|
|
689
735
|
return adopt(this.edytor, text, value, undefined, local + insert.length);
|
|
690
736
|
};
|
|
691
|
-
#texts = (id) => [...(this.#contents.get(id) ?? [])].flatMap((element) => this.
|
|
737
|
+
#texts = (id) => [...(this.#contents.get(id) ?? [])].flatMap((element) => this.handleOf(element, 'text') ?? []);
|
|
692
738
|
/** A content's DOM: its registered text and atom elements in order; null when not shown. */
|
|
693
739
|
#read = (id) => {
|
|
694
740
|
const elements = [...(this.#contents.get(id) ?? [])];
|
|
@@ -697,20 +743,17 @@ export class SurfaceObserver {
|
|
|
697
743
|
elements.sort((a, b) => a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1);
|
|
698
744
|
const host = this.#blocks.get(id);
|
|
699
745
|
let text = '';
|
|
700
|
-
let broken = host && !host.isConnected
|
|
746
|
+
let broken = !!host && !host.isConnected;
|
|
701
747
|
for (const element of elements) {
|
|
702
748
|
const kind = this.#registry.get(element)?.kind;
|
|
703
749
|
// A mark is inside its text element: its presence is the shape check's.
|
|
704
750
|
if (kind === 'mark')
|
|
705
751
|
continue;
|
|
706
|
-
|
|
707
|
-
broken ??= 'missing';
|
|
708
|
-
else if (host && !host.contains(element))
|
|
709
|
-
broken ??= 'escaped';
|
|
752
|
+
broken ||= !element.isConnected || (!!host && !host.contains(element));
|
|
710
753
|
if (kind === 'atom')
|
|
711
754
|
text += ATOM;
|
|
712
755
|
else if (kind === 'text') {
|
|
713
|
-
const found = this.
|
|
756
|
+
const found = this.handleOf(element, 'text');
|
|
714
757
|
text += (found ? readDomText(found) : (element.textContent ?? '')).replace(ZWSP, '');
|
|
715
758
|
}
|
|
716
759
|
}
|
|
@@ -718,46 +761,50 @@ export class SurfaceObserver {
|
|
|
718
761
|
};
|
|
719
762
|
/**
|
|
720
763
|
* Equal text, compared structurally: each text element (a strict
|
|
721
|
-
* container) holds one node per render delta
|
|
722
|
-
* registered mark element whose registered marks follow
|
|
764
|
+
* container) holds one node per render delta (the empty filler's one) — a
|
|
765
|
+
* text node, or a registered mark element whose registered marks follow
|
|
766
|
+
* the delta's chain and that renders one text node. Answers whether it
|
|
767
|
+
* differs; `write`: a content that does not gets each delta's text back
|
|
768
|
+
* in its node.
|
|
723
769
|
*/
|
|
724
|
-
#shape = (id) => {
|
|
770
|
+
#shape = (id, write) => {
|
|
771
|
+
const writes = [];
|
|
725
772
|
for (const text of this.#texts(id)) {
|
|
726
773
|
const element = text.node;
|
|
727
774
|
if (!element)
|
|
728
775
|
continue;
|
|
729
776
|
const deltas = this.edytor.deltasOf(text);
|
|
777
|
+
const shown = deltas.length ? deltas : [{ text: '\u200B', marks: [] }];
|
|
730
778
|
const nodes = this.#nodes(element);
|
|
731
|
-
if (
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
continue;
|
|
735
|
-
}
|
|
736
|
-
if (nodes.length !== deltas.length)
|
|
737
|
-
return 'partition';
|
|
738
|
-
for (const [i, delta] of deltas.entries()) {
|
|
779
|
+
if (nodes.length !== shown.length)
|
|
780
|
+
return true;
|
|
781
|
+
for (const [i, delta] of shown.entries()) {
|
|
739
782
|
const node = nodes[i];
|
|
783
|
+
let leaf = node;
|
|
740
784
|
const chain = delta.marks
|
|
741
785
|
.map(([name]) => name)
|
|
742
786
|
.filter((name) => rendersMark(this.edytor.marks, name));
|
|
743
|
-
if (
|
|
744
|
-
if (node
|
|
745
|
-
return
|
|
746
|
-
|
|
787
|
+
if (chain.length) {
|
|
788
|
+
if (!this.#registry.has(node))
|
|
789
|
+
return true;
|
|
790
|
+
const marks = [node, ...node.querySelectorAll('[data-edytor-mark]')]
|
|
791
|
+
.map((el) => this.#registry.get(el)?.name)
|
|
792
|
+
.filter(Boolean);
|
|
793
|
+
leaf = textLeaf(node);
|
|
794
|
+
// The snippet's markup is its own, but the delta renders one text node.
|
|
795
|
+
if (marks.join() !== chain.join() || !leaf || textLeaf(node, 1))
|
|
796
|
+
return true;
|
|
747
797
|
}
|
|
748
|
-
if (
|
|
749
|
-
return
|
|
750
|
-
|
|
751
|
-
.
|
|
752
|
-
.filter(Boolean);
|
|
753
|
-
if (marks.join() !== chain.join())
|
|
754
|
-
return 'mark-chain';
|
|
755
|
-
// The snippet's markup is its own, but the delta renders one text node.
|
|
756
|
-
if (!textLeaf(node) || textLeaf(node, 1))
|
|
757
|
-
return 'mark-text';
|
|
798
|
+
else if (node.nodeType !== Node.TEXT_NODE)
|
|
799
|
+
return true;
|
|
800
|
+
if (leaf.data !== delta.text)
|
|
801
|
+
writes.push([leaf, delta.text]);
|
|
758
802
|
}
|
|
759
803
|
}
|
|
760
|
-
|
|
804
|
+
if (write)
|
|
805
|
+
for (const [leaf, value] of writes)
|
|
806
|
+
leaf.data = value;
|
|
807
|
+
return false;
|
|
761
808
|
};
|
|
762
809
|
/** The root's children differ from its registered block elements in cell order, the anchor, Svelte's anchors. */
|
|
763
810
|
#rootDiverges = () => {
|
|
@@ -23,15 +23,15 @@ export declare class Pin {
|
|
|
23
23
|
/** The session's preview (the browser draws it itself when `native`). */
|
|
24
24
|
show: (text: string, marks: Record<string, unknown> | undefined, native: boolean) => void;
|
|
25
25
|
/**
|
|
26
|
-
* The first write replaced a range across
|
|
27
|
-
* cell lost the segments and atoms it covered, and the rest of
|
|
28
|
-
* now follows the preview in
|
|
29
|
-
*
|
|
30
|
-
* host element
|
|
31
|
-
*
|
|
32
|
-
*
|
|
26
|
+
* The first write replaced a range across an inline atom inside one block:
|
|
27
|
+
* the host's cell lost the segments and atoms it covered, and the rest of
|
|
28
|
+
* its end text now follows the preview in its own element right after the
|
|
29
|
+
* host (`rest`), so the IME's node is never rewritten (the IME's write
|
|
30
|
+
* moves the host element's render anchors into the next segment's,
|
|
31
|
+
* Chromium). A range across blocks never merges: the session replaces it
|
|
32
|
+
* at its start (`Composition.#rangeless`).
|
|
33
33
|
*/
|
|
34
|
-
merge: (cell: Cell, items: Item[]
|
|
34
|
+
merge: (cell: Cell, items: Item[]) => void;
|
|
35
35
|
/** The session ended: the host renders from its cell again (the catch-up patch). */
|
|
36
36
|
release: () => null;
|
|
37
37
|
/** `node` lies inside the pinned element. */
|
|
@@ -48,7 +48,7 @@ export declare class Pin {
|
|
|
48
48
|
deltas: RenderDelta[];
|
|
49
49
|
empty: boolean;
|
|
50
50
|
};
|
|
51
|
-
/** What follows the pinned segment `key` of `block` in its own element (`merge
|
|
51
|
+
/** What follows the pinned segment `key` of `block` in its own element (`merge`). */
|
|
52
52
|
rest: (block: BlockId, key: string) => RenderDelta[];
|
|
53
53
|
/**
|
|
54
54
|
* What the IME shows in the pinned element: its text between the frozen
|
|
@@ -29,7 +29,6 @@ export class Pin {
|
|
|
29
29
|
head: cut(frozen, from)[0],
|
|
30
30
|
tail: cut(frozen, Math.max(from, to))[1],
|
|
31
31
|
merged: [],
|
|
32
|
-
beside: false,
|
|
33
32
|
element,
|
|
34
33
|
// Until the first preview the render stays as the IME found it: a
|
|
35
34
|
// write now would collapse its DOM range before it replaces it.
|
|
@@ -44,18 +43,18 @@ export class Pin {
|
|
|
44
43
|
this.#held = { ...held, preview: { text, marks: set && Object.fromEntries(set), native } };
|
|
45
44
|
};
|
|
46
45
|
/**
|
|
47
|
-
* The first write replaced a range across
|
|
48
|
-
* cell lost the segments and atoms it covered, and the rest of
|
|
49
|
-
* now follows the preview in
|
|
50
|
-
*
|
|
51
|
-
* host element
|
|
52
|
-
*
|
|
53
|
-
*
|
|
46
|
+
* The first write replaced a range across an inline atom inside one block:
|
|
47
|
+
* the host's cell lost the segments and atoms it covered, and the rest of
|
|
48
|
+
* its end text now follows the preview in its own element right after the
|
|
49
|
+
* host (`rest`), so the IME's node is never rewritten (the IME's write
|
|
50
|
+
* moves the host element's render anchors into the next segment's,
|
|
51
|
+
* Chromium). A range across blocks never merges: the session replaces it
|
|
52
|
+
* at its start (`Composition.#rangeless`).
|
|
54
53
|
*/
|
|
55
|
-
merge = (cell, items
|
|
54
|
+
merge = (cell, items) => {
|
|
56
55
|
const held = this.#held;
|
|
57
56
|
if (held)
|
|
58
|
-
this.#held = { ...held, parts: partsOf(cell.runs), merged: items
|
|
57
|
+
this.#held = { ...held, parts: partsOf(cell.runs), merged: items };
|
|
59
58
|
};
|
|
60
59
|
/** The session ended: the host renders from its cell again (the catch-up patch). */
|
|
61
60
|
release = () => (this.#held = null);
|
|
@@ -68,14 +67,10 @@ export class Pin {
|
|
|
68
67
|
const held = this.#held;
|
|
69
68
|
if (!held || held.block !== block || held.key !== key)
|
|
70
69
|
return null;
|
|
71
|
-
const { preview, head, tail
|
|
72
|
-
const merged = beside ? [] : held.merged;
|
|
70
|
+
const { preview, head, tail } = held;
|
|
73
71
|
if (!preview || preview.native)
|
|
74
|
-
return {
|
|
75
|
-
|
|
76
|
-
empty: held.empty && !textOf(merged)
|
|
77
|
-
};
|
|
78
|
-
const items = [...head, preview, ...tail, ...merged];
|
|
72
|
+
return { deltas: [...held.frozen], empty: held.empty };
|
|
73
|
+
const items = [...head, preview, ...tail];
|
|
79
74
|
return { deltas: renderDeltas(items), empty: !textOf(items) };
|
|
80
75
|
};
|
|
81
76
|
/** The render of `segment` of `cell`: pinned while it hosts the IME, else its kind's (`segmentDeltas`). */
|
|
@@ -83,11 +78,10 @@ export class Pin {
|
|
|
83
78
|
deltas: segmentDeltas(cell, segment, transform),
|
|
84
79
|
empty: segment.text === ''
|
|
85
80
|
};
|
|
86
|
-
/** What follows the pinned segment `key` of `block` in its own element (`merge
|
|
81
|
+
/** What follows the pinned segment `key` of `block` in its own element (`merge`). */
|
|
87
82
|
rest = (block, key) => {
|
|
88
83
|
const held = this.#held;
|
|
89
|
-
|
|
90
|
-
return shown ? renderDeltas(held.merged) : [];
|
|
84
|
+
return held?.block === block && held.key === key ? renderDeltas(held.merged) : [];
|
|
91
85
|
};
|
|
92
86
|
/**
|
|
93
87
|
* What the IME shows in the pinned element: its text between the frozen
|
|
@@ -102,8 +96,7 @@ export class Pin {
|
|
|
102
96
|
const dom = (held.element.textContent ?? '').replace(/\u200B/g, '');
|
|
103
97
|
if (!held.preview && dom === textOf(held.frozen))
|
|
104
98
|
return null;
|
|
105
|
-
const
|
|
106
|
-
const [head, tail] = [textOf(held.head), textOf(inside)];
|
|
99
|
+
const [head, tail] = [textOf(held.head), textOf(held.tail)];
|
|
107
100
|
if (dom.length < head.length + tail.length)
|
|
108
101
|
return null;
|
|
109
102
|
return dom.startsWith(head) && dom.endsWith(tail)
|
|
@@ -28,11 +28,23 @@ export declare class Projector {
|
|
|
28
28
|
* removal re-rendering the text under a range) runs now. The flag is no
|
|
29
29
|
* reactive source, so no later flush is owed: without this, the range the
|
|
30
30
|
* render moved (Firefox keeps it where the removed element stood) would be
|
|
31
|
-
* adopted as the gesture's when its `selectionchange` lands.
|
|
32
|
-
* calls it too once it processed DOM records: a display that waits gets a
|
|
33
|
-
* pass.
|
|
31
|
+
* adopted as the gesture's when its `selectionchange` lands.
|
|
34
32
|
*/
|
|
35
33
|
inputHandled: () => void;
|
|
34
|
+
/**
|
|
35
|
+
* The observer compared the records of a DOM change (a render, a browser
|
|
36
|
+
* edit): a display that waits on them gets a pass (`inputHandled`), and a
|
|
37
|
+
* DOM selection they moved off the last observation with no gesture since
|
|
38
|
+
* is render drift no event reports (`sel.drift.unreported`). A render no
|
|
39
|
+
* commit asked for (a kind's decoration reading state of its own: a code
|
|
40
|
+
* grammar that lands re-tokenizes its lines) replaced the text node the
|
|
41
|
+
* caret was in; the browser left the caret where that node stood (the
|
|
42
|
+
* line's start) and Chromium and WebKit send no `selectionchange` for it,
|
|
43
|
+
* while one that comes would read as foreign (no flush ran the projector
|
|
44
|
+
* since). The value is displayed again now, in the render's own task:
|
|
45
|
+
* before a key, or its `beforeinput` declaring that caret, can read it.
|
|
46
|
+
*/
|
|
47
|
+
rendered: () => void;
|
|
36
48
|
/**
|
|
37
49
|
* Classify a `selectionchange` by comparing the DOM selection with the
|
|
38
50
|
* last display, the render epoch and the gesture serial:
|