@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 CHANGED
@@ -511,7 +511,7 @@ declare const insertLineBreak: Command.Pure;
511
511
  The command that handles enter presses. The default handler will,
512
512
  if the selection is not in an inline context, insert an empty
513
513
  default textblock in its position. Otherwise it first tries
514
- `liftEmptyTextblock`, then `splitTextblock`.
514
+ `liftEmptyBlock`, then `splitTextblock`.
515
515
  */
516
516
  declare const enter: Command.Pure;
517
517
  /**
@@ -705,11 +705,11 @@ selection is empty.
705
705
  */
706
706
  declare function deleteSelection(state: GardState): Transaction.Spec | false;
707
707
  /**
708
- If the cursor is inside an empty textblock, return a transaction
709
- that deletes the entire block. `dir` determines which way the
710
- cursor moves after the deletion.
708
+ If the cursor is inside an empty plot, return a transaction that
709
+ deletes the entire plot. `dir` determines which way the cursor
710
+ moves after the deletion.
711
711
  */
712
- declare function deleteEmptyTextblock(state: GardState, dir?: -1 | 1): Transaction.Spec | false;
712
+ declare function deleteEmptyPlot(state: GardState, dir?: -1 | 1): Transaction.Spec | false;
713
713
  /**
714
714
  If the cursor is at the start of a textblock that can be joined to
715
715
  a textblock before it, return a transaction to performs this join.
@@ -793,4 +793,4 @@ updated to perform those joins.
793
793
  */
794
794
  declare function autoJoinBlocks(state: GardState, tr: Transaction.Spec): Transaction.Spec;
795
795
 
796
- export { Command, Menu, autoJoinBlocks, canAddMarkInRange, clearNonFitting, deleteBackward, deleteEmptyTextblock, deleteForward, deleteLine, deleteSelection, deleteToLineEnd, deleteUnit, deleteWord, doUnwrapBlock, enter, 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 };
796
+ export { Command, Menu, autoJoinBlocks, canAddMarkInRange, clearNonFitting, deleteBackward, deleteEmptyPlot, deleteForward, deleteLine, deleteSelection, deleteToLineEnd, deleteUnit, deleteWord, doUnwrapBlock, enter, 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 };
package/dist/command.js CHANGED
@@ -150,14 +150,14 @@ function deleteSelection(state) {
150
150
  userEvent: "delete.selection"
151
151
  });
152
152
  }
