@portone/docx-editor 0.4.0 → 0.5.1

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 (180) hide show
  1. package/CHANGELOG.md +201 -0
  2. package/CONTRIBUTING.md +2 -0
  3. package/assets/editor.png +0 -0
  4. package/dist/DocxEditor.d.ts +1 -1
  5. package/dist/DocxEditor.js +44 -25
  6. package/dist/docx/cloning.js +4 -9
  7. package/dist/docx/commentOnlyChange.d.ts +5 -3
  8. package/dist/docx/comments/grammar.d.ts +27 -10
  9. package/dist/docx/comments/grammar.js +18 -67
  10. package/dist/docx/comments/model.d.ts +5 -6
  11. package/dist/docx/comments/model.js +0 -9
  12. package/dist/docx/comments/parts.d.ts +16 -10
  13. package/dist/docx/comments/parts.js +65 -13
  14. package/dist/docx/comments/people.d.ts +11 -3
  15. package/dist/docx/comments/people.js +20 -6
  16. package/dist/docx/comments/policy.js +7 -1
  17. package/dist/docx/comments/reading.d.ts +4 -2
  18. package/dist/docx/comments/reading.js +1 -14
  19. package/dist/docx/comments/writing.js +65 -28
  20. package/dist/docx/exportDocx.js +10 -14
  21. package/dist/docx/exportRefs.d.ts +8 -3
  22. package/dist/docx/exportRefs.js +2 -1
  23. package/dist/docx/fidelity.js +24 -10
  24. package/dist/docx/fields.d.ts +42 -0
  25. package/dist/docx/fields.js +76 -0
  26. package/dist/docx/formatting/styles.d.ts +0 -2
  27. package/dist/docx/formatting/styles.js +2 -15
  28. package/dist/docx/headersFooters.d.ts +89 -20
  29. package/dist/docx/headersFooters.js +163 -172
  30. package/dist/docx/identities.js +17 -26
  31. package/dist/docx/importDocx.d.ts +1 -1
  32. package/dist/docx/importDocx.js +100 -48
  33. package/dist/docx/importParagraph.d.ts +18 -5
  34. package/dist/docx/importParagraph.js +75 -128
  35. package/dist/docx/importPolicy.d.ts +70 -0
  36. package/dist/docx/importPolicy.js +195 -0
  37. package/dist/docx/importPreserved.d.ts +30 -0
  38. package/dist/docx/importPreserved.js +54 -0
  39. package/dist/docx/importTable.js +56 -12
  40. package/dist/docx/invariants.js +16 -18
  41. package/dist/docx/notes.d.ts +6 -1
  42. package/dist/docx/notes.js +6 -16
  43. package/dist/docx/pageGeometry.d.ts +5 -14
  44. package/dist/docx/pageGeometry.js +0 -12
  45. package/dist/docx/protectionPolicy.d.ts +13 -5
  46. package/dist/docx/protectionPolicy.js +32 -22
  47. package/dist/docx/scan.d.ts +18 -8
  48. package/dist/docx/scan.js +17 -11
  49. package/dist/docx/sections.d.ts +126 -0
  50. package/dist/docx/sections.js +207 -0
  51. package/dist/docx/serializeBlock.d.ts +13 -4
  52. package/dist/docx/serializeBlock.js +16 -18
  53. package/dist/docx/serializeParagraph.d.ts +9 -4
  54. package/dist/docx/serializeParagraph.js +23 -58
  55. package/dist/docx/serializePreserved.d.ts +14 -0
  56. package/dist/docx/serializePreserved.js +24 -0
  57. package/dist/docx/serializeStory.d.ts +17 -0
  58. package/dist/docx/serializeStory.js +17 -0
  59. package/dist/docx/serializeTable.js +10 -13
  60. package/dist/docx/session.d.ts +23 -6
  61. package/dist/docx/session.js +26 -5
  62. package/dist/docx/story.d.ts +140 -0
  63. package/dist/docx/story.js +237 -0
  64. package/dist/docx/storyProjection.js +6 -1
  65. package/dist/docx/tableTemplate.d.ts +25 -0
  66. package/dist/docx/tableTemplate.js +30 -16
  67. package/dist/docx/wrappers.d.ts +67 -0
  68. package/dist/docx/wrappers.js +117 -0
  69. package/dist/editor/clipboard/blockReaders.d.ts +74 -0
  70. package/dist/editor/clipboard/blockReaders.js +193 -0
  71. package/dist/editor/clipboard/htmlReader.d.ts +26 -0
  72. package/dist/editor/clipboard/htmlReader.js +376 -0
  73. package/dist/editor/clipboard/inlineFormatting.d.ts +0 -7
  74. package/dist/editor/clipboard/inlineFormatting.js +1 -7
  75. package/dist/editor/clipboard/internalChannel.d.ts +57 -0
  76. package/dist/editor/clipboard/internalChannel.js +58 -0
  77. package/dist/editor/clipboard/normalizers.d.ts +99 -0
  78. package/dist/editor/clipboard/normalizers.js +199 -0
  79. package/dist/editor/clipboard/parser.d.ts +34 -0
  80. package/dist/editor/clipboard/parser.js +58 -0
  81. package/dist/editor/clipboard/plugin.d.ts +21 -0
  82. package/dist/editor/clipboard/plugin.js +188 -0
  83. package/dist/editor/clipboard/readContext.d.ts +34 -0
  84. package/dist/editor/clipboard/readContext.js +25 -0
  85. package/dist/editor/clipboard/readers.d.ts +33 -0
  86. package/dist/editor/clipboard/readers.js +37 -0
  87. package/dist/editor/clipboard/source.d.ts +12 -0
  88. package/dist/editor/clipboard/source.js +21 -0
  89. package/dist/editor/commands/comments/editing.d.ts +26 -6
  90. package/dist/editor/commands/comments/editing.js +177 -106
  91. package/dist/editor/commands/comments/model.d.ts +34 -17
  92. package/dist/editor/commands/comments/model.js +11 -0
  93. package/dist/editor/commands/comments/reading.d.ts +5 -3
  94. package/dist/editor/commands/comments/reading.js +8 -58
  95. package/dist/editor/commands/index.d.ts +2 -2
  96. package/dist/editor/commands/index.js +2 -0
  97. package/dist/editor/commands/linkCommands.js +20 -5
  98. package/dist/editor/commands/listCommands.d.ts +6 -0
  99. package/dist/editor/commands/listCommands.js +3 -2
  100. package/dist/editor/commands/lockCommands.js +59 -16
  101. package/dist/editor/commands/noteQueries.d.ts +22 -5
  102. package/dist/editor/commands/noteQueries.js +34 -6
  103. package/dist/editor/commands/paragraphCommands.js +2 -2
  104. package/dist/editor/createEditor.js +13 -3
  105. package/dist/editor/documentStyles.d.ts +26 -4
  106. package/dist/editor/documentStyles.js +9 -4
  107. package/dist/editor/imageFiles.d.ts +2 -2
  108. package/dist/editor/imageFiles.js +2 -0
  109. package/dist/editor/insertTable.d.ts +1 -1
  110. package/dist/editor/insertTable.js +2 -2
  111. package/dist/editor/plainText.d.ts +12 -0
  112. package/dist/editor/plainText.js +2 -1
  113. package/dist/editor/plugins/columnResize.js +6 -3
  114. package/dist/editor/plugins/commentComposer.d.ts +28 -0
  115. package/dist/editor/plugins/commentComposer.js +46 -0
  116. package/dist/editor/plugins/commentDecorations.d.ts +29 -2
  117. package/dist/editor/plugins/commentDecorations.js +89 -15
  118. package/dist/editor/plugins/documentProjection.d.ts +31 -0
  119. package/dist/editor/plugins/documentProjection.js +22 -0
  120. package/dist/editor/plugins/imagePaste.js +15 -8
  121. package/dist/editor/plugins/linkPanel.d.ts +7 -3
  122. package/dist/editor/plugins/linkPanel.js +13 -27
  123. package/dist/editor/plugins/panelState.d.ts +67 -0
  124. package/dist/editor/plugins/panelState.js +54 -0
  125. package/dist/editor/plugins/tableContextMenu.d.ts +1 -1
  126. package/dist/editor/plugins/tableContextMenu.js +30 -47
  127. package/dist/editor/plugins/textContextMenu.d.ts +1 -1
  128. package/dist/editor/plugins/textContextMenu.js +30 -47
  129. package/dist/ooxml/fragment.d.ts +11 -0
  130. package/dist/ooxml/fragment.js +14 -4
  131. package/dist/ooxml/image.d.ts +3 -4
  132. package/dist/ooxml/image.js +50 -5
  133. package/dist/ooxml/names.d.ts +6 -0
  134. package/dist/ooxml/names.js +2 -0
  135. package/dist/ooxml/props.d.ts +10 -0
  136. package/dist/ooxml/props.js +28 -4
  137. package/dist/ooxml/rangeMarkers.d.ts +5 -0
  138. package/dist/ooxml/rangeMarkers.js +24 -0
  139. package/dist/page/PageGuides.d.ts +7 -3
  140. package/dist/page/PageGuides.js +13 -24
  141. package/dist/page/pageLayout.d.ts +64 -5
  142. package/dist/page/pageLayout.js +90 -29
  143. package/dist/page/usePageLayout.d.ts +14 -4
  144. package/dist/page/usePageLayout.js +19 -20
  145. package/dist/schema/attrRoles.js +46 -23
  146. package/dist/schema/clipboard.d.ts +72 -0
  147. package/dist/schema/clipboard.js +288 -0
  148. package/dist/schema/docxSchema.d.ts +9 -1
  149. package/dist/schema/docxSchema.js +217 -165
  150. package/dist/schema/editGuard.d.ts +1 -1
  151. package/dist/schema/guards.js +2 -2
  152. package/dist/schema/index.d.ts +1 -0
  153. package/dist/schema/locks.d.ts +3 -0
  154. package/dist/schema/locks.js +29 -12
  155. package/dist/schema/preservedFragments.d.ts +12 -0
  156. package/dist/schema/preservedFragments.js +38 -0
  157. package/dist/schema/preservedGuards.d.ts +9 -7
  158. package/dist/schema/preservedGuards.js +112 -11
  159. package/dist/schema/protection.d.ts +34 -5
  160. package/dist/schema/protection.js +42 -14
  161. package/dist/schema/rendering.d.ts +14 -0
  162. package/dist/schema/rendering.js +27 -12
  163. package/dist/schema/stories.d.ts +49 -0
  164. package/dist/schema/stories.js +78 -0
  165. package/dist/schema/wrappers.d.ts +58 -0
  166. package/dist/schema/wrappers.js +77 -0
  167. package/dist/styles/classNames.d.ts +3 -5
  168. package/dist/styles/classNames.js +3 -5
  169. package/dist/styles.css +74 -12
  170. package/dist/table/resize.d.ts +5 -3
  171. package/dist/ui/CommentsPanel.d.ts +1 -3
  172. package/dist/ui/CommentsPanel.js +24 -15
  173. package/dist/ui/NotesPanel.js +2 -2
  174. package/dist/ui/TextMenu.d.ts +1 -3
  175. package/dist/ui/TextMenu.js +10 -6
  176. package/dist/ui/comments/CommentComposer.d.ts +6 -1
  177. package/dist/ui/comments/CommentComposer.js +26 -3
  178. package/package.json +5 -4
  179. package/dist/editor/externalClipboard.d.ts +0 -22
  180. package/dist/editor/externalClipboard.js +0 -486
