@nerd-bible/wordgard 0.3.5 → 0.5.2-beta

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/editor.js CHANGED
@@ -1,11 +1,167 @@
1
1
  import { GardState, GardSelection, TextblockMap, BidiSpan, Transaction } from 'wordgard/state';
2
- import { Attributes, Elt, Node, Leaf, ChangeSet, parse, Slice, Plot, serialize, Pos, ValidationError } from 'wordgard/doc';
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
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';
6
6
  import { PhraseSet, phrases } from 'wordgard/phrases';
7
7
  import { history } from 'wordgard/history';
8
8
 
9
+ function eqArray(a, b) {
10
+ if (!a || !b)
11
+ return a == b;
12
+ if (a == b)
13
+ return true;
14
+ if (a.length != b.length)
15
+ return false;
16
+ for (let i = 0; i < a.length; i++)
17
+ if (!a[i].eq(b[i]))
18
+ return false;
19
+ return true;
20
+ }
21
+ const exceptionSink = /*@__PURE__*/GardState.Facet.define();
22
+ function logException(state, exception, context) {
23
+ let handler = state.facet(exceptionSink);
24
+ if (handler.length)
25
+ handler[0](exception);
26
+ else if (window.onerror)
27
+ window.onerror(String(exception), context, undefined, undefined, exception);
28
+ else if (context)
29
+ console.error(context + ":", exception);
30
+ else
31
+ console.error(exception);
32
+ }
33
+ function findAbove(array, start, n) {
34
+ let from = start, to = array.length;
35
+ for (;;) {
36
+ if (from == to)
37
+ return from;
38
+ let mid = (from + to) >> 1;
39
+ if (array[mid] > n)
40
+ to = mid;
41
+ else
42
+ from = mid + 1;
43
+ }
44
+ }
45
+
46
+ function addUpdated(sections, updated) {
47
+ let result = [];
48
+ let j = 0, [uFrom, uTo] = updated.length ? [updated[j++], updated[j++]] : [1e9, 1e9];
49
+ for (let i = 0, pos = 0; i < sections.length;) {
50
+ let len = sections[i++], ins = sections[i++];
51
+ if (ins == -1) {
52
+ let end = pos + len;
53
+ while (uFrom < end) {
54
+ if (uTo > pos) {
55
+ if (uFrom > pos)
56
+ addSection(result, uFrom - pos, -1);
57
+ addSection(result, Math.min(uTo, end) - Math.max(pos, uFrom), -2);
58
+ pos = uTo;
59
+ }
60
+ if (uTo >= end)
61
+ break;
62
+ if (j == updated.length) {
63
+ uFrom = uTo = 1e9;
64
+ break;
65
+ }
66
+ uFrom = updated[j++];
67
+ uTo = updated[j++];
68
+ }
69
+ if (pos < end)
70
+ addSection(result, end - pos, -1);
71
+ pos = end;
72
+ }
73
+ else {
74
+ addSection(result, len, ins);
75
+ pos += len;
76
+ }
77
+ }
78
+ return result;
79
+ }
80
+ function addSection(sections, len, ins) {
81
+ let last = sections.length - 1;
82
+ if (last >= 0) {
83
+ let lastIns = sections[last];
84
+ if (lastIns >= 0 && ins >= 0) {
85
+ sections[last - 1] += len;
86
+ sections[last] += ins;
87
+ return;
88
+ }
89
+ if (lastIns < 0 && lastIns == ins) {
90
+ sections[last - 1] += len;
91
+ return;
92
+ }
93
+ }
94
+ sections.push(len, ins);
95
+ }
96
+ function separateChange(changes, fromB, toB) {
97
+ let result = [];
98
+ let lenI = 0, dLen = 0;
99
+ for (let posB = 0, done = false, i = 0; i < changes.length;) {
100
+ let len = changes[i++], ins = changes[i++], endB = posB + (ins < 0 ? len : ins);
101
+ if (fromB > endB || toB < posB) {
102
+ result.push(len, ins);
103
+ }
104
+ else {
105
+ if (ins >= 0) {
106
+ if (posB < fromB || endB > toB)
107
+ return null;
108
+ dLen = len - ins;
109
+ }
110
+ if (posB < fromB)
111
+ result.push(fromB - posB, ins);
112
+ if (!done) {
113
+ lenI = result.length;
114
+ result.push(0, toB - fromB);
115
+ done = true;
116
+ }
117
+ if (endB > toB)
118
+ result.push(endB - toB, ins);
119
+ }
120
+ posB = endB;
121
+ }
122
+ result[lenI] = (toB - fromB) + dLen;
123
+ return result;
124
+ }
125
+ function isEmpty(changes) {
126
+ return changes.length == 0 || changes.length == 2 && changes[1] == -1;
127
+ }
128
+ function addRange(ranges, from, to) {
129
+ if (!ranges.length || ranges[ranges.length - 1] < from) {
130
+ ranges.push(from, to);
131
+ return;
132
+ }
133
+ let i = findAbove(ranges, 0, from) & -2, j = i;
134
+ if (j && ranges[j - 1] == from) {
135
+ j -= 2;
136
+ from = ranges[j];
137
+ }
138
+ while (i < ranges.length && ranges[i] <= to) {
139
+ from = Math.min(from, ranges[i++]);
140
+ to = Math.max(to, ranges[i++]);
141
+ }
142
+ ranges.splice(j, i - j, from, to);
143
+ }
144
+ function joinRanges(ranges) {
145
+ if (ranges.length == 1)
146
+ return ranges[0];
147
+ let result = [], index = ranges.map(() => 0);
148
+ for (;;) {
149
+ let minI = -1, minFrom = -1;
150
+ for (let i = 0; i < ranges.length; i++) {
151
+ let idx = index[i], set = ranges[i];
152
+ if (idx < set.length && (minI < 0 || set[idx] < minFrom)) {
153
+ minI = i;
154
+ minFrom = set[idx];
155
+ }
156
+ }
157
+ if (minI < 0)
158
+ return result;
159
+ let idx = index[minI], set = ranges[minI];
160
+ addRange(result, set[idx], set[idx + 1]);
161
+ index[minI] += 2;
162
+ }
163
+ }
164
+
9
165
  class Widget {
10
166
  value;
11
167
  constructor(type,
@@ -68,6 +224,18 @@ class Widget {
68
224
  Widget.EditableText = Widget.define({
69
225
  render: s => document.createTextNode(s)
70
226
  });
227
+ Widget.img = Widget.create({
228
+ render() {
229
+ let img = document.createElement("img");
230
+ img.className = "wg-buffer";
231
+ return img;
232
+ },
233
+ editable: true
234
+ });
235
+ Widget.br = Widget.create({
236
+ render() { return document.createElement("br"); },
237
+ editable: true
238
+ });
71
239
  ;return Widget})(Widget);
72
240
  const Decoration = /*@__PURE__*/(function (Decoration) {
73
241
  (function (Tag) {
@@ -229,6 +397,15 @@ const baseTagShape = /*@__PURE__*/memo((tag) => {
229
397
  return addMarkAttributes(tag.is(Leaf.Text) ? Widget.EditableText.of(tag.param)
230
398
  : tag.type.shape.create(tag.param), tag);
231
399
  });
400
+ function renderMarks(marks, around) {
401
+ let result = addMarkAttributes(Elt.create("span", Attributes.none, [around]), Leaf.text(around, marks));
402
+ for (let i = marks.length - 1; i >= 0; i--) {
403
+ let mark = marks[i];
404
+ if (mark.type.element)
405
+ result = renderMarkWrapper(mark).fill([result]);
406
+ }
407
+ return result.toDOM();
408
+ }
232
409
  class AttributeRangeDecoration extends Decoration.Range {
233
410
  attribute;
234
411
  value;
@@ -328,18 +505,6 @@ function nodeSelection(state) {
328
505
  return PointSet.create([[state.selection.from, nodeSelectionDeco]]);
329
506
  return PointSet.empty;
330
507
  }
331
- function findAbove(array, start, n) {
332
- let from = start, to = array.length;
333
- for (;;) {
334
- if (from == to)
335
- return from;
336
- let mid = (from + to) >> 1;
337
- if (array[mid] > n)
338
- to = mid;
339
- else
340
- from = mid + 1;
341
- }
342
- }
343
508
  const none = [];
344
509
  class PointSet {
345
510
  values;
@@ -351,16 +516,17 @@ class PointSet {
351
516
  this.positions = positions;
352
517
  }
353
518
  get length() { return this.positions.length; }
354
- map(changes) {
519
+ get size() { return this.positions.length ? this.positions[this.positions.length - 1] : 0; }
520
+ map(changes, start = 0) {
355
521
  if (changes.empty)
356
522
  return this;
357
523
  let positions = this.positions.slice();
358
- let pos = 0, i = 0;
524
+ let pos = start, i = 0, startB = start && changes.mapPos(start, -1);
359
525
  let deleted = [], deletions = 0;
360
- changes.iterGaps((fromA, toA, fromB) => {
361
- let off = fromB - fromA, end = toA - 1;
526
+ changes.iterGaps((fromA, toA, fromB, _toB, last) => {
527
+ let off = fromB - fromA, end = last ? toA : toA - 1;
362
528
  if (end > pos) {
363
- let nextI = findAbove(positions, i, end);
529
+ let nextI = findAbove(positions, i, end - start);
364
530
  if (off)
365
531
  for (; i < nextI; i++)
366
532
  positions[i] += off;
@@ -369,15 +535,15 @@ class PointSet {
369
535
  pos = end;
370
536
  }
371
537
  }, (_fromA, toA) => {
372
- let nextI = findAbove(positions, i, toA + 1);
538
+ let nextI = findAbove(positions, i, toA + 1 + start);
373
539
  for (; i < nextI; i++) {
374
- let mapped = changes.mapPos(positions[i], this.values[i].side < 0 ? -1 : 1, this.values[i].trackMode);
540
+ let mapped = changes.mapPos(positions[i] + start, this.values[i].side < 0 ? -1 : 1, this.values[i].trackMode);
375
541
  if (mapped == null) {
376
542
  addDel(deleted, i);
377
543
  deletions++;
378
544
  }
379
545
  else
380
- positions[i] = mapped;
546
+ positions[i] = mapped - startB;
381
547
  }
382
548
  pos = toA + 1;
383
549
  });
@@ -385,22 +551,22 @@ class PointSet {
385
551
  return new PointSet(this.values, positions);
386
552
  return new PointSet(applyDel(deleted, deletions, this.values), applyDel(deleted, deletions, positions));
387
553
  }
388
- merge(other) {
554
+ merge(other, maskFrom, maskTo = maskFrom) {
389
555
  if (!this.length)
390
556
  return other;
391
- if (!other.length)
557
+ if (!other.length && maskFrom == null)
392
558
  return this;
393
559
  let posA = this.positions, posB = other.positions;
394
- let pos = new Array(posA.length, posB.length), values = new Array(pos.length);
560
+ let pos = new Array((maskFrom == null ? posA.length : 0) + posB.length), values = new Array(pos.length);
395
561
  for (let i = 0, a = 0, b = 0;;) {
396
- let nextA = a < posA.length ? posA[a] : 1e9;
397
- let nextB = b < posB.length ? posB[b] : 1e9;
398
- let cmp = nextA - nextB || this.values[a].side - other.values[b].side;
399
- if (cmp < 0) {
400
- pos[i] = posA[a];
401
- values[i++] = this.values[a++];
402
- }
403
- else if (nextB < 1e9) {
562
+ if (a < posA.length && (b == posB.length || (posA[a] - posB[b] || this.values[a].side - other.values[b].side) < 0)) {
563
+ if (maskFrom == null || maskFrom > posA[a] || maskTo < posA[a]) {
564
+ pos[i] = posA[a];
565
+ values[i++] = this.values[a];
566
+ }
567
+ a++;
568
+ }
569
+ else if (b < posB.length) {
404
570
  pos[i] = posB[b];
405
571
  values[i++] = other.values[b++];
406
572
  }
@@ -456,9 +622,8 @@ class PointSet {
456
622
  for (let i = positions.length;;) {
457
623
  positions[i] = positions[i - 1];
458
624
  values[i] = values[i - 1];
459
- if (--i < 0)
460
- break;
461
- if (!i-- || (positions[i] - pos || values[i].side - value.side) <= 0) {
625
+ --i;
626
+ if (!i || (positions[i - 1] - pos || values[i - 1].side - value.side) <= 0) {
462
627
  positions[i] = pos;
463
628
  values[i] = value;
464
629
  break;
@@ -483,14 +648,15 @@ class PointIterator {
483
648
  this.set = set;
484
649
  this.fill(0);
485
650
  }
651
+ get to() { return this.from; }
486
652
  fill(i) {
487
653
  this.i = i;
488
654
  if (i < this.set.positions.length) {
489
- this.pos = this.set.positions[i];
655
+ this.from = this.set.positions[i];
490
656
  this.value = this.set.values[i];
491
657
  }
492
658
  else {
493
- this.pos = 1e8;
659
+ this.from = 1e8;
494
660
  this.value = null;
495
661
  this.done = true;
496
662
  }
@@ -552,16 +718,17 @@ class RangeSet {
552
718
  this.to = to;
553
719
  }
554
720
  get length() { return this.from.length; }
555
- map(changes) {
721
+ get size() { return this.to.length ? this.to[this.to.length - 1] : 0; }
722
+ map(changes, start = 0) {
556
723
  if (changes.empty || !this.length)
557
724
  return this;
558
725
  let from = this.from.slice(), to = this.to.slice();
559
- let pos = 0, i = 0;
726
+ let pos = start, i = 0, startB = start && changes.mapPos(start, -1);
560
727
  let deleted = [], deletions = 0;
561
- changes.iterGaps((fromA, toA, fromB) => {
562
- let off = fromB - fromA, end = toA - 1;
728
+ changes.iterGaps((fromA, toA, fromB, _toB, last) => {
729
+ let off = fromB - fromA, end = last ? toA : toA - 1;
563
730
  if (end > pos) {
564
- let nextI = findAbove(from, i, end);
731
+ let nextI = findAbove(to, i, end - start);
565
732
  if (off)
566
733
  for (; i < nextI; i++) {
567
734
  from[i] += off;
@@ -572,18 +739,18 @@ class RangeSet {
572
739
  pos = end;
573
740
  }
574
741
  }, (_fromA, toA) => {
575
- let nextI = findAbove(to, i, toA + 1);
742
+ let nextI = findAbove(from, i, toA - start);
576
743
  for (; i < nextI; i++) {
577
744
  let value = this.values[i];
578
- let mappedFrom = changes.mapPos(from[i], value.inclusiveStart ? -1 : 1);
579
- let mappedTo = changes.mapPos(to[i], value.inclusiveEnd ? 1 : -1);
745
+ let mappedFrom = changes.mapPos(from[i] + start, value.inclusiveStart ? -1 : 1);
746
+ let mappedTo = changes.mapPos(to[i] + start, value.inclusiveEnd ? 1 : -1);
580
747
  if (mappedFrom >= mappedTo) {
581
748
  addDel(deleted, i);
582
749
  deletions++;
583
750
  }
584
751
  else {
585
- from[i] = mappedFrom;
586
- to[i] = mappedTo;
752
+ from[i] = mappedFrom - startB;
753
+ to[i] = mappedTo - startB;
587
754
  }
588
755
  }
589
756
  pos = toA + 1;
@@ -592,6 +759,35 @@ class RangeSet {
592
759
  return new RangeSet(this.values, from, to);
593
760
  return new RangeSet(applyDel(deleted, deletions, this.values), applyDel(deleted, deletions, from), applyDel(deleted, deletions, to));
594
761
  }
762
+ merge(other, maskFrom, maskTo = maskFrom) {
763
+ if (!this.length)
764
+ return other;
765
+ if (!other.length && maskFrom == null)
766
+ return this;
767
+ let fromA = this.from, fromB = other.from;
768
+ let from = new Array((maskFrom == null ? fromA.length : 0) + fromB.length);
769
+ let to = new Array(from.length), values = new Array(from.length);
770
+ for (let i = 0, a = 0, b = 0, at = 0;;) {
771
+ if (a < fromA.length && (b == fromB.length || fromA[a] < fromB[b])) {
772
+ if (maskFrom == null || maskFrom >= this.to[a] || maskTo <= this.from[a]) {
773
+ if ((from[i] = fromA[a]) < at)
774
+ throw new Error("Overlapping ranges");
775
+ at = to[i] = this.to[a];
776
+ values[i++] = this.values[a];
777
+ }
778
+ a++;
779
+ }
780
+ else if (b < fromB.length) {
781
+ if ((from[i] = fromB[b]) < at)
782
+ throw new Error("Overlapping ranges");
783
+ at = to[i] = other.to[b];
784
+ values[i++] = other.values[b++];
785
+ }
786
+ else {
787
+ return new RangeSet(values, from, to);
788
+ }
789
+ }
790
+ }
595
791
  iter() {
596
792
  return new RangeIterator(this);
597
793
  }
@@ -639,6 +835,7 @@ class RangeSet {
639
835
  throw new Error("Ranges must be added in order and cannot overlap");
640
836
  from.push(f);
641
837
  to.push(t);
838
+ curPos = t;
642
839
  values.push(value);
643
840
  });
644
841
  return new RangeSet(values, from, to);
@@ -674,31 +871,87 @@ class RangeIterator {
674
871
  this.fill(findAbove(this.set.to, 0, pos));
675
872
  }
676
873
  }
677
- function addRange(ranges, from, to) {
678
- let last = ranges.length - 1;
679
- if (last < 0 || ranges[last] < from)
680
- ranges.push(from, to);
681
- else
682
- ranges[last] = Math.max(to, ranges[last]);
683
- }
684
- function joinRanges(ranges) {
685
- if (ranges.length == 1)
686
- return ranges[0];
687
- let result = [], index = ranges.map(() => 0);
688
- for (;;) {
689
- let minI = -1, minFrom = -1;
690
- for (let i = 0; i < ranges.length; i++) {
691
- let idx = index[i], set = ranges[i];
692
- if (idx < set.length && (minI < 0 || set[idx] < minFrom)) {
693
- minI = i;
694
- minFrom = set[idx];
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;
695
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++;
696
946
  }
697
- if (minI < 0)
698
- return result;
699
- let idx = index[minI], set = ranges[minI];
700
- addRange(result, set[idx], set[idx + 1]);
701
- index[minI] += 2;
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);
702
955
  }
703
956
  }
704
957
  function compareDecoSet(setA, setB, cmp) {
@@ -738,8 +991,11 @@ function findChangedRanges(prevState, prevDeco, state, deco, sections) {
738
991
  compareDecoSet(prevDeco.points, deco.points, (a, b) => {
739
992
  (a || PointSet.empty).compareRange(posA, b || PointSet.empty, posB, len, (pos, val) => {
740
993
  add(pos, Math.min(pos + (val instanceof WidgetDecoration ? 0 : 1), endB));
741
- if (val instanceof ShapeDecoration && !globalChange)
742
- shapeChanges.push(pos);
994
+ if (val instanceof ShapeDecoration && !globalChange) {
995
+ let idx = findAbove(shapeChanges, 0, pos - 1);
996
+ if (idx == shapeChanges.length || shapeChanges[idx] != pos)
997
+ shapeChanges.splice(idx, 0, pos);
998
+ }
743
999
  });
744
1000
  });
745
1001
  let joined = joinRanges(ranges), pos = posB, end = pos + len, j = 0;
@@ -772,15 +1028,12 @@ function findChangedRanges(prevState, prevDeco, state, deco, sections) {
772
1028
  return addAtomicityChanges(result, prevState, shapeChanges);
773
1029
  return result;
774
1030
  }
775
- function addAtomicityChanges(sections, prev, changes) {
1031
+ function addAtomicityChanges(changes, prev, nodes) {
776
1032
  let added = [];
777
- let scan = prev.doc.resolve(0), last = -1, sectionPos = 0, sectionI = 0, off = 0;
778
- for (let posB of changes.sort()) {
779
- if (posB == last)
780
- continue;
781
- last = posB;
1033
+ let scan = prev.doc.resolve(0), sectionPos = 0, sectionI = 0, off = 0;
1034
+ for (let posB of nodes) {
782
1035
  while (posB >= sectionPos) {
783
- let len = sections[sectionI++], ins = sections[sectionI++];
1036
+ let len = changes[sectionI++], ins = changes[sectionI++];
784
1037
  if (ins < 0) {
785
1038
  sectionPos += len;
786
1039
  }
@@ -797,35 +1050,7 @@ function addAtomicityChanges(sections, prev, changes) {
797
1050
  continue;
798
1051
  added.push(posA, posA + node.length);
799
1052
  }
800
- if (!added.length)
801
- return sections;
802
- let changedSections = [], pos = 0;
803
- for (let i = 0; i < added.length;) {
804
- let from = added[i++], to = added[i++];
805
- if (from > pos)
806
- changedSections.push(from - pos, -1);
807
- changedSections.push(to - from, to - from);
808
- pos = to;
809
- }
810
- if (pos < prev.doc.length)
811
- changedSections.push(prev.doc.length - pos, -1);
812
- return ChangeSet.composeSections(changedSections, sections);
813
- }
814
- function addSection(sections, len, ins) {
815
- let last = sections.length - 1;
816
- if (last >= 0) {
817
- let lastIns = sections[last];
818
- if (lastIns >= 0 && ins >= 0) {
819
- sections[last - 1] += len;
820
- sections[last] += ins;
821
- return;
822
- }
823
- if (lastIns < 0 && lastIns == ins) {
824
- sections[last - 1] += len;
825
- return;
826
- }
827
- }
828
- sections.push(len, ins);
1053
+ return added.length ? addUpdated(changes, added) : changes;
829
1054
  }
830
1055
  class HeapIterator {
831
1056
  rangeHeap;
@@ -863,7 +1088,7 @@ class HeapIterator {
863
1088
  ? [rangeHeap[0].from, rangeHeap[0].value.inclusiveStart ? -1 : 1]
864
1089
  : [1e9, 0];
865
1090
  let [endPos, endSide] = active.length ? [active[0].to, active[0].value.inclusiveEnd ? 1 : -1] : [1e9, 0];
866
- let { pos: pointPos, side: pointSide } = pointHeap.length ? pointHeap[0] : { pos: 1e9, side: 1 };
1091
+ let { from: pointPos, side: pointSide } = pointHeap.length ? pointHeap[0] : { from: 1e9, side: 1 };
867
1092
  let nextPos = Math.min(startPos, endPos, pointPos);
868
1093
  if (this.to == this.end && nextPos > this.to) {
869
1094
  this.done = true;
@@ -940,7 +1165,7 @@ function cmpRangeTo(a, b) {
940
1165
  return a.to - b.to || cmpBool(a.value.inclusiveEnd, b.value.inclusiveEnd);
941
1166
  }
942
1167
  function cmpPoint(a, b) {
943
- return a.pos - b.pos || a.side - b.side;
1168
+ return a.from - b.from || a.side - b.side;
944
1169
  }
945
1170
  function nodeWrappers(schema, tag, active, atom) {
946
1171
  let wrappers;
@@ -1009,6 +1234,8 @@ class DecoIterator {
1009
1234
  }
1010
1235
  }
1011
1236
  widgets(tag, place, walker) {
1237
+ if (place == 2 && tag.type.isInline)
1238
+ walker.widget(Widget.img, -1);
1012
1239
  for (let src of this.globalWidgets) {
1013
1240
  if (src.place == place && tag.type == src.type) {
1014
1241
  let widget = typeof src.widget == "function" ? src.widget(tag) : src.widget;
@@ -1016,6 +1243,8 @@ class DecoIterator {
1016
1243
  walker.widget(widget, place == 0 || place == 3 ? 1 : -1);
1017
1244
  }
1018
1245
  }
1246
+ if (place == 3 && tag.type.isInline)
1247
+ walker.widget(Widget.img, 1);
1019
1248
  }
1020
1249
  hasEndWidget(type) {
1021
1250
  return this.globalWidgets.some(tw => tw.type == type &&
@@ -1185,31 +1414,6 @@ function compareSetPrec(setA, setB, array) {
1185
1414
  return 0;
1186
1415
  }
1187
1416
 
1188
- function eqArray(a, b) {
1189
- if (!a || !b)
1190
- return a == b;
1191
- if (a == b)
1192
- return true;
1193
- if (a.length != b.length)
1194
- return false;
1195
- for (let i = 0; i < a.length; i++)
1196
- if (!a[i].eq(b[i]))
1197
- return false;
1198
- return true;
1199
- }
1200
- const exceptionSink = /*@__PURE__*/GardState.Facet.define();
1201
- function logException(state, exception, context) {
1202
- let handler = state.facet(exceptionSink);
1203
- if (handler.length)
1204
- handler[0](exception);
1205
- else if (window.onerror)
1206
- window.onerror(String(exception), context, undefined, undefined, exception);
1207
- else if (context)
1208
- console.error(context + ":", exception);
1209
- else
1210
- console.error(exception);
1211
- }
1212
-
1213
1417
  function getSelection(root) {
1214
1418
  let target;
1215
1419
  if (root.nodeType == 11) { target = root.getSelection ? root : root.ownerDocument;
@@ -1248,7 +1452,7 @@ function rmDOM(dom) {
1248
1452
  function isBlockElement(node) {
1249
1453
  let tile = node.wgTile;
1250
1454
  if (tile?.node)
1251
- return tile.node.type.isBlock;
1455
+ return tile.node.isBlock;
1252
1456
  return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
1253
1457
  }
1254
1458
  function isBlocking(node) {
@@ -1588,6 +1792,12 @@ class Tile {
1588
1792
  let last = this.children.length - 1;
1589
1793
  return last < 0 ? null : this.children[last];
1590
1794
  }
1795
+ get nodeParent() {
1796
+ let tile = this;
1797
+ while (!tile.node)
1798
+ tile = tile.parent;
1799
+ return tile;
1800
+ }
1591
1801
  ignoreEvent(event) { return false; }
1592
1802
  get ignoreMutations() { return false; }
1593
1803
  toString() { return this.dom.nodeName + (this.children.length ? `(${this.children})` : ""); }
@@ -1664,7 +1874,7 @@ class CompositeTile extends Tile {
1664
1874
  orientation = node.type.orientation == "row" ? 0 : 1;
1665
1875
  if (node.isTextblock)
1666
1876
  textblock = TextblockMap.get(state, start, node);
1667
- else if (node.type.isBlock)
1877
+ else if (node.isBlock)
1668
1878
  textblock = null;
1669
1879
  }
1670
1880
  else if (node && node.isText) {
@@ -1684,7 +1894,7 @@ class CompositeTile extends Tile {
1684
1894
  posAtCoordsRow(start, state, x, y, textblock) {
1685
1895
  let result = rowScan(x, y, add => {
1686
1896
  for (let child of this.children) {
1687
- if (child.isPoint)
1897
+ if (child instanceof WidgetTile && !child.widget.type.inFlow)
1688
1898
  continue;
1689
1899
  let rects, { dom } = child;
1690
1900
  if (dom.nodeType == 1)
@@ -1702,6 +1912,14 @@ class CompositeTile extends Tile {
1702
1912
  return null;
1703
1913
  let { closest, rect } = result;
1704
1914
  let pos = this.posBeforeChild(closest, start);
1915
+ if (closest.dom.nodeName == "BR")
1916
+ return CoordPos.create(pos, 1);
1917
+ if (closest.node && closest.node.isPlot && closest.node.isInline) {
1918
+ if (x > rect.right)
1919
+ return CoordPos.create(pos + closest.length, -1);
1920
+ if (x < rect.left)
1921
+ return CoordPos.create(pos, 1);
1922
+ }
1705
1923
  return closest.posAtCoordsInner(pos + closest.boundary, state, x, Math.max(rect.top, Math.min(rect.bottom, y)), textblock, 0);
1706
1924
  }
1707
1925
  posAtCoordsCol(start, state, x, y, textblock) {
@@ -1786,29 +2004,29 @@ class DocTile extends CompositeTile {
1786
2004
  let changed = findChangedRanges(this.state, this.decoSet, state, decoSet, changes);
1787
2005
  return this.updateRanges(state, decoSet, changed, wg, composition);
1788
2006
  }
1789
- updateRanges(state, decoSet, sections, wg, composition) {
2007
+ updateRanges(state, decoSet, changes, wg, composition) {
1790
2008
  let wrapper = composition?.wrapCursor || null;
1791
- if ((!sections.length || sections.length == 2 && sections[1] == -1) && eqArray(wrapper, this.cursorWrapper))
2009
+ if (isEmpty(changes) && eqArray(wrapper, this.cursorWrapper))
1792
2010
  return this;
1793
2011
  if (composition) {
1794
- let separated = separateComposition(sections, composition);
2012
+ let separated = separateChange(changes, composition.fromB, composition.toB);
1795
2013
  if (!separated)
1796
2014
  composition = null;
1797
2015
  else
1798
- sections = separated;
2016
+ changes = separated;
1799
2017
  }
1800
2018
  let builder = new ContentUpdate(state, this, wg, new DecoIterator(state, decoSet), wrapper);
1801
- for (let i = 0, posB = 0, startCovered = false; i < sections.length;) {
1802
- let len = sections[i++], ins = sections[i++];
2019
+ for (let i = 0, posB = 0, startCovered = false; i < changes.length;) {
2020
+ let len = changes[i++], ins = changes[i++];
1803
2021
  if (composition && posB == composition.fromB && ins >= 0) {
1804
2022
  if (!startCovered)
1805
2023
  builder.update(0, false);
1806
2024
  builder.composition(composition, len);
1807
- if (ins && (startCovered = i == sections.length || sections[i + 1] == -1))
2025
+ if (ins && (startCovered = i == changes.length || changes[i + 1] == -1))
1808
2026
  builder.update(0, false);
1809
2027
  }
1810
2028
  else if (ins == -1) {
1811
- builder.keep(len, !startCovered, i == sections.length);
2029
+ builder.keep(len, !startCovered, i == changes.length);
1812
2030
  startCovered = false;
1813
2031
  }
1814
2032
  else if (ins == -2) {
@@ -1877,7 +2095,8 @@ class DocTile extends CompositeTile {
1877
2095
  else if (pos == end)
1878
2096
  i = j + 1;
1879
2097
  }
1880
- if (ch.isPlotContent && !ch.boundary ? pos >= off && pos <= end : pos > off && pos < end) {
2098
+ if (!ch.isPoint &&
2099
+ ((ch.isPlotContent || ch.isNodeInner) && !ch.boundary ? pos >= off && pos <= end : pos > off && pos < end)) {
1881
2100
  if (ch instanceof TextTile)
1882
2101
  return new TilePos(ch, pos - off, pos);
1883
2102
  else if (ch.isAtom) {
@@ -1986,6 +2205,8 @@ class DocTile extends CompositeTile {
1986
2205
  dom = dom.parentNode;
1987
2206
  domBefore = dom.previousSibling;
1988
2207
  }
2208
+ if (elt.node && elt.node.isInline && elt.node.isPlot && (!domBefore || !domBefore.nextSibling))
2209
+ return domBefore ? elt.posAfter : elt.posBefore;
1989
2210
  while (domBefore && !((eltBefore = domBefore.wgTile) && eltBefore.parent == elt))
1990
2211
  domBefore = domBefore.previousSibling;
1991
2212
  return domBefore ? elt.posBeforeChild(eltBefore) + eltBefore.length : elt.posAtStart;
@@ -2180,7 +2401,7 @@ class TilePointer {
2180
2401
  }
2181
2402
  if (!dist && next.isNodeInner && !nodeBoundary)
2182
2403
  break;
2183
- if (next.length <= dist) {
2404
+ if (next.length < dist || next.length == dist && (next.isPoint || next.boundary)) {
2184
2405
  if (walker)
2185
2406
  walker.skip(next, 0, next.length);
2186
2407
  dist -= next.length;
@@ -2416,7 +2637,7 @@ class ContentUpdate {
2416
2637
  if (mark.type.element) {
2417
2638
  this.openWrapper(renderMarkWrapper(mark), mark.spanning, false);
2418
2639
  }
2419
- this.new.addChild(new WidgetTile(imgHack, null, 16 | 32, imgHack.render(this.wg)));
2640
+ this.new.addChild(new WidgetTile(Widget.img, null, 16 | 32, Widget.img.render(this.wg)));
2420
2641
  return;
2421
2642
  }
2422
2643
  let found = [];
@@ -2610,24 +2831,24 @@ class ContentUpdate {
2610
2831
  return tile;
2611
2832
  }
2612
2833
  }
2613
- ensureBR() {
2834
+ ensureHackNode() {
2614
2835
  let tile = this.new;
2615
2836
  if (!tile.isPlotContent)
2616
2837
  return;
2617
2838
  while (tile.isNodeInner)
2618
2839
  tile = tile.parent;
2619
2840
  let node = tile.node;
2620
- if (!node || !node.isPlot || !node.isTextblock)
2841
+ if (!node || !node.isPlot || !(node.isTextblock || node.isInline))
2621
2842
  return;
2622
2843
  let hasHack = -1, needsHack = true;
2623
2844
  for (let parent = this.new, i = parent.children.length;;) {
2624
2845
  if (i > 0) {
2625
2846
  let next = parent.children[--i];
2626
2847
  if (next.isNodeInner || next instanceof WidgetTile && !next.widget.type.inFlow) ;
2627
- else if (next instanceof WidgetTile && next.widget == brHack && parent == this.new) {
2848
+ else if (next instanceof WidgetTile && next.widget == Widget.br && parent == this.new) {
2628
2849
  hasHack = i;
2629
2850
  }
2630
- else if (next.dom.nodeName == "BR" || next instanceof TextTile && /\n$/.test(next.text)) {
2851
+ else if (next.dom.nodeName == "BR") {
2631
2852
  break;
2632
2853
  }
2633
2854
  else if (next instanceof CompositeTile && !next.isAtom) {
@@ -2652,11 +2873,11 @@ class ContentUpdate {
2652
2873
  this.new.children.splice(hasHack, 1);
2653
2874
  }
2654
2875
  else if (needsHack) {
2655
- this.new.addChild(new WidgetTile(brHack, null, 16 | 64, brHack.render(this.wg)));
2876
+ this.new.addChild(new WidgetTile(Widget.br, null, 16 | 64, Widget.br.render(this.wg)));
2656
2877
  }
2657
2878
  }
2658
2879
  up() {
2659
- this.ensureBR();
2880
+ this.ensureHackNode();
2660
2881
  this.new = this.new.parent;
2661
2882
  }
2662
2883
  leaveNode() {
@@ -2739,7 +2960,7 @@ class ContentUpdate {
2739
2960
  finish() {
2740
2961
  while (!(this.new instanceof DocTile))
2741
2962
  this.up();
2742
- this.ensureBR();
2963
+ this.ensureHackNode();
2743
2964
  return this.new;
2744
2965
  }
2745
2966
  }
@@ -2779,56 +3000,25 @@ function updateAttributes(dom, a, b) {
2779
3000
  }
2780
3001
  return changed;
2781
3002
  }
2782
- const brHack = /*@__PURE__*/Widget.create({
2783
- render() { return document.createElement("br"); },
2784
- editable: true
2785
- });
2786
- const imgHack = /*@__PURE__*/Widget.create({
2787
- render() { return document.createElement("img"); },
2788
- editable: true
2789
- });
2790
- function separateComposition(sections, comp) {
2791
- let result = [], { fromB, toB } = comp;
2792
- let lenI = 0, dLen = 0;
2793
- for (let posB = 0, done = false, i = 0; i < sections.length;) {
2794
- let len = sections[i++], ins = sections[i++], endB = posB + (ins < 0 ? len : ins);
2795
- if (fromB > endB || toB < posB) {
2796
- result.push(len, ins);
2797
- }
2798
- else {
2799
- if (ins >= 0) {
2800
- if (posB < fromB || endB > toB)
2801
- return null;
2802
- dLen = len - ins;
2803
- }
2804
- if (posB < fromB)
2805
- result.push(fromB - posB, ins);
2806
- if (!done) {
2807
- lenI = result.length;
2808
- result.push(0, comp.text.length);
2809
- done = true;
2810
- }
2811
- if (endB > toB)
2812
- result.push(endB - toB, ins);
2813
- }
2814
- posB = endB;
3003
+
3004
+ class Coords {
3005
+ ref;
3006
+ rect;
3007
+ constructor(ref, rect) {
3008
+ this.ref = ref;
3009
+ this.rect = rect;
2815
3010
  }
2816
- result[lenI] = comp.text.length + dLen;
2817
- return result;
2818
3011
  }
2819
-
2820
3012
  function coordsAtPos(wg, pos, assoc) {
2821
3013
  let { offset, tile, pos: tilePos } = wg.docTile.resolve(pos, assoc);
2822
3014
  if (tile instanceof TextTile) {
2823
- let node = tile.dom, len = node.nodeValue.length;
2824
- if (!len)
2825
- return singleRect(textRange(node, 0, 0), 1);
2826
- let from = offset, to = offset, side = assoc < 0 && from || from == len ? 1 : -1;
3015
+ let from = offset, to = offset;
3016
+ let side = from == 0 ? -1 : from == tile.length ? 1 : -assoc;
2827
3017
  if (side < 0)
2828
3018
  to++;
2829
3019
  else
2830
3020
  from--;
2831
- return flattenV(singleRect(textRange(node, from, to), side, true), (side < 0) == ltrAt(wg.state, pos, assoc));
3021
+ return new Coords(tile, flattenV(singleRect(textRange(tile.dom, from, to), side, true), (side < 0) == ltrAt(wg.state, pos, assoc)));
2832
3022
  }
2833
3023
  let tagTile = tile;
2834
3024
  while (!tagTile.node)
@@ -2839,10 +3029,10 @@ function coordsAtPos(wg, pos, assoc) {
2839
3029
  if (tile.widget.type.inFlow) {
2840
3030
  let rect = singleRect(tile.dom, after ? 1 : -1);
2841
3031
  if (rect.width || rect.height)
2842
- return horizontal ? flattenH(rect, !after) : flattenV(rect, ltrAt(wg.state, pos, 1) == !after);
3032
+ return new Coords(tile, horizontal ? flattenH(rect, !after) : flattenV(rect, ltrAt(wg.state, pos, 1) == !after));
2843
3033
  }
2844
3034
  if (!tile.parent)
2845
- return new DOMRect;
3035
+ return new Coords(tile, new DOMRect);
2846
3036
  offset = tile.parent.children.indexOf(tile) + (after ? 1 : 0);
2847
3037
  tile = tile.parent;
2848
3038
  assoc = after ? 1 : -1;
@@ -2852,9 +3042,11 @@ function coordsAtPos(wg, pos, assoc) {
2852
3042
  let before = tile.children[i - 1];
2853
3043
  if (before instanceof WidgetTile && !before.widget.type.inFlow)
2854
3044
  continue;
3045
+ if (before.dom.nodeName == "BR")
3046
+ break;
2855
3047
  let rect = singleRect(before.dom, 1);
2856
3048
  if (rect.width || rect.height)
2857
- return horizontal ? flattenH(rect, false) : flattenV(rect, !ltrAt(wg.state, pos, 1));
3049
+ return new Coords(before, horizontal ? flattenH(rect, false) : flattenV(rect, !ltrAt(wg.state, pos, 1)));
2858
3050
  }
2859
3051
  }
2860
3052
  else { for (let i = offset; i < tile.children.length; i++) {
@@ -2863,12 +3055,12 @@ function coordsAtPos(wg, pos, assoc) {
2863
3055
  continue;
2864
3056
  let rect = singleRect(after.dom, -1);
2865
3057
  if (rect.width || rect.height)
2866
- return horizontal ? flattenH(rect, true) : flattenV(rect, ltrAt(wg.state, pos, 1));
3058
+ return new Coords(after, horizontal ? flattenH(rect, true) : flattenV(rect, ltrAt(wg.state, pos, 1)));
2867
3059
  }
2868
3060
  }
2869
3061
  }
2870
3062
  let rect = singleRect(tile.dom, -assoc);
2871
- return horizontal ? flattenH(rect, assoc < 0) : flattenV(rect, assoc < 0);
3063
+ return new Coords(tile, horizontal ? flattenH(rect, assoc < 0) : flattenV(rect, assoc < 0));
2872
3064
  }
2873
3065
  function flattenV(rect, left) {
2874
3066
  return rect.width ? new DOMRect(left ? rect.left : rect.right, rect.top, 0, rect.height) : rect;
@@ -3141,6 +3333,13 @@ const baseStyles = /*@__PURE__*/buildTheme("." + styleID, {
3141
3333
  borderLeft: "1.8px solid currentColor",
3142
3334
  marginLeft: "-0.9px",
3143
3335
  },
3336
+ ".wg-cursor-v.wg-cursor-bold": {
3337
+ borderLeft: "2.4px solid currentColor",
3338
+ marginLeft: "-1.2px",
3339
+ },
3340
+ ".wg-cursor-v.wg-cursor-italic": {
3341
+ transform: "rotate(10deg)"
3342
+ },
3144
3343
  ".wg-cursor-h": {
3145
3344
  borderTop: "1.8px solid currentColor",
3146
3345
  marginTop: "-0.9px",
@@ -3151,6 +3350,10 @@ const baseStyles = /*@__PURE__*/buildTheme("." + styleID, {
3151
3350
  backgroundColor: "transparent"
3152
3351
  }
3153
3352
  },
3353
+ ".wg-buffer": {
3354
+ verticalAlign: "bottom",
3355
+ height: "1em",
3356
+ },
3154
3357
  "wg-placeholder": {
3155
3358
  opacity: "0.6",
3156
3359
  display: "inline-block",
@@ -3256,6 +3459,32 @@ function readDOMSelection(wg, range) {
3256
3459
  : wg.posAtDOM(range.focusNode, range.focusOffset);
3257
3460
  return GardSelection.range(anchor, head);
3258
3461
  }
3462
+ function selectionFromTouch(event, wg) {
3463
+ let pos = wg.posAtCoords({ x: event.touches[0].clientX, y: event.touches[0].clientY });
3464
+ if (pos.target != null) {
3465
+ let target = wg.state.doc.nodeAt(pos.target);
3466
+ if (target && target.type.isSelectable && wg.state.isAtom(target.type))
3467
+ return GardSelection.node(pos.target, target);
3468
+ }
3469
+ return GardSelection.near(wg.state, pos.pos, pos.side);
3470
+ }
3471
+ function rangeForClick(wg, pos, type) {
3472
+ if (type < 3 && pos.target != null) {
3473
+ let target = wg.state.doc.nodeAt(pos.target);
3474
+ if (target && target.type.isSelectable && wg.state.isAtom(target.type))
3475
+ return GardSelection.node(pos.target, target);
3476
+ }
3477
+ if (type == 1) { return GardSelection.near(wg.state, pos.pos, pos.side || -1);
3478
+ }
3479
+ else if (type == 2) { return wg.state.wordAt(pos.pos, pos.side || 1);
3480
+ }
3481
+ else { let cx = wg.state.doc.resolve(pos.pos), block = cx.textblockParent;
3482
+ if (block)
3483
+ return GardSelection.range(block.start, block.end);
3484
+ else
3485
+ return GardSelection.near(wg.state, pos.pos, pos.side || -1);
3486
+ }
3487
+ }
3259
3488
  const Y_STEP = 5;
3260
3489
  function moveVertically(wg, start, forward, distance = 0, selectNode = false) {
3261
3490
  let editorRect = wg.contentDOM.getBoundingClientRect();
@@ -3478,13 +3707,21 @@ class DOMObserver {
3478
3707
  this.wg.scheduleFlush();
3479
3708
  }
3480
3709
  pollSelection() {
3710
+ let { wg } = this;
3481
3711
  if (this.selectionChanged &&
3482
- (this.wg.hasFocus || !this.wg.focusable) && hasSelection(this.wg.contentDOM, this.selectionRange)) {
3712
+ (wg.hasFocus || !wg.focusable) && hasSelection(wg.contentDOM, this.selectionRange)) {
3483
3713
  this.selectionChanged = false;
3484
- let sel = readDOMSelection(this.wg, this.selectionRange);
3485
- if (!sel.eqPos(this.wg.state.selection)) {
3486
- let userEvent = this.wg.inputState.lastTouchTime > Date.now() - 100 ? "select.pointer" : "select";
3487
- this.wg.dispatch({ selection: sel, userEvent });
3714
+ let fromTouch = wg.inputState.lastTouchTime > Date.now() - 100;
3715
+ let sel = readDOMSelection(wg, this.selectionRange);
3716
+ if (!sel.eqPos(wg.state.selection)) {
3717
+ let userEvent = "select";
3718
+ if (fromTouch) {
3719
+ userEvent = "select.pointer";
3720
+ let event = wg.inputState.lastTouchEvent;
3721
+ if (event.touches.length == 1 && sel.isCursor)
3722
+ sel = selectionFromTouch(event, wg);
3723
+ }
3724
+ wg.dispatch({ selection: sel, userEvent });
3488
3725
  }
3489
3726
  }
3490
3727
  }
@@ -3533,11 +3770,7 @@ class DOMObserver {
3533
3770
  return records;
3534
3771
  }
3535
3772
  addDirtyRange(from, to) {
3536
- let sections = from ? [from, -1] : [], len = this.wg.flushedState.doc.length;
3537
- sections.push(to - from, -2);
3538
- if (to < len)
3539
- sections.push(len - to, -1);
3540
- this.dirty = this.dirty ? ChangeSet.composeSections(this.dirty, sections) : sections;
3773
+ addRange(this.dirty || (this.dirty = []), from, to);
3541
3774
  }
3542
3775
  processRecords(records) {
3543
3776
  for (let record of records) {
@@ -3853,6 +4086,7 @@ class InputState {
3853
4086
  lastKeyCode = 0;
3854
4087
  lastKeyTime = 0;
3855
4088
  lastTouchTime = 0;
4089
+ lastTouchEvent = null;
3856
4090
  lastScrollTop = 0;
3857
4091
  lastScrollLeft = 0;
3858
4092
  lastContextMenu = 0;
@@ -3963,7 +4197,7 @@ class InputState {
3963
4197
  let inText = node.nodeType == 3;
3964
4198
  let ref = this.wg.docTile.posFromDOM(node, inText ? 0 : offset);
3965
4199
  let dir = -1;
3966
- let textBefore = textNodeBefore(node.parentNode, domIndex(node));
4200
+ let textBefore = node.parentNode && textNodeBefore(node.parentNode, domIndex(node));
3967
4201
  let prev = textBefore && Tile.get(textBefore);
3968
4202
  if (prev instanceof TextTile && prev.length < prev.dom.nodeValue.length)
3969
4203
  dir = 1;
@@ -3996,7 +4230,7 @@ class InputState {
3996
4230
  }
3997
4231
  let command = inputTypeCommands[type];
3998
4232
  if ((type == "deleteContentBackward" || type == "deleteContentForward") && range &&
3999
- (sel.empty
4233
+ range.from != range.to && (sel.empty
4000
4234
  ? !isSingleChar(this.domDoc, data.domRange.from, data.domRange.to) ||
4001
4235
  sel.head != (type == "deleteContentBackward" ? range.to : range.from)
4002
4236
  : sel.from != range.from || sel.to != range.to)) {
@@ -4081,6 +4315,10 @@ class InputState {
4081
4315
  : prev == after ? after : before;
4082
4316
  }
4083
4317
  }
4318
+ recordTouch(e) {
4319
+ this.lastTouchTime = Date.now();
4320
+ this.lastTouchEvent = e;
4321
+ }
4084
4322
  connect() {
4085
4323
  this.ensureHandlers(this.wg.state);
4086
4324
  }
@@ -4280,23 +4518,6 @@ function eventBelongsToEditor(wg, event) {
4280
4518
  function queryPos(wg, event) {
4281
4519
  return wg.posAtCoords({ x: event.clientX, y: event.clientY });
4282
4520
  }
4283
- function rangeForClick(wg, pos, type) {
4284
- if (type < 3 && pos.target != null) {
4285
- let target = wg.state.doc.nodeAt(pos.target);
4286
- if (target && target.type.isSelectable && wg.state.isAtom(target.type))
4287
- return GardSelection.node(pos.target, target);
4288
- }
4289
- if (type == 1) { return GardSelection.near(wg.state, pos.pos, pos.side || -1);
4290
- }
4291
- else if (type == 2) { return wg.state.wordAt(pos.pos, pos.side || 1);
4292
- }
4293
- else { let cx = wg.state.doc.resolve(pos.pos), block = cx.textblockParent;
4294
- if (block)
4295
- return GardSelection.range(block.start, block.end);
4296
- else
4297
- return GardSelection.near(wg.state, pos.pos, pos.side || -1);
4298
- }
4299
- }
4300
4521
  function basicMouseSelection(wg, event) {
4301
4522
  let start = queryPos(wg, event), type = event.detail;
4302
4523
  let startSel = wg.state.selection;
@@ -4392,11 +4613,13 @@ function compositionUpdate(wg, event) {
4392
4613
  if (!wg.inputState.composing) {
4393
4614
  wg.inputState.composing = { changes: 0, target: null };
4394
4615
  let wrap = null;
4395
- if (!wg.inputState.composing.changes && !event.data) {
4616
+ if (!event.data) {
4396
4617
  let sel = wg.state.selection, rSel = wg.state.sel;
4397
4618
  if (sel.empty && (sel instanceof GardSelection.Text && sel.marks || !rSel.head.inText && rSel.head.index) &&
4398
4619
  !eqArray(rSel.head.nodeBefore?.tag.marks, rSel.activeMarks))
4399
4620
  wrap = rSel.activeMarks;
4621
+ else if (sel.empty && inlineBoundNear(wg.state.sel.head))
4622
+ wrap = rSel.activeMarks;
4400
4623
  }
4401
4624
  if (wrap)
4402
4625
  try {
@@ -4408,6 +4631,13 @@ function compositionUpdate(wg, event) {
4408
4631
  }
4409
4632
  }
4410
4633
  }
4634
+ function inlineBoundNear(pos) {
4635
+ let { parent, index, inText } = pos;
4636
+ if (inText || !parent.node.inlineContent)
4637
+ return false;
4638
+ return (index ? parent.node.content[index - 1].isPlot : parent.node.isInline) ||
4639
+ (index < parent.node.content.length ? parent.node.content[index].isPlot : parent.node.isInline);
4640
+ }
4411
4641
  function isDeletionInputEvent(type) { return /^delete(Content|Word)/.test(type); }
4412
4642
  const inputTypeCommands = /*@__PURE__*/(() => ({
4413
4643
  historyUndo: undo,
@@ -4584,12 +4814,8 @@ const baseObservers = {
4584
4814
  wg.inputState.lastScrollTop = wg.scrollDOM.scrollTop;
4585
4815
  wg.inputState.lastScrollLeft = wg.scrollDOM.scrollLeft;
4586
4816
  },
4587
- touchstart(wg, e) {
4588
- wg.inputState.lastTouchTime = Date.now();
4589
- },
4590
- touchmove(wg) {
4591
- wg.inputState.lastTouchTime = Date.now();
4592
- },
4817
+ touchstart(wg, e) { wg.inputState.recordTouch(e); },
4818
+ touchmove(wg, e) { wg.inputState.recordTouch(e); },
4593
4819
  focus(wg) {
4594
4820
  if (!wg.scrollDOM.scrollTop && (wg.inputState.lastScrollTop || wg.inputState.lastScrollLeft)) {
4595
4821
  wg.scrollDOM.scrollTop = wg.inputState.lastScrollTop;
@@ -4712,9 +4938,34 @@ class ViewState {
4712
4938
  const cursorBlinkRate = /*@__PURE__*/GardState.Facet.define({
4713
4939
  combine: inputs => inputs.length ? Math.min(...inputs) : 1200
4714
4940
  });
4715
- class cursorLayer {
4941
+ class CursorStyle {
4942
+ height;
4943
+ align;
4944
+ color;
4945
+ bold;
4946
+ italic;
4947
+ constructor(height, align, color, bold, italic) {
4948
+ this.height = height;
4949
+ this.align = align;
4950
+ this.color = color;
4951
+ this.bold = bold;
4952
+ this.italic = italic;
4953
+ }
4954
+ static read(node, height) {
4955
+ let win = node.ownerDocument.defaultView || window;
4956
+ let elt = node.nodeType == 1 ? node : node.parentNode;
4957
+ let style = win.getComputedStyle(elt);
4958
+ return new CursorStyle(height, style.verticalAlign, style.color, +style.fontWeight > 400, style.fontStyle == "italic");
4959
+ }
4960
+ eq(other) {
4961
+ return other && this.height == other.height && this.align == other.align &&
4962
+ this.color == other.color && this.bold == other.bold && this.italic == other.italic;
4963
+ }
4964
+ }
4965
+ class CursorLayer {
4716
4966
  layer;
4717
- pos = null;
4967
+ info = null;
4968
+ cached = null;
4718
4969
  constructor(wg) {
4719
4970
  this.layer = wg.scrollDOM.appendChild(document.createElement("wg-cursor-layer"));
4720
4971
  this.positionCursor = this.positionCursor.bind(this);
@@ -4737,48 +4988,135 @@ class cursorLayer {
4737
4988
  this.layer.remove();
4738
4989
  }
4739
4990
  positionCursor(wg) {
4740
- let pos = cursorPos(wg), cur = this.pos;
4741
- if (!pos ? cur : !cur || cur.left != pos.left || cur.top != pos.top || cur.size != pos.size) {
4742
- this.pos = pos;
4743
- wg.scheduleDOMWrite(() => {
4744
- let cursor = this.layer.firstChild;
4745
- if (!pos) {
4746
- if (cursor)
4747
- cursor.remove();
4748
- }
4749
- else {
4750
- if (!cursor)
4751
- cursor = this.layer.appendChild(document.createElement("wg-cursor"));
4752
- cursor.className = "wg-cursor-" + (pos.horiz ? "h" : "v");
4753
- cursor.style.top = pos.top + "px";
4754
- cursor.style.left = pos.left + "px";
4755
- cursor.style.width = pos.horiz ? pos.size + "px" : "";
4756
- cursor.style.height = pos.horiz ? "" : pos.size + "px";
4757
- }
4758
- });
4759
- }
4991
+ getCursorInfo(wg, this, info => {
4992
+ let cur = this.info;
4993
+ if (!info ? cur : !cur || cur.left != info.left || cur.top != info.top || cur.size != info.size ||
4994
+ (cur.style ? !cur.style.eq(info.style) : info.style)) {
4995
+ this.info = info;
4996
+ wg.scheduleDOMWrite(() => {
4997
+ let cursor = this.layer.firstChild;
4998
+ if (!info) {
4999
+ if (cursor)
5000
+ cursor.remove();
5001
+ }
5002
+ else {
5003
+ if (!cursor)
5004
+ cursor = this.layer.appendChild(document.createElement("wg-cursor"));
5005
+ cursor.className = "wg-cursor-" + (info.horiz ? "h" : "v");
5006
+ cursor.style.top = info.top + "px";
5007
+ cursor.style.left = info.left + "px";
5008
+ cursor.style.width = info.horiz ? info.size + "px" : "";
5009
+ cursor.style.height = info.horiz ? "" : info.size + "px";
5010
+ cursor.style.borderLeftColor = info.style ? info.style.color : "";
5011
+ cursor.classList.toggle("wg-cursor-bold", info.style?.bold ?? false);
5012
+ cursor.classList.toggle("wg-cursor-italic", info.style?.italic ?? false);
5013
+ }
5014
+ });
5015
+ }
5016
+ });
4760
5017
  }
4761
5018
  }
5019
+ function alignOffset(align, height) {
5020
+ if (align == "super")
5021
+ return height * 0.36;
5022
+ if (align == "sub")
5023
+ return height * -0.17;
5024
+ if (align.endsWith("px"))
5025
+ return +align.slice(0, align.length - 2);
5026
+ if (align.endsWith("%"))
5027
+ return height * (+align.slice(0, align.length - 1)) * 100;
5028
+ return 0;
5029
+ }
5030
+ function vertOverlap(a, b) {
5031
+ let margin = a.height / 3;
5032
+ return a.top < b.bottom - margin && a.bottom > b.top + margin;
5033
+ }
4762
5034
  const VertWidth = 30, VertGap = 5;
4763
- function cursorPos(wg) {
5035
+ function getCursorInfo(wg, plugin, cont) {
4764
5036
  let { state } = wg;
4765
5037
  if (!state.selection.isCursor)
4766
- return null;
4767
- let { head, headSide } = state.selection;
4768
- let { left, right, top, bottom } = wg.coordsAtPos(head, headSide);
4769
- let horiz = top == bottom, size = horiz ? right - left : bottom - top;
4770
- if (horiz && size > VertWidth) {
4771
- size = VertWidth;
4772
- if (!wg.state.textLTR)
4773
- left = right - size;
5038
+ return cont(null);
5039
+ let { head, headSide } = state.selection, { sel } = wg.state;
5040
+ let { ref, rect } = coordsAtPos(wg, head, headSide);
5041
+ let doc = wg.contentDOM.getBoundingClientRect();
5042
+ if (!sel.head.parent.node.inlineContent) {
5043
+ let width = Math.min(VertWidth, rect.width), top = rect.top;
4774
5044
  let other = wg.coordsAtPos(head, headSide > 0 ? -1 : 1);
4775
5045
  if (other.top == other.bottom && other.top != top) {
4776
5046
  let move = Math.min(VertGap, Math.abs(other.top - top) / 2);
4777
- top = bottom = top + move * (other.top < top ? -1 : 1);
5047
+ top = top + move * (other.top < top ? -1 : 1);
4778
5048
  }
5049
+ return cont({
5050
+ left: (wg.state.textLTR ? rect.left : rect.right - width) - doc.left,
5051
+ top: top - doc.top,
5052
+ size: width,
5053
+ horiz: true, style: null
5054
+ });
4779
5055
  }
4780
- let doc = wg.contentDOM.getBoundingClientRect();
4781
- return { left: left - doc.left, top: top - doc.top, size, horiz };
5056
+ let finish = (style, vertRect) => {
5057
+ if (style && (!plugin.cached || plugin.cached.style != style))
5058
+ plugin.cached = { style, marks, parent: sel.head.parent.node.tag };
5059
+ let height = style ? style.height : rect.height;
5060
+ let bot = rect.bottom;
5061
+ if (vertRect && vertOverlap(vertRect, rect)) {
5062
+ bot = vertRect.bottom;
5063
+ }
5064
+ else {
5065
+ let win = ref.dom.ownerDocument.defaultView || window;
5066
+ let refAlign = win.getComputedStyle((ref.dom.nodeType == 1 ? ref.dom : ref.dom.parentNode)).verticalAlign;
5067
+ if (style && refAlign != style.align) {
5068
+ bot += alignOffset(refAlign, rect.height) - alignOffset(style.align, style.height);
5069
+ }
5070
+ }
5071
+ cont({
5072
+ left: rect.left - doc.left,
5073
+ top: bot - height - doc.top,
5074
+ size: height,
5075
+ horiz: false, style
5076
+ });
5077
+ };
5078
+ let marks = sel.activeMarks;
5079
+ if (ref instanceof TextTile) {
5080
+ let node = ref.posBefore < head ? state.sel.head.nodeBefore : state.sel.head.nodeAfter;
5081
+ if (node && node.isText && Mark.sameSet(node.marks, marks))
5082
+ return finish(CursorStyle.read(ref.dom, rect.height), rect);
5083
+ }
5084
+ let pos = sel.head.parent.start, foundRect, foundNode;
5085
+ scan: for (let sibling of sel.head.parent.node.content) {
5086
+ if (sibling.isText && Mark.sameSet(sibling.marks, marks)) {
5087
+ let { tile } = wg.docTile.resolve(pos, 1);
5088
+ if (tile instanceof TextTile) {
5089
+ let rects = textRange(tile.dom, 0, tile.length).getClientRects();
5090
+ foundNode = tile.dom;
5091
+ for (let i = 0; i < rects.length; i++) {
5092
+ foundRect = rects[i];
5093
+ if (vertOverlap(foundRect, rect))
5094
+ break scan;
5095
+ }
5096
+ }
5097
+ }
5098
+ pos += sibling.length;
5099
+ }
5100
+ if (foundNode)
5101
+ return finish(CursorStyle.read(foundNode, foundRect.height), foundRect);
5102
+ if (plugin.cached && Mark.sameSet(plugin.cached.marks, marks) && plugin.cached.parent.eq(sel.head.parent.node.tag))
5103
+ return finish(plugin.cached.style);
5104
+ if (!marks.length)
5105
+ return finish(null);
5106
+ let target = sel.head.parent.node.isDoc ? wg.contentDOM : wg.nodeDOM(sel.head.parent.before);
5107
+ wg.scheduleDOMWrite(() => {
5108
+ let temp = renderMarks(marks, "M");
5109
+ temp.style.position = "absolute";
5110
+ wg.observer.ignore(() => target.insertBefore(temp, target.firstChild));
5111
+ wg.scheduleDOMRead(() => {
5112
+ wg.scheduleDOMWrite(() => wg.observer.ignore(() => temp.remove()));
5113
+ let inner = temp;
5114
+ while (inner.firstChild)
5115
+ inner = inner.firstChild;
5116
+ let r = textRange(inner, 0, 1).getClientRects()[0];
5117
+ finish(CursorStyle.read(inner, r.height), r);
5118
+ });
5119
+ });
4782
5120
  }
4783
5121
  function setBlinkRate(state, dom) {
4784
5122
  dom.style.animationDuration = state.facet(cursorBlinkRate) + "ms";
@@ -4971,7 +5309,7 @@ class Wordgard {
4971
5309
  }
4972
5310
  runUpdate(update, domChanges) {
4973
5311
  let composition = this.composing ? getCompositionInfo(this) : null;
4974
- let changes = domChanges ? ChangeSet.composeSections(domChanges, update.changes.sections) : update.changes.sections;
5312
+ let changes = domChanges ? addUpdated(update.changes.sections, domChanges) : update.changes.sections;
4975
5313
  let prevDocTile = this.docTile;
4976
5314
  if (!update.empty) {
4977
5315
  this.updatePlugins(update);
@@ -5132,7 +5470,7 @@ class Wordgard {
5132
5470
  }
5133
5471
  coordsAtPos(pos, assoc = -1) {
5134
5472
  this.ensureFlushed();
5135
- return coordsAtPos(this, pos, assoc);
5473
+ return coordsAtPos(this, pos, assoc).rect;
5136
5474
  }
5137
5475
  coordsForElement(pos) {
5138
5476
  this.ensureFlushed();
@@ -5381,7 +5719,7 @@ function attrsFromFacet(wg, facet, base) {
5381
5719
  Wordgard.Update = Update;
5382
5720
  ;return Wordgard})(Wordgard);
5383
5721
  const editorPlugin = /*@__PURE__*/GardState.Facet.define();
5384
- const cursorPlugin = /*@__PURE__*/Wordgard.Plugin.fromClass(cursorLayer);
5722
+ const cursorPlugin = /*@__PURE__*/Wordgard.Plugin.fromClass(CursorLayer);
5385
5723
  class PluginInstance {
5386
5724
  spec;
5387
5725
  mustUpdate = null;
@@ -5682,6 +6020,7 @@ class BarButton {
5682
6020
  this.item = item;
5683
6021
  this.dom = document.createElement("button");
5684
6022
  this.dom.className = "wg-menu-button";
6023
+ this.dom.type = "button";
5685
6024
  this.dom.tabIndex = -1;
5686
6025
  labelButton(wg, this.dom, item.label);
5687
6026
  if (item.description) {
@@ -5767,6 +6106,7 @@ class BarSubmenu {
5767
6106
  this.item = item;
5768
6107
  this.dom = document.createElement("wg-submenu");
5769
6108
  this.button = this.dom.appendChild(document.createElement("button"));
6109
+ this.button.type = "button";
5770
6110
  this.button.tabIndex = -1;
5771
6111
  this.button.className = "wg-menu-button";
5772
6112
  this.button.setAttribute("aria-haspopup", "true");