@quillmark/svelte 0.5.0 → 0.6.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 +8 -2
- package/THEMING.md +22 -27
- package/dist/core/address.d.ts +17 -3
- package/dist/core/address.js +20 -3
- package/dist/core/codec/blocks.d.ts +37 -0
- package/dist/core/codec/blocks.js +163 -0
- package/dist/core/codec/field.js +3 -5
- package/dist/core/codec/index.d.ts +1 -1
- package/dist/core/codec/index.js +3 -2
- package/dist/core/codec/inputrules.js +24 -144
- package/dist/core/codec/prose.css +14 -25
- package/dist/core/codec/schema.d.ts +8 -0
- package/dist/core/codec/schema.js +33 -1
- package/dist/core/codec/slash.d.ts +18 -16
- package/dist/core/codec/slash.js +0 -0
- package/dist/core/codec/table-view.js +127 -157
- package/dist/core/codec/table.d.ts +0 -10
- package/dist/core/codec/table.js +0 -15
- package/dist/core/theme.css +350 -347
- package/dist/preset/recipes.css +10 -21
- package/dist/preset/scale.css +6 -7
- package/dist/preview/Preview.svelte +4 -2
- package/dist/preview/geometry.d.ts +10 -9
- package/dist/preview/geometry.js +11 -13
- package/dist/visual/ArrayField.svelte +242 -63
- package/dist/visual/ArrayField.svelte.d.ts +5 -1
- package/dist/visual/Card.svelte +21 -2
- package/dist/visual/DiagnosticList.svelte +6 -11
- package/dist/visual/EnumField.svelte +33 -1
- package/dist/visual/Field.svelte +29 -5
- package/dist/visual/FieldLabel.svelte +2 -2
- package/dist/visual/FormatPopover.svelte +13 -1
- package/dist/visual/ObjectField.svelte +157 -35
- package/dist/visual/ObjectField.svelte.d.ts +13 -4
- package/dist/visual/ProseArrayElement.svelte +22 -2
- package/dist/visual/ProseArrayElement.svelte.d.ts +1 -0
- package/dist/visual/TipsCard.svelte +33 -13
- package/dist/visual/VariantField.svelte +98 -0
- package/dist/visual/VariantField.svelte.d.ts +21 -0
- package/dist/visual/VisualEditorInner.svelte +18 -15
- package/dist/visual/controls.css +282 -283
- package/dist/visual/diagnostics.d.ts +15 -24
- package/dist/visual/diagnostics.js +14 -30
- package/dist/visual/domid.d.ts +13 -0
- package/dist/visual/domid.js +16 -0
- package/dist/visual/leaves.d.ts +9 -7
- package/dist/visual/links.d.ts +5 -2
- package/dist/visual/links.js +9 -3
- package/dist/visual/props.d.ts +1 -1
- package/dist/visual/strings.d.ts +21 -6
- package/dist/visual/strings.js +3 -0
- package/dist/visual/structure.d.ts +32 -7
- package/dist/visual/structure.js +49 -3
- package/package.json +2 -2
- package/dist/core/codec/clipboard.d.ts +0 -59
- package/dist/core/codec/clipboard.js +0 -202
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// Input rules: the markdown-shorthand plugin the codec owns (`**`→strong, `*`→em,
|
|
2
2
|
// `~~`→strike, `` ` ``→code, `# `→heading, `- `/`1. `→lists, `> `→blockquote,
|
|
3
|
-
// `---`→divider).
|
|
4
|
-
// Mark rules are hand-rolled (prosemirror-inputrules ships only node rules);
|
|
5
|
-
// block rule
|
|
6
|
-
//
|
|
7
|
-
//
|
|
3
|
+
// `---`→divider, a fence→code block).
|
|
4
|
+
// Mark rules are hand-rolled (prosemirror-inputrules ships only node rules); every
|
|
5
|
+
// block rule is one regexp over a command in `blocks.ts`, which is the slash menu's
|
|
6
|
+
// implementation too. These produce transactions the codec already lowers: they add no
|
|
7
|
+
// new lowering surface.
|
|
8
8
|
//
|
|
9
9
|
// A rule declines only where what it would mint is unrepresentable, or is a shape
|
|
10
10
|
// another gesture owns — never on how a quill renders it. What a quill typesets is not
|
|
@@ -12,9 +12,8 @@
|
|
|
12
12
|
// sets"), and a construct that arrives through `importMarkdown` must stay authorable or
|
|
13
13
|
// a document opens carrying a shape the editor refuses to make. So one guard survives:
|
|
14
14
|
// `- ` / `1. ` at the head of an existing item, where Tab owns the nesting.
|
|
15
|
-
import { InputRule, inputRules
|
|
16
|
-
import {
|
|
17
|
-
import { Selection } from 'prosemirror-state';
|
|
15
|
+
import { InputRule, inputRules } from 'prosemirror-inputrules';
|
|
16
|
+
import { consuming, insertDivider, toCodeBlock, toHeading, wrapInList, wrapInQuote } from './blocks.js';
|
|
18
17
|
/**
|
|
19
18
|
* A mark input rule: when `regexp` (ending in the closing delimiter) matches
|
|
20
19
|
* before the caret, wrap capture group 1 in `markType` and drop the delimiters.
|
|
@@ -46,69 +45,19 @@ function markInputRule(regexp, markType, delimLen) {
|
|
|
46
45
|
return tr;
|
|
47
46
|
});
|
|
48
47
|
}
|
|
49
|
-
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
* inside the item is the gesture that mints it ({@link markdownInputRules}). A wrap
|
|
55
|
-
* keeping the heading would be a second door onto that shape, so `- ` / `1. ` mint a
|
|
56
|
-
* paragraph item wherever they fire.
|
|
57
|
-
*
|
|
58
|
-
* `item` is the second guard, and it is about the gesture rather than the shape: the
|
|
59
|
-
* rule declines at the start of an item's own first block ({@link openingAnItem}).
|
|
60
|
-
*/
|
|
61
|
-
function listWrappingRule(regexp, listType, paragraph, item, getAttrs, joinPredicate) {
|
|
62
|
-
return new InputRule(regexp, (state, match, start, end) => {
|
|
63
|
-
if (openingAnItem(state.doc.resolve(start), item))
|
|
64
|
-
return null;
|
|
65
|
-
const attrs = getAttrs ? getAttrs(match) : null;
|
|
66
|
-
const tr = state.tr.delete(start, end);
|
|
67
|
-
// Positions survive the retype: same content, same size.
|
|
68
|
-
if (tr.doc.resolve(start).parent.type !== paragraph) {
|
|
69
|
-
tr.setBlockType(start, start, paragraph);
|
|
70
|
-
}
|
|
71
|
-
const $start = tr.doc.resolve(start);
|
|
72
|
-
const range = $start.blockRange();
|
|
73
|
-
const wrapping = range && findWrapping(range, listType, attrs);
|
|
74
|
-
if (!range || !wrapping)
|
|
75
|
-
return null;
|
|
76
|
-
tr.wrap(range, wrapping);
|
|
77
|
-
// Join a preceding list of the same type: the one boundary this rule
|
|
78
|
-
// itself opens (`lists.ts` §cleanup: command-local, never a global pass).
|
|
79
|
-
const before = tr.doc.resolve(start - 1).nodeBefore;
|
|
80
|
-
if (before &&
|
|
81
|
-
before.type === listType &&
|
|
82
|
-
canJoin(tr.doc, start - 1) &&
|
|
83
|
-
(!joinPredicate || joinPredicate(match, before))) {
|
|
84
|
-
tr.join(start - 1);
|
|
85
|
-
}
|
|
86
|
-
return tr;
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Whether `$start` is the head of an item that already exists: offset 0 of a
|
|
91
|
-
* `list_item`'s first block, which is where a list shorthand is the text an author
|
|
92
|
-
* typed rather than a shorthand at all.
|
|
93
|
-
*
|
|
94
|
-
* Firing there wraps the item's own paragraph in a fresh list, minting an item whose
|
|
95
|
-
* only content is another item — a level with nothing on it. Tab is the indent gesture
|
|
96
|
-
* and lands the shape a nesting is supposed to have, under the previous sibling, so
|
|
97
|
-
* what the rule adds here is a second door onto a worse answer. A later block of the
|
|
98
|
-
* item is not this case: wrapping a continuation paragraph is how a sub-list opens
|
|
99
|
-
* under text.
|
|
100
|
-
*/
|
|
101
|
-
function openingAnItem($start, item) {
|
|
102
|
-
if (!item || $start.depth < 2 || $start.parentOffset !== 0)
|
|
103
|
-
return false;
|
|
104
|
-
return $start.node(-1).type === item && $start.index(-1) === 0;
|
|
48
|
+
/** A block shorthand: the regexp that fires it, and the command it runs in the match's
|
|
49
|
+
* place. Every one is `^`-anchored, so the command's own head-of-a-textblock guard is
|
|
50
|
+
* the same position the regexp already stands at. */
|
|
51
|
+
function blockRule(regexp, command) {
|
|
52
|
+
return new InputRule(regexp, (state, match, start, end) => consuming(state, start, end, command(match))?.tr ?? null);
|
|
105
53
|
}
|
|
106
54
|
/** The full markdown-shorthand rule set for a block schema. */
|
|
107
55
|
export function markdownInputRules(schema) {
|
|
108
56
|
const rules = [];
|
|
109
57
|
const m = schema.marks;
|
|
110
|
-
|
|
111
|
-
//
|
|
58
|
+
// Inline marks. Non-greedy capture between matching delimiters. Built only where the
|
|
59
|
+
// schema has the mark: the inline schema omits them, and a plaintext one declares none
|
|
60
|
+
// at all.
|
|
112
61
|
if (m.strong)
|
|
113
62
|
rules.push(markInputRule(/\*\*([^*]+)\*\*$/, m.strong, 2));
|
|
114
63
|
if (m.em)
|
|
@@ -117,85 +66,16 @@ export function markdownInputRules(schema) {
|
|
|
117
66
|
rules.push(markInputRule(/~~([^~]+)~~$/, m.strike, 2));
|
|
118
67
|
if (m.code)
|
|
119
68
|
rules.push(markInputRule(/`([^`]+)`$/, m.code, 1));
|
|
120
|
-
// Block shorthands
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
// from `- # title`, so a rule that declined there would refuse to author what a
|
|
126
|
-
// document can arrive carrying.
|
|
127
|
-
const heading = schema.nodes.heading;
|
|
128
|
-
rules.push(new InputRule(/^(#{1,6})\s$/, (state, match, start, end) => {
|
|
129
|
-
const $start = state.doc.resolve(start);
|
|
130
|
-
if (!$start.node(-1).canReplaceWith($start.index(-1), $start.indexAfter(-1), heading)) {
|
|
131
|
-
return null;
|
|
132
|
-
}
|
|
133
|
-
return state.tr
|
|
134
|
-
.delete(start, end)
|
|
135
|
-
.setBlockType(start, start, heading, { level: match[1].length });
|
|
136
|
-
}));
|
|
137
|
-
}
|
|
138
|
-
if (schema.nodes.code_block && schema.nodes.paragraph) {
|
|
139
|
-
// `textblockTypeInputRule`'s body plus the exit the `---` rule mints below, for a
|
|
140
|
-
// sharper version of its reason: a code block is the one block a GAP CURSOR will
|
|
141
|
-
// not sit beside either (`closedBefore`/`closedAfter` both fail on a textblock), so
|
|
142
|
-
// a fence at the end of a body leaves the caret with no way forward at all.
|
|
143
|
-
const code = schema.nodes.code_block;
|
|
144
|
-
const paragraph = schema.nodes.paragraph;
|
|
145
|
-
rules.push(new InputRule(/^```$/, (state, _match, start, end) => {
|
|
146
|
-
const $start = state.doc.resolve(start);
|
|
147
|
-
if (!$start.node(-1).canReplaceWith($start.index(-1), $start.indexAfter(-1), code)) {
|
|
148
|
-
return null;
|
|
149
|
-
}
|
|
150
|
-
const tr = state.tr.delete(start, end).setBlockType(start, start, code);
|
|
151
|
-
const at = tr.doc.resolve(start).after();
|
|
152
|
-
if (!tr.doc.nodeAt(at))
|
|
153
|
-
tr.insert(at, paragraph.create());
|
|
154
|
-
// The caret stays in the fence, not in the exit it minted.
|
|
155
|
-
return tr;
|
|
156
|
-
}));
|
|
157
|
-
}
|
|
158
|
-
if (schema.nodes.horizontal_rule && schema.nodes.paragraph) {
|
|
159
|
-
// A divider is a whole block, so this rule replaces its textblock rather than
|
|
160
|
-
// retyping one: `horizontal_rule` holds no content to retype into.
|
|
161
|
-
const rule = schema.nodes.horizontal_rule;
|
|
162
|
-
const paragraph = schema.nodes.paragraph;
|
|
163
|
-
rules.push(new InputRule(/^---$/, (state, _match, start, end) => {
|
|
164
|
-
const $start = state.doc.resolve(start);
|
|
165
|
-
// The block entirely, not a prefix of one. `[start, end)` is the match minus
|
|
166
|
-
// the just-typed `-`, so the block holds `--` when this fires; anything else
|
|
167
|
-
// in it is a paragraph being edited, which a replace would eat.
|
|
168
|
-
if ($start.parentOffset !== 0 || $start.parent.content.size !== end - start) {
|
|
169
|
-
return null;
|
|
170
|
-
}
|
|
171
|
-
if (!$start.node(-1).canReplaceWith($start.index(-1), $start.indexAfter(-1), rule)) {
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
const from = $start.before();
|
|
175
|
-
const tr = state.tr.replaceWith(from, $start.after(), rule.create());
|
|
176
|
-
// A divider at the end of a body leaves nowhere to type; the paragraph after
|
|
177
|
-
// it is the exit, and it is where the caret lands (`slash.ts` §insertBlock
|
|
178
|
-
// opens the same block for the same reason).
|
|
179
|
-
const at = from + 1;
|
|
180
|
-
if (!tr.doc.nodeAt(at))
|
|
181
|
-
tr.insert(at, paragraph.create());
|
|
182
|
-
tr.setSelection(Selection.near(tr.doc.resolve(at), 1));
|
|
183
|
-
return tr;
|
|
184
|
-
}));
|
|
185
|
-
}
|
|
69
|
+
// Block shorthands. Each command answers for the schema it is run against, so a
|
|
70
|
+
// schema without the node declines rather than being guarded here.
|
|
71
|
+
rules.push(blockRule(/^(#{1,6})\s$/, (match) => toHeading(match[1].length)));
|
|
72
|
+
rules.push(blockRule(/^```$/, toCodeBlock));
|
|
73
|
+
rules.push(blockRule(/^---$/, insertDivider));
|
|
186
74
|
// `- ` / `1. ` at the start of a block, here or nested inside an item — but not at
|
|
187
|
-
// the start of an item's own first block, where
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
if (schema.nodes.ordered_list && schema.nodes.paragraph) {
|
|
192
|
-
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]));
|
|
193
|
-
}
|
|
194
|
-
// No item guard: a quote wrapping an item's own first block is a container the
|
|
195
|
-
// content holds, where a list wrapping one is an item whose only content is an item.
|
|
196
|
-
if (schema.nodes.blockquote) {
|
|
197
|
-
rules.push(wrappingInputRule(/^\s*>\s$/, schema.nodes.blockquote));
|
|
198
|
-
}
|
|
75
|
+
// the start of an item's own first block, where the command declines.
|
|
76
|
+
rules.push(blockRule(/^\s*([-+*])\s$/, () => wrapInList(false)));
|
|
77
|
+
rules.push(blockRule(/^(\d+)\.\s$/, (match) => wrapInList(true, { start: +match[1] }, (node) => node.childCount + node.attrs.start === +match[1])));
|
|
78
|
+
rules.push(blockRule(/^\s*>\s$/, wrapInQuote));
|
|
199
79
|
return rules;
|
|
200
80
|
}
|
|
201
81
|
/** The input-rules plugin the field mounts by default (opt-out via createField). */
|
|
@@ -432,11 +432,11 @@ li.ProseMirror-selectednode::after {
|
|
|
432
432
|
which is drawn through a drag the pointer has travelled off — answering which line.
|
|
433
433
|
|
|
434
434
|
Opacity rather than absence, so every control keeps its box and is hit-tested wherever
|
|
435
|
-
it sits. A grip's reach is the pointer's
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
one a keyboard has reached. */
|
|
435
|
+
it sits. A grip's reach is the pointer's alone and it holds no focus, so it carries no
|
|
436
|
+
focus rung: what a keyboard draws a line with is the cell the selection runs through
|
|
437
|
+
(`table-view.ts` §`cellKeys`), and the wash and the selected grip's tint are what say
|
|
438
|
+
so. The two bars are in the tab order, the Tab that declines off the last cell landing
|
|
439
|
+
on them, so the rung there draws one a keyboard has reached. */
|
|
440
440
|
:is(.qm-table-grip, .qm-table-add) {
|
|
441
441
|
opacity: 0;
|
|
442
442
|
transition: opacity var(--_qm-duration-fast) var(--_qm-ease-reverse);
|
|
@@ -447,32 +447,21 @@ li.ProseMirror-selectednode::after {
|
|
|
447
447
|
:where(.qm-table-island:hover) :is(.qm-table-grip, .qm-table-add) {
|
|
448
448
|
opacity: var(--_qm-opacity-muted);
|
|
449
449
|
}
|
|
450
|
-
|
|
451
|
-
.qm-table-grip:is([aria-pressed='true'], .qm-table-lifted) {
|
|
450
|
+
.qm-table-add:is(:hover, :focus-visible),
|
|
451
|
+
.qm-table-grip:is(:hover, [aria-pressed='true'], .qm-table-lifted) {
|
|
452
452
|
opacity: 1;
|
|
453
453
|
}
|
|
454
454
|
|
|
455
|
-
/* The ring, on the
|
|
456
|
-
is its caret and a cell's is too, so the outline is dropped there; a control
|
|
457
|
-
up out of nothing has the reveal, and a ring under a keyboard is what says
|
|
458
|
-
there rather than on the paper behind it. Declared here rather than
|
|
459
|
-
`.qm-focus-ring`: that recipe is the visual tier's stylesheet, and the
|
|
460
|
-
the leaf, which is the codec's.
|
|
461
|
-
|
|
462
|
-
A selected grip takes none. Focus and selection arrive together there — the grip takes
|
|
463
|
-
the focus because the selection's keys bind on it — and the tint plus the wash already
|
|
464
|
-
say where the next command lands. A ring on top is a second box around the loudest
|
|
465
|
-
thing on the island, drawn on the press that made the selection: a pointer gesture
|
|
466
|
-
answering with a keyboard's indicator. Stated as `none` rather than left unset, because
|
|
467
|
-
what fills the gap is the browser's own ring, which is the same box in a colour the
|
|
468
|
-
surface does not own. */
|
|
469
|
-
:is(.qm-table-grip, .qm-table-add):focus-visible {
|
|
455
|
+
/* The ring, on the two add bars and nowhere else in this file. A prose leaf's focus
|
|
456
|
+
indicator is its caret and a cell's is too, so the outline is dropped there; a control
|
|
457
|
+
that comes up out of nothing has the reveal, and a ring under a keyboard is what says
|
|
458
|
+
the focus is there rather than on the paper behind it. Declared here rather than
|
|
459
|
+
reached for as `.qm-focus-ring`: that recipe is the visual tier's stylesheet, and the
|
|
460
|
+
island is inside the leaf, which is the codec's. */
|
|
461
|
+
.qm-table-add:focus-visible {
|
|
470
462
|
outline: var(--_qm-ring);
|
|
471
463
|
outline-offset: var(--_qm-ring-offset);
|
|
472
464
|
}
|
|
473
|
-
.qm-table-grip[aria-pressed='true']:focus-visible {
|
|
474
|
-
outline: none;
|
|
475
|
-
}
|
|
476
465
|
|
|
477
466
|
/* A drag in flight. The lifted line reads as picked up by tone, never a shadow:
|
|
478
467
|
elevation is a surface rung here, which says the same thing at both poles. The drop
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { Schema } from 'prosemirror-model';
|
|
2
|
+
/**
|
|
3
|
+
* Whether a link carrying `href` renders as one. A value with no scheme is relative
|
|
4
|
+
* to the embedding page and has none to refuse.
|
|
5
|
+
*
|
|
6
|
+
* A refused href is unchanged: it stays on the mark and round-trips, so a document
|
|
7
|
+
* survives an editor that declines to make it clickable.
|
|
8
|
+
*/
|
|
9
|
+
export declare function rendersHref(href: string): boolean;
|
|
2
10
|
/** The full field schema: every block kind, container, mark, and island. */
|
|
3
11
|
export declare const blockSchema: Schema<string, string>;
|
|
4
12
|
/** The constrained inline schema: one paragraph, no block splitting, no
|
|
@@ -10,6 +10,33 @@
|
|
|
10
10
|
// not marks here (decorations).
|
|
11
11
|
import { Schema } from 'prosemirror-model';
|
|
12
12
|
import { islandBlockSpec, islandInlineSpec } from './islands.js';
|
|
13
|
+
// ── The href gate ───────────────────────────────────────────────────────────
|
|
14
|
+
// An `href` is an attribute value and not markup, so the markdown → typed node → DOM
|
|
15
|
+
// path that keeps a document's text from becoming tags never reaches it: what the
|
|
16
|
+
// mark carries is what `toDOM` emits, and `renderContent` paints marks outside a
|
|
17
|
+
// `contenteditable` (the tips card), where a click is a plain one.
|
|
18
|
+
//
|
|
19
|
+
// An allowlist, because the set a document can spell is open: naming the dangerous
|
|
20
|
+
// schemes instead loses to the first one this has not heard of. It holds what the
|
|
21
|
+
// surface has a caller for; a scheme reaching it later is a line.
|
|
22
|
+
const RENDERED_SCHEMES = new Set(['http', 'https', 'mailto', 'tel', 'ftp']);
|
|
23
|
+
/** A scheme and its colon: a letter, then letters, digits, `+`, `-` or `.` (RFC 3986). */
|
|
24
|
+
const SCHEME = /^([a-z][a-z0-9+.-]*):/i;
|
|
25
|
+
/** Dropped before the scheme is read, so a tab spliced into `javascript:` is tested
|
|
26
|
+
* as what it navigates to. Wider than the URL parser's own tab/newline rule: no
|
|
27
|
+
* scheme carries a control character, and the strip decides without rewriting. */
|
|
28
|
+
const IGNORED = /[\u0000-\u0020]/g;
|
|
29
|
+
/**
|
|
30
|
+
* Whether a link carrying `href` renders as one. A value with no scheme is relative
|
|
31
|
+
* to the embedding page and has none to refuse.
|
|
32
|
+
*
|
|
33
|
+
* A refused href is unchanged: it stays on the mark and round-trips, so a document
|
|
34
|
+
* survives an editor that declines to make it clickable.
|
|
35
|
+
*/
|
|
36
|
+
export function rendersHref(href) {
|
|
37
|
+
const scheme = SCHEME.exec(href.replace(IGNORED, ''));
|
|
38
|
+
return scheme === null || RENDERED_SCHEMES.has(scheme[1].toLowerCase());
|
|
39
|
+
}
|
|
13
40
|
// ── Marks (the block and inline schemas share them; plaintext declares none) ─
|
|
14
41
|
const marks = {
|
|
15
42
|
// Order matters: it fixes mark-set sort order and parse precedence. `link`
|
|
@@ -26,7 +53,12 @@ const marks = {
|
|
|
26
53
|
parseDOM: [
|
|
27
54
|
{ tag: 'a[href]', getAttrs: (el) => ({ href: el.getAttribute('href') }) }
|
|
28
55
|
],
|
|
29
|
-
|
|
56
|
+
// A refused href draws as a bare span: the text stands, unstyled and
|
|
57
|
+
// unclickable, and the mark keeps its value for encode.
|
|
58
|
+
toDOM: (mark) => {
|
|
59
|
+
const href = mark.attrs.href;
|
|
60
|
+
return rendersHref(href) ? ['a', { href }, 0] : ['span', 0];
|
|
61
|
+
}
|
|
30
62
|
},
|
|
31
63
|
// The open-set escape hatch: an inert mark that renders as a bare span and
|
|
32
64
|
// re-emits its stored `type`/`attrs` on encode. `excludes: ''` lets marks of
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Plugin, type Command } from 'prosemirror-state';
|
|
1
|
+
import { EditorState, Plugin, type Command } from 'prosemirror-state';
|
|
2
2
|
import type { EditorView } from 'prosemirror-view';
|
|
3
3
|
import { type RangeAnchor } from './anchor.js';
|
|
4
4
|
/** The menu as the chrome sees it: the command names the query matched, the keyboard's
|
|
@@ -25,18 +25,21 @@ export interface SlashStrings {
|
|
|
25
25
|
* chrome's wording follows, and the visual `strings` set extends both. */
|
|
26
26
|
export declare const DEFAULT_SLASH_STRINGS: SlashStrings;
|
|
27
27
|
/**
|
|
28
|
-
* The vocabulary, in menu order: the names `SLASH_COMMANDS` is keyed
|
|
29
|
-
*
|
|
28
|
+
* The vocabulary a caret can run, in menu order: the names `SLASH_COMMANDS` is keyed
|
|
29
|
+
* by, each asked whether it would fire here. A row that declines when picked is worse
|
|
30
|
+
* than an absent one, and asking the command rather than restating its guards is what
|
|
31
|
+
* keeps the menu from drifting off what the shorthand does. So the offers narrow as the
|
|
32
|
+
* caret moves: the island alone mid-paragraph, no list at an item's head.
|
|
30
33
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* is, and a second door onto the same construct is a second thing to keep true. A table
|
|
34
|
-
* has no prefix a rule can fire on (a pipe row is not one), so it is the menu's alone.
|
|
34
|
+
* `state` is the state a pick leaves behind — the run already consumed — since that is
|
|
35
|
+
* the caret every command answers for (`blocks.ts`).
|
|
35
36
|
*/
|
|
36
|
-
export declare function slashItems(): string[];
|
|
37
|
-
/** The query filter: a case-insensitive prefix of the name
|
|
38
|
-
*
|
|
39
|
-
*
|
|
37
|
+
export declare function slashItems(state: EditorState): string[];
|
|
38
|
+
/** The query filter: a case-insensitive prefix of the name or of any of its words, which
|
|
39
|
+
* is how a command line completes and how a two-word name stays reachable by the noun a
|
|
40
|
+
* writer has — `/list` offers both lists. A prefix and not a substring: `note` is inside
|
|
41
|
+
* `footnote` and completes nothing. An empty query offers everything, and the
|
|
42
|
+
* vocabulary's order survives, so the menu never reorders under a keystroke. */
|
|
40
43
|
export declare function filterItems(items: string[], query: string): string[];
|
|
41
44
|
/** The live trigger: where the `/` sits, what has been typed after it, and which
|
|
42
45
|
* offer the keyboard is on. */
|
|
@@ -67,11 +70,10 @@ export declare function slashKeymap(): Record<string, Command>;
|
|
|
67
70
|
* highlight has one lane rather than a hover painting a second one. */
|
|
68
71
|
export declare function focusSlashItem(view: EditorView, name: string): void;
|
|
69
72
|
/**
|
|
70
|
-
* Run a command: one transaction that deletes the trigger run and applies the
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* exactly what they were computed for.
|
|
73
|
+
* Run a command: one transaction that deletes the trigger run and applies the command's
|
|
74
|
+
* own edit (`blocks.ts` §`consuming`). A command that declines consumes nothing. The
|
|
75
|
+
* menu never picks one, offering only what would run; `slashPick` is a public seam and
|
|
76
|
+
* can.
|
|
75
77
|
*/
|
|
76
78
|
export declare function runSlashItem(view: EditorView, name: string): void;
|
|
77
79
|
export {};
|
package/dist/core/codec/slash.js
CHANGED
|
Binary file
|