@quillmark/svelte 0.1.1 → 0.3.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 +16 -12
- package/THEMING.md +48 -25
- package/dist/core/address.d.ts +42 -7
- package/dist/core/address.js +49 -21
- package/dist/core/bloom.d.ts +5 -5
- package/dist/core/bloom.js +8 -8
- package/dist/core/codec/anchor.d.ts +1 -1
- package/dist/core/codec/anchor.js +1 -1
- package/dist/core/codec/atoms.d.ts +10 -0
- package/dist/core/codec/atoms.js +78 -0
- package/dist/core/codec/clipboard.d.ts +59 -0
- package/dist/core/codec/clipboard.js +202 -0
- package/dist/core/codec/code.d.ts +1 -1
- package/dist/core/codec/code.js +4 -4
- package/dist/core/codec/decode.d.ts +1 -1
- package/dist/core/codec/decode.js +8 -8
- package/dist/core/codec/encode.d.ts +4 -4
- package/dist/core/codec/encode.js +14 -11
- package/dist/core/codec/field.d.ts +22 -27
- package/dist/core/codec/field.js +33 -32
- package/dist/core/codec/index.js +5 -8
- package/dist/core/codec/inputrules.js +56 -18
- package/dist/core/codec/islands.d.ts +11 -7
- package/dist/core/codec/islands.js +16 -10
- package/dist/core/codec/keymap.d.ts +5 -6
- package/dist/core/codec/keymap.js +8 -7
- package/dist/core/codec/lists.d.ts +3 -3
- package/dist/core/codec/lists.js +32 -13
- package/dist/core/codec/marks.d.ts +1 -1
- package/dist/core/codec/marks.js +1 -1
- package/dist/core/codec/positions.js +1 -1
- package/dist/core/codec/prose.css +266 -228
- package/dist/core/codec/schema.js +4 -4
- package/dist/core/codec/slash.d.ts +39 -41
- package/dist/core/codec/slash.js +0 -0
- package/dist/core/codec/table-view.d.ts +12 -16
- package/dist/core/codec/table-view.js +488 -292
- package/dist/core/codec/table.d.ts +42 -15
- package/dist/core/codec/table.js +73 -27
- package/dist/core/errors.d.ts +17 -7
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +6 -5
- package/dist/core/lifecycle.d.ts +21 -5
- package/dist/core/lifecycle.js +34 -8
- package/dist/core/rebind.svelte.d.ts +18 -4
- package/dist/core/rebind.svelte.js +30 -12
- package/dist/core/teardown.d.ts +1 -1
- package/dist/core/teardown.js +1 -1
- package/dist/core/theme.css +173 -58
- package/dist/preset/index.css +1 -1
- package/dist/preset/recipes.css +79 -30
- package/dist/preset/scale.css +40 -28
- package/dist/preview/Preview.svelte +22 -19
- package/dist/preview/Preview.svelte.d.ts +8 -8
- package/dist/preview/bridge.d.ts +6 -4
- package/dist/preview/bridge.js +19 -9
- package/dist/preview/controller.d.ts +13 -6
- package/dist/preview/controller.js +8 -5
- package/dist/preview/geometry.d.ts +17 -2
- package/dist/preview/geometry.js +21 -1
- package/dist/preview/index.d.ts +1 -0
- package/dist/preview/index.js +9 -2
- package/dist/preview/overlay.d.ts +2 -1
- package/dist/preview/overlay.js +26 -11
- package/dist/preview/paint.js +19 -17
- package/dist/visual/ArrayField.svelte +103 -83
- package/dist/visual/ArrayField.svelte.d.ts +5 -4
- package/dist/visual/BooleanField.svelte +8 -6
- package/dist/visual/BooleanField.svelte.d.ts +2 -2
- package/dist/visual/Card.svelte +161 -333
- package/dist/visual/Card.svelte.d.ts +5 -4
- package/dist/visual/CardControls.svelte +1 -1
- package/dist/visual/DateField.svelte +19 -19
- package/dist/visual/DateField.svelte.d.ts +3 -3
- package/dist/visual/DiagnosticList.svelte +2 -2
- package/dist/visual/EnumField.svelte +48 -28
- package/dist/visual/EnumField.svelte.d.ts +5 -4
- package/dist/visual/Field.svelte +207 -132
- package/dist/visual/Field.svelte.d.ts +11 -7
- package/dist/visual/FieldHint.svelte +79 -48
- package/dist/visual/FieldHint.svelte.d.ts +1 -1
- package/dist/visual/FieldLabel.svelte +8 -8
- package/dist/visual/FormatPopover.svelte +82 -35
- package/dist/visual/NumberField.svelte +5 -5
- package/dist/visual/NumberField.svelte.d.ts +1 -1
- package/dist/visual/ObjectField.svelte +28 -9
- package/dist/visual/ObjectField.svelte.d.ts +4 -2
- package/dist/visual/ProseArrayElement.svelte +12 -19
- package/dist/visual/ProseArrayElement.svelte.d.ts +1 -2
- package/dist/visual/ProseField.svelte +62 -85
- package/dist/visual/ProseField.svelte.d.ts +14 -17
- package/dist/visual/SlashMenu.svelte +42 -16
- package/dist/visual/TextField.svelte +5 -5
- package/dist/visual/TextField.svelte.d.ts +1 -1
- package/dist/visual/TipsCard.svelte +43 -28
- package/dist/visual/VisualEditor.svelte +18 -19
- package/dist/visual/VisualEditor.svelte.d.ts +2 -3
- package/dist/visual/VisualEditorInner.svelte +251 -184
- package/dist/visual/VisualEditorInner.svelte.d.ts +2 -3
- package/dist/visual/controls.css +85 -38
- package/dist/visual/diagnostics.d.ts +13 -21
- package/dist/visual/diagnostics.js +7 -19
- package/dist/visual/domid.js +3 -3
- package/dist/visual/ext.d.ts +1 -1
- package/dist/visual/ext.js +1 -1
- package/dist/visual/icons/nodes.d.ts +3 -0
- package/dist/visual/icons/nodes.js +2 -1
- package/dist/visual/index.d.ts +1 -0
- package/dist/visual/index.js +10 -3
- package/dist/visual/leaves.d.ts +47 -0
- package/dist/visual/leaves.js +31 -0
- package/dist/visual/links.d.ts +24 -0
- package/dist/visual/links.js +67 -0
- package/dist/visual/props.d.ts +33 -12
- package/dist/visual/signals.d.ts +6 -6
- package/dist/visual/strings.d.ts +14 -19
- package/dist/visual/strings.js +5 -7
- package/dist/visual/structure.d.ts +24 -17
- package/dist/visual/structure.js +8 -8
- package/dist/visual/synced.svelte.d.ts +1 -1
- package/dist/visual/synced.svelte.js +4 -4
- package/dist/visual/tips.d.ts +4 -2
- package/dist/visual/tips.js +7 -4
- package/package.json +2 -2
package/dist/core/codec/field.js
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
// (b) lower the tr to a `ChangeBundle` (or `overwrite` for a field not yet at
|
|
7
7
|
// content rest, which has nothing to splice),
|
|
8
8
|
// (c) commit via `doc.applyChange(addr, bundle)`,
|
|
9
|
-
// (d) fire `onChange` when the transaction
|
|
9
|
+
// (d) fire `onChange` when the transaction committed, then `onCaretMove` with
|
|
10
10
|
// the new USV caret, which a bare selection move fires on its own.
|
|
11
11
|
// On an `applyChange` throw the optimistic PM state stays and the failure reports
|
|
12
12
|
// through `onError`: never a crash. Caret continuity across own-edits is the PM
|
|
13
|
-
// `StepMap`; an
|
|
13
|
+
// `StepMap`; an external content change re-hydrates through `applyExternal`, gated
|
|
14
14
|
// by `reconcile`.
|
|
15
15
|
import { baseKeymap, toggleMark } from 'prosemirror-commands';
|
|
16
16
|
import { gapCursor } from 'prosemirror-gapcursor';
|
|
@@ -19,6 +19,7 @@ import { keymap } from 'prosemirror-keymap';
|
|
|
19
19
|
import { EditorState, NodeSelection, Plugin, PluginKey, Selection, TextSelection } from 'prosemirror-state';
|
|
20
20
|
import { Decoration, DecorationSet, EditorView } from 'prosemirror-view';
|
|
21
21
|
import { reportError, errorMessage } from '../errors.js';
|
|
22
|
+
import { tableClipboardPlugin } from './clipboard.js';
|
|
22
23
|
import { decode } from './decode.js';
|
|
23
24
|
import { usvToPM, pmToUsv, buildLineIndex } from './positions.js';
|
|
24
25
|
import { lower, pmToContent, contentEdit } from './encode.js';
|
|
@@ -28,7 +29,7 @@ import { inputRulesPlugin } from './inputrules.js';
|
|
|
28
29
|
import { bodyKeymap } from './keymap.js';
|
|
29
30
|
import { blockSchema, inlineSchema } from './schema.js';
|
|
30
31
|
import { DEFAULT_TABLE_STRINGS, tableNodeView } from './table-view.js';
|
|
31
|
-
import {
|
|
32
|
+
import { focusSlashItem, runSlashItem, slashPlugin } from './slash.js';
|
|
32
33
|
const anchorKey = new PluginKey('quill-anchors');
|
|
33
34
|
/** Plugin-held anchor positions (PM coords), mapped through every edit's `StepMap`
|
|
34
35
|
* and mutated by an {@link AnchorEdit} meta (an insert/remove at a selection). */
|
|
@@ -62,8 +63,8 @@ function anchorPlugin(seed) {
|
|
|
62
63
|
*
|
|
63
64
|
* Bound rather than verbatim, because the storage form is not the leaf's business
|
|
64
65
|
* and the verbatim read cannot answer it alone. A field the editor committed rests
|
|
65
|
-
* as `Content`; a field a markdown parse left rests as the authored
|
|
66
|
-
* what that string
|
|
66
|
+
* as `Content`; a field a markdown parse left rests as the authored string, and
|
|
67
|
+
* what that string means is the declared type's to say: `richtext` is markdown,
|
|
67
68
|
* `plaintext` is literal text, and reading one as the other silently eats every
|
|
68
69
|
* `*` the author typed. */
|
|
69
70
|
function readLeaf(reader, addr) {
|
|
@@ -77,9 +78,9 @@ export function emptyContent() {
|
|
|
77
78
|
/** Whether ops may commit here: the stored value is a `Content` object, so
|
|
78
79
|
* `applyChange` splices exactly the content the leaf read and PM is showing.
|
|
79
80
|
*
|
|
80
|
-
* The two other rest forms both take `overwrite` instead. An
|
|
81
|
+
* The two other rest forms both take `overwrite` instead. An unset field (`undefined`;
|
|
81
82
|
* a default-only richtext field before its first edit) has nothing to splice.
|
|
82
|
-
* An
|
|
83
|
+
* An authored string is the trap: `applyChange` reads it as markdown whatever the
|
|
83
84
|
* declared type is, so on a `plaintext` field its pre-image is not the content the
|
|
84
85
|
* leaf read and a delta computed against that content lands at the wrong offsets.
|
|
85
86
|
* Installing over it costs nothing either way, since content-only marks do not
|
|
@@ -119,15 +120,14 @@ export function createField(opts) {
|
|
|
119
120
|
// The ghost's live cell: the placeholder plugin reads it per pass, so moving it
|
|
120
121
|
// is an assignment plus a re-render rather than a rebuilt plugin stack.
|
|
121
122
|
let placeholderText = opts.placeholder;
|
|
122
|
-
// The views nested
|
|
123
|
+
// The views nested inside this leaf: one per table cell (`table-view.ts`). The
|
|
123
124
|
// leaf holds the set because chrome asks the leaf, not the island, which view
|
|
124
125
|
// holds the caret: the format popover raises over a cell exactly as over the
|
|
125
126
|
// body, and only the leaf knows both.
|
|
126
127
|
const nested = new Set();
|
|
127
|
-
// The menu's
|
|
128
|
-
//
|
|
129
|
-
const
|
|
130
|
-
const slash = !inline && opts.onSlash ? { items: menuItems, onState: opts.onSlash } : undefined;
|
|
128
|
+
// The menu's report channel, and the whole of what a leaf holds of it: the
|
|
129
|
+
// vocabulary is the codec's own constant (`slash.ts`), not a wording to derive.
|
|
130
|
+
const slash = inline ? undefined : opts.onSlash;
|
|
131
131
|
const state = buildState(reconciler.last);
|
|
132
132
|
index = buildLineIndex(state.doc);
|
|
133
133
|
view = new EditorView(container, {
|
|
@@ -154,7 +154,7 @@ export function createField(opts) {
|
|
|
154
154
|
const next = view.state.apply(tr);
|
|
155
155
|
view.updateState(next); // (a) optimistic
|
|
156
156
|
index = buildLineIndex(next.doc);
|
|
157
|
-
// Commit a content edit
|
|
157
|
+
// Commit a content edit or an anchor mutation. An anchor insert/remove is
|
|
158
158
|
// zero-width, so `docChanged` is false: the `anchorKey` meta is what
|
|
159
159
|
// routes it through the same commit path (the diff emits the anchor op).
|
|
160
160
|
if (tr.docChanged || tr.getMeta(anchorKey)) {
|
|
@@ -203,7 +203,7 @@ export function createField(opts) {
|
|
|
203
203
|
// Returns whether anything landed: the caller's change signal, which must not
|
|
204
204
|
// be a property of which branch the commit took.
|
|
205
205
|
function commitEdit(oldRt, newDoc) {
|
|
206
|
-
// The projection and its text splice, each computed
|
|
206
|
+
// The projection and its text splice, each computed once per keystroke: the gate
|
|
207
207
|
// below, both overwrite fallbacks, and `lower` all read this one `edit`.
|
|
208
208
|
const edit = contentEdit(oldRt, pmToContent(newDoc));
|
|
209
209
|
try {
|
|
@@ -226,7 +226,7 @@ export function createField(opts) {
|
|
|
226
226
|
return true;
|
|
227
227
|
}
|
|
228
228
|
catch (e) {
|
|
229
|
-
// Bound the damage: an op path the gates missed leaves the store
|
|
229
|
+
// Bound the damage: an op path the gates missed leaves the store stale
|
|
230
230
|
// while PM keeps the edit; and because the reconciler then re-diffs
|
|
231
231
|
// from that stale content, every later edit re-throws and the field
|
|
232
232
|
// silently stops persisting. Install the full projection instead
|
|
@@ -256,7 +256,7 @@ export function createField(opts) {
|
|
|
256
256
|
}
|
|
257
257
|
/** The anchors the plugin currently holds (PM coords); `[]` before the first apply. */
|
|
258
258
|
const heldAnchors = () => anchorKey.getState(view.state) ?? [];
|
|
259
|
-
/** Anchor positions in the
|
|
259
|
+
/** Anchor positions in the current (new) doc, as USV. */
|
|
260
260
|
function readAnchorsUsv(newDoc) {
|
|
261
261
|
return heldAnchors().map((a) => ({ id: a.id, pos: pmToUsv(index, a.pos) }));
|
|
262
262
|
}
|
|
@@ -326,13 +326,13 @@ export function createField(opts) {
|
|
|
326
326
|
})
|
|
327
327
|
.map((a) => a.id);
|
|
328
328
|
},
|
|
329
|
-
slashFocus(
|
|
329
|
+
slashFocus(name) {
|
|
330
330
|
if (slash)
|
|
331
|
-
focusSlashItem(view,
|
|
331
|
+
focusSlashItem(view, name);
|
|
332
332
|
},
|
|
333
|
-
slashPick(
|
|
333
|
+
slashPick(name) {
|
|
334
334
|
if (slash)
|
|
335
|
-
runSlashItem(view,
|
|
335
|
+
runSlashItem(view, name);
|
|
336
336
|
},
|
|
337
337
|
selectionRange() {
|
|
338
338
|
const { from, to } = view.state.selection;
|
|
@@ -365,13 +365,13 @@ export function createField(opts) {
|
|
|
365
365
|
* leaf-specific plugins (`afterHistory`: the addressed leaf passes its
|
|
366
366
|
* anchor-position plugin; a by-value array element passes none), the
|
|
367
367
|
* markdown-shorthand input rules, then the field keymap over the base keymap, and
|
|
368
|
-
* last the two that answer to a caret or a selection
|
|
368
|
+
* last the two that answer to a caret or a selection beside a block: the gap cursor
|
|
369
369
|
* and {@link pastAtomPlugin}.
|
|
370
370
|
*
|
|
371
371
|
* A `plaintext` field carries no marks (decode strips them, the keymap suppresses
|
|
372
372
|
* Mod-b/i/u), so it also skips the input rules: `inlineSchema` still declares the
|
|
373
|
-
* mark types, so a `**bold**` rule would apply a strong mark
|
|
374
|
-
* delimiters. The inline schema has no block nodes, so those rules are the
|
|
373
|
+
* mark types, so a `**bold**` rule would apply a strong mark and eat the literal
|
|
374
|
+
* delimiters. The inline schema has no block nodes, so those rules are the only
|
|
375
375
|
* ones it would add: skip all.
|
|
376
376
|
*/
|
|
377
377
|
export function proseLeafPlugins(schema, opts) {
|
|
@@ -380,21 +380,22 @@ export function proseLeafPlugins(schema, opts) {
|
|
|
380
380
|
list.push(slashPlugin(opts.slash));
|
|
381
381
|
if (!opts.noInputRules && !opts.plaintext)
|
|
382
382
|
list.push(inputRulesPlugin(schema));
|
|
383
|
-
list.push(keymap(editorKeymap(schema, opts.inline, opts.plaintext, opts.slash
|
|
383
|
+
list.push(keymap(editorKeymap(schema, opts.inline, opts.plaintext, !!opts.slash)));
|
|
384
384
|
list.push(keymap(baseKeymap));
|
|
385
|
-
//
|
|
386
|
-
//
|
|
385
|
+
// All three answer to something only a block leaf holds: an inline leaf is one
|
|
386
|
+
// textblock with no island, no gap to put a cursor in, and nowhere for a pasted
|
|
387
|
+
// table to land.
|
|
387
388
|
if (!opts.inline)
|
|
388
|
-
list.push(gapCursor(), pastAtomPlugin());
|
|
389
|
+
list.push(gapCursor(), pastAtomPlugin(), tableClipboardPlugin(schema));
|
|
389
390
|
if (opts.placeholder)
|
|
390
391
|
list.push(placeholderPlugin(opts.placeholder));
|
|
391
392
|
return list;
|
|
392
393
|
}
|
|
393
394
|
/**
|
|
394
|
-
* A printable key over a selected atom writes
|
|
395
|
+
* A printable key over a selected atom writes past it. A selection is the subject
|
|
395
396
|
* of the next command, never a thing armed for
|
|
396
397
|
* replacement: Backspace deletes it, Mod-C copies it, and a character lands beside
|
|
397
|
-
* it. A
|
|
398
|
+
* it. A block island takes a new paragraph after it, an inline one the caret after
|
|
398
399
|
* the image in the same line: one rule, both node types.
|
|
399
400
|
*
|
|
400
401
|
* PM routes text input over a non-`TextSelection` through `handleTextInput` before
|
|
@@ -461,13 +462,13 @@ function placeholderPlugin(read) {
|
|
|
461
462
|
* The field's keymap: history, mark toggles, and the body's structural keys; Enter
|
|
462
463
|
* suppressed inline.
|
|
463
464
|
*
|
|
464
|
-
* Tab forks on the leaf's
|
|
465
|
+
* Tab forks on the leaf's role, not on the caret's position. An inline/plaintext
|
|
465
466
|
* leaf is a form field: Tab stays unbound, so field navigation is open for a shell
|
|
466
467
|
* keymap (VISUAL_EDITOR §Settled and open). A block-schema body is a document: Tab
|
|
467
468
|
* is structural, a chain each nested surface prepends to (`keymap.ts`). One key
|
|
468
469
|
* never means two things within one surface.
|
|
469
470
|
*/
|
|
470
|
-
function editorKeymap(schema, inline, plaintext,
|
|
471
|
+
function editorKeymap(schema, inline, plaintext, slash) {
|
|
471
472
|
const map = {
|
|
472
473
|
'Mod-z': undo,
|
|
473
474
|
'Mod-y': redo,
|
|
@@ -487,7 +488,7 @@ function editorKeymap(schema, inline, plaintext, slashItems) {
|
|
|
487
488
|
}
|
|
488
489
|
else {
|
|
489
490
|
// Each binding falls through to `baseKeymap` when no link claims the key.
|
|
490
|
-
Object.assign(map, bodyKeymap(schema,
|
|
491
|
+
Object.assign(map, bodyKeymap(schema, slash));
|
|
491
492
|
}
|
|
492
493
|
return map;
|
|
493
494
|
}
|
package/dist/core/codec/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// The codec barrel: the bidirectional bridge between one content field (`Content`)
|
|
2
|
-
// and one ProseMirror document (CODEC). Decode (content → PM), lower (PM tr → a
|
|
2
|
+
// and one ProseMirror document (CODEC.md). Decode (content → PM), lower (PM tr → a
|
|
3
3
|
// `ChangeBundle` for `applyChange`), the USV↔PM position map, and the `createField`
|
|
4
4
|
// prose leaf. Consumed by the VisualEditor and the codec test suite.
|
|
5
5
|
// ProseMirror's structural base styles and the gap cursor's, then the retint of the
|
|
6
|
-
// hues they mint. Imported
|
|
6
|
+
// hues they mint. Imported here because `createField` is what mounts a view: a
|
|
7
7
|
// consumer reaching the codec directly gets them with it, and the order is what makes
|
|
8
8
|
// the retint win.
|
|
9
9
|
import 'prosemirror-view/style/prosemirror.css';
|
|
@@ -24,12 +24,9 @@ export { DEFAULT_SLASH_STRINGS } from './slash.js';
|
|
|
24
24
|
export { blockSchema, inlineSchema } from './schema.js';
|
|
25
25
|
// Decode / encode / positions (tests + VisualEditor).
|
|
26
26
|
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
// within `codec/`, so none of them is here. An export nothing imports is surface
|
|
31
|
-
// that still has to stay honest; a symbol earns its line when a caller outside
|
|
32
|
-
// this folder wants it.
|
|
27
|
+
// The barrel carries what has an off-barrel caller, and nothing else. A symbol
|
|
28
|
+
// reached only by relative import within `codec/` stays off it: an export nothing
|
|
29
|
+
// imports is surface that still has to stay honest.
|
|
33
30
|
export { decode, renderContent, usvLength } from './decode.js';
|
|
34
31
|
export { pmToContent, contentEdit, lower } from './encode.js';
|
|
35
32
|
export { usvToPM, pmToUsv, buildLineIndex } from './positions.js';
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// Input rules: the markdown-shorthand plugin the codec owns (`**`→strong, `*`→em,
|
|
2
2
|
// `~~`→strike, `` ` ``→code, `# `→heading, `- `/`1. `→lists, `> `→blockquote,
|
|
3
3
|
// `---`→divider).
|
|
4
|
-
// Mark rules are hand-rolled (prosemirror-inputrules ships only node rules);
|
|
5
|
-
//
|
|
6
|
-
// transactions the codec already lowers:
|
|
7
|
-
|
|
4
|
+
// Mark rules are hand-rolled (prosemirror-inputrules ships only node rules); the one
|
|
5
|
+
// block rule with no guard of its own reuses `wrappingInputRule`, and the rest mirror
|
|
6
|
+
// an upstream body around one. These produce transactions the codec already lowers:
|
|
7
|
+
// they add no new lowering surface.
|
|
8
|
+
import { InputRule, inputRules, wrappingInputRule } from 'prosemirror-inputrules';
|
|
8
9
|
import { canJoin, findWrapping } from 'prosemirror-transform';
|
|
9
10
|
import { Selection } from 'prosemirror-state';
|
|
10
11
|
/**
|
|
@@ -12,8 +13,8 @@ import { Selection } from 'prosemirror-state';
|
|
|
12
13
|
* before the caret, wrap capture group 1 in `markType` and drop the delimiters.
|
|
13
14
|
* `delimLen` is the delimiter length (opening == closing for every rule here).
|
|
14
15
|
* `match[0]` anatomy is `prefix? open captured close`: the em rule's
|
|
15
|
-
* `(?:^|[^*])` guard consumes
|
|
16
|
-
* positions are derived from the
|
|
16
|
+
* `(?:^|[^*])` guard consumes one non-delimiter prefix char into the match, so
|
|
17
|
+
* positions are derived from the known delimiter/capture lengths, never from
|
|
17
18
|
* `indexOf` (which both deletes that prefix char and mis-anchors when it
|
|
18
19
|
* happens to equal the captured text). `[start, end)` spans `match[0]` minus
|
|
19
20
|
* the just-typed text (not yet inserted when the rule fires), so the trailing
|
|
@@ -48,9 +49,14 @@ function markInputRule(regexp, markType, delimLen) {
|
|
|
48
49
|
* body paragraphs (`usaf_memo`'s `render-body`), where a heading resolves to
|
|
49
50
|
* nothing. This is the wrap-side route into that shape; the `# ` rule guards the
|
|
50
51
|
* other, in {@link markdownInputRules}.
|
|
52
|
+
*
|
|
53
|
+
* `item` is the second guard, and it is about the gesture rather than the shape: the
|
|
54
|
+
* rule declines at the start of an item's own first block ({@link openingAnItem}).
|
|
51
55
|
*/
|
|
52
|
-
function listWrappingRule(regexp, listType, paragraph, getAttrs, joinPredicate) {
|
|
56
|
+
function listWrappingRule(regexp, listType, paragraph, item, getAttrs, joinPredicate) {
|
|
53
57
|
return new InputRule(regexp, (state, match, start, end) => {
|
|
58
|
+
if (openingAnItem(state.doc.resolve(start), item))
|
|
59
|
+
return null;
|
|
54
60
|
const attrs = getAttrs ? getAttrs(match) : null;
|
|
55
61
|
const tr = state.tr.delete(start, end);
|
|
56
62
|
// Positions survive the retype: same content, same size.
|
|
@@ -75,10 +81,28 @@ function listWrappingRule(regexp, listType, paragraph, getAttrs, joinPredicate)
|
|
|
75
81
|
return tr;
|
|
76
82
|
});
|
|
77
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Whether `$start` is the head of an item that already exists: offset 0 of a
|
|
86
|
+
* `list_item`'s first block, which is where a list shorthand is the text an author
|
|
87
|
+
* typed rather than a shorthand at all.
|
|
88
|
+
*
|
|
89
|
+
* Firing there wraps the item's own paragraph in a fresh list, minting an item whose
|
|
90
|
+
* only content is another item — a level with nothing on it. Tab is the indent gesture
|
|
91
|
+
* and lands the shape a nesting is supposed to have, under the previous sibling, so
|
|
92
|
+
* what the rule adds here is a second door onto a worse answer. A later block of the
|
|
93
|
+
* item is not this case: wrapping a continuation paragraph is how a sub-list opens
|
|
94
|
+
* under text.
|
|
95
|
+
*/
|
|
96
|
+
function openingAnItem($start, item) {
|
|
97
|
+
if (!item || $start.depth < 2 || $start.parentOffset !== 0)
|
|
98
|
+
return false;
|
|
99
|
+
return $start.node(-1).type === item && $start.index(-1) === 0;
|
|
100
|
+
}
|
|
78
101
|
/** The full markdown-shorthand rule set for a block schema. */
|
|
79
102
|
export function markdownInputRules(schema) {
|
|
80
103
|
const rules = [];
|
|
81
104
|
const m = schema.marks;
|
|
105
|
+
const item = schema.nodes.list_item;
|
|
82
106
|
// Inline marks. Non-greedy capture between matching delimiters.
|
|
83
107
|
if (m.strong)
|
|
84
108
|
rules.push(markInputRule(/\*\*([^*]+)\*\*$/, m.strong, 2));
|
|
@@ -95,7 +119,6 @@ export function markdownInputRules(schema) {
|
|
|
95
119
|
// declines (null) and stays literal text, rather than minting the unrenderable
|
|
96
120
|
// `list_item > heading` {@link listWrappingRule} normalizes away on the wrap side.
|
|
97
121
|
const heading = schema.nodes.heading;
|
|
98
|
-
const item = schema.nodes.list_item;
|
|
99
122
|
rules.push(new InputRule(/^(#{1,6})\s$/, (state, match, start, end) => {
|
|
100
123
|
const $start = state.doc.resolve(start);
|
|
101
124
|
for (let d = $start.depth; d > 0; d--) {
|
|
@@ -110,18 +133,34 @@ export function markdownInputRules(schema) {
|
|
|
110
133
|
.setBlockType(start, start, heading, { level: match[1].length });
|
|
111
134
|
}));
|
|
112
135
|
}
|
|
113
|
-
if (schema.nodes.code_block) {
|
|
114
|
-
|
|
136
|
+
if (schema.nodes.code_block && schema.nodes.paragraph) {
|
|
137
|
+
// `textblockTypeInputRule`'s body plus the exit the `---` rule mints below, for a
|
|
138
|
+
// sharper version of its reason: a code block is the one block a GAP CURSOR will
|
|
139
|
+
// not sit beside either (`closedBefore`/`closedAfter` both fail on a textblock), so
|
|
140
|
+
// a fence at the end of a body leaves the caret with no way forward at all.
|
|
141
|
+
const code = schema.nodes.code_block;
|
|
142
|
+
const paragraph = schema.nodes.paragraph;
|
|
143
|
+
rules.push(new InputRule(/^```$/, (state, _match, start, end) => {
|
|
144
|
+
const $start = state.doc.resolve(start);
|
|
145
|
+
if (!$start.node(-1).canReplaceWith($start.index(-1), $start.indexAfter(-1), code)) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
const tr = state.tr.delete(start, end).setBlockType(start, start, code);
|
|
149
|
+
const at = tr.doc.resolve(start).after();
|
|
150
|
+
if (!tr.doc.nodeAt(at))
|
|
151
|
+
tr.insert(at, paragraph.create());
|
|
152
|
+
// The caret stays in the fence, not in the exit it minted.
|
|
153
|
+
return tr;
|
|
154
|
+
}));
|
|
115
155
|
}
|
|
116
156
|
if (schema.nodes.horizontal_rule && schema.nodes.paragraph) {
|
|
117
|
-
// A divider is a whole block, so this rule
|
|
157
|
+
// A divider is a whole block, so this rule replaces its textblock rather than
|
|
118
158
|
// retyping one: `horizontal_rule` holds no content to retype into.
|
|
119
159
|
const rule = schema.nodes.horizontal_rule;
|
|
120
160
|
const paragraph = schema.nodes.paragraph;
|
|
121
|
-
const item = schema.nodes.list_item;
|
|
122
161
|
rules.push(new InputRule(/^---$/, (state, _match, start, end) => {
|
|
123
162
|
const $start = state.doc.resolve(start);
|
|
124
|
-
// The block
|
|
163
|
+
// The block entirely, not a prefix of one. `[start, end)` is the match minus
|
|
125
164
|
// the just-typed `-`, so the block holds `--` when this fires; anything else
|
|
126
165
|
// in it is a paragraph being edited, which a replace would eat.
|
|
127
166
|
if ($start.parentOffset !== 0 || $start.parent.content.size !== end - start) {
|
|
@@ -148,14 +187,13 @@ export function markdownInputRules(schema) {
|
|
|
148
187
|
return tr;
|
|
149
188
|
}));
|
|
150
189
|
}
|
|
151
|
-
// `- ` / `1. ` at the start of a block, here or nested inside an item
|
|
152
|
-
// the
|
|
153
|
-
// caret then lives in, which is what that menu is the set of (`slash.ts`).
|
|
190
|
+
// `- ` / `1. ` at the start of a block, here or nested inside an item — but not at
|
|
191
|
+
// the start of an item's own first block, where {@link openingAnItem} declines.
|
|
154
192
|
if (schema.nodes.bullet_list && schema.nodes.paragraph) {
|
|
155
|
-
rules.push(listWrappingRule(/^\s*([-+*])\s$/, schema.nodes.bullet_list, schema.nodes.paragraph));
|
|
193
|
+
rules.push(listWrappingRule(/^\s*([-+*])\s$/, schema.nodes.bullet_list, schema.nodes.paragraph, item));
|
|
156
194
|
}
|
|
157
195
|
if (schema.nodes.ordered_list && schema.nodes.paragraph) {
|
|
158
|
-
rules.push(listWrappingRule(/^(\d+)\.\s$/, schema.nodes.ordered_list, schema.nodes.paragraph, (match) => ({ start: +match[1] }), (match, node) => node.childCount + node.attrs.start === +match[1]));
|
|
196
|
+
rules.push(listWrappingRule(/^(\d+)\.\s$/, schema.nodes.ordered_list, schema.nodes.paragraph, item, (match) => ({ start: +match[1] }), (match, node) => node.childCount + node.attrs.start === +match[1]));
|
|
159
197
|
}
|
|
160
198
|
if (schema.nodes.blockquote) {
|
|
161
199
|
rules.push(wrappingInputRule(/^\s*>\s$/, schema.nodes.blockquote));
|
|
@@ -23,14 +23,18 @@ export declare function islandEntryFromNode(attrs: IslandNodeAttrs): ContentIsla
|
|
|
23
23
|
* rather than at each reader. */
|
|
24
24
|
export declare function tablePropsOfNode(node: PMNode): TableProps | undefined;
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* never a
|
|
26
|
+
* A minter over one document: the positional `isl-{n}` sequence continued past the
|
|
27
|
+
* highest id the field already holds, handing out consecutive ids, so a caller placing
|
|
28
|
+
* several islands at once keeps one minter for the lot. A minted id is this tier's to
|
|
29
|
+
* produce and it is part of the document's canonical bytes (CODEC §Islands), so it is a
|
|
30
|
+
* counter over the projection in hand — never a UUID, never a clock reading, and never a
|
|
31
|
+
* re-numbering of the ids already there, which are identities.
|
|
31
32
|
*
|
|
32
|
-
* Reads the PM doc rather than the stored content because that is what the caller
|
|
33
|
-
*
|
|
33
|
+
* Reads the PM doc rather than the stored content because that is what the caller (an
|
|
34
|
+
* insert command, a paste) holds and what the commit will project: an id minted against
|
|
34
35
|
* a stale content could collide with one the same transaction places.
|
|
35
36
|
*/
|
|
37
|
+
export declare function islandMinter(doc: PMNode): () => string;
|
|
38
|
+
/** The next island id for a field: one draw from {@link islandMinter}, which is what a
|
|
39
|
+
* command placing exactly one island wants. */
|
|
36
40
|
export declare function mintIslandId(doc: PMNode): string;
|
|
@@ -4,7 +4,7 @@ export const ISLAND_SLOT = '';
|
|
|
4
4
|
// The four island node attributes carry the content entry verbatim: `id` is the
|
|
5
5
|
// stable identity (rebases like an anchor), `islandType` the discriminator, `props`
|
|
6
6
|
// the opaque payload the codec preserves byte-for-byte across a round-trip, and
|
|
7
|
-
// `loss` how faithfully markdown can carry it. `loss` is
|
|
7
|
+
// `loss` how faithfully markdown can carry it. `loss` is authored, not derived:
|
|
8
8
|
// `applyChange` stores the class an island op gives it and re-derives nothing, so
|
|
9
9
|
// an entry that did not carry its own would promote a degraded table to lossless
|
|
10
10
|
// on the first cell edit. The default is the class an unknown one reads as, so a
|
|
@@ -58,17 +58,18 @@ export function tablePropsOfNode(node) {
|
|
|
58
58
|
return isTableIsland(entry) ? entry.props : undefined;
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* never a
|
|
61
|
+
* A minter over one document: the positional `isl-{n}` sequence continued past the
|
|
62
|
+
* highest id the field already holds, handing out consecutive ids, so a caller placing
|
|
63
|
+
* several islands at once keeps one minter for the lot. A minted id is this tier's to
|
|
64
|
+
* produce and it is part of the document's canonical bytes (CODEC §Islands), so it is a
|
|
65
|
+
* counter over the projection in hand — never a UUID, never a clock reading, and never a
|
|
66
|
+
* re-numbering of the ids already there, which are identities.
|
|
66
67
|
*
|
|
67
|
-
* Reads the PM doc rather than the stored content because that is what the caller
|
|
68
|
-
*
|
|
68
|
+
* Reads the PM doc rather than the stored content because that is what the caller (an
|
|
69
|
+
* insert command, a paste) holds and what the commit will project: an id minted against
|
|
69
70
|
* a stale content could collide with one the same transaction places.
|
|
70
71
|
*/
|
|
71
|
-
export function
|
|
72
|
+
export function islandMinter(doc) {
|
|
72
73
|
let next = 0;
|
|
73
74
|
doc.descendants((node) => {
|
|
74
75
|
if (node.type.name !== 'island_block' && node.type.name !== 'island_inline')
|
|
@@ -78,5 +79,10 @@ export function mintIslandId(doc) {
|
|
|
78
79
|
next = Math.max(next, Number(m[1]) + 1);
|
|
79
80
|
return false;
|
|
80
81
|
});
|
|
81
|
-
return `isl-${next}`;
|
|
82
|
+
return () => `isl-${next++}`;
|
|
83
|
+
}
|
|
84
|
+
/** The next island id for a field: one draw from {@link islandMinter}, which is what a
|
|
85
|
+
* command placing exactly one island wants. */
|
|
86
|
+
export function mintIslandId(doc) {
|
|
87
|
+
return islandMinter(doc)();
|
|
82
88
|
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import type { Schema } from 'prosemirror-model';
|
|
2
2
|
import type { Command } from 'prosemirror-state';
|
|
3
|
-
import { type SlashItem } from './slash.js';
|
|
4
3
|
/** The block-schema body's structural keys: `{}` for the inline/plaintext schemas,
|
|
5
|
-
* whose leaves declare neither lists nor code blocks. The argument list
|
|
4
|
+
* whose leaves declare neither lists nor code blocks. The argument list is the
|
|
6
5
|
* precedence order VISUAL_EDITOR §Chrome states, so a new link is one more argument
|
|
7
6
|
* at its place in that order rather than a nesting to read inside out.
|
|
8
7
|
*
|
|
9
8
|
* The slash menu is innermost of the three, and it is conditional rather than
|
|
10
|
-
* schema-gated: the keys exist only where a menu can be
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
export declare function bodyKeymap(schema: Schema,
|
|
9
|
+
* schema-gated: the keys exist only where a menu can be drawn (`slash`), because Enter
|
|
10
|
+
* and Escape claimed by a surface nobody can see is a body that swallows two keys for
|
|
11
|
+
* no visible reason. */
|
|
12
|
+
export declare function bodyKeymap(schema: Schema, slash?: boolean): Record<string, Command>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { chainCommands } from 'prosemirror-commands';
|
|
2
|
+
import { atomKeymap } from './atoms.js';
|
|
2
3
|
import { codeKeymap } from './code.js';
|
|
3
4
|
import { listKeymap } from './lists.js';
|
|
4
5
|
import { slashKeymap } from './slash.js';
|
|
@@ -15,15 +16,15 @@ function chainKeymaps(...maps) {
|
|
|
15
16
|
return out;
|
|
16
17
|
}
|
|
17
18
|
/** The block-schema body's structural keys: `{}` for the inline/plaintext schemas,
|
|
18
|
-
* whose leaves declare neither lists nor code blocks. The argument list
|
|
19
|
+
* whose leaves declare neither lists nor code blocks. The argument list is the
|
|
19
20
|
* precedence order VISUAL_EDITOR §Chrome states, so a new link is one more argument
|
|
20
21
|
* at its place in that order rather than a nesting to read inside out.
|
|
21
22
|
*
|
|
22
23
|
* The slash menu is innermost of the three, and it is conditional rather than
|
|
23
|
-
* schema-gated: the keys exist only where a menu can be
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
export function bodyKeymap(schema,
|
|
27
|
-
const links =
|
|
28
|
-
return chainKeymaps(...links, codeKeymap(schema), listKeymap(schema));
|
|
24
|
+
* schema-gated: the keys exist only where a menu can be drawn (`slash`), because Enter
|
|
25
|
+
* and Escape claimed by a surface nobody can see is a body that swallows two keys for
|
|
26
|
+
* no visible reason. */
|
|
27
|
+
export function bodyKeymap(schema, slash) {
|
|
28
|
+
const links = slash ? [slashKeymap()] : [];
|
|
29
|
+
return chainKeymaps(...links, codeKeymap(schema), listKeymap(schema), atomKeymap(schema));
|
|
29
30
|
}
|
|
@@ -8,14 +8,14 @@ import type { Command } from 'prosemirror-state';
|
|
|
8
8
|
* Tab more locally under the same rule (`prose/canon/VISUAL_EDITOR.md` §Chrome)
|
|
9
9
|
* prepend a link ahead of these in `keymap.ts`: a `code_block` takes literal
|
|
10
10
|
* indentation (`code.ts`), an island takes cell traversal once added. Outside all
|
|
11
|
-
* of them every link returns false and the key is
|
|
11
|
+
* of them every link returns false and the key is not swallowed: Tab keeps its
|
|
12
12
|
* default meaning, which is the body's only keyboard exit and the open seam for a
|
|
13
13
|
* shell structural keymap (VISUAL_EDITOR §Settled and open).
|
|
14
14
|
*
|
|
15
|
-
* `Enter` is a chain in precedence order: the
|
|
15
|
+
* `Enter` is a chain in precedence order: the escape-above gesture, then
|
|
16
16
|
* `splitListItem`, then `liftListItem`. The middle link carries two behaviors of
|
|
17
17
|
* its own: it splits a non-empty item, and on an empty item it either splits the
|
|
18
|
-
*
|
|
18
|
+
* wrapping item (a nested empty item, so Enter lifts exactly one level) or bails
|
|
19
19
|
* so the third link lifts a top-level empty item out to a paragraph. `Backspace`
|
|
20
20
|
* forks on the item's index: the first item lifts, any later one merges.
|
|
21
21
|
*/
|
package/dist/core/codec/lists.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { liftListItem, sinkListItem, splitListItem } from 'prosemirror-schema-list';
|
|
2
2
|
import { chainCommands, joinTextblockBackward } from 'prosemirror-commands';
|
|
3
3
|
/** The caret's enclosing `list_item` when it sits at the very start of that item's
|
|
4
|
-
*
|
|
4
|
+
* first block: the position both structural keys branch on. `null` otherwise
|
|
5
5
|
* (mid-text, a non-first block of a multi-paragraph item, a non-empty selection),
|
|
6
6
|
* which is where the key keeps its ordinary meaning. */
|
|
7
7
|
function atItemStart(state, itemType) {
|
|
@@ -21,13 +21,30 @@ function atItemStart(state, itemType) {
|
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
24
|
+
* Whether a caret already fits immediately above the list at `pos`. The node ending
|
|
25
|
+
* there answers for its last child, so a quote counts through its final paragraph;
|
|
26
|
+
* an atom (an island, a rule) does not, and neither does a list opening its parent.
|
|
27
|
+
* Those are the shapes `prosemirror-gapcursor` declines too, its `closedBefore`
|
|
28
|
+
* asking this question from the other side.
|
|
29
|
+
*/
|
|
30
|
+
function writableAbove(doc, pos) {
|
|
31
|
+
let before = doc.resolve(pos).nodeBefore;
|
|
32
|
+
while (before && !before.isTextblock && !before.isAtom)
|
|
33
|
+
before = before.lastChild;
|
|
34
|
+
return !!before?.isTextblock;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Enter at the very start of a top-level list's first item, where nothing above the
|
|
38
|
+
* list takes a caret → an empty paragraph above it, caret staying with the text it
|
|
39
|
+
* pushed down. A list that opens a document, or one under an island or a rule, has
|
|
40
|
+
* nowhere above to write and nothing else that opens one. Where a caret already
|
|
41
|
+
* fits above, the key is the conventional split instead, at every item alike: an
|
|
42
|
+
* escape a writer reaches by pressing Up needs no key of its own.
|
|
26
43
|
*
|
|
27
|
-
*
|
|
28
|
-
* an empty paragraph into the parent item (`list_item` is `block+`, so the
|
|
29
|
-
* is representable and wrong), where the conventional split (an empty item
|
|
30
|
-
* is what the next link does. An empty first item falls through to that same
|
|
44
|
+
* Also only where the list itself is not inside an item: in a nested list this
|
|
45
|
+
* pushes an empty paragraph into the parent item (`list_item` is `block+`, so the
|
|
46
|
+
* shape is representable and wrong), where the conventional split (an empty item
|
|
47
|
+
* above) is what the next link does. An empty first item falls through to that same
|
|
31
48
|
* link, which exits the list instead.
|
|
32
49
|
*/
|
|
33
50
|
function paragraphAboveList(itemType, paragraph) {
|
|
@@ -37,13 +54,15 @@ function paragraphAboveList(itemType, paragraph) {
|
|
|
37
54
|
return false;
|
|
38
55
|
if (state.selection.$from.parent.content.size === 0)
|
|
39
56
|
return false;
|
|
57
|
+
if (writableAbove(state.doc, at.listPos))
|
|
58
|
+
return false;
|
|
40
59
|
if (dispatch)
|
|
41
60
|
dispatch(state.tr.insert(at.listPos, paragraph.create()).scrollIntoView());
|
|
42
61
|
return true;
|
|
43
62
|
};
|
|
44
63
|
}
|
|
45
64
|
/**
|
|
46
|
-
* Backspace at the start of a list's
|
|
65
|
+
* Backspace at the start of a list's first item → lift it: one level out for a
|
|
47
66
|
* nested item, out of the list entirely (to a paragraph) at the top.
|
|
48
67
|
*/
|
|
49
68
|
function liftAtListStart(itemType) {
|
|
@@ -55,10 +74,10 @@ function liftAtListStart(itemType) {
|
|
|
55
74
|
};
|
|
56
75
|
}
|
|
57
76
|
/**
|
|
58
|
-
* Backspace at the start of any
|
|
77
|
+
* Backspace at the start of any later item → join its text into the previous
|
|
59
78
|
* item's last block, so the two items become one line.
|
|
60
79
|
*
|
|
61
|
-
* Not the base keymap's `joinBackward`, which merges the
|
|
80
|
+
* Not the base keymap's `joinBackward`, which merges the block and leaves
|
|
62
81
|
* `list_item(paragraph, paragraph)`: the item's marker gone while its text stays
|
|
63
82
|
* on its own line, which the reference quill typesets as an unnumbered
|
|
64
83
|
* continuation paragraph.
|
|
@@ -79,14 +98,14 @@ function mergeIntoPreviousItem(itemType) {
|
|
|
79
98
|
* Tab more locally under the same rule (`prose/canon/VISUAL_EDITOR.md` §Chrome)
|
|
80
99
|
* prepend a link ahead of these in `keymap.ts`: a `code_block` takes literal
|
|
81
100
|
* indentation (`code.ts`), an island takes cell traversal once added. Outside all
|
|
82
|
-
* of them every link returns false and the key is
|
|
101
|
+
* of them every link returns false and the key is not swallowed: Tab keeps its
|
|
83
102
|
* default meaning, which is the body's only keyboard exit and the open seam for a
|
|
84
103
|
* shell structural keymap (VISUAL_EDITOR §Settled and open).
|
|
85
104
|
*
|
|
86
|
-
* `Enter` is a chain in precedence order: the
|
|
105
|
+
* `Enter` is a chain in precedence order: the escape-above gesture, then
|
|
87
106
|
* `splitListItem`, then `liftListItem`. The middle link carries two behaviors of
|
|
88
107
|
* its own: it splits a non-empty item, and on an empty item it either splits the
|
|
89
|
-
*
|
|
108
|
+
* wrapping item (a nested empty item, so Enter lifts exactly one level) or bails
|
|
90
109
|
* so the third link lifts a top-level empty item out to a paragraph. `Backspace`
|
|
91
110
|
* forks on the item's index: the first item lifts, any later one merges.
|
|
92
111
|
*/
|
|
@@ -18,7 +18,7 @@ export declare function descriptorOf(m: ContentMark): Record<string, unknown>;
|
|
|
18
18
|
/**
|
|
19
19
|
* A stable grouping key for the mark diff: marks sharing a key union into one
|
|
20
20
|
* coverage set. Formatting keys on its type; a link keys on type+url and an
|
|
21
|
-
* unknown on type+attrs, because `applyChange`'s `remove` matches type
|
|
21
|
+
* unknown on type+attrs, because `applyChange`'s `remove` matches type and attrs
|
|
22
22
|
* (verified), so different urls / attrs are independent mark families.
|
|
23
23
|
*/
|
|
24
24
|
export declare function markKey(descriptor: Record<string, unknown>): string;
|
package/dist/core/codec/marks.js
CHANGED
|
@@ -49,7 +49,7 @@ export function descriptorOf(m) {
|
|
|
49
49
|
/**
|
|
50
50
|
* A stable grouping key for the mark diff: marks sharing a key union into one
|
|
51
51
|
* coverage set. Formatting keys on its type; a link keys on type+url and an
|
|
52
|
-
* unknown on type+attrs, because `applyChange`'s `remove` matches type
|
|
52
|
+
* unknown on type+attrs, because `applyChange`'s `remove` matches type and attrs
|
|
53
53
|
* (verified), so different urls / attrs are independent mark families.
|
|
54
54
|
*/
|
|
55
55
|
export function markKey(descriptor) {
|