@sametalmdr/word-editor 0.2.0-alpha → 0.3.0-alpha

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.
Files changed (82) hide show
  1. package/CHANGELOG.md +108 -70
  2. package/LICENSE +21 -21
  3. package/README.md +286 -247
  4. package/dist/canvas/box-clipboard.js +238 -238
  5. package/dist/canvas/box-selection.js +80 -80
  6. package/dist/canvas/canvas-editor.js +502 -502
  7. package/dist/canvas/controller.js +717 -717
  8. package/dist/canvas/geometry.js +131 -131
  9. package/dist/canvas/gesture.d.ts +0 -1
  10. package/dist/canvas/gesture.js +289 -289
  11. package/dist/canvas/handles.js +212 -212
  12. package/dist/canvas/menu.js +82 -82
  13. package/dist/canvas/mode-controller.js +81 -81
  14. package/dist/canvas/nudge.js +108 -108
  15. package/dist/canvas/overlay.js +149 -149
  16. package/dist/canvas/snap.js +132 -132
  17. package/dist/canvas/viewport.js +112 -112
  18. package/dist/canvas/z-order.js +171 -171
  19. package/dist/commands/alignment.js +150 -150
  20. package/dist/commands/box.js +272 -272
  21. package/dist/commands/clipboard.js +93 -93
  22. package/dist/commands/graphemes.js +112 -112
  23. package/dist/commands/marks.js +153 -153
  24. package/dist/commands/paragraph-props.js +176 -176
  25. package/dist/commands/size.js +368 -368
  26. package/dist/commands/text.js +291 -291
  27. package/dist/docx.d.ts +1 -0
  28. package/dist/docx.js +15 -13
  29. package/dist/editor/editing-host.js +434 -434
  30. package/dist/element.js +41 -41
  31. package/dist/fonts/OFL-Carlito.txt +94 -0
  32. package/dist/fonts/calibri-metrics.d.ts +27 -0
  33. package/dist/fonts/calibri-metrics.js +88 -0
  34. package/dist/history/history.js +433 -433
  35. package/dist/index.js +63 -63
  36. package/dist/input/beforeinput.js +224 -224
  37. package/dist/input/clipboard.js +107 -107
  38. package/dist/input/composition.js +341 -341
  39. package/dist/input/keymap.js +54 -54
  40. package/dist/io/ooxml/custgeom.js +72 -72
  41. package/dist/io/ooxml/drawingml.js +232 -232
  42. package/dist/io/ooxml/edits.js +67 -67
  43. package/dist/io/ooxml/export-splice.js +880 -880
  44. package/dist/io/ooxml/export.js +365 -365
  45. package/dist/io/ooxml/fill.d.ts +93 -8
  46. package/dist/io/ooxml/fill.js +618 -562
  47. package/dist/io/ooxml/fit.d.ts +252 -0
  48. package/dist/io/ooxml/fit.js +598 -0
  49. package/dist/io/ooxml/import-gate.js +200 -200
  50. package/dist/io/ooxml/import.js +691 -691
  51. package/dist/io/ooxml/inflate.js +126 -126
  52. package/dist/io/ooxml/media.js +87 -87
  53. package/dist/io/ooxml/ns.d.ts +2 -0
  54. package/dist/io/ooxml/ns.js +26 -21
  55. package/dist/io/ooxml/numbering.js +290 -290
  56. package/dist/io/ooxml/opc-write.js +171 -171
  57. package/dist/io/ooxml/provenance.js +93 -93
  58. package/dist/io/ooxml/recipe.js +708 -708
  59. package/dist/io/ooxml/relationships.js +59 -59
  60. package/dist/io/ooxml/run-fonts.d.ts +89 -0
  61. package/dist/io/ooxml/run-fonts.js +378 -0
  62. package/dist/io/ooxml/slots.d.ts +4 -0
  63. package/dist/io/ooxml/slots.js +582 -581
  64. package/dist/io/ooxml/styles-resolve.js +257 -257
  65. package/dist/io/ooxml/units.js +43 -43
  66. package/dist/io/ooxml/value.js +120 -120
  67. package/dist/io/ooxml/xml-scan.js +616 -616
  68. package/dist/io/ooxml/zip-container.js +228 -228
  69. package/dist/io/ooxml/zip-reject.js +106 -106
  70. package/dist/measure/placement.js +83 -83
  71. package/dist/measure/report.js +75 -75
  72. package/dist/model/ids.js +70 -70
  73. package/dist/model/normalize.js +146 -146
  74. package/dist/model/position.js +283 -283
  75. package/dist/model/transaction.js +209 -209
  76. package/dist/platform/capabilities.js +25 -25
  77. package/dist/react/index.js +21 -21
  78. package/dist/selection/controller.js +315 -315
  79. package/dist/selection/dom-map.js +334 -334
  80. package/dist/state/editor-state.js +109 -109
  81. package/dist/styles.css +66 -66
  82. package/package.json +94 -93
