@nerd-bible/wordgard 0.5.2-beta3 → 0.5.2-beta5

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 CHANGED
@@ -1,14 +1,20 @@
1
1
  # Wordgard
2
2
 
3
- [ [**WEBSITE**](https://wordgard.net/) | [**DOCS**](https://wordgard.net/docs/) | [**ISSUES**](https://code.haverbeke.berlin/wordgard/wordgard/issues) | [**FORUM**](https://discuss.wordgard.net/) ]
3
+ This is a downstream fork of
4
+ [Wordgard](https://code.haverbeke.berlin/wordgard/wordgard).
4
5
 
5
- This is the [Wordgard](https://wordgard.net) package. It implements a
6
- rich text editor framework for the browser.
6
+ It follows these tagging rules:
7
+
8
+ - If this upstream repo tagged the latest commit, it'll publish that version.
9
+ - Otherwise, it'll add a -beta1 suffix. If the last tag already has such a suffix, it'll bump it.
10
+ - The beta tag is always latest rather than prerelease. If you want only the upstream tagged versions, use the [official package](https://www.npmjs.com/package/@nerd-bible/wordgard).
7
11
 
8
12
  ```bash
9
- npm i wordgard
13
+ npm install wordgard@npm:@nerd-bible/wordgard
10
14
  ```
11
15
 
16
+ The upstream support has been so excellent that I currently don't plan on doing anything with my fork besides CI.
17
+
12
18
  ```javascript
13
19
  import {Wordgard, menuBar} from "wordgard/editor"
14
20
  import {fullSchema} from "wordgard/schema"
@@ -20,5 +26,3 @@ const myEditor = Wordgard.create({
20
26
  config: [fullSchema(), history(), menuBar()]
21
27
  })
22
28
  ```
23
-
24
- This project is licensed under an MIT license.
package/dist/command.d.ts CHANGED
@@ -539,6 +539,24 @@ points.
539
539
  */
540
540
  declare const deleteToLineEnd: Command<"forward" | "backward">;
541
541
  /**
542
+ Command similar to forward `deleteToLineEnd` for macOS's Ctrl-k
543
+ binding. If there's no content left on the line, this will delete
544
+ the node (generally a line break) after the cursor of, if at end
545
+ of textblock, join it to the next textblock.
546
+
547
+ Content deleted by this command is added to a ‘kill buffer’, and
548
+ can be reinserted with {@link yankKilled}. Multiple kill actions
549
+ in sequence will accumulate the deleted content in the buffer.
550
+ Doing anything else and then running this command again will reset
551
+ the buffer to hold only the newly killed content.
552
+ */
553
+ declare const killToLineEnd: Command;
554
+ /**
555
+ Insert the content killed by the most recent {@link killToLineEnd}
556
+ command (or sequence thereof) at the cursor position.
557
+ */
558
+ declare const yankKilled: Command;
559
+ /**
542
560
  Delete the selection, or if that is empty, the line around the
543
561
  cursor.
544
562
  */
@@ -805,4 +823,4 @@ updated to perform those joins.
805
823
  */
806
824
  declare function autoJoinBlocks(state: GardState, tr: Transaction.Spec): Transaction.Spec;
807
825
 
808
- export { Command, Menu, autoJoinBlocks, canAddMarkInRange, clearNonFitting, deleteBackward, deleteEmptyPlot, deleteForward, deleteLine, deleteSelection, deleteToLineEnd, deleteUnit, deleteWord, doUnwrapBlock, enter, enterInCode, findUnwrappable, findWrappable, insertLineBreak, insertText, joinBackward, joinBlocks, joinForward, joinListItems, liftEmptyBlock, listIsActive, moveByLine, moveByPage, moveByUnit, moveByWord, moveToDocSide, moveToLineSide, moveToTextblockSide, redo, selectAll, selectedTextblocks, setAlignment, setDirection, setTextblockType, splitTextblock, toggleBlock, toggleEmphasis, toggleList, toggleMark, toggleStrong, toggleUnderline, transposeChars, undo, unwrapBlock, wrapBlock, wrapBlockRange };
826
+ export { Command, Menu, autoJoinBlocks, canAddMarkInRange, clearNonFitting, deleteBackward, deleteEmptyPlot, deleteForward, deleteLine, deleteSelection, deleteToLineEnd, deleteUnit, deleteWord, doUnwrapBlock, enter, enterInCode, findUnwrappable, findWrappable, insertLineBreak, insertText, joinBackward, joinBlocks, joinForward, joinListItems, killToLineEnd, liftEmptyBlock, listIsActive, moveByLine, moveByPage, moveByUnit, moveByWord, moveToDocSide, moveToLineSide, moveToTextblockSide, redo, selectAll, selectedTextblocks, setAlignment, setDirection, setTextblockType, splitTextblock, toggleBlock, toggleEmphasis, toggleList, toggleMark, toggleStrong, toggleUnderline, transposeChars, undo, unwrapBlock, wrapBlock, wrapBlockRange, yankKilled };
package/dist/command.js CHANGED
@@ -807,7 +807,7 @@ const deleteToLineEnd = (wg, dir) => {
807
807
  return false;
808
808
  let tr = deleteSelection(wg.state), { selection } = wg.state;
809
809
  if (tr)
810
- return (wg.dispatch(tr), true);
810
+ return tr;
811
811
  if (!(selection instanceof GardSelection.Text))
812
812
  return false;
813
813
  let end = wg.moveToLineBoundary(selection, dir == "forward");
@@ -819,12 +819,76 @@ const deleteToLineEnd = (wg, dir) => {
819
819
  userEvent: "delete." + dir
820
820
  };
821
821
  };
822
+ const addToKillBuffer = /*@__PURE__*/Transaction.Effect.define();
823
+ const killBuffer = /*@__PURE__*/GardState.Field.define({
824
+ create() { return { content: [], active: false }; },
825
+ update(value, tr) {
826
+ let add = tr.effects.find(e => e.is(addToKillBuffer));
827
+ if (add)
828
+ return { content: value.active ? value.content.concat(add.value) : add.value, active: true };
829
+ return !value.active || tr.annotation(Transaction.remote) || !(tr.docChanged || tr.selection)
830
+ ? value : { content: value.content, active: false };
831
+ }
832
+ });
833
+ function killText(state, from, to) {
834
+ let add = addToKillBuffer.of(state.doc.slice(from, to).content);
835
+ return state.field(killBuffer, false) ? [add] : [GardState.appendConfig.of(killBuffer), add];
836
+ }
837
+ const killToLineEnd = wg => {
838
+ let { state } = wg, { selection } = state;
839
+ if (state.readOnly || !(selection instanceof GardSelection.Text))
840
+ return false;
841
+ let end = wg.moveToLineBoundary(selection, true);
842
+ if (!end)
843
+ return false;
844
+ if (end.head > selection.head)
845
+ return {
846
+ changes: { correct: { from: selection.head, to: end.head } },
847
+ scrollIntoView: true,
848
+ effects: killText(state, selection.head, end.head),
849
+ userEvent: "delete.forward"
850
+ };
851
+ let join = joinForward(state);
852
+ if (join) {
853
+ let joinEnd;
854
+ state.doc.iterate(state.sel.head.textblockParent.after, state.doc.length, (node, pos) => {
855
+ if (joinEnd != null)
856
+ return false;
857
+ if (node.isPlot && node.isTextblock)
858
+ joinEnd = pos + 1;
859
+ });
860
+ return Transaction.merge(state, join, {
861
+ effects: joinEnd == null ? undefined : killText(state, selection.head, joinEnd)
862
+ });
863
+ }
864
+ let next = state.sel.head.nodeAfter;
865
+ if (next)
866
+ return {
867
+ changes: { correct: { from: selection.head, to: selection.head + next.length } },
868
+ scrollIntoView: true,
869
+ effects: killText(state, selection.head, selection.head + next.length),
870
+ userEvent: "delete.forward"
871
+ };
872
+ return false;
873
+ };
874
+ const yankKilled = wg => {
875
+ let { state } = wg, buffer = wg.state.field(killBuffer, false);
876
+ if (!buffer || !buffer.content.length || !(state.selection instanceof GardSelection.Text))
877
+ return false;
878
+ let { selection } = state;
879
+ return {
880
+ changes: { correct: { from: selection.from, to: selection.to, insert: buffer.content } },
881
+ scrollIntoView: true,
882
+ selection: (cx, changes) => GardSelection.near(cx, changes.mapPos(selection.from, 1)),
883
+ userEvent: "input.yank"
884
+ };
885
+ };
822
886
  const deleteLine = wg => {
823
887
  if (wg.state.readOnly)
824
888
  return false;
825
889
  let tr = deleteSelection(wg.state), { selection } = wg.state;
826
890
  if (tr)
827
- return (wg.dispatch(tr), true);
891
+ return tr;
828
892
  if (!(selection instanceof GardSelection.Text))
829
893
  return false;
830
894
  let start = wg.moveToLineBoundary(selection, false), end = wg.moveToLineBoundary(selection, true);
@@ -1476,4 +1540,4 @@ const Menu = /*@__PURE__*/(function (Menu) {
1476
1540
  Menu.resolve = resolve;
1477
1541
  ;return Menu})({});
1478
1542
 
1479
- export { Command, Menu, autoJoinBlocks, canAddMarkInRange, clearNonFitting, deleteBackward, deleteEmptyPlot, deleteForward, deleteLine, deleteSelection, deleteToLineEnd, deleteUnit, deleteWord, doUnwrapBlock, enter, enterInCode, findUnwrappable, findWrappable, insertLineBreak, insertText, joinBackward, joinBlocks, joinForward, joinListItems, liftEmptyBlock, listIsActive, moveByLine, moveByPage, moveByUnit, moveByWord, moveToDocSide, moveToLineSide, moveToTextblockSide, redo, selectAll, selectedTextblocks, setAlignment, setDirection, setTextblockType, splitTextblock, toggleBlock, toggleEmphasis, toggleList, toggleMark, toggleStrong, toggleUnderline, transposeChars, undo, unwrapBlock, wrapBlock, wrapBlockRange };
1543
+ export { Command, Menu, autoJoinBlocks, canAddMarkInRange, clearNonFitting, deleteBackward, deleteEmptyPlot, deleteForward, deleteLine, deleteSelection, deleteToLineEnd, deleteUnit, deleteWord, doUnwrapBlock, enter, enterInCode, findUnwrappable, findWrappable, insertLineBreak, insertText, joinBackward, joinBlocks, joinForward, joinListItems, killToLineEnd, liftEmptyBlock, listIsActive, moveByLine, moveByPage, moveByUnit, moveByWord, moveToDocSide, moveToLineSide, moveToTextblockSide, redo, selectAll, selectedTextblocks, setAlignment, setDirection, setTextblockType, splitTextblock, toggleBlock, toggleEmphasis, toggleList, toggleMark, toggleStrong, toggleUnderline, transposeChars, undo, unwrapBlock, wrapBlock, wrapBlockRange, yankKilled };
package/dist/editor.d.ts CHANGED
@@ -1422,11 +1422,12 @@ declare namespace KeyBinding {
1422
1422
  - `Ctrl-e` to {@link command.moveToTextblockSide} (`{dir: "forward"}`)
1423
1423
  - `Ctrl-d` to {@link command.deleteUnit} (`"forward"`)
1424
1424
  - `Ctrl-h` to {@link command.deleteUnit} (`"backward"`)
1425
- - `Ctrl-k` to {@link command.deleteToLineEnd} (`"forward"`)
1425
+ - `Ctrl-k` to {@link command.killToLineEnd}
1426
1426
  - `Ctrl-Alt-h` to {@link command.deleteWord} (`"backward"`)
1427
1427
  - `Ctrl-o` to {@link command.insertLineBreak}
1428
1428
  - `Ctrl-t` to {@link command.transposeChars}
1429
1429
  - `Ctrl-v` to {@link command.moveByPage} (`{dir: "down"}`)
1430
+ - `Ctrl-y` to {@link command.yankKilled}
1430
1431
  */
1431
1432
  const defaultKeymap: readonly KeyBinding[];
1432
1433
  }
package/dist/editor.js CHANGED
@@ -2,7 +2,7 @@ import { GardState, GardSelection, TextblockMap, BidiSpan, Transaction } from 'w
2
2
  import { Attributes, Elt, Node, Leaf, parse, Slice, Plot, serialize, Pos, ChangeSet, ValidationError, Mark } from 'wordgard/doc';
3
3
  import { StyleModule } from 'style-mod';
4
4
  import { findClusterBreak } from '@marijn/find-cluster-break';
5
- import { enter, insertLineBreak, selectAll, undo, redo, transposeChars, Command, deleteUnit, deleteWord, deleteToLineEnd, moveByUnit, moveByLine, moveByWord, moveToLineSide, moveToDocSide, moveByPage, moveToTextblockSide, setAlignment, toggleUnderline, toggleEmphasis, toggleStrong, deleteLine, insertText, setDirection, deleteSelection, Menu, findWrappable, wrapBlockRange, autoJoinBlocks } from 'wordgard/command';
5
+ import { enter, insertLineBreak, selectAll, undo, redo, killToLineEnd, yankKilled, transposeChars, Command, deleteUnit, deleteWord, deleteToLineEnd, moveByUnit, moveByLine, moveByWord, moveToLineSide, moveToDocSide, moveByPage, moveToTextblockSide, setAlignment, toggleUnderline, toggleEmphasis, toggleStrong, deleteLine, insertText, setDirection, deleteSelection, Menu, findWrappable, wrapBlockRange, autoJoinBlocks } from 'wordgard/command';
6
6
  import { PhraseSet, phrases } from 'wordgard/phrases';
7
7
  import { history } from 'wordgard/history';
8
8
 
@@ -643,7 +643,6 @@ class PointSet {
643
643
  }
644
644
  class PointIterator {
645
645
  set;
646
- done = false;
647
646
  constructor(set) {
648
647
  this.set = set;
649
648
  this.fill(0);
@@ -658,18 +657,16 @@ class PointIterator {
658
657
  else {
659
658
  this.from = 1e8;
660
659
  this.value = null;
661
- this.done = true;
662
660
  }
663
661
  }
664
662
  next() {
665
- if (!this.done)
663
+ if (this.value)
666
664
  this.fill(this.i + 1);
667
665
  }
668
666
  get side() {
669
- return this.done ? 1 : this.value.side;
667
+ return this.value ? this.value.side : 1;
670
668
  }
671
669
  goto(pos, inclusive) {
672
- this.done = false;
673
670
  let i = findAbove(this.set.positions, 0, pos - 1);
674
671
  if (!inclusive) {
675
672
  while (i < this.set.values.length && this.set.values[i].side < 1000000000)
@@ -844,7 +841,6 @@ class RangeSet {
844
841
  }
845
842
  class RangeIterator {
846
843
  set;
847
- done = false;
848
844
  constructor(set) {
849
845
  this.set = set;
850
846
  this.fill(0);
@@ -859,101 +855,16 @@ class RangeIterator {
859
855
  else {
860
856
  this.from = this.to = 1e8;
861
857
  this.value = null;
862
- this.done = true;
863
858
  }
864
859
  }
865
860
  next() {
866
- if (!this.done)
861
+ if (this.value)
867
862
  this.fill(this.i + 1);
868
863
  }
869
864
  goto(pos) {
870
- this.done = false;
871
865
  this.fill(findAbove(this.set.to, 0, pos));
872
866
  }
873
867
  }
874
- class MultiSet {
875
- sets;
876
- pos;
877
- constructor(sets, pos) {
878
- this.sets = sets;
879
- this.pos = pos;
880
- }
881
- map(changes) {
882
- if (changes.empty)
883
- return this;
884
- let pos = this.pos.slice(), sets = this.sets.slice(), i = 0;
885
- changes.iterGaps((fromA, toA, fromB, _toB, last) => {
886
- while (i < sets.length && (last || pos[i] + sets[i].length < fromA)) {
887
- pos[i++] += fromB - fromA;
888
- }
889
- }, (_fromA, toA) => {
890
- while (i < sets.length && pos[i] <= toA) {
891
- sets[i] = sets[i].map(changes, pos[i]);
892
- pos[i] = changes.mapPos(pos[i], -1);
893
- i++;
894
- }
895
- });
896
- return new MultiSet(sets, pos);
897
- }
898
- iter() {
899
- return new MultiIterator(this);
900
- }
901
- static empty = /*@__PURE__*/(() => new MultiSet([], []))();
902
- static create(f) {
903
- let sets = [], pos = [], at = 0;
904
- f((p, set) => {
905
- if (p < at)
906
- throw new Error("Overlapping sets in MultiSet.create");
907
- sets.push(set);
908
- pos.push(p);
909
- at = p + set.size;
910
- });
911
- return sets.length ? new MultiSet(sets, pos) : MultiSet.empty;
912
- }
913
- }
914
- const empty = {
915
- from: 1e9, to: 1e9,
916
- value: null,
917
- done: true,
918
- next() { },
919
- goto() { }
920
- };
921
- class MultiIterator {
922
- set;
923
- get value() { return this.cur.value; }
924
- get done() { return this.cur.done; }
925
- get from() { return this.cur.from + this.offset; }
926
- get to() { return this.cur.to + this.offset; }
927
- i = 0;
928
- constructor(set) {
929
- this.set = set;
930
- this.nextSet();
931
- }
932
- next() {
933
- this.cur.next();
934
- while (this.cur != empty && this.cur.done)
935
- this.nextSet();
936
- }
937
- nextSet() {
938
- if (this.i == this.set.sets.length) {
939
- this.offset = 0;
940
- this.cur = empty;
941
- }
942
- else {
943
- this.offset = this.set.pos[this.i];
944
- this.cur = this.set.sets[this.i].iter();
945
- this.i++;
946
- }
947
- }
948
- goto(pos, inclusive = false) {
949
- let i = 0;
950
- while (i < this.set.pos.length && this.set.pos[i] + this.set.sets[i].size)
951
- i++;
952
- this.i = i;
953
- this.nextSet();
954
- this.cur.goto(pos - this.offset, inclusive);
955
- }
956
- }
957
868
  function compareDecoSet(setA, setB, cmp) {
958
869
  for (let [srcA, valA] of setA)
959
870
  cmp(valA, setB.get(srcA) || null);
@@ -1076,7 +987,7 @@ class HeapIterator {
1076
987
  return this;
1077
988
  if (this.point) {
1078
989
  this.point.next();
1079
- if (this.point.done)
990
+ if (!this.point.value)
1080
991
  popHeap(this.pointHeap, cmpPoint);
1081
992
  else
1082
993
  bubble(this.pointHeap, 0, cmpPoint);
@@ -1112,7 +1023,7 @@ class HeapIterator {
1112
1023
  else {
1113
1024
  let first = active[0];
1114
1025
  first.next();
1115
- if (!first.done)
1026
+ if (first.value)
1116
1027
  sink(rangeHeap, rangeHeap.push(first) - 1, cmpRangeFrom);
1117
1028
  popHeap(active, cmpRangeTo);
1118
1029
  }
@@ -1256,7 +1167,7 @@ class DecoIterator {
1256
1167
  i.goto(from);
1257
1168
  for (let i of this.pointIter)
1258
1169
  i.goto(from, inclusiveStart);
1259
- let iter = new HeapIterator(this.rangeIter.filter(i => !i.done), this.pointIter.filter(i => !i.done), from, to);
1170
+ let iter = new HeapIterator(this.rangeIter.filter(i => i.value), this.pointIter.filter(i => i.value), from, to);
1260
1171
  let pos = this.pos.advance(from - this.pos.pos), started = inclusiveStart;
1261
1172
  let atomParent;
1262
1173
  for (let p = pos.parent; p; p = p.parent)
@@ -3946,7 +3857,8 @@ class KeyBinding {
3946
3857
  shift: Command.bind(moveToTextblockSide, { dir: "forward", extend: true }) },
3947
3858
  { mac: "Ctrl-d", run: Command.bind(deleteUnit, "forward") },
3948
3859
  { mac: "Ctrl-h", run: Command.bind(deleteUnit, "backward") },
3949
- { mac: "Ctrl-k", run: Command.bind(deleteToLineEnd, "forward") },
3860
+ { mac: "Ctrl-k", run: killToLineEnd },
3861
+ { mac: "Ctrl-y", run: yankKilled },
3950
3862
  { mac: "Ctrl-Alt-h", run: Command.bind(deleteWord, "backward") },
3951
3863
  { mac: "Ctrl-o", run: insertLineBreak },
3952
3864
  { mac: "Ctrl-t", run: transposeChars },
@@ -6299,7 +6211,7 @@ class MenuBar {
6299
6211
  }
6300
6212
  if (update && selection.some(e => e.flags & 32)) {
6301
6213
  let reset = selection[0].flags & 32 ? findChild(this.children, true) : selection[0];
6302
- this.setSelection(reset ? [reset] : [], this.dom.contains(document.activeElement));
6214
+ this.setSelection(reset ? [reset] : [], this.dom.contains(this.wg.root.activeElement));
6303
6215
  }
6304
6216
  }
6305
6217
  setSelection(selection, focus = true) {
@@ -6398,7 +6310,7 @@ class MenuBar {
6398
6310
  event.preventDefault();
6399
6311
  }
6400
6312
  globalClick(event) {
6401
- if (!this.dom.contains(event.target)) {
6313
+ if (!event.composedPath().includes(this.dom)) {
6402
6314
  this.dom.ownerDocument.removeEventListener("mousedown", this.globalClick);
6403
6315
  if (this.selection.length > 1)
6404
6316
  this.setSelection([this.selection[0]], false);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nerd-bible/wordgard",
3
- "version": "0.5.2-beta3",
3
+ "version": "0.5.2-beta5",
4
4
  "description": "Semantic rich text editor system",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -36,7 +36,7 @@
36
36
  "style-mod": "^4.1.3"
37
37
  },
38
38
  "devDependencies": {
39
- "@nerd-bible/config": "^0.2.2",
39
+ "@nerd-bible/config": "^0.2.5",
40
40
  "@swc/wasm-typescript": "^1.15.3",
41
41
  "@types/mocha": "^10.0.10",
42
42
  "@types/node": "^24.10.2",