@nerd-bible/wordgard 0.3.4 → 0.3.6
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/dist/command.d.ts +6 -6
- package/dist/command.js +32 -36
- package/dist/doc.d.ts +15 -1
- package/dist/doc.js +37 -13
- package/dist/editor.d.ts +16 -6
- package/dist/editor.js +352 -92
- package/dist/schema.js +93 -2
- package/dist/state.d.ts +4 -8
- package/dist/state.js +54 -21
- package/dist/table.js +1 -1
- package/package.json +1 -1
package/dist/schema.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Paragraph, Heading, Direction, Blockquote, HorizontalRule, Alignment, D
|
|
|
4
4
|
import { phrases, imagePhrases, colorNames } from 'wordgard/phrases';
|
|
5
5
|
import { Command, setTextblockType, Menu, setAlignment, setDirection, toggleBlock, listIsActive, toggleList, enter, toggleMark } from 'wordgard/command';
|
|
6
6
|
import { history } from 'wordgard/history';
|
|
7
|
-
import { KeyBinding, InputRule, Wordgard, Panel, Dialog,
|
|
7
|
+
import { KeyBinding, InputRule, Wordgard, Widget, Decoration, Panel, Dialog, PointSet, Tooltip } from 'wordgard/editor';
|
|
8
8
|
import cr from 'crelt';
|
|
9
9
|
|
|
10
10
|
function blockDoc() {
|
|
@@ -313,6 +313,97 @@ function codeBlock() {
|
|
|
313
313
|
};
|
|
314
314
|
}));
|
|
315
315
|
;return codeBlock})(codeBlock);
|
|
316
|
+
function codeBlockLanguage(options = {}) {
|
|
317
|
+
return [
|
|
318
|
+
GardState.schemaElement.of(CodeBlockLanguage),
|
|
319
|
+
options.languages ? [
|
|
320
|
+
languageStyles,
|
|
321
|
+
languageOptions.of(options.languages),
|
|
322
|
+
languageSelect,
|
|
323
|
+
codeBlockLanguage.selectLanguageBinding
|
|
324
|
+
] : []
|
|
325
|
+
];
|
|
326
|
+
}
|
|
327
|
+
const languageStyles = /*@__PURE__*/Wordgard.styles({
|
|
328
|
+
pre: {
|
|
329
|
+
position: "relative",
|
|
330
|
+
"& select.wg-code-language": {
|
|
331
|
+
font: "var(--wg-dialog-font)",
|
|
332
|
+
fontSize: "70%",
|
|
333
|
+
position: "absolute",
|
|
334
|
+
top: "2px",
|
|
335
|
+
right: "4px",
|
|
336
|
+
border: "none",
|
|
337
|
+
borderRadius: "4px"
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
const languageOptions = /*@__PURE__*/GardState.Facet.define({
|
|
342
|
+
combine: input => input.reduce((a, b) => a.concat(b), [])
|
|
343
|
+
});
|
|
344
|
+
function option(text, selected, value) {
|
|
345
|
+
let node = document.createElement("option");
|
|
346
|
+
node.textContent = text;
|
|
347
|
+
node.value = value;
|
|
348
|
+
if (selected)
|
|
349
|
+
node.selected = true;
|
|
350
|
+
return node;
|
|
351
|
+
}
|
|
352
|
+
const languageWidget = /*@__PURE__*/Widget.define({
|
|
353
|
+
render({ options, value }, wg) {
|
|
354
|
+
let sel = document.createElement("select");
|
|
355
|
+
sel.className = "wg-code-language";
|
|
356
|
+
sel.tabIndex = -1;
|
|
357
|
+
sel.appendChild(option("Plain", value == null, ""));
|
|
358
|
+
let selected = value && options.find(o => o.toLowerCase() == value);
|
|
359
|
+
if (value && !selected)
|
|
360
|
+
sel.appendChild(option(value, true, value));
|
|
361
|
+
for (let opt of options)
|
|
362
|
+
sel.appendChild(option(opt, opt == selected, opt.toLowerCase()));
|
|
363
|
+
sel.onchange = () => {
|
|
364
|
+
let value = sel.value || undefined;
|
|
365
|
+
let pos = wg.posAtDOM(sel.parentNode);
|
|
366
|
+
let block = pos > 0 && wg.state.doc.nodeAt(pos - 1);
|
|
367
|
+
if (block && block.type == CodeBlock.type && block.mark(CodeBlockLanguage) != value) {
|
|
368
|
+
wg.dispatch({
|
|
369
|
+
changes: value ? { from: pos - 1, add: CodeBlockLanguage.of(value) } :
|
|
370
|
+
{ from: pos - 1, remove: CodeBlockLanguage.isInSet(block.tag.marks) }
|
|
371
|
+
});
|
|
372
|
+
wg.focus();
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
return sel;
|
|
376
|
+
},
|
|
377
|
+
propagateEvent: false,
|
|
378
|
+
eq(a, b) {
|
|
379
|
+
return a.value == b.value && a.options.length == b.options.length &&
|
|
380
|
+
a.options.every((o, i) => o == b.options[i]);
|
|
381
|
+
},
|
|
382
|
+
inFlow: false
|
|
383
|
+
});
|
|
384
|
+
const languageSelect = /*@__PURE__*/Decoration.Tag.widget.dynamic(CodeBlock, "end", state => {
|
|
385
|
+
let options = state.facet(languageOptions);
|
|
386
|
+
return tag => {
|
|
387
|
+
return languageWidget.of({ options, value: tag.mark(CodeBlockLanguage) });
|
|
388
|
+
};
|
|
389
|
+
});
|
|
390
|
+
;codeBlockLanguage = /*@__PURE__*/(function (codeBlockLanguage) {
|
|
391
|
+
codeBlockLanguage.selectLanguage = wg => {
|
|
392
|
+
let blk = wg.state.sel.head.textblockParent;
|
|
393
|
+
if (!blk || blk.node.type != CodeBlock.type)
|
|
394
|
+
return false;
|
|
395
|
+
let sel = wg.nodeDOM(blk.before)?.querySelector("select.wg-code-language");
|
|
396
|
+
if (!sel)
|
|
397
|
+
return false;
|
|
398
|
+
sel.focus();
|
|
399
|
+
sel.showPicker();
|
|
400
|
+
return true;
|
|
401
|
+
};
|
|
402
|
+
codeBlockLanguage.selectLanguageBinding = KeyBinding.of({
|
|
403
|
+
key: "Shift-Mod-l",
|
|
404
|
+
run: codeBlockLanguage.selectLanguage
|
|
405
|
+
});
|
|
406
|
+
;return codeBlockLanguage})(codeBlockLanguage);
|
|
316
407
|
|
|
317
408
|
function strong() {
|
|
318
409
|
return [GardState.schemaElement.of(Strong), strong.button, strong.keyBinding];
|
|
@@ -1188,7 +1279,7 @@ function toggleLink(wg) {
|
|
|
1188
1279
|
let url = form && form.elements.namedItem("url")?.value;
|
|
1189
1280
|
if (url)
|
|
1190
1281
|
wg.dispatch({
|
|
1191
|
-
changes: selection.ranges.map(r => ({ from: r.from, to: r.to, add: Link.of(url) })),
|
|
1282
|
+
changes: wg.state.selection.ranges.map(r => ({ from: r.from, to: r.to, add: Link.of(url) })),
|
|
1192
1283
|
userEvent: "mark.add"
|
|
1193
1284
|
});
|
|
1194
1285
|
});
|
package/dist/state.d.ts
CHANGED
|
@@ -136,6 +136,10 @@ declare abstract class GardSelection {
|
|
|
136
136
|
*/
|
|
137
137
|
static cursor(pos: number, side?: -1 | 1, goalColumn?: number): GardSelection.Text;
|
|
138
138
|
/**
|
|
139
|
+
Find a normal cursor near the given position.
|
|
140
|
+
*/
|
|
141
|
+
static near(cx: GardSelection.Context, pos: number, side?: -1 | 1, goalColumn?: number): GardSelection.Text;
|
|
142
|
+
/**
|
|
139
143
|
Create a text selection.
|
|
140
144
|
*/
|
|
141
145
|
static range(anchor: number, head?: number, headSide?: -1 | 1, goalColumn?: number): GardSelection.Text;
|
|
@@ -161,18 +165,10 @@ declare abstract class GardSelection {
|
|
|
161
165
|
*/
|
|
162
166
|
nextNormalCursor(cx: GardSelection.Context, forward?: boolean): GardSelection.Text | null;
|
|
163
167
|
/**
|
|
164
|
-
Get a normal cursor at the start or end of this selection.
|
|
165
|
-
*/
|
|
166
|
-
normalCursorAtBound(cx: GardSelection.Context, forward?: boolean): GardSelection.Text | null;
|
|
167
|
-
/**
|
|
168
168
|
Move across one word starting from this selection's head.
|
|
169
169
|
*/
|
|
170
170
|
skipWord(cx: GardSelection.Context, forward?: boolean): GardSelection.Text | null;
|
|
171
171
|
/**
|
|
172
|
-
Find a normal selection near the given position.
|
|
173
|
-
*/
|
|
174
|
-
static near(cx: GardSelection.Context, pos: number, bias?: -1 | 1): GardSelection.Text;
|
|
175
|
-
/**
|
|
176
172
|
Find a normal selection at the start of the document or the
|
|
177
173
|
given textblock.
|
|
178
174
|
*/
|
package/dist/state.js
CHANGED
|
@@ -384,7 +384,7 @@ class TextblockMap {
|
|
|
384
384
|
sectionPos = pos + ch.length;
|
|
385
385
|
}
|
|
386
386
|
}
|
|
387
|
-
else if (ch.type.
|
|
387
|
+
else if (ch.type.cursorInsideBounds) {
|
|
388
388
|
text += " ";
|
|
389
389
|
scan(ch, pos + 1);
|
|
390
390
|
text += " ";
|
|
@@ -590,6 +590,10 @@ class GardSelection {
|
|
|
590
590
|
static cursor(pos, side, goalColumn) {
|
|
591
591
|
return GardSelection.Text.createInner(pos, pos, side, goalColumn);
|
|
592
592
|
}
|
|
593
|
+
static near(cx, pos, side = 1, goalColumn) {
|
|
594
|
+
let norm = findNormalAt(cx, pos, side);
|
|
595
|
+
return GardSelection.cursor(norm.pos, norm.side, goalColumn);
|
|
596
|
+
}
|
|
593
597
|
static range(anchor, head, headSide, goalColumn) {
|
|
594
598
|
return GardSelection.Text.createInner(anchor, head ?? anchor, headSide, goalColumn);
|
|
595
599
|
}
|
|
@@ -597,23 +601,13 @@ class GardSelection {
|
|
|
597
601
|
return GardSelection.Node.create(pos, node, goalColumn);
|
|
598
602
|
}
|
|
599
603
|
nextNormalCursor(cx, forward = true) {
|
|
600
|
-
let found = scanNormalFrom(cx, this.head, this.headSide, forward
|
|
601
|
-
return found && GardSelection.cursor(found.pos, found.side);
|
|
602
|
-
}
|
|
603
|
-
normalCursorAtBound(cx, forward = true) {
|
|
604
|
-
let found = scanNormalFrom(cx, forward ? this.to : this.from, forward ? -1 : 1, forward, false);
|
|
604
|
+
let found = scanNormalFrom(cx, this.head, this.headSide, forward);
|
|
605
605
|
return found && GardSelection.cursor(found.pos, found.side);
|
|
606
606
|
}
|
|
607
607
|
skipWord(cx, forward = true) {
|
|
608
608
|
let found = skipWord(cx, this.head, this.headSide, forward);
|
|
609
609
|
return found && GardSelection.cursor(found.pos, found.side);
|
|
610
610
|
}
|
|
611
|
-
static near(cx, pos, bias = 1) {
|
|
612
|
-
let norm = scanNormalFrom(cx, pos, bias, bias > 0, false) ??
|
|
613
|
-
scanNormalFrom(cx, pos, -bias, bias < 0, false) ??
|
|
614
|
-
{ pos: pos, side: -1 };
|
|
615
|
-
return GardSelection.cursor(norm.pos, norm.side);
|
|
616
|
-
}
|
|
617
611
|
static atStart(cx, block) {
|
|
618
612
|
return cursorAtStart(cx, block);
|
|
619
613
|
}
|
|
@@ -621,7 +615,7 @@ class GardSelection {
|
|
|
621
615
|
let found = block
|
|
622
616
|
? TextblockMap.get(cx, block.start, block.node).visualSide(false)
|
|
623
617
|
: cx.doc.inlineContent ? TextblockMap.get(cx, 0, cx.doc).visualSide(false)
|
|
624
|
-
:
|
|
618
|
+
: findNormalAt(cx, cx.doc.length, -1);
|
|
625
619
|
return GardSelection.cursor(found.pos, found.side);
|
|
626
620
|
}
|
|
627
621
|
}
|
|
@@ -760,7 +754,7 @@ function cursorAtStart(cx, block) {
|
|
|
760
754
|
let found = block
|
|
761
755
|
? TextblockMap.get(cx, block.start, block.node).visualSide(true)
|
|
762
756
|
: cx.doc.inlineContent ? TextblockMap.get(cx, 0, cx.doc).visualSide(true)
|
|
763
|
-
:
|
|
757
|
+
: findNormalAt(cx, 0, 1);
|
|
764
758
|
return GardSelection.cursor(found.pos, found.side);
|
|
765
759
|
}
|
|
766
760
|
function isBarrier(cx, node) {
|
|
@@ -769,13 +763,11 @@ function isBarrier(cx, node) {
|
|
|
769
763
|
let override = node.type.spec.cursorBarrier;
|
|
770
764
|
if (override != null)
|
|
771
765
|
return override;
|
|
772
|
-
return node.type.
|
|
766
|
+
return node.type.isBlock && (node.type.isolating || node.type.preserveWhitespace || cx.config.isAtom(node.type));
|
|
773
767
|
}
|
|
774
|
-
function scanNormalFrom(cx, from, side, forward
|
|
768
|
+
function scanNormalFrom(cx, from, side, forward) {
|
|
775
769
|
let pos = cx.doc.resolve(from), pastBarrier = false;
|
|
776
770
|
if (pos.parent.node.inlineContent) {
|
|
777
|
-
if (!mustMove)
|
|
778
|
-
return { pos: pos.pos, side };
|
|
779
771
|
let block = pos.textblockParent;
|
|
780
772
|
let map = TextblockMap.get(cx, block.start, block.node);
|
|
781
773
|
let next = cx.config.visualCursorMotion ? map.moveVisually(pos.pos, side, forward) : map.moveLogically(pos.pos, forward);
|
|
@@ -813,7 +805,7 @@ function scanNormalFrom(cx, from, side, forward, mustMove) {
|
|
|
813
805
|
}
|
|
814
806
|
if (index == (forward ? node.content.length : 0)) {
|
|
815
807
|
let barrier = !next || isBarrier(cx, node);
|
|
816
|
-
if ((bottom != from
|
|
808
|
+
if ((bottom != from) && pastBarrier && barrier)
|
|
817
809
|
return { pos: bottom, side: forward ? -1 : 1 };
|
|
818
810
|
if (!next)
|
|
819
811
|
return null;
|
|
@@ -827,7 +819,7 @@ function scanNormalFrom(cx, from, side, forward, mustMove) {
|
|
|
827
819
|
else {
|
|
828
820
|
let nextNode = node.content[index - (forward ? 0 : 1)];
|
|
829
821
|
let barrier = isBarrier(cx, nextNode);
|
|
830
|
-
if (pastBarrier && (bottom != from
|
|
822
|
+
if (pastBarrier && (bottom != from) && barrier)
|
|
831
823
|
return { pos: bottom, side: forward ? -1 : 1 };
|
|
832
824
|
if (nextNode.isLeaf || cx.config.isAtom(nextNode.type)) {
|
|
833
825
|
index += step;
|
|
@@ -847,12 +839,52 @@ function scanNormalFrom(cx, from, side, forward, mustMove) {
|
|
|
847
839
|
}
|
|
848
840
|
}
|
|
849
841
|
}
|
|
842
|
+
function findNormalAt(cx, pos, bias) {
|
|
843
|
+
let res = cx.doc.resolve(pos), lowest = pos;
|
|
844
|
+
if (res.inText) {
|
|
845
|
+
let text = res.parent.node.content[res.index].tag.param;
|
|
846
|
+
let off = bias > 0 ? findClusterBreak(text, res.inText - 1) : findClusterBreak(text, res.inText + 1, false);
|
|
847
|
+
pos += off - res.inText;
|
|
848
|
+
if (off > 0 && off < text.length)
|
|
849
|
+
return { pos, side: bias };
|
|
850
|
+
res = Pos.create(res.parent, pos, res.index + (off ? 1 : 0), 0);
|
|
851
|
+
}
|
|
852
|
+
for (let pass = 0; pass < 2; pass++) {
|
|
853
|
+
let dir = !pass ? bias : -bias, { parent, index } = res, curPos = pos;
|
|
854
|
+
for (;;) {
|
|
855
|
+
if (parent.node.inlineContent &&
|
|
856
|
+
(parent.node.type.isBlock || curPos > parent.start && curPos < parent.end || parent.node.type.cursorInsideBounds))
|
|
857
|
+
return { pos: curPos, side: bias };
|
|
858
|
+
if (index == (dir > 0 ? parent.node.content.length : 0)) {
|
|
859
|
+
if ((parent.node.type.isInline ? parent.node.type.cursorInsideBounds : isBarrier(cx, parent.node)) ||
|
|
860
|
+
!parent.parent)
|
|
861
|
+
break;
|
|
862
|
+
lowest = curPos = curPos + dir;
|
|
863
|
+
index = parent.index + (dir > 0 ? 1 : 0);
|
|
864
|
+
parent = parent.parent;
|
|
865
|
+
}
|
|
866
|
+
else {
|
|
867
|
+
let next = parent.node.content[index + (dir > 0 ? 0 : -1)];
|
|
868
|
+
if (next.isLeaf || isBarrier(cx, next))
|
|
869
|
+
break;
|
|
870
|
+
if (!parent.node.inlineContent && next.inlineContent && cx.config.visualCursorMotion) {
|
|
871
|
+
let startPos = curPos - (dir > 0 ? 0 : next.length) + 1;
|
|
872
|
+
return TextblockMap.get(cx, startPos, next).visualSide(dir > 0);
|
|
873
|
+
}
|
|
874
|
+
parent = Pos.Plot.create(parent, next, curPos, index);
|
|
875
|
+
index = dir > 0 ? 0 : next.content.length;
|
|
876
|
+
curPos += dir;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
return { pos: lowest, side: bias };
|
|
881
|
+
}
|
|
850
882
|
function skipWord(cx, start, side, forward) {
|
|
851
883
|
let last = null;
|
|
852
884
|
for (let pos = start, visually = cx.config.visualCursorMotion;;) {
|
|
853
885
|
let block = cx.doc.resolve(pos).textblockParent;
|
|
854
886
|
if (!block) {
|
|
855
|
-
let next = scanNormalFrom(cx, pos, side, forward
|
|
887
|
+
let next = scanNormalFrom(cx, pos, side, forward);
|
|
856
888
|
if (!next)
|
|
857
889
|
return last;
|
|
858
890
|
({ pos, side } = next);
|
|
@@ -1884,6 +1916,7 @@ function scanChanges(changes, doc, corrections) {
|
|
|
1884
1916
|
pos = off;
|
|
1885
1917
|
break;
|
|
1886
1918
|
}
|
|
1919
|
+
off = end;
|
|
1887
1920
|
}
|
|
1888
1921
|
if (queried.has(pos))
|
|
1889
1922
|
return;
|
package/dist/table.js
CHANGED
|
@@ -159,7 +159,7 @@ function sameCell(a, b) {
|
|
|
159
159
|
return a != 0 && a == b;
|
|
160
160
|
}
|
|
161
161
|
function computeMap(table) {
|
|
162
|
-
if (table.
|
|
162
|
+
if (table.type != Table.type)
|
|
163
163
|
throw new RangeError(`Not a table node: ${table.type.name}`);
|
|
164
164
|
let width = table.content[0].content.reduce((w, c) => w + (c.mark(ColSpan) ?? 1), 0);
|
|
165
165
|
let height = table.content.length;
|