@@ -1,93 +1,93 @@
1
- import { createRun, locateParagraph, paragraphText } from "../model/node.js";
2
- import {
3
- collapsedSelection,
4
- containerIdOf,
5
- createPosition,
6
- isCollapsed,
7
- paragraphIdOf,
8
- selectionRange,
9
- withOffset,
10
- } from "../model/position.js";
11
- import { insertStep, splitParagraphStep } from "../model/step.js";
12
- import { createTransaction } from "../model/transaction.js";
13
- import { marksAt } from "./marks.js";
14
- import { deleteRange } from "./text.js";
15
-
16
- /**
17
- * Plain-text clipboard commands. ARCHITECTURE.md §2: the browser's HTML is
18
- * never the data model, which cuts the other way too — we never *write* HTML
19
- * to the clipboard either. Everything here is `text/plain`, in and out.
20
- */
21
-
22
- /**
23
- * Pastes pre-split lines at the cursor, replacing the selection first in the
24
- * same transaction — same single-transaction rule as `insertText` (§2.5):
25
- * pasting over a selection is one user action, not two.
26
- *
27
- * Lines after the first each start a new paragraph, exactly as if the user
28
- * had typed the text with Enter between lines. Splitting on `\r\n|\r|\n` is
29
- * the caller's job (`input/clipboard.js`) — this command only knows about
30
- * already-separated lines, so it stays testable without a `ClipboardEvent`.
31
- *
32
- * @param {import("../state/editor-state.js").EditorState} state
33
- * @param {string[]} lines
34
- * @param {import("../model/ids.js").IdFactory} ids
35
- * @returns {import("../model/transaction.js").Transaction | null}
36
- */
37
- export function pasteText(state, lines, ids) {
38
- if (lines.length === 0 || (lines.length === 1 && lines[0] === "")) {
39
- return null;
40
- }
41
-
42
- const tr = createTransaction(state.doc, state.selection, "paste");
43
- let at = isCollapsed(state.selection) ? state.selection.head : deleteRange(tr, state.selection);
44
-
45
- const marks = state.storedMarks ?? marksAt(tr.doc, at);
46
-
47
- for (let i = 0; i < lines.length; i += 1) {
48
- if (lines[i] !== "") {
49
- tr.step(insertStep(at, [createRun(lines[i], marks)]));
50
- at = withOffset(at, at.offset + lines[i].length);
51
- }
52
- if (i < lines.length - 1) {
53
- const newParagraphId = ids.next();
54
- tr.step(splitParagraphStep(at, newParagraphId));
55
- // The pasted paragraphs stay inside the container the cursor was in —
56
- // pasting multi-line text never spills into the next box.
57
- at = createPosition(containerIdOf(at), newParagraphId, 0);
58
- }
59
- }
60
-
61
- tr.setSelection(collapsedSelection(at));
62
- return tr.build();
63
- }
64
-
65
- /**
66
- * The selected text as a plain string, paragraphs joined by `\n`. Used by
67
- * `copy` and `cut` alike — cut is this plus a deletion, not a different
68
- * serialisation.
69
- * @param {import("../model/node.js").Doc} doc
70
- * @param {import("../model/position.js").Selection} selection
71
- * @returns {string}
72
- */
73
- export function serializeToText(doc, selection) {
74
- const range = selectionRange(doc, selection);
75
- if (range === null) {
76
- return "";
77
- }
78
-
79
- const fromAt = locateParagraph(doc, paragraphIdOf(range.from));
80
- const toAt = locateParagraph(doc, paragraphIdOf(range.to));
81
- if (fromAt === null || toAt === null || fromAt.containerIndex !== toAt.containerIndex) {
82
- return "";
83
- }
84
-
85
- const parts = [];
86
- for (let i = fromAt.paraIndex; i <= toAt.paraIndex; i += 1) {
87
- const text = paragraphText(fromAt.container.content[i]);
88
- const start = i === fromAt.paraIndex ? range.from.offset : 0;
89
- const end = i === toAt.paraIndex ? range.to.offset : text.length;
90
- parts.push(text.slice(start, end));
91
- }
92
- return parts.join("\n");
93
- }
1
+ import { createRun, locateParagraph, paragraphText } from "../model/node.js";
2
+ import {
3
+ collapsedSelection,
4
+ containerIdOf,
5
+ createPosition,
6
+ isCollapsed,
7
+ paragraphIdOf,
8
+ selectionRange,
9
+ withOffset,
10
+ } from "../model/position.js";
11
+ import { insertStep, splitParagraphStep } from "../model/step.js";
12
+ import { createTransaction } from "../model/transaction.js";
13
+ import { marksAt } from "./marks.js";
14
+ import { deleteRange } from "./text.js";
15
+
16
+ /**
17
+ * Plain-text clipboard commands. ARCHITECTURE.md §2: the browser's HTML is
18
+ * never the data model, which cuts the other way too — we never *write* HTML
19
+ * to the clipboard either. Everything here is `text/plain`, in and out.
20
+ */
21
+
22
+ /**
23
+ * Pastes pre-split lines at the cursor, replacing the selection first in the
24
+ * same transaction — same single-transaction rule as `insertText` (§2.5):
25
+ * pasting over a selection is one user action, not two.
26
+ *
27
+ * Lines after the first each start a new paragraph, exactly as if the user
28
+ * had typed the text with Enter between lines. Splitting on `\r\n|\r|\n` is
29
+ * the caller's job (`input/clipboard.js`) — this command only knows about
30
+ * already-separated lines, so it stays testable without a `ClipboardEvent`.
31
+ *
32
+ * @param {import("../state/editor-state.js").EditorState} state
33
+ * @param {string[]} lines
34
+ * @param {import("../model/ids.js").IdFactory} ids
35
+ * @returns {import("../model/transaction.js").Transaction | null}
36
+ */
37
+ export function pasteText(state, lines, ids) {
38
+ if (lines.length === 0 || (lines.length === 1 && lines[0] === "")) {
39
+ return null;
40
+ }
41
+
42
+ const tr = createTransaction(state.doc, state.selection, "paste");
43
+ let at = isCollapsed(state.selection) ? state.selection.head : deleteRange(tr, state.selection);
44
+
45
+ const marks = state.storedMarks ?? marksAt(tr.doc, at);
46
+
47
+ for (let i = 0; i < lines.length; i += 1) {
48
+ if (lines[i] !== "") {
49
+ tr.step(insertStep(at, [createRun(lines[i], marks)]));
50
+ at = withOffset(at, at.offset + lines[i].length);
51
+ }
52
+ if (i < lines.length - 1) {
53
+ const newParagraphId = ids.next();
54
+ tr.step(splitParagraphStep(at, newParagraphId));
55
+ // The pasted paragraphs stay inside the container the cursor was in —
56
+ // pasting multi-line text never spills into the next box.
57
+ at = createPosition(containerIdOf(at), newParagraphId, 0);
58
+ }
59
+ }
60
+
61
+ tr.setSelection(collapsedSelection(at));
62
+ return tr.build();
63
+ }
64
+
65
+ /**
66
+ * The selected text as a plain string, paragraphs joined by `\n`. Used by
67
+ * `copy` and `cut` alike — cut is this plus a deletion, not a different
68
+ * serialisation.
69
+ * @param {import("../model/node.js").Doc} doc
70
+ * @param {import("../model/position.js").Selection} selection
71
+ * @returns {string}
72
+ */
73
+ export function serializeToText(doc, selection) {
74
+ const range = selectionRange(doc, selection);
75
+ if (range === null) {
76
+ return "";
77
+ }
78
+
79
+ const fromAt = locateParagraph(doc, paragraphIdOf(range.from));
80
+ const toAt = locateParagraph(doc, paragraphIdOf(range.to));
81
+ if (fromAt === null || toAt === null || fromAt.containerIndex !== toAt.containerIndex) {
82
+ return "";
83
+ }
84
+
85
+ const parts = [];
86
+ for (let i = fromAt.paraIndex; i <= toAt.paraIndex; i += 1) {
87
+ const text = paragraphText(fromAt.container.content[i]);
88
+ const start = i === fromAt.paraIndex ? range.from.offset : 0;
89
+ const end = i === toAt.paraIndex ? range.to.offset : text.length;
90
+ parts.push(text.slice(start, end));
91
+ }
92
+ return parts.join("\n");
93
+ }
@@ -1,112 +1,112 @@
1
- // Backspace must remove a whole grapheme cluster, never a UTF-16 code unit.
2
- // "👍" is two code units; "e" plus a combining acute is two code points; Turkish
3
- // text composed with a dead key can be either. Deleting half of one corrupts the
4
- // document.
5
- //
6
- // The input layer prefers the browser's own answer from `getTargetRanges()`.
7
- // This module is the fallback chain behind that: Intl.Segmenter, then surrogate
8
- // pair handling on its own.
9
-
10
- /** @type {Intl.Segmenter | null | undefined} */
11
- let cachedSegmenter;
12
-
13
- /**
14
- * @returns {Intl.Segmenter | null}
15
- */
16
- function graphemeSegmenter() {
17
- if (cachedSegmenter !== undefined) {
18
- return cachedSegmenter;
19
- }
20
- cachedSegmenter =
21
- typeof Intl !== "undefined" && typeof Intl.Segmenter === "function"
22
- ? new Intl.Segmenter(undefined, { granularity: "grapheme" })
23
- : null;
24
- return cachedSegmenter;
25
- }
26
-
27
- /**
28
- * Start index of the grapheme that ends at `offset`.
29
- * @param {string} text
30
- * @param {number} offset
31
- * @returns {number}
32
- */
33
- export function previousGraphemeStart(text, offset) {
34
- if (offset <= 0) {
35
- return 0;
36
- }
37
-
38
- const segmenter = graphemeSegmenter();
39
- if (segmenter === null) {
40
- return previousCodePointStart(text, offset);
41
- }
42
-
43
- let start = 0;
44
- for (const { index } of segmenter.segment(text)) {
45
- if (index >= offset) {
46
- break;
47
- }
48
- start = index;
49
- }
50
- return start;
51
- }
52
-
53
- /**
54
- * End index of the grapheme that starts at `offset`.
55
- * @param {string} text
56
- * @param {number} offset
57
- * @returns {number}
58
- */
59
- export function nextGraphemeEnd(text, offset) {
60
- if (offset >= text.length) {
61
- return text.length;
62
- }
63
-
64
- const segmenter = graphemeSegmenter();
65
- if (segmenter === null) {
66
- return nextCodePointEnd(text, offset);
67
- }
68
-
69
- for (const { index, segment } of segmenter.segment(text)) {
70
- const end = index + segment.length;
71
- if (end > offset) {
72
- return end;
73
- }
74
- }
75
- return text.length;
76
- }
77
-
78
- /**
79
- * Surrogate-aware fallback. Handles astral characters but not combining marks —
80
- * it exists only for engines without `Intl.Segmenter`.
81
- * @param {string} text
82
- * @param {number} offset
83
- * @returns {number}
84
- */
85
- export function previousCodePointStart(text, offset) {
86
- let index = offset - 1;
87
- if (index > 0) {
88
- const code = text.charCodeAt(index);
89
- const previous = text.charCodeAt(index - 1);
90
- if (code >= 0xdc00 && code <= 0xdfff && previous >= 0xd800 && previous <= 0xdbff) {
91
- index -= 1;
92
- }
93
- }
94
- return Math.max(0, index);
95
- }
96
-
97
- /**
98
- * @param {string} text
99
- * @param {number} offset
100
- * @returns {number}
101
- */
102
- export function nextCodePointEnd(text, offset) {
103
- let index = offset + 1;
104
- if (index < text.length) {
105
- const code = text.charCodeAt(offset);
106
- const next = text.charCodeAt(index);
107
- if (code >= 0xd800 && code <= 0xdbff && next >= 0xdc00 && next <= 0xdfff) {
108
- index += 1;
109
- }
110
- }
111
- return Math.min(text.length, index);
112
- }
1
+ // Backspace must remove a whole grapheme cluster, never a UTF-16 code unit.
2
+ // "👍" is two code units; "e" plus a combining acute is two code points; Turkish
3
+ // text composed with a dead key can be either. Deleting half of one corrupts the
4
+ // document.
5
+ //
6
+ // The input layer prefers the browser's own answer from `getTargetRanges()`.
7
+ // This module is the fallback chain behind that: Intl.Segmenter, then surrogate
8
+ // pair handling on its own.
9
+
10
+ /** @type {Intl.Segmenter | null | undefined} */
11
+ let cachedSegmenter;
12
+
13
+ /**
14
+ * @returns {Intl.Segmenter | null}
15
+ */
16
+ function graphemeSegmenter() {
17
+ if (cachedSegmenter !== undefined) {
18
+ return cachedSegmenter;
19
+ }
20
+ cachedSegmenter =
21
+ typeof Intl !== "undefined" && typeof Intl.Segmenter === "function"
22
+ ? new Intl.Segmenter(undefined, { granularity: "grapheme" })
23
+ : null;
24
+ return cachedSegmenter;
25
+ }
26
+
27
+ /**
28
+ * Start index of the grapheme that ends at `offset`.
29
+ * @param {string} text
30
+ * @param {number} offset
31
+ * @returns {number}
32
+ */
33
+ export function previousGraphemeStart(text, offset) {
34
+ if (offset <= 0) {
35
+ return 0;
36
+ }
37
+
38
+ const segmenter = graphemeSegmenter();
39
+ if (segmenter === null) {
40
+ return previousCodePointStart(text, offset);
41
+ }
42
+
43
+ let start = 0;
44
+ for (const { index } of segmenter.segment(text)) {
45
+ if (index >= offset) {
46
+ break;
47
+ }
48
+ start = index;
49
+ }
50
+ return start;
51
+ }
52
+
53
+ /**
54
+ * End index of the grapheme that starts at `offset`.
55
+ * @param {string} text
56
+ * @param {number} offset
57
+ * @returns {number}
58
+ */
59
+ export function nextGraphemeEnd(text, offset) {
60
+ if (offset >= text.length) {
61
+ return text.length;
62
+ }
63
+
64
+ const segmenter = graphemeSegmenter();
65
+ if (segmenter === null) {
66
+ return nextCodePointEnd(text, offset);
67
+ }
68
+
69
+ for (const { index, segment } of segmenter.segment(text)) {
70
+ const end = index + segment.length;
71
+ if (end > offset) {
72
+ return end;
73
+ }
74
+ }
75
+ return text.length;
76
+ }
77
+
78
+ /**
79
+ * Surrogate-aware fallback. Handles astral characters but not combining marks —
80
+ * it exists only for engines without `Intl.Segmenter`.
81
+ * @param {string} text
82
+ * @param {number} offset
83
+ * @returns {number}
84
+ */
85
+ export function previousCodePointStart(text, offset) {
86
+ let index = offset - 1;
87
+ if (index > 0) {
88
+ const code = text.charCodeAt(index);
89
+ const previous = text.charCodeAt(index - 1);
90
+ if (code >= 0xdc00 && code <= 0xdfff && previous >= 0xd800 && previous <= 0xdbff) {
91
+ index -= 1;
92
+ }
93
+ }
94
+ return Math.max(0, index);
95
+ }
96
+
97
+ /**
98
+ * @param {string} text
99
+ * @param {number} offset
100
+ * @returns {number}
101
+ */
102
+ export function nextCodePointEnd(text, offset) {
103
+ let index = offset + 1;
104
+ if (index < text.length) {
105
+ const code = text.charCodeAt(offset);
106
+ const next = text.charCodeAt(index);
107
+ if (code >= 0xd800 && code <= 0xdbff && next >= 0xdc00 && next <= 0xdfff) {
108
+ index += 1;
109
+ }
110
+ }
111
+ return Math.min(text.length, index);
112
+ }