@@ -1,29 +1,14 @@
1
1
  // src/editor/plugins/tableContextMenu.ts
2
2
  import {
3
- Plugin,
4
- PluginKey,
5
3
  TextSelection
6
4
  } from "prosemirror-state";
7
5
  import { editsShut } from "../../schema/protectionState.js";
8
- var CLOSE = "close";
9
- var menuKey = new PluginKey("docxEditorTableMenu");
10
- function isRecord(value) {
11
- return typeof value === "object" && value !== null;
12
- }
13
- function toAnchor(value) {
14
- if (!isRecord(value)) return null;
6
+ import { panelPlugin } from "./panelState.js";
7
+ function isTableMenuAnchor(value) {
8
+ if (typeof value !== "object" || value === null) return false;
15
9
  const { clientX, clientY } = value;
16
- if (typeof clientX !== "number" || typeof clientY !== "number") return null;
17
- return { clientX, clientY };
18
- }
19
- function tableMenuAnchor(state) {
20
- return menuKey.getState(state) ?? null;
10
+ return typeof clientX === "number" && typeof clientY === "number";
21
11
  }
22
- var closeTableMenu = (state, dispatch) => {
23
- if (tableMenuAnchor(state) === null) return false;
24
- if (dispatch) dispatch(state.tr.setMeta(menuKey, CLOSE));
25
- return true;
26
- };
27
12
  function cellStart($pos) {
28
13
  for (let depth = $pos.depth; depth > 0; depth -= 1) {
29
14
  if ($pos.node(depth).type.spec.tableRole === "cell") {
@@ -46,41 +31,39 @@ function selectionCovers(state, cellPos) {
46
31
  }
47
32
  function openMenu(view, cellPos, anchor) {
48
33
  const state = view.state;
49
- const tr = state.tr.setMeta(menuKey, anchor);
34
+ const tr = menu.opening(state.tr, anchor);
50
35
  if (!selectionCovers(state, cellPos)) {
51
36
  tr.setSelection(TextSelection.near(tr.doc.resolve(cellPos + 1)));
52
37
  }
53
38
  view.dispatch(tr);
54
39
  }
55
- function tableContextMenu() {
56
- return new Plugin({
57
- key: menuKey,
58
- state: {
59
- init: () => null,
60
- apply: (tr, current) => {
61
- const meta = tr.getMeta(menuKey);
62
- if (meta === CLOSE) return null;
63
- const anchor = toAnchor(meta);
64
- if (anchor) return anchor;
65
- return tr.docChanged ? null : current;
66
- }
67
- },
68
- props: {
69
- handleDOMEvents: {
70
- contextmenu(view, event) {
71
- if (editsShut(view.state)) return false;
72
- const cellPos = cellAtEvent(view, event.target);
73
- if (cellPos === null) return false;
74
- event.preventDefault();
75
- openMenu(view, cellPos, {
76
- clientX: event.clientX,
77
- clientY: event.clientY
78
- });
79
- return true;
80
- }
40
+ var menu = panelPlugin({
41
+ name: "docxEditorTableMenu",
42
+ isAnchor: isTableMenuAnchor,
43
+ // Once the document changes, the cell the menu pointed at may no longer be there
44
+ onDocChange: "close",
45
+ props: {
46
+ handleDOMEvents: {
47
+ contextmenu(view, event) {
48
+ if (editsShut(view.state)) return false;
49
+ const cellPos = cellAtEvent(view, event.target);
50
+ if (cellPos === null) return false;
51
+ event.preventDefault();
52
+ openMenu(view, cellPos, {
53
+ clientX: event.clientX,
54
+ clientY: event.clientY
55
+ });
56
+ return true;
81
57
  }
82
58
  }
83
- });
59
+ }
60
+ });
61
+ function tableMenuAnchor(state) {
62
+ return menu.anchor(state);
63
+ }
64
+ var closeTableMenu = menu.close;
65
+ function tableContextMenu() {
66
+ return menu.plugin;
84
67
  }
85
68
  export {
86
69
  closeTableMenu,
@@ -2,7 +2,7 @@
2
2
  * Replaces the native text context menu and moves the selection to the clicked position. Unselected
3
3
  * table cells are left to the table context-menu plugin.
4
4
  */
5
- import { type Command, type EditorState, Plugin } from "prosemirror-state";
5
+ import { type Command, type EditorState, type Plugin } from "prosemirror-state";
6
6
  /** Where the menu should stand (viewport coordinates) */
7
7
  export interface TextMenuAnchor {
8
8
  clientX: number;
@@ -1,30 +1,15 @@
1
1
  // src/editor/plugins/textContextMenu.ts
2
2
  import {
3
- Plugin,
4
- PluginKey,
5
3
  TextSelection
6
4
  } from "prosemirror-state";
7
5
  import { CellSelection } from "prosemirror-tables";
8
6
  import { editingProtection, editsShut } from "../../schema/protectionState.js";
9
- var CLOSE = "close";
10
- var menuKey = new PluginKey("docxEditorTextMenu");
11
- function isRecord(value) {
12
- return typeof value === "object" && value !== null;
13
- }
14
- function toAnchor(value) {
15
- if (!isRecord(value)) return null;
7
+ import { panelPlugin } from "./panelState.js";
8
+ function isTextMenuAnchor(value) {
9
+ if (typeof value !== "object" || value === null) return false;
16
10
  const { clientX, clientY } = value;
17
- if (typeof clientX !== "number" || typeof clientY !== "number") return null;
18
- return { clientX, clientY };
19
- }
20
- function textMenuAnchor(state) {
21
- return menuKey.getState(state) ?? null;
11
+ return typeof clientX === "number" && typeof clientY === "number";
22
12
  }
23
- var closeTextMenu = (state, dispatch) => {
24
- if (textMenuAnchor(state) === null) return false;
25
- if (dispatch) dispatch(state.tr.setMeta(menuKey, CLOSE));
26
- return true;
27
- };
28
13
  function isInEditor(view, target) {
29
14
  return target instanceof Node && view.dom.contains(target);
30
15
  }
@@ -50,43 +35,41 @@ function forTableMenu(view, target, spot) {
50
35
  return aboutCells && isInTableCell(view, target);
51
36
  }
52
37
  function openMenu(state, event, spot) {
53
- const tr = state.tr.setMeta(menuKey, {
38
+ const tr = menu.opening(state.tr, {
54
39
  clientX: event.clientX,
55
40
  clientY: event.clientY
56
41
  });
57
42
  if (isInSelection(state, spot)) return tr;
58
43
  return tr.setSelection(TextSelection.near(tr.doc.resolve(spot)));
59
44
  }
60
- function textContextMenu() {
61
- return new Plugin({
62
- key: menuKey,
63
- state: {
64
- init: () => null,
65
- apply: (tr, current) => {
66
- const meta = tr.getMeta(menuKey);
67
- if (meta === CLOSE) return null;
68
- const anchor = toAnchor(meta);
69
- if (anchor) return anchor;
70
- return tr.docChanged ? null : current;
71
- }
72
- },
73
- props: {
74
- handleDOMEvents: {
75
- contextmenu(view, event) {
76
- if (editingProtection(view.state) === "readOnly") return false;
77
- if (!isInEditor(view, event.target)) return false;
78
- const spot = clickedSpot(view, event);
79
- if (editsShut(view.state) && !isInSelection(view.state, spot)) {
80
- return false;
81
- }
82
- if (forTableMenu(view, event.target, spot)) return false;
83
- event.preventDefault();
84
- view.dispatch(openMenu(view.state, event, spot));
85
- return true;
45
+ var menu = panelPlugin({
46
+ name: "docxEditorTextMenu",
47
+ isAnchor: isTextMenuAnchor,
48
+ // Once the document changes, what the menu was aimed at may no longer be there
49
+ onDocChange: "close",
50
+ props: {
51
+ handleDOMEvents: {
52
+ contextmenu(view, event) {
53
+ if (editingProtection(view.state) === "readOnly") return false;
54
+ if (!isInEditor(view, event.target)) return false;
55
+ const spot = clickedSpot(view, event);
56
+ if (editsShut(view.state) && !isInSelection(view.state, spot)) {
57
+ return false;
86
58
  }
59
+ if (forTableMenu(view, event.target, spot)) return false;
60
+ event.preventDefault();
61
+ view.dispatch(openMenu(view.state, event, spot));
62
+ return true;
87
63
  }
88
64
  }
89
- });
65
+ }
66
+ });
67
+ function textMenuAnchor(state) {
68
+ return menu.anchor(state);
69
+ }
70
+ var closeTextMenu = menu.close;
71
+ function textContextMenu() {
72
+ return menu.plugin;
90
73
  }
91
74
  export {
92
75
  closeTextMenu,
@@ -16,6 +16,7 @@
16
16
  * What a raw XML string has to look like to be let into the attr that carries it.
17
17
  *
18
18
  * `element` is a whole element: a properties fragment, a drawing, an annotation reference.
19
+ * `elements` is a run of them standing side by side, as a table carries between its rows.
19
20
  * `attributes` is what stood inside an opening tag, as `attrString` writes it.
20
21
  * `openTag` is an opening tag with everything it wrapped cut away, which the writer puts back by
21
22
  * appending the closing text `closedBy` names. `head` is what may still stand between the two:
@@ -24,6 +25,8 @@
24
25
  export type RawXmlShape = {
25
26
  kind: "element";
26
27
  names: readonly string[] | "any";
28
+ } | {
29
+ kind: "elements";
27
30
  } | {
28
31
  kind: "attributes";
29
32
  } | {
@@ -42,6 +45,14 @@ export declare function ELEMENT(...names: string[]): RawXmlShape;
42
45
  * turn a document the editor reads today into a demoted one.
43
46
  */
44
47
  export declare const ANY_ELEMENT: RawXmlShape;
48
+ /**
49
+ * A run of one or more elements standing side by side, of any name, in any namespace.
50
+ *
51
+ * A table carries the markers that stood between its rows, and a row those between its cells
52
+ * (`docx/importTable`), and there may be several of them in a row: the end of one bookmark and the
53
+ * start of the next stand together with nothing between them.
54
+ */
55
+ export declare const ANY_ELEMENTS: RawXmlShape;
45
56
  /** The attributes of an opening tag, with neither the tag nor anything it held */
46
57
  export declare const ATTRIBUTES: RawXmlShape;
47
58
  /**
@@ -10,17 +10,22 @@ function ELEMENT(...names) {
10
10
  return { kind: "element", names };
11
11
  }
12
12
  var ANY_ELEMENT = { kind: "element", names: "any" };
13
+ var ANY_ELEMENTS = { kind: "elements" };
13
14
  var ATTRIBUTES = { kind: "attributes" };
14
- function loneElement(xml) {
15
+ function wrappedElements(xml) {
15
16
  let root;
16
17
  try {
17
18
  root = parseXml(`<x ${namespaceDecls(xml)}>${xml}</x>`).documentElement;
18
19
  } catch {
19
20
  return null;
20
21
  }
21
- if (root.childNodes.length !== 1) return null;
22
- const el = elementChildren(root)[0];
23
- return el === void 0 || rebindsReservedPrefix(el) ? null : el;
22
+ const elements = elementChildren(root);
23
+ if (root.childNodes.length !== elements.length) return null;
24
+ return elements.some(rebindsReservedPrefix) ? null : elements;
25
+ }
26
+ function loneElement(xml) {
27
+ const elements = wrappedElements(xml);
28
+ return elements?.length === 1 ? elements[0] : null;
24
29
  }
25
30
  function isNamed(el, names) {
26
31
  if (!names.includes(el.localName)) return false;
@@ -54,6 +59,10 @@ function holdsShape(shape, value) {
54
59
  if (el === null) return false;
55
60
  return shape.names === "any" || isNamed(el, shape.names);
56
61
  }
62
+ case "elements": {
63
+ const elements = wrappedElements(value);
64
+ return elements !== null && elements.length > 0;
65
+ }
57
66
  case "attributes": {
58
67
  const el = loneElement(`<y ${value}></y>`);
59
68
  return el !== null && el.childNodes.length === 0;
@@ -70,6 +79,7 @@ function acceptRawXml(shape, value) {
70
79
  }
71
80
  export {
72
81
  ANY_ELEMENT,
82
+ ANY_ELEMENTS,
73
83
  ATTRIBUTES,
74
84
  ELEMENT,
75
85
  acceptRawXml
@@ -67,11 +67,10 @@ export interface DrawingPicture {
67
67
  */
68
68
  export declare function readDrawingPicture(drawing: Element): DrawingPicture | null;
69
69
  /**
70
- * The same drawing XML with both extents set to this size.
70
+ * The same drawing XML with its inline extent and its picture transform's extent set to this size.
71
71
  *
72
- * Only those two elements are touched; every other byte of the original stays where it
73
- * was. Handed the size it was imported with, this gives back the original string
74
- * unchanged, so nothing has to keep track of whether a resize happened.
72
+ * Only those two elements' size attributes are touched; every other byte stays where it was.
73
+ * Values already equal to the requested size keep their original spelling as well.
75
74
  */
76
75
  export declare function withExtent(xml: string, extent: ImageExtent): string;
77
76
  /** A picture that was inserted during editing and has no original XML to go back to */
@@ -1,6 +1,7 @@
1
1
  // src/ooxml/image.ts
2
2
  import { NAMESPACES } from "./names.js";
3
- import { childByLocalName, escapeXml, R_NS } from "./xml.js";
3
+ import { parseAttrs, readTag } from "./tagScan.js";
4
+ import { childByLocalName, escapeXml, localPart, R_NS } from "./xml.js";
4
5
  var EMU_PER_PX = 9525;
5
6
  function emuToPx(emu) {
6
7
  return emu / EMU_PER_PX;
@@ -76,11 +77,55 @@ function readDrawingPicture(drawing) {
76
77
  const descr = childOf(inline, "docPr")?.getAttribute("descr") ?? null;
77
78
  return { relId, extent, alt: descr === "" ? null : descr };
78
79
  }
79
- var EXTENT_TAG = /<([\w.-]+:)?extent\b[^>]*\/>/g;
80
- var EXT_TAG = /<([\w.-]+:)?ext\b[^>]*\/>/g;
80
+ function isPictureSize(path) {
81
+ const names = path.join("/");
82
+ return names === "drawing/inline/extent" || names === "drawing/inline/graphic/graphicData/pic/spPr/xfrm/ext";
83
+ }
84
+ function resizeTag(open, extent) {
85
+ const tag = readTag(open, 0);
86
+ if (!tag) return open;
87
+ const attrs = new Map(
88
+ parseAttrs(
89
+ open.slice(tag.nameEnd, tag.end - (tag.kind === "empty" ? 2 : 1))
90
+ ) ?? []
91
+ );
92
+ return open.replace(
93
+ /(\s+)([^\s=/>]+)(\s*=\s*)(["'])([\s\S]*?)\4/g,
94
+ (whole, gap, name, equals, quote) => {
95
+ if (name !== "cx" && name !== "cy") return whole;
96
+ const size = extent[name];
97
+ if (Number(attrs.get(name)) === size) return whole;
98
+ return `${gap}${name}${equals}${quote}${size}${quote}`;
99
+ }
100
+ );
101
+ }
81
102
  function withExtent(xml, extent) {
82
- const size = `cx="${extent.cx}" cy="${extent.cy}"`;
83
- return xml.replace(EXTENT_TAG, (_match, prefix) => `<${prefix ?? ""}extent ${size}/>`).replace(EXT_TAG, (_match, prefix) => `<${prefix ?? ""}ext ${size}/>`);
103
+ const path = [];
104
+ const parts = [];
105
+ let at = 0;
106
+ let kept = 0;
107
+ for (; ; ) {
108
+ const lt = xml.indexOf("<", at);
109
+ if (lt === -1) break;
110
+ const tag = readTag(xml, lt);
111
+ if (!tag) return xml;
112
+ if (tag.kind === "close") {
113
+ path.pop();
114
+ } else if (tag.kind !== "other") {
115
+ path.push(localPart(tag.name));
116
+ if (isPictureSize(path)) {
117
+ parts.push(
118
+ xml.slice(kept, lt),
119
+ resizeTag(xml.slice(lt, tag.end), extent)
120
+ );
121
+ kept = tag.end;
122
+ }
123
+ if (tag.kind === "empty") path.pop();
124
+ }
125
+ at = tag.end;
126
+ }
127
+ parts.push(xml.slice(kept));
128
+ return parts.join("");
84
129
  }
85
130
  function imageDrawingXml(image) {
86
131
  const size = `cx="${image.extent.cx}" cy="${image.extent.cy}"`;
@@ -13,6 +13,12 @@ export declare const W_NS = "http://schemas.openxmlformats.org/wordprocessingml/
13
13
  * every relationship type name is built on
14
14
  */
15
15
  export declare const R_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
16
+ /**
17
+ * The math namespace. Nothing here is written under a prefix of ours - a `m:oMath` travels as the
18
+ * XML it arrived as - so it names the namespace alone and stays out of `NAMESPACES`, which is the
19
+ * list of prefixes the writer spells out.
20
+ */
21
+ export declare const M_NS = "http://schemas.openxmlformats.org/officeDocument/2006/math";
16
22
  /** The prefix every WordprocessingML element and attribute this package writes carries */
17
23
  export declare const W_PREFIX = "w";
18
24
  export declare const NAMESPACES: {
@@ -1,6 +1,7 @@
1
1
  // src/ooxml/names.ts
2
2
  var W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
3
3
  var R_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
4
+ var M_NS = "http://schemas.openxmlformats.org/officeDocument/2006/math";
4
5
  var W_PREFIX = "w";
5
6
  var NAMESPACES = {
6
7
  w: W_NS,
@@ -25,6 +26,7 @@ function xmlnsDecl(prefix) {
25
26
  return `xmlns:${prefix}="${NAMESPACES[prefix]}"`;
26
27
  }
27
28
  export {
29
+ M_NS,
28
30
  NAMESPACES,
29
31
  R_NS,
30
32
  W_NS,
@@ -52,6 +52,14 @@ export declare function renderProps(props: Props): string;
52
52
  * carries children in a different order under a different parent (`pPr/rPr`).
53
53
  */
54
54
  export declare function setChild(props: Props, name: string, xml: string | null, parent?: string): Props;
55
+ /**
56
+ * Replaces every child of one name with this list of them, in the order given.
57
+ *
58
+ * `CT_SectPr` lets a section name a header and a footer once per variant, so a name standing more
59
+ * than once is a list rather than a single child, and writing one of them is writing the list. An
60
+ * empty list takes them all away.
61
+ */
62
+ export declare function setChildren(props: Props, name: string, xmls: readonly string[], parent?: string): Props;
55
63
  export declare function propsChild(children: readonly PropsChild[], name: string): PropsChild | undefined;
56
64
  /**
57
65
  * The opening tag's attributes as pairs, and null when their shape cannot be made out.
@@ -110,3 +118,5 @@ export declare function editChild(props: Props, path: readonly [string, ...strin
110
118
  * null if its shape cannot be made out, in which case the caller leaves the display values alone.
111
119
  */
112
120
  export declare function parsePropsXml(xml: string): Element | null;
121
+ /** The style name the paragraph formatting XML points at. null if it points at none */
122
+ export declare function styleIdOf(pPr: unknown): string | null;
@@ -3,6 +3,7 @@ import { childOrderOf } from "./childOrder.js";
3
3
  import { attrsText, emptyTagXml } from "./element.js";
4
4
  import { wName } from "./names.js";
5
5
  import { parseAttrs, readTag } from "./tagScan.js";
6
+ import { childValue } from "./units.js";
6
7
  import { elementChildren, localPart, namespaceDecls, parseXml } from "./xml.js";
7
8
  function rawAttrs(source, tag) {
8
9
  const closeLength = tag.kind === "empty" ? 2 : 1;
@@ -115,28 +116,37 @@ function tailWith(props, carried) {
115
116
  return tail === "" ? {} : { tail };
116
117
  }
117
118
  function setChild(props, name, xml, parent) {
119
+ return setChildren(props, name, xml === null ? [] : [xml], parent);
120
+ }
121
+ function setChildren(props, name, xmls, parent) {
118
122
  const order = childOrderOf(localPart(props.tag), parent);
119
123
  const at = props.children.findIndex((entry) => entry.name === name);
120
- if (at !== -1 && xml !== null) {
124
+ if (at !== -1 && xmls.length > 0) {
121
125
  const rest = dropChildren(props.children.slice(at + 1), name);
122
126
  return {
123
127
  ...props,
124
128
  children: [
125
129
  ...props.children.slice(0, at),
126
- { ...props.children[at], xml },
130
+ ...xmls.map(
131
+ (xml, index2) => index2 === 0 ? { ...props.children[at], xml } : { name, xml }
132
+ ),
127
133
  ...rest.kept
128
134
  ],
129
135
  ...tailWith(props, rest.carried)
130
136
  };
131
137
  }
132
138
  const { kept, carried } = dropChildren(props.children, name);
133
- if (xml === null) {
139
+ if (xmls.length === 0) {
134
140
  return { ...props, children: kept, ...tailWith(props, carried) };
135
141
  }
136
142
  const index = insertIndex(kept, name, order, props.tag);
137
143
  return {
138
144
  ...props,
139
- children: [...kept.slice(0, index), { name, xml }, ...kept.slice(index)],
145
+ children: [
146
+ ...kept.slice(0, index),
147
+ ...xmls.map((xml) => ({ name, xml })),
148
+ ...kept.slice(index)
149
+ ],
140
150
  ...tailWith(props, carried)
141
151
  };
142
152
  }
@@ -198,6 +208,18 @@ function parsePropsXml(xml) {
198
208
  return null;
199
209
  }
200
210
  }
211
+ var styleIdsByPPr = /* @__PURE__ */ new Map();
212
+ var STYLE_ID_CACHE_LIMIT = 2e3;
213
+ function styleIdOf(pPr) {
214
+ if (typeof pPr !== "string" || !pPr.includes("pStyle")) return null;
215
+ const known = styleIdsByPPr.get(pPr);
216
+ if (known !== void 0) return known;
217
+ const el = parsePropsXml(pPr);
218
+ const id = el ? childValue(el, "pStyle") : null;
219
+ if (styleIdsByPPr.size >= STYLE_ID_CACHE_LIMIT) styleIdsByPPr.clear();
220
+ styleIdsByPPr.set(pPr, id);
221
+ return id;
222
+ }
201
223
  export {
202
224
  attrsOf,
203
225
  childElement,
@@ -210,5 +232,7 @@ export {
210
232
  renderElement,
211
233
  renderProps,
212
234
  setChild,
235
+ setChildren,
236
+ styleIdOf,
213
237
  withAttrs
214
238
  };
@@ -0,0 +1,5 @@
1
+ /** `EG_RangeMarkupElements` less the comment markers modelled inside paragraphs and wrappers */
2
+ export declare const RANGE_MARKERS: string[];
3
+ /** The markers of `EG_RunLevelElts` that stand outside `EG_RangeMarkupElements` */
4
+ export declare const PERMISSION_MARKERS: string[];
5
+ export declare const COMMENT_RANGE_MARKERS: string[];
@@ -0,0 +1,24 @@
1
+ // src/ooxml/rangeMarkers.ts
2
+ var RANGE_MARKERS = [
3
+ "bookmarkStart",
4
+ "bookmarkEnd",
5
+ "moveFromRangeStart",
6
+ "moveFromRangeEnd",
7
+ "moveToRangeStart",
8
+ "moveToRangeEnd",
9
+ "customXmlInsRangeStart",
10
+ "customXmlInsRangeEnd",
11
+ "customXmlDelRangeStart",
12
+ "customXmlDelRangeEnd",
13
+ "customXmlMoveFromRangeStart",
14
+ "customXmlMoveFromRangeEnd",
15
+ "customXmlMoveToRangeStart",
16
+ "customXmlMoveToRangeEnd"
17
+ ];
18
+ var PERMISSION_MARKERS = ["permStart", "permEnd"];
19
+ var COMMENT_RANGE_MARKERS = ["commentRangeStart", "commentRangeEnd"];
20
+ export {
21
+ COMMENT_RANGE_MARKERS,
22
+ PERMISSION_MARKERS,
23
+ RANGE_MARKERS
24
+ };
@@ -6,11 +6,15 @@
6
6
  * Only the places crossed by a block taller than one page are marked over the text, with
7
7
  * a faint dotted line.
8
8
  * The box only paints, so the mouse still reaches the text beneath it.
9
+ *
10
+ * Each page draws the header and footer of the section it opens with, since a document names its
11
+ * headers section by section (`docx/headersFooters`).
9
12
  */
10
13
  import type { ReactElement } from "react";
11
14
  import { type HeadersFooters } from "../docx/headersFooters";
12
- import type { PageOverlay } from "./usePageLayout";
13
- export declare function PageGuides({ overlay, headersFooters, }: {
15
+ import type { PageFace, PageOverlay } from "./usePageLayout";
16
+ export declare function PageGuides({ overlay, headersFootersFor, }: {
14
17
  overlay: PageOverlay;
15
- headersFooters?: HeadersFooters;
18
+ /** The stories the section this page belongs to shows. Left out while no document is open */
19
+ headersFootersFor?: (page: PageFace) => HeadersFooters | null;
16
20
  }): ReactElement;
@@ -1,14 +1,14 @@
1
1
  // src/page/PageGuides.tsx
2
2
  import {
3
3
  displayPageNumber,
4
- headerFooterAlign,
4
+ headerFooterOn,
5
5
  headerFooterText
6
6
  } from "../docx/headersFooters.js";
7
7
  import { editorClassNames } from "../styles/classNames.js";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
9
9
  function PageGuides({
10
10
  overlay,
11
- headersFooters
11
+ headersFootersFor
12
12
  }) {
13
13
  const totalPages = overlay.pages.length;
14
14
  return /* @__PURE__ */ jsxs(
@@ -34,30 +34,19 @@ function PageGuides({
34
34
  },
35
35
  mark.page
36
36
  )),
37
- headersFooters && overlay.pages.flatMap((page) => {
38
- if (page.crossed) return [];
37
+ headersFootersFor && overlay.pages.flatMap((page) => {
38
+ const headersFooters = page.crossed ? null : headersFootersFor(page);
39
+ if (headersFooters === null) return [];
39
40
  const number = displayPageNumber(headersFooters, page.page);
40
- const header = headerFooterText(
41
+ const header = headerFooterOn(
41
42
  headersFooters.headers,
42
43
  headersFooters,
43
- page.page,
44
- totalPages
44
+ page.pageInSection
45
45
  );
46
- const footer = headerFooterText(
46
+ const footer = headerFooterOn(
47
47
  headersFooters.footers,
48
48
  headersFooters,
49
- page.page,
50
- totalPages
51
- );
52
- const headerAlign = headerFooterAlign(
53
- headersFooters.headers,
54
- headersFooters,
55
- page.page
56
- );
57
- const footerAlign = headerFooterAlign(
58
- headersFooters.footers,
59
- headersFooters,
60
- page.page
49
+ page.pageInSection
61
50
  );
62
51
  return [
63
52
  header === null ? null : /* @__PURE__ */ jsx(
@@ -68,10 +57,10 @@ function PageGuides({
68
57
  top: `${page.headerTop}px`,
69
58
  left: `${page.left}px`,
70
59
  width: `${page.width}px`,
71
- textAlign: headerAlign ?? void 0
60
+ textAlign: header.align ?? void 0
72
61
  },
73
62
  title: `Page ${number} header`,
74
- children: header
63
+ children: headerFooterText(header.story, number, totalPages)
75
64
  },
76
65
  `header-${page.page}`
77
66
  ),
@@ -83,10 +72,10 @@ function PageGuides({
83
72
  top: `${page.footerTop}px`,
84
73
  left: `${page.left}px`,
85
74
  width: `${page.width}px`,
86
- textAlign: footerAlign ?? void 0
75
+ textAlign: footer.align ?? void 0
87
76
  },
88
77
  title: `Page ${number} footer`,
89
- children: footer
78
+ children: headerFooterText(footer.story, number, totalPages)
90
79
  },
91
80
  `footer-${page.page}`
92
81
  )