153
- function deleteEmptyTextblock(state, dir = -1) {
153
+ function deleteEmptyPlot(state, dir = -1) {
154
154
  if (!state.selection.isCursor)
155
155
  return false;
156
- let block = state.sel.head.textblockParent;
157
- if (!block || block.start < block.end || block.before == 0 && block.after == state.doc.length)
156
+ let plot = state.sel.head.parent;
157
+ if (!plot.parent || plot.start < plot.end)
158
158
  return false;
159
159
  return {
160
- changes: { from: block.before, to: block.after, fit: true },
160
+ changes: { from: plot.before, to: plot.after, fit: true },
161
161
  selection: (cx, changes) => GardSelection.near(cx, changes.mapPos(state.selection.head), dir),
162
162
  scrollIntoView: true,
163
163
  userEvent: dir < 0 ? "delete.backward" : "delete.forward"
@@ -289,12 +289,9 @@ function deleteBackward(state, word = false) {
289
289
  for (;;) {
290
290
  if (next.isPlot && next.type.isolating)
291
291
  return false;
292
- if (next.isLeaf || state.isAtom(next.type))
292
+ if (next.isLeaf || state.isAtom(next.type) || !next.content.length)
293
293
  break;
294
- let last = next.content.length - 1;
295
- if (last < 0)
296
- return false;
297
- next = next.content[last];
294
+ next = next.content[next.content.length - 1];
298
295
  pos--;
299
296
  }
300
297
  if (next.is(Leaf.Text)) {
@@ -336,13 +333,14 @@ function deleteBackward(state, word = false) {
336
333
  }
337
334
  let from = pos - next.length, to = pos;
338
335
  let parent = state.doc.resolve(pos).parent;
339
- while (parent && parent.node.type.isBlock && parent.node.content.length == 1) {
340
- if (!parent.parent)
341
- return false;
342
- parent = parent.parent;
343
- from--;
344
- to++;
345
- }
336
+ if (next.type.isBlock)
337
+ while (parent && parent.node.type.isBlock && parent.node.content.length == 1) {
338
+ if (!parent.parent)
339
+ return false;
340
+ parent = parent.parent;
341
+ from--;
342
+ to++;
343
+ }
346
344
  return {
347
345
  changes: { from, to },
348
346
  scrollIntoView: true,
@@ -366,10 +364,8 @@ function deleteForward(state, word = false) {
366
364
  for (;;) {
367
365
  if (next.isPlot && next.type.isolating)
368
366
  return false;
369
- if (next.isLeaf || state.isAtom(next.type))
367
+ if (next.isLeaf || state.isAtom(next.type) || !next.content.length)
370
368
  break;
371
- if (!next.content.length)
372
- return false;
373
369
  next = next.content[0];
374
370
  pos++;
375
371
  }
@@ -412,13 +408,14 @@ function deleteForward(state, word = false) {
412
408
  }
413
409
  let from = pos, to = pos + next.length;
414
410
  let parent = state.doc.resolve(pos).parent;
415
- while (parent && parent.node.type.isBlock && parent.node.content.length == 1) {
416
- if (!parent.parent)
417
- return false;
418
- parent = parent.parent;
419
- from--;
420
- to++;
421
- }
411
+ if (next.type.isBlock)
412
+ while (parent && parent.node.type.isBlock && parent.node.content.length == 1) {
413
+ if (!parent.parent)
414
+ return false;
415
+ parent = parent.parent;
416
+ from--;
417
+ to++;
418
+ }
422
419
  return {
423
420
  changes: { from, to },
424
421
  scrollIntoView: true,
@@ -766,15 +763,15 @@ const deleteUnit = ({ state }, dir) => {
766
763
  if (state.readOnly)
767
764
  return false;
768
765
  return deleteSelection(state) || (dir == "forward"
769
- ? joinForward(state) || deleteForward(state) || deleteEmptyTextblock(state, 1)
770
- : joinListItems(state) || joinBackward(state) || deleteBackward(state) || deleteEmptyTextblock(state, -1));
766
+ ? joinForward(state) || deleteForward(state) || deleteEmptyPlot(state, 1)
767
+ : joinListItems(state) || joinBackward(state) || deleteBackward(state) || deleteEmptyPlot(state, -1));
771
768
  };
772
769
  const deleteWord = ({ state }, dir) => {
773
770
  if (state.readOnly)
774
771
  return false;
775
772
  return deleteSelection(state) || (dir == "forward"
776
- ? joinForward(state) || deleteForward(state, true) || deleteEmptyTextblock(state, 1)
777
- : joinListItems(state) || joinBackward(state) || deleteBackward(state, true) || deleteEmptyTextblock(state, -1));
773
+ ? joinForward(state) || deleteForward(state, true) || deleteEmptyPlot(state, 1)
774
+ : joinListItems(state) || joinBackward(state) || deleteBackward(state, true) || deleteEmptyPlot(state, -1));
778
775
  };
779
776
  const deleteToLineEnd = (wg, dir) => {
780
777
  if (wg.state.readOnly)
@@ -1101,8 +1098,7 @@ function extendSel(base, head) {
1101
1098
  const moveByUnit = ({ state }, { dir, extend }) => {
1102
1099
  let forward = isForward(dir, state), selection = asTextSel(state.selection, forward);
1103
1100
  if (!selection.empty && !extend) {
1104
- let next = selection.normalCursorAtBound(state, forward);
1105
- return next ? setSelection(next) : false;
1101
+ return setSelection(GardSelection.near(state, forward ? selection.to : selection.from, forward ? -1 : 1));
1106
1102
  }
1107
1103
  else {
1108
1104
  let next = selection.nextNormalCursor(state, forward);
@@ -1133,10 +1129,10 @@ function nextVertical(wg, sel, forward, distance, allowNode) {
1133
1129
  }
1134
1130
  const moveByLine = (wg, { dir, extend }) => {
1135
1131
  let { state } = wg, { selection } = state, forward = dir == "down";
1136
- if (state.selection instanceof GardSelection.Node) {
1137
- let next = !extend && state.selection.normalCursorAtBound(state, forward);
1132
+ if (selection instanceof GardSelection.Node) {
1133
+ let next = !extend && GardSelection.near(state, forward ? selection.to : selection.from, forward ? -1 : 1);
1138
1134
  if (next && !state.doc.resolve(next.head).parent.node.inlineContent)
1139
- return setSelection(GardSelection.cursor(next.head, next.headSide, state.selection.goalColumn));
1135
+ return setSelection(GardSelection.cursor(next.head, next.headSide, selection.goalColumn));
1140
1136
  selection = GardSelection.cursor(forward ? selection.to : selection.from, undefined, selection.goalColumn);
1141
1137
  }
1142
1138
  else {
@@ -1448,4 +1444,4 @@ const Menu = /*@__PURE__*/(function (Menu) {
1448
1444
  Menu.resolve = resolve;
1449
1445
  ;return Menu})({});
1450
1446
 
1451
- export { Command, Menu, autoJoinBlocks, canAddMarkInRange, clearNonFitting, deleteBackward, deleteEmptyTextblock, deleteForward, deleteLine, deleteSelection, deleteToLineEnd, deleteUnit, deleteWord, doUnwrapBlock, enter, 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 };
1447
+ export { Command, Menu, autoJoinBlocks, canAddMarkInRange, clearNonFitting, deleteBackward, deleteEmptyPlot, deleteForward, deleteLine, deleteSelection, deleteToLineEnd, deleteUnit, deleteWord, doUnwrapBlock, enter, 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 };
package/dist/doc.d.ts CHANGED
@@ -250,7 +250,7 @@ declare class Elt<T = string> {
250
250
  /**
251
251
  Convert an element (with only string content) to a DOM tree.
252
252
  */
253
- toDOM(this: Elt<string>, doc?: Document): Element | Text;
253
+ toDOM(this: Elt<string>, doc?: Document): Element;
254
254
  }
255
255
  declare namespace Elt {
256
256
  /**
@@ -1536,6 +1536,15 @@ declare class Plot implements Node.Shared {
1536
1536
  node that overlaps the given range, outer nodes before inner
1537
1537
  nodes. When the function returns `false` for a node, descendents
1538
1538
  of that node are not iterated.
1539
+
1540
+ When `from` is greater than `to`, iteration happens in inverted
1541
+ order, yielding later siblings before earlier ones.
1542
+
1543
+ Note that positions passed to the callback are relative to the
1544
+ start of the node this method is called on. That will usually be
1545
+ the document, in which case they are normal document positions,
1546
+ but you can also call it on an arbitrary plot, in which case
1547
+ they are local positions.
1539
1548
  */
1540
1549
  iterate(from: number, to: number, f: (node: Node, pos: number, parent: Plot | null, index: number) => boolean | void): void;
1541
1550
  iterate(f: (node: Node, pos: number, parent: Plot | null, index: number) => boolean | void): void;
@@ -1676,6 +1685,11 @@ declare namespace Plot {
1676
1685
  */
1677
1686
  readonly orientation: "row" | "column";
1678
1687
  /**
1688
+ True if this is an inline plot with {@link
1689
+ Plot.Spec.cursorInsideBounds `cursorInsideBounds`} set.
1690
+ */
1691
+ readonly cursorInsideBounds: boolean;
1692
+ /**
1679
1693
  The spec used to define this plot type.
1680
1694
  */
1681
1695
  readonly spec: Plot.Spec<any>;
package/dist/doc.js CHANGED
@@ -1301,8 +1301,12 @@ class Plot {
1301
1301
  }
1302
1302
  iterate(a, b, c) {
1303
1303
  let [from, to, f] = typeof a == "number" ? [a, b, c] : [0, this.length, a];
1304
- if (this.isDoc || f(this, 0, null, 0) !== false)
1305
- this.iterInner(0, from, to, f);
1304
+ if (this.isDoc || f(this, 0, null, 0) !== false) {
1305
+ if (to < from)
1306
+ this.iterBack(this.contentLength, to, from, f);
1307
+ else
1308
+ this.iterInner(0, from, to, f);
1309
+ }
1306
1310
  }
1307
1311
  nodeAt(pos) {
1308
1312
  for (let node of this.content) {
@@ -1333,12 +1337,20 @@ class Plot {
1333
1337
  break;
1334
1338
  let node = this.content[i], start = pos;
1335
1339
  pos += node.length;
1336
- if (pos <= from)
1337
- continue;
1338
- if (f(node, start, this, i) !== false && node.isPlot)
1340
+ if (pos > from && f(node, start, this, i) !== false && node.isPlot)
1339
1341
  node.iterInner(start + 1, from, to, f);
1340
1342
  }
1341
1343
  }
1344
+ iterBack(contentEnd, from, to, f) {
1345
+ for (let pos = contentEnd, i = this.content.length - 1; i >= 0; i--) {
1346
+ if (pos <= from)
1347
+ break;
1348
+ let node = this.content[i], end = pos;
1349
+ pos -= node.length;
1350
+ if (pos < to && f(node, pos, this, i) !== false && node.isPlot)
1351
+ node.iterBack(end - 1, from, to, f);
1352
+ }
1353
+ }
1342
1354
  toString() {
1343
1355
  return this.name + markString(this.tag.marks) + "(" + this.content.join() + ")";
1344
1356
  }
@@ -1417,6 +1429,7 @@ class Plot {
1417
1429
  neutral;
1418
1430
  preserveWhitespace;
1419
1431
  orientation;
1432
+ cursorInsideBounds;
1420
1433
  spec;
1421
1434
  constructor(name, flags, spec) {
1422
1435
  super(name, flags, spec, NodeShape.from(name, false, spec.shape));
@@ -1428,6 +1441,7 @@ class Plot {
1428
1441
  this.neutral = spec.neutral ?? !this.defining;
1429
1442
  this.preserveWhitespace = spec.preserveWhitespace ?? !!this.hasRole(Node.Role.Code);
1430
1443
  this.orientation = flags & 2 ? "row" : spec.orientation || "column";
1444
+ this.cursorInsideBounds = !!((flags & 1) && spec.cursorInsideBounds);
1431
1445
  this.default = "defaultParam" in spec ? Plot.Tag.new(this, spec.defaultParam, none) :
1432
1446
  (flags & 16) ? Plot.Tag.new(this, null, none) : null;
1433
1447
  if (!this.shape.atom && this.isInline && !this.inlineContent)
@@ -2945,15 +2959,17 @@ class SectionIter {
2945
2959
  }
2946
2960
  next() {
2947
2961
  let { sections } = this;
2962
+ this.off = 0;
2948
2963
  if (this.i < sections.length) {
2949
2964
  this.len = sections[this.i++];
2950
2965
  this.ins = sections[this.i++];
2966
+ if (this.len == 0 && this.ins < 0)
2967
+ this.next();
2951
2968
  }
2952
2969
  else {
2953
2970
  this.len = 0;
2954
2971
  this.ins = -3;
2955
2972
  }
2956
- this.off = 0;
2957
2973
  }
2958
2974
  get keep() { return this.ins == -1 || this.ins == -2; }
2959
2975
  get done() { return this.ins == -3; }
@@ -3055,6 +3071,9 @@ function splatContext(top, cx) {
3055
3071
  for (let ch of cx.children)
3056
3072
  top.push(ch);
3057
3073
  }
3074
+ function isFitBarrier(plot) {
3075
+ return plot.isolating || plot.cursorInsideBounds;
3076
+ }
3058
3077
  function fitReplacement(doc, from, to, slice, context) {
3059
3078
  if (!slice.length)
3060
3079
  return fitDeletion(doc, from, to);
@@ -3085,10 +3104,10 @@ function fitReplacement(doc, from, to, slice, context) {
3085
3104
  let found, foundCost = 1e8;
3086
3105
  let neutral = true, toEnd = true;
3087
3106
  scan: for (let cxFrom = from.parent, cxTo = to.parent, fromDepth = from.depth, toDepth = to.depth, start = from.pos, end = to.pos; cxFrom.parent; cxFrom = cxFrom.parent, start--, fromDepth--) {
3088
- if (cxFrom.start != start || cxFrom.node.type.isolating)
3107
+ if (cxFrom.start != start || isFitBarrier(cxFrom.node.type))
3089
3108
  break;
3090
3109
  while (toDepth > fromDepth) {
3091
- if (cxTo.node.type.isolating)
3110
+ if (isFitBarrier(cxTo.node.type))
3092
3111
  break scan;
3093
3112
  cxTo = cxTo.parent;
3094
3113
  toDepth--;
@@ -3122,17 +3141,22 @@ function fitReplacement(doc, from, to, slice, context) {
3122
3141
  if (found)
3123
3142
  return found;
3124
3143
  if (from.pos == to.pos && !from.inText) {
3125
- let cx = from.parent, before = from.pos, after = from.pos;
3126
- for (; cx.parent && !cx.node.type.isolating && (before == cx.start || after == cx.end); cx = cx.parent, before--, after++) {
3144
+ let cx = from.parent;
3145
+ for (let before = from.index ? -1 : from.pos, after = from.pos == from.parent.end ? from.pos : -1; before > -1 || after > -1;) {
3127
3146
  for (let i = -1; i < context.length; i++) {
3128
3147
  let type = i >= 0 ? context[i].type : firstType;
3129
3148
  if (!type)
3130
3149
  continue;
3131
- if (doc.schema.canContain(cx.parent.node.type, type)) {
3132
- let pos = before == cx.start ? cx.before : cx.after;
3150
+ if (doc.schema.canContain(cx.node.type, type)) {
3151
+ let pos = before > -1 ? before : after;
3133
3152
  return { from: pos, to: pos, slice: i >= 0 ? closeSlice(doc.schema, slice, context, i + 1, true) : slice };
3134
3153
  }
3135
3154
  }
3155
+ if (isFitBarrier(cx.node.type) || !cx.parent)
3156
+ break;
3157
+ before = before == cx.start ? before - 1 : -1;
3158
+ after = after == cx.end ? after + 1 : -1;
3159
+ cx = cx.parent;
3136
3160
  }
3137
3161
  }
3138
3162
  for (let i = 0; i < context.length; i++) {
@@ -3147,7 +3171,7 @@ function fitDeletion(doc, from, to) {
3147
3171
  let toDepth = to.depth;
3148
3172
  let covered;
3149
3173
  for (let cx = from.parent, cxTo = to.parent, depth = from.depth, start = from.pos, end = to.pos; cx.parent; start--, cx = cx.parent, depth--) {
3150
- if (cx.start != start || cx.node.type.isolating)
3174
+ if (cx.start != start || isFitBarrier(cx.node.type))
3151
3175
  break;
3152
3176
  while (toDepth > depth) {
3153
3177
  cxTo = cxTo.parent;
package/dist/editor.d.ts CHANGED
@@ -46,7 +46,7 @@ declare namespace Widget {
46
46
  /**
47
47
  How to render the widget as DOM content.
48
48
  */
49
- render: (value: Param) => Element | Text;
49
+ render: (value: Param, wg: Wordgard) => Element | Text;
50
50
  /**
51
51
  Compare the widget value for equality. Will default to `===`.
52
52
  */
@@ -64,16 +64,22 @@ declare namespace Widget {
64
64
  */
65
65
  disconnect?: (value: Param, dom: Element | Text) => void;
66
66
  /**
67
- Called before the editor handles a DOM event that comes from
68
- inside the widget. May return true to indicate that no further
69
- handling of the event should happen.
67
+ Used to determine whether events originating from the widget's
68
+ DOM should ignored by the editor. `false` or a function that
69
+ returns `false` for the event will prevent the editor's
70
+ regular event handling for the event.
70
71
  */
71
- handleEvent?: (event: Event, wg: Wordgard) => boolean;
72
+ propagateEvent?: boolean | ((event: Event) => boolean);
72
73
  /**
73
74
  Set this to false for widgets that either aren't visible or
74
75
  are positioned outside of the regular document flow.
75
76
  */
76
77
  inFlow?: boolean;
78
+ /**
79
+ By default, widgets are set to be ineditable. Set this to
80
+ `true` to suppress that.
81
+ */
82
+ editable?: boolean;
77
83
  };
78
84
  /**
79
85
  Each widget has an associated type that describes how it
@@ -506,7 +512,7 @@ declare abstract class Tile {
506
512
  get boundary(): 0 | 1;
507
513
  get firstChild(): Tile | null;
508
514
  get lastChild(): Tile | null;
509
- handleEvent(event: Event, wg: Wordgard): boolean;
515
+ ignoreEvent(event: Event): boolean;
510
516
  get ignoreMutations(): boolean;
511
517
  toString(): string;
512
518
  sync(): void;
@@ -606,6 +612,10 @@ declare class Wordgard {
606
612
  private editorAttrs;
607
613
  private contentAttrs;
608
614
  private styleModules;
615
+ /**
616
+ True when the editor is connected to a DOM document.
617
+ */
618
+ connected: boolean;
609
619
  private flushing;
610
620
  private willFlush;
611
621
  private flushFunc;