@nerd-bible/wordgard 0.5.2-beta2 → 0.5.2-beta4
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 +19 -1
- package/dist/command.js +68 -4
- package/dist/editor.d.ts +4 -2
- package/dist/editor.js +66 -29
- package/package.json +2 -2
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
|
@@ -676,7 +676,7 @@ function joinBlocks(before, after) {
|
|
|
676
676
|
if (atEnd)
|
|
677
677
|
end++;
|
|
678
678
|
else
|
|
679
|
-
tokensAfter.
|
|
679
|
+
tokensAfter.unshift(level.parent.node.tag);
|
|
680
680
|
}
|
|
681
681
|
}
|
|
682
682
|
if (tokensAfter.length || end > posAfter)
|
|
@@ -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
|
|
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
|
|
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
|
@@ -524,10 +524,11 @@ declare abstract class Tile {
|
|
|
524
524
|
ignoreEvent(event: Event): boolean;
|
|
525
525
|
get ignoreMutations(): boolean;
|
|
526
526
|
toString(): string;
|
|
527
|
-
sync(): void;
|
|
527
|
+
abstract sync(): void;
|
|
528
528
|
connect(): void;
|
|
529
529
|
disconnect(reused?: Map<Tile, Reused>): void;
|
|
530
530
|
nearestNode(): Tile;
|
|
531
|
+
markDirty(): void;
|
|
531
532
|
posAtCoords(state: GardState, x: number, y: number): CoordPos;
|
|
532
533
|
abstract posAtCoordsInner(start: number, state: GardState, x: number, y: number, textblock: TextblockMap | null, orientation: Orientation): CoordPos;
|
|
533
534
|
static get(node: DOMNode): Tile | undefined;
|
|
@@ -1421,11 +1422,12 @@ declare namespace KeyBinding {
|
|
|
1421
1422
|
- `Ctrl-e` to {@link command.moveToTextblockSide} (`{dir: "forward"}`)
|
|
1422
1423
|
- `Ctrl-d` to {@link command.deleteUnit} (`"forward"`)
|
|
1423
1424
|
- `Ctrl-h` to {@link command.deleteUnit} (`"backward"`)
|
|
1424
|
-
- `Ctrl-k` to {@link command.
|
|
1425
|
+
- `Ctrl-k` to {@link command.killToLineEnd}
|
|
1425
1426
|
- `Ctrl-Alt-h` to {@link command.deleteWord} (`"backward"`)
|
|
1426
1427
|
- `Ctrl-o` to {@link command.insertLineBreak}
|
|
1427
1428
|
- `Ctrl-t` to {@link command.transposeChars}
|
|
1428
1429
|
- `Ctrl-v` to {@link command.moveByPage} (`{dir: "down"}`)
|
|
1430
|
+
- `Ctrl-y` to {@link command.yankKilled}
|
|
1429
1431
|
*/
|
|
1430
1432
|
const defaultKeymap: readonly KeyBinding[];
|
|
1431
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
|
|
|
@@ -218,10 +218,10 @@ class Widget {
|
|
|
218
218
|
of(value) { return Widget.new(this, value); }
|
|
219
219
|
}
|
|
220
220
|
Widget.Type = Type;
|
|
221
|
-
Widget.
|
|
221
|
+
Widget.text = Widget.define({
|
|
222
222
|
render: s => document.createTextNode(s)
|
|
223
223
|
});
|
|
224
|
-
Widget.
|
|
224
|
+
Widget.editableText = Widget.define({
|
|
225
225
|
render: s => document.createTextNode(s)
|
|
226
226
|
});
|
|
227
227
|
Widget.img = Widget.create({
|
|
@@ -394,7 +394,7 @@ function applyDeco(shape, deco, tag) {
|
|
|
394
394
|
return shape;
|
|
395
395
|
}
|
|
396
396
|
const baseTagShape = /*@__PURE__*/memo((tag) => {
|
|
397
|
-
return addMarkAttributes(tag.is(Leaf.Text) ? Widget.
|
|
397
|
+
return addMarkAttributes(tag.is(Leaf.Text) ? Widget.editableText.of(tag.param)
|
|
398
398
|
: tag.type.shape.create(tag.param), tag);
|
|
399
399
|
});
|
|
400
400
|
function renderMarks(marks, around) {
|
|
@@ -1801,7 +1801,6 @@ class Tile {
|
|
|
1801
1801
|
ignoreEvent(event) { return false; }
|
|
1802
1802
|
get ignoreMutations() { return false; }
|
|
1803
1803
|
toString() { return this.dom.nodeName + (this.children.length ? `(${this.children})` : ""); }
|
|
1804
|
-
sync() { }
|
|
1805
1804
|
connect() {
|
|
1806
1805
|
for (let ch of this.children)
|
|
1807
1806
|
ch.connect();
|
|
@@ -1817,12 +1816,25 @@ class Tile {
|
|
|
1817
1816
|
tile = tile.parent;
|
|
1818
1817
|
return tile;
|
|
1819
1818
|
}
|
|
1819
|
+
markDirty() {
|
|
1820
|
+
if (!(this.flags & 8192)) {
|
|
1821
|
+
this.flags |= 8192;
|
|
1822
|
+
this.parent?.markDirty();
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1820
1825
|
posAtCoords(state, x, y) {
|
|
1821
1826
|
let nodeTile = this.nearestNode();
|
|
1822
1827
|
return nodeTile.posAtCoordsInner(nodeTile.posAtStart, state, x, y, null, 1);
|
|
1823
1828
|
}
|
|
1824
1829
|
static get(node) { return node.wgTile; }
|
|
1825
1830
|
}
|
|
1831
|
+
function checkSync(tile) {
|
|
1832
|
+
if ((tile.flags & 256) && !(tile.flags & 8192))
|
|
1833
|
+
return false;
|
|
1834
|
+
tile.flags |= 256;
|
|
1835
|
+
tile.flags &= -8193;
|
|
1836
|
+
return true;
|
|
1837
|
+
}
|
|
1826
1838
|
class CompositeTile extends Tile {
|
|
1827
1839
|
children = [];
|
|
1828
1840
|
addChild(child) {
|
|
@@ -1840,9 +1852,8 @@ class CompositeTile extends Tile {
|
|
|
1840
1852
|
child.parent = this;
|
|
1841
1853
|
}
|
|
1842
1854
|
sync() {
|
|
1843
|
-
if (this
|
|
1855
|
+
if (!checkSync(this))
|
|
1844
1856
|
return;
|
|
1845
|
-
this.flags |= 256;
|
|
1846
1857
|
let len = this.boundary * 2;
|
|
1847
1858
|
for (let ch of this.children) {
|
|
1848
1859
|
ch.sync();
|
|
@@ -2270,6 +2281,15 @@ class EltTile extends CompositeTile {
|
|
|
2270
2281
|
return new EltTile(elt, node, flags, length, dom || elt.outerDOM());
|
|
2271
2282
|
}
|
|
2272
2283
|
}
|
|
2284
|
+
function setUneditable(dom) {
|
|
2285
|
+
if (dom.nodeType != 1) {
|
|
2286
|
+
let span = document.createElement("span");
|
|
2287
|
+
span.appendChild(dom);
|
|
2288
|
+
dom = span;
|
|
2289
|
+
}
|
|
2290
|
+
if (dom.contentEditable == "inherit" && !/^(br|hr|img|input|wbr)$/i.test(dom.nodeName))
|
|
2291
|
+
dom.contentEditable = "false";
|
|
2292
|
+
}
|
|
2273
2293
|
class WidgetTile extends Tile {
|
|
2274
2294
|
widget;
|
|
2275
2295
|
_node;
|
|
@@ -2278,14 +2298,14 @@ class WidgetTile extends Tile {
|
|
|
2278
2298
|
this.widget = widget;
|
|
2279
2299
|
this._node = _node;
|
|
2280
2300
|
this.length = length;
|
|
2281
|
-
if (dom.nodeType == 1 && !widget.type.editable && dom.contentEditable == "inherit")
|
|
2282
|
-
dom.contentEditable = "false";
|
|
2283
2301
|
}
|
|
2284
2302
|
get isNodeOuter() { return !!this._node; }
|
|
2285
2303
|
get isAtom() { return true; }
|
|
2286
2304
|
get node() { return this._node; }
|
|
2287
2305
|
get children() { return noChildren; }
|
|
2288
2306
|
ignoreEvent(event) { return !this.widget.type.propagateEvent(event); }
|
|
2307
|
+
get ignoreMutations() { return !this.widget.type.editable; }
|
|
2308
|
+
sync() { checkSync(this); }
|
|
2289
2309
|
connect() {
|
|
2290
2310
|
this.widget.type.connect?.(this.widget.value, this.dom);
|
|
2291
2311
|
}
|
|
@@ -2294,7 +2314,7 @@ class WidgetTile extends Tile {
|
|
|
2294
2314
|
this.widget.type.disconnect?.(this.widget.value, this.dom);
|
|
2295
2315
|
}
|
|
2296
2316
|
toString() {
|
|
2297
|
-
return this.widget.type == Widget.
|
|
2317
|
+
return this.widget.type == Widget.editableText || this.widget.type == Widget.text
|
|
2298
2318
|
? JSON.stringify(this.widget.value) : super.toString();
|
|
2299
2319
|
}
|
|
2300
2320
|
posAtCoordsInner(start, state, x, y, textblock, orientation) {
|
|
@@ -2319,9 +2339,8 @@ class TextTile extends Tile {
|
|
|
2319
2339
|
get isNodeOuter() { return true; }
|
|
2320
2340
|
get isAtom() { return true; }
|
|
2321
2341
|
sync() {
|
|
2322
|
-
if (this
|
|
2342
|
+
if (!checkSync(this))
|
|
2323
2343
|
return;
|
|
2324
|
-
this.flags |= 256;
|
|
2325
2344
|
if (this.dom.nodeValue != this.text)
|
|
2326
2345
|
this.dom.nodeValue = this.text;
|
|
2327
2346
|
}
|
|
@@ -2754,7 +2773,10 @@ class ContentUpdate {
|
|
|
2754
2773
|
: endOld && this.posB == end ? endOld.matchingWidget(widget, sideFlag, this.reused)
|
|
2755
2774
|
: null;
|
|
2756
2775
|
if (!tile) {
|
|
2757
|
-
|
|
2776
|
+
let dom = widget.render(this.wg);
|
|
2777
|
+
if (!widget.type.editable)
|
|
2778
|
+
setUneditable(dom);
|
|
2779
|
+
tile = new WidgetTile(widget, null, 16 | sideFlag, dom);
|
|
2758
2780
|
if (widget.type.connect)
|
|
2759
2781
|
this.toConnect.push(tile);
|
|
2760
2782
|
}
|
|
@@ -2789,11 +2811,13 @@ class ContentUpdate {
|
|
|
2789
2811
|
}
|
|
2790
2812
|
return null;
|
|
2791
2813
|
}
|
|
2792
|
-
buildNodeShape(node, shape, reuse, afterContent = 0) {
|
|
2814
|
+
buildNodeShape(node, shape, reuse, inEditable = true, afterContent = 0) {
|
|
2793
2815
|
if (shape instanceof Elt) {
|
|
2794
|
-
if (
|
|
2795
|
-
!/^(br|hr|img|input|wbr)$/i.test(shape.tagName))
|
|
2796
|
-
|
|
2816
|
+
if (inEditable && !shape.hasContent) {
|
|
2817
|
+
if (Attributes.get(shape.attrs, "contenteditable") == null && !/^(br|hr|img|input|wbr)$/i.test(shape.tagName))
|
|
2818
|
+
shape = Elt.create(shape.tagName, Attributes.merge(shape.attrs, ["contenteditable", "false"]), shape.children);
|
|
2819
|
+
inEditable = false;
|
|
2820
|
+
}
|
|
2797
2821
|
let reusable, dom, strict = true;
|
|
2798
2822
|
if (reusable = this.findReusableTile(shape, reuse, strict) || this.findReusableTile(shape, reuse, strict = false)) {
|
|
2799
2823
|
this.reused.set(reusable, 2);
|
|
@@ -2813,7 +2837,7 @@ class ContentUpdate {
|
|
|
2813
2837
|
tile.flags |= 2;
|
|
2814
2838
|
}
|
|
2815
2839
|
else {
|
|
2816
|
-
tile.addChild(this.buildNodeShape(null, typeof ch == "string" ? Widget.
|
|
2840
|
+
tile.addChild(this.buildNodeShape(null, typeof ch == "string" ? Widget.text.of(ch) : ch, reusable ? reusable.children : reuse, inEditable, afterContentInner));
|
|
2817
2841
|
}
|
|
2818
2842
|
}
|
|
2819
2843
|
return tile;
|
|
@@ -2824,8 +2848,13 @@ class ContentUpdate {
|
|
|
2824
2848
|
this.reused.set(reusable, 2);
|
|
2825
2849
|
dom = reusable.dom;
|
|
2826
2850
|
}
|
|
2851
|
+
else {
|
|
2852
|
+
dom = shape.render(this.wg);
|
|
2853
|
+
}
|
|
2854
|
+
if (inEditable && !shape.type.editable)
|
|
2855
|
+
setUneditable(dom);
|
|
2827
2856
|
let flags = (node ? 512 : 16 | 1) | afterContent;
|
|
2828
|
-
let tile = new WidgetTile(shape, node, flags, dom
|
|
2857
|
+
let tile = new WidgetTile(shape, node, flags, dom, node ? node.length : 0);
|
|
2829
2858
|
if (shape.type.connect)
|
|
2830
2859
|
this.toConnect.push(tile);
|
|
2831
2860
|
return tile;
|
|
@@ -3783,7 +3812,7 @@ class DOMObserver {
|
|
|
3783
3812
|
let tile = this.wg.docTile.nearest(record.target);
|
|
3784
3813
|
if (!tile || tile.ignoreMutations)
|
|
3785
3814
|
return null;
|
|
3786
|
-
tile.
|
|
3815
|
+
tile.markDirty();
|
|
3787
3816
|
if (record.type == "attributes" || record.type == "characterData") {
|
|
3788
3817
|
if (tile == this.wg.docTile) {
|
|
3789
3818
|
return null;
|
|
@@ -3917,7 +3946,8 @@ class KeyBinding {
|
|
|
3917
3946
|
shift: Command.bind(moveToTextblockSide, { dir: "forward", extend: true }) },
|
|
3918
3947
|
{ mac: "Ctrl-d", run: Command.bind(deleteUnit, "forward") },
|
|
3919
3948
|
{ mac: "Ctrl-h", run: Command.bind(deleteUnit, "backward") },
|
|
3920
|
-
{ mac: "Ctrl-k", run:
|
|
3949
|
+
{ mac: "Ctrl-k", run: killToLineEnd },
|
|
3950
|
+
{ mac: "Ctrl-y", run: yankKilled },
|
|
3921
3951
|
{ mac: "Ctrl-Alt-h", run: Command.bind(deleteWord, "backward") },
|
|
3922
3952
|
{ mac: "Ctrl-o", run: insertLineBreak },
|
|
3923
3953
|
{ mac: "Ctrl-t", run: transposeChars },
|
|
@@ -4620,9 +4650,9 @@ function compositionUpdate(wg, event) {
|
|
|
4620
4650
|
if (!event.data) {
|
|
4621
4651
|
let sel = wg.state.selection, rSel = wg.state.sel;
|
|
4622
4652
|
if (sel.empty && (sel instanceof GardSelection.Text && sel.marks || !rSel.head.inText && rSel.head.index) &&
|
|
4623
|
-
!eqArray(rSel.head.nodeBefore?.tag.marks, rSel.activeMarks)
|
|
4624
|
-
|
|
4625
|
-
|
|
4653
|
+
!eqArray(rSel.head.nodeBefore?.tag.marks, rSel.activeMarks) ||
|
|
4654
|
+
sel.empty && inlineBoundNear(wg.state.sel.head) ||
|
|
4655
|
+
!inEditableDOM(wg, wg.observer.selectionRange.focusNode))
|
|
4626
4656
|
wrap = rSel.activeMarks;
|
|
4627
4657
|
}
|
|
4628
4658
|
if (wrap)
|
|
@@ -4642,6 +4672,12 @@ function inlineBoundNear(pos) {
|
|
|
4642
4672
|
return (index ? parent.node.content[index - 1].isPlot : parent.node.isInline) ||
|
|
4643
4673
|
(index < parent.node.content.length ? parent.node.content[index].isPlot : parent.node.isInline);
|
|
4644
4674
|
}
|
|
4675
|
+
function inEditableDOM(wg, node) {
|
|
4676
|
+
if (!node)
|
|
4677
|
+
return false;
|
|
4678
|
+
let tile = wg.docTile.nearest(node);
|
|
4679
|
+
return tile ? !(tile.isPoint || tile instanceof WidgetTile) : false;
|
|
4680
|
+
}
|
|
4645
4681
|
function isDeletionInputEvent(type) { return /^delete(Content|Word)/.test(type); }
|
|
4646
4682
|
const inputTypeCommands = /*@__PURE__*/(() => ({
|
|
4647
4683
|
historyUndo: undo,
|
|
@@ -4781,16 +4817,17 @@ const baseHandlers = {
|
|
|
4781
4817
|
data: event.data,
|
|
4782
4818
|
domRange: null,
|
|
4783
4819
|
};
|
|
4784
|
-
let ranges = event.getTargetRanges();
|
|
4820
|
+
let ranges = event.getTargetRanges(), editable = true;
|
|
4785
4821
|
if (ranges.length) {
|
|
4786
|
-
let r = ranges[0];
|
|
4787
|
-
|
|
4788
|
-
|
|
4822
|
+
let r = ranges[0], empty = r.collapsed;
|
|
4823
|
+
let from = wg.inputState.getDOMPos(r.startContainer, r.startOffset);
|
|
4824
|
+
data.domRange = { from, to: empty ? from : wg.inputState.getDOMPos(r.endContainer, r.endOffset) };
|
|
4825
|
+
editable = inEditableDOM(wg, r.startContainer) && (empty || inEditableDOM(wg, r.endContainer));
|
|
4789
4826
|
}
|
|
4790
4827
|
wg.inputState.beforeInput(event, wg.inputState.pendingInputEvent = data);
|
|
4791
4828
|
wg.scheduleFlush();
|
|
4792
4829
|
let allow = type == "insertCompositionText" ||
|
|
4793
|
-
(type == "insertText" || isDeletionInputEvent(type) &&
|
|
4830
|
+
editable && (type == "insertText" || isDeletionInputEvent(type) &&
|
|
4794
4831
|
data.domRange && inlineContext(wg.inputState.domDoc, data.domRange));
|
|
4795
4832
|
return !allow;
|
|
4796
4833
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nerd-bible/wordgard",
|
|
3
|
-
"version": "0.5.2-
|
|
3
|
+
"version": "0.5.2-beta4",
|
|
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.
|
|
39
|
+
"@nerd-bible/config": "^0.2.4",
|
|
40
40
|
"@swc/wasm-typescript": "^1.15.3",
|
|
41
41
|
"@types/mocha": "^10.0.10",
|
|
42
42
|
"@types/node": "^24.10.2",
|