@portone/docx-editor 0.6.0 → 0.6.2

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 (146) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/DocxEditor.js +84 -26
  3. package/dist/core.d.ts +4 -3
  4. package/dist/docx/exportDocx.d.ts +3 -2
  5. package/dist/docx/exportDocx.js +6 -10
  6. package/dist/docx/headersFooters.js +35 -23
  7. package/dist/docx/identities.d.ts +30 -0
  8. package/dist/docx/identities.js +43 -10
  9. package/dist/docx/importDocx.js +25 -26
  10. package/dist/docx/importParagraph.d.ts +1 -4
  11. package/dist/docx/importParagraph.js +4 -18
  12. package/dist/docx/invariants.d.ts +13 -10
  13. package/dist/docx/invariants.js +300 -116
  14. package/dist/docx/newLists.js +12 -3
  15. package/dist/docx/notes/newNote.d.ts +39 -0
  16. package/dist/docx/notes/newNote.js +122 -0
  17. package/dist/docx/notes/numbering.d.ts +27 -0
  18. package/dist/docx/notes/numbering.js +113 -0
  19. package/dist/docx/notes/reading.d.ts +58 -0
  20. package/dist/docx/notes/reading.js +98 -0
  21. package/dist/docx/notes/references.d.ts +33 -0
  22. package/dist/docx/notes/references.js +37 -0
  23. package/dist/docx/notes/writing.d.ts +19 -0
  24. package/dist/docx/notes/writing.js +95 -0
  25. package/dist/docx/partPlan.d.ts +3 -0
  26. package/dist/docx/partPlanners.d.ts +13 -0
  27. package/dist/docx/partPlanners.js +48 -0
  28. package/dist/docx/sections.d.ts +5 -0
  29. package/dist/docx/sections.js +7 -2
  30. package/dist/docx/serializeParagraph.js +7 -2
  31. package/dist/docx/serializePreserved.d.ts +5 -1
  32. package/dist/docx/serializePreserved.js +24 -7
  33. package/dist/docx/session.d.ts +5 -0
  34. package/dist/docx/session.js +6 -0
  35. package/dist/docx/storyParts.d.ts +133 -0
  36. package/dist/docx/storyParts.js +314 -0
  37. package/dist/editor/clipboard/htmlReader.d.ts +24 -4
  38. package/dist/editor/clipboard/htmlReader.js +39 -4
  39. package/dist/editor/clipboard/internalChannel.d.ts +30 -3
  40. package/dist/editor/clipboard/internalChannel.js +43 -3
  41. package/dist/editor/clipboard/normalizers.d.ts +35 -8
  42. package/dist/editor/clipboard/normalizers.js +60 -11
  43. package/dist/editor/clipboard/plugin.js +42 -22
  44. package/dist/editor/commands/exportQueries.d.ts +4 -3
  45. package/dist/editor/commands/index.d.ts +2 -1
  46. package/dist/editor/commands/index.js +18 -0
  47. package/dist/editor/commands/noteCommands.d.ts +47 -0
  48. package/dist/editor/commands/noteCommands.js +95 -0
  49. package/dist/editor/commands/noteQueries.d.ts +22 -3
  50. package/dist/editor/commands/noteQueries.js +21 -13
  51. package/dist/editor/createEditor.js +21 -3
  52. package/dist/editor/documentStyles.d.ts +3 -0
  53. package/dist/editor/documentStyles.js +2 -1
  54. package/dist/editor/editorDocument.d.ts +24 -0
  55. package/dist/editor/editorDocument.js +34 -4
  56. package/dist/editor/notes/noteSurface.d.ts +53 -0
  57. package/dist/editor/notes/noteSurface.js +246 -0
  58. package/dist/editor/plugins/compositionSelection.d.ts +20 -0
  59. package/dist/editor/plugins/compositionSelection.js +19 -0
  60. package/dist/editor/plugins/keymap.d.ts +14 -0
  61. package/dist/editor/plugins/keymap.js +13 -1
  62. package/dist/editor/plugins/noteLifecycle.d.ts +18 -0
  63. package/dist/editor/plugins/noteLifecycle.js +117 -0
  64. package/dist/editor/plugins/noteNavigation.d.ts +45 -0
  65. package/dist/editor/plugins/noteNavigation.js +86 -0
  66. package/dist/editor/plugins/noteNumbering.d.ts +13 -0
  67. package/dist/editor/plugins/noteNumbering.js +58 -0
  68. package/dist/editor/plugins/rowResize.js +2 -7
  69. package/dist/editor/plugins/tabLayout.js +2 -7
  70. package/dist/editor/stories/storyMarkup.d.ts +24 -0
  71. package/dist/editor/stories/storyMarkup.js +35 -0
  72. package/dist/editor/stories/storyState.d.ts +34 -0
  73. package/dist/editor/stories/storyState.js +74 -0
  74. package/dist/editor/stories/storyView.d.ts +115 -0
  75. package/dist/editor/stories/storyView.js +144 -0
  76. package/dist/index.d.ts +2 -2
  77. package/dist/numbering/spellers.d.ts +1 -1
  78. package/dist/numbering/spellers.js +6 -1
  79. package/dist/ooxml/errors.d.ts +135 -2
  80. package/dist/ooxml/errors.js +6 -0
  81. package/dist/page/blockKinds.d.ts +23 -4
  82. package/dist/page/demands/footnoteDemands.d.ts +18 -0
  83. package/dist/page/demands/footnoteDemands.js +51 -0
  84. package/dist/page/demands/index.d.ts +43 -0
  85. package/dist/page/demands/index.js +8 -0
  86. package/dist/page/kinds/paragraphKind.js +7 -1
  87. package/dist/page/kinds/tableKind.js +9 -1
  88. package/dist/page/measureBlocks.d.ts +2 -0
  89. package/dist/page/measureBlocks.js +31 -10
  90. package/dist/page/pageDecorations.d.ts +9 -2
  91. package/dist/page/pageDecorations.js +18 -5
  92. package/dist/page/pageLayout.d.ts +77 -1
  93. package/dist/page/pageLayout.js +186 -9
  94. package/dist/page/usePageLayout.d.ts +57 -2
  95. package/dist/page/usePageLayout.js +50 -5
  96. package/dist/schema/attrRoles.js +3 -2
  97. package/dist/schema/docxSchema.js +5 -2
  98. package/dist/schema/editGuard.d.ts +1 -1
  99. package/dist/schema/guards.js +1 -2
  100. package/dist/schema/preservedGuards.d.ts +4 -6
  101. package/dist/schema/preservedGuards.js +0 -9
  102. package/dist/schema/stories.d.ts +42 -2
  103. package/dist/schema/stories.js +28 -5
  104. package/dist/styles/classNames.d.ts +21 -6
  105. package/dist/styles/classNames.js +21 -6
  106. package/dist/styles/inlineStyle.d.ts +7 -0
  107. package/dist/styles/inlineStyle.js +11 -5
  108. package/dist/styles/visualScale.d.ts +19 -0
  109. package/dist/styles/visualScale.js +26 -0
  110. package/dist/styles.css +89 -32
  111. package/dist/ui/InsertImageButton.d.ts +3 -1
  112. package/dist/ui/InsertImageButton.js +3 -2
  113. package/dist/ui/TableMenu.d.ts +5 -2
  114. package/dist/ui/TableMenu.js +18 -1
  115. package/dist/ui/TextMenu.d.ts +5 -2
  116. package/dist/ui/TextMenu.js +21 -6
  117. package/dist/ui/Toolbar.d.ts +17 -2
  118. package/dist/ui/Toolbar.js +23 -10
  119. package/dist/ui/comments/useCommentRailLayout.js +2 -4
  120. package/dist/ui/noteItems.d.ts +21 -0
  121. package/dist/ui/noteItems.js +30 -0
  122. package/dist/ui/notes/NoteAreas.d.ts +73 -0
  123. package/dist/ui/notes/NoteAreas.js +131 -0
  124. package/dist/ui/notes/NoteList.d.ts +25 -0
  125. package/dist/ui/notes/NoteList.js +58 -0
  126. package/dist/ui/notes/StoryRow.d.ts +75 -0
  127. package/dist/ui/notes/StoryRow.js +122 -0
  128. package/dist/ui/notes/noteBands.d.ts +100 -0
  129. package/dist/ui/notes/noteBands.js +142 -0
  130. package/dist/ui/notes/noteHeights.d.ts +10 -0
  131. package/dist/ui/notes/noteHeights.js +24 -0
  132. package/dist/ui/notes/noteSeparator.d.ts +12 -0
  133. package/dist/ui/notes/noteSeparator.js +9 -0
  134. package/dist/ui/notes/useStorySurface.d.ts +68 -0
  135. package/dist/ui/notes/useStorySurface.js +95 -0
  136. package/dist/ui/runCommand.d.ts +8 -1
  137. package/dist/ui/runCommand.js +2 -2
  138. package/dist/ui/shortcutLabels.d.ts +16 -0
  139. package/dist/ui/shortcutLabels.js +9 -0
  140. package/dist/ui/usePageRoom.d.ts +13 -0
  141. package/dist/ui/usePageRoom.js +31 -0
  142. package/package.json +1 -1
  143. package/dist/docx/notes.d.ts +0 -27
  144. package/dist/docx/notes.js +0 -55
  145. package/dist/ui/NotesPanel.d.ts +0 -6
  146. package/dist/ui/NotesPanel.js +0 -33
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # @portone/docx-editor
2
2
 
3
+ ## 0.6.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#143](https://github.com/portone-io/docx-editor/pull/143) [`2238131`](https://github.com/portone-io/docx-editor/commit/2238131792ecab86ad544545bc28eae24841cbca) Thanks [@Deea222](https://github.com/Deea222)! - Say which content blocks an export, and where it stands.
8
+
9
+ Every entry `exportProblems`, `documentExportProblems` and a `blocked` `downloadDocx` report now carries a `reason`: a discriminated union naming the situation met and the content it is about, such as `unwritten-story-change` with the story and what was done to it, or `missing-content-types` with the part. It tells apart the situations one code covers, so a host can write the sentence its own user reads instead of matching an English message. The codes and messages are unchanged, so a `switch` over `code` keeps working.
10
+
11
+ A problem about a footnote or an endnote now carries `pos` as well, the first reference to that note in the body, since the note's own text stands nowhere there. A note nothing refers to still has no position.
12
+
13
+ `DocxExportError` carries the entry it was raised for as `problem`, so a `catch` reads the same `reason` and `pos` without asking again. A refusal met while writing that the invariants did not predict leaves it absent.
14
+
15
+ The checks are now asked of every side story an edit reached - a footnote, an endnote, a header, a footer, a comment body - as well as of the body, so an unmatched bookmark, a vertical merge past its table, a placeholder that lost its XML or a paragraph in an undefined list is refused wherever it stands instead of going into the file unchecked. Each such reason carries a `story` saying where, `null` for the body. A story nobody touched goes out as it arrived and is not asked about.
16
+
17
+ Since 0.6.1, `exportBytes` and `exportDocx` refuse a document holding a change nothing writes back - a header or footer story added or removed, an edited footnote or endnote separator - instead of saving the file without it. Check `exportProblems` before writing, or catch `DocxExportError`.
18
+
19
+ ## 0.6.1
20
+
21
+ ### Patch Changes
22
+
23
+ - [#137](https://github.com/portone-io/docx-editor/pull/137) [`a2f1a96`](https://github.com/portone-io/docx-editor/commit/a2f1a96a97c9dd45e07c962ae4a85e8d6954f657) Thanks [@Deea222](https://github.com/Deea222)! - Typing with an IME over a selection that begins at a footnote's number now replaces the selection. Selecting the whole of a footnote and typing over it left its words standing behind the composed text.
24
+
25
+ - [#138](https://github.com/portone-io/docx-editor/pull/138) [`16467c2`](https://github.com/portone-io/docx-editor/commit/16467c21934d9659f6e0b85d2094277cfcaa7366) Thanks [@Deea222](https://github.com/Deea222)! - Edit, insert, delete, and copy endnotes the way footnotes are, and draw them after the last paragraph of the document.
26
+
27
+ - [#137](https://github.com/portone-io/docx-editor/pull/137) [`a2f1a96`](https://github.com/portone-io/docx-editor/commit/a2f1a96a97c9dd45e07c962ae4a85e8d6954f657) Thanks [@Deea222](https://github.com/Deea222)! - Paste a copied footnote reference with a copy of its footnote.
28
+
29
+ - [#137](https://github.com/portone-io/docx-editor/pull/137) [`a2f1a96`](https://github.com/portone-io/docx-editor/commit/a2f1a96a97c9dd45e07c962ae4a85e8d6954f657) Thanks [@Deea222](https://github.com/Deea222)! - Edit footnotes in place: insert one from the right-click menu or with Mod+Alt+F, open it by clicking its number, and leave it with Escape.
30
+
31
+ - [#136](https://github.com/portone-io/docx-editor/pull/136) [`3c74b87`](https://github.com/portone-io/docx-editor/commit/3c74b87e52233b990e820752d50383411032e81d) Thanks [@Deea222](https://github.com/Deea222)! - Add the `insertFootnote`, `canInsertFootnote`, and `setFootnoteBody` commands, and delete a footnote together with its last reference, so one undo brings both back.
32
+
33
+ - [#136](https://github.com/portone-io/docx-editor/pull/136) [`3c74b87`](https://github.com/portone-io/docx-editor/commit/3c74b87e52233b990e820752d50383411032e81d) Thanks [@Deea222](https://github.com/Deea222)! - Draw footnotes at the foot of the page that refers to them, with the page ending above them, and list endnotes with their formatting after the last page, in place of the plain-text notes list.
34
+
35
+ - [#135](https://github.com/portone-io/docx-editor/pull/135) [`d6c701e`](https://github.com/portone-io/docx-editor/commit/d6c701ee3ce60b52772c7413e9f003fc717dbd77) Thanks [@Deea222](https://github.com/Deea222)! - Number footnotes and endnotes in the document's own format, starting number, and section restarts, and renumber them after every edit. A reference that carries its own mark, such as a dagger, is announced by its kind alone rather than by a number it does not have.
36
+
37
+ - [#135](https://github.com/portone-io/docx-editor/pull/135) [`d6c701e`](https://github.com/portone-io/docx-editor/commit/d6c701ee3ce60b52772c7413e9f003fc717dbd77) Thanks [@Deea222](https://github.com/Deea222)! - Save an edited, added, or deleted footnote or endnote into the file, creating the notes part where the document had none. An export that changed something nothing writes back yet, such as a note separator, is refused rather than saved without the change.
38
+
39
+ - [#140](https://github.com/portone-io/docx-editor/pull/140) [`a35b24c`](https://github.com/portone-io/docx-editor/commit/a35b24ccbac8cbc9d851381290e0e469a4abc6f1) Thanks [@Deea222](https://github.com/Deea222)! - Fix arrow navigation entering page-break spacing near footnotes, where typed text could appear without being saved in the document.
40
+
41
+ - [#139](https://github.com/portone-io/docx-editor/pull/139) [`0e8c474`](https://github.com/portone-io/docx-editor/commit/0e8c474a913c5dba8f40cdc539c401782fba5c64) Thanks [@Deea222](https://github.com/Deea222)! - A document breaks into the same pages at every zoom and window width. Below a zoom of about 0.8, a document sitting close to a page boundary could gain a page and its pages could move as the window narrowed; a scale an application puts around the editor, such as a dialog that opens on a scale, no longer moves them either.
42
+
3
43
  ## 0.6.0
4
44
 
5
45
  ### Minor Changes
@@ -1,6 +1,7 @@
1
1
  // src/DocxEditor.tsx
2
2
  import {
3
3
  forwardRef,
4
+ useCallback,
4
5
  useEffect,
5
6
  useImperativeHandle,
6
7
  useLayoutEffect,
@@ -15,6 +16,13 @@ import { exportProblems } from "./docx/invariants.js";
15
16
  import { sectionIn, sectionsOf } from "./docx/sections.js";
16
17
  import { activeLinkSpan } from "./editor/commands/linkCommands.js";
17
18
  import { createEditorView, editorStateForSession } from "./editor/createEditor.js";
19
+ import { sectionGeometryAt } from "./editor/documentStyles.js";
20
+ import { storyDocument } from "./editor/editorDocument.js";
21
+ import {
22
+ noteExtensions,
23
+ noteHost,
24
+ returnToReference
25
+ } from "./editor/notes/noteSurface.js";
18
26
  import {
19
27
  closeCommentComposer,
20
28
  isCommentComposerOpen
@@ -22,6 +30,10 @@ import {
22
30
  import { commentProjection } from "./editor/plugins/commentDecorations.js";
23
31
  import { setProtection } from "./editor/plugins/documentProtection.js";
24
32
  import { isLinkPanelOpen } from "./editor/plugins/linkPanel.js";
33
+ import {
34
+ openNoteCommand,
35
+ requestedNote
36
+ } from "./editor/plugins/noteNavigation.js";
25
37
  import { tableMenuAnchor } from "./editor/plugins/tableContextMenu.js";
26
38
  import { textMenuAnchor } from "./editor/plugins/textContextMenu.js";
27
39
  import { DocxImportError } from "./ooxml/errors.js";
@@ -34,16 +46,32 @@ import { editorClassNames } from "./styles/classNames.js";
34
46
  import { CommentsPanel, shownBesideThePage } from "./ui/CommentsPanel.js";
35
47
  import { LinkCard } from "./ui/LinkCard.js";
36
48
  import { LinkPanel } from "./ui/LinkPanel.js";
37
- import { NotesPanel } from "./ui/NotesPanel.js";
49
+ import { NotesAroundPage, useNoteBands } from "./ui/notes/noteBands.js";
50
+ import {
51
+ useStorySurface
52
+ } from "./ui/notes/useStorySurface.js";
38
53
  import { TableMenu } from "./ui/TableMenu.js";
39
54
  import { TextMenu } from "./ui/TextMenu.js";
40
55
  import { Toolbar } from "./ui/Toolbar.js";
41
56
  import { useFitWidthZoom } from "./ui/useFitWidthZoom.js";
57
+ import { usePageRoom } from "./ui/usePageRoom.js";
42
58
  import { normalizeZoom } from "./ui/zoom.js";
43
59
  import { jsx, jsxs } from "react/jsx-runtime";
44
60
  function runOn(view, command) {
45
61
  command(view.state, (transaction) => view.dispatch(transaction));
46
62
  }
63
+ var NOTE_SURFACE = {
64
+ hostOf: noteHost,
65
+ extensionsOf: (main, row) => noteExtensions(main, row.key, () => row.label),
66
+ // The paper a note wraps at is the paper of the section its reference stands in, which a story
67
+ // holds no section of its own to say (`editor/documentStyles`)
68
+ documentFor: (main, snapshot, row) => storyDocument(snapshot, sectionGeometryAt(main, row.referencePos)),
69
+ requestedIn: requestedNote,
70
+ // The kind's own public command is what a consumer calls; here the kind is a value the row
71
+ // carries, so the factory behind the two takes it
72
+ openIn: (main, row) => runOn(main, openNoteCommand(row.kind, row.id)),
73
+ returnFrom: (main, row) => returnToReference(main, row.key)
74
+ };
47
75
  function affordancesOf(mode) {
48
76
  switch (mode.kind) {
49
77
  case "readOnly":
@@ -169,6 +197,12 @@ function DocxEditorSurface({
169
197
  );
170
198
  const viewRef = useRef(null);
171
199
  const [live, setLive] = useState(null);
200
+ const [pageBox, setPageBox] = useState(null);
201
+ const [pageLayer, setPageLayer] = useState(null);
202
+ const holdPageLayer = useCallback((node) => {
203
+ layerRef.current = node;
204
+ setPageLayer(node);
205
+ }, []);
172
206
  const [commentsOpen, setCommentsOpen] = useState(false);
173
207
  const [uncontrolledZoom, setUncontrolledZoom] = useState(
174
208
  () => normalizeZoom(defaultZoom)
@@ -179,6 +213,7 @@ function DocxEditorSurface({
179
213
  const page = opened?.status === "opened" ? pagePixels(opened.session.geometry) : A4_PAGE_PIXELS;
180
214
  const fitWidth = useFitWidthZoom(rootRef, page.pageWidth);
181
215
  const effectiveZoom = selectedZoom === "fit-width" ? fitWidth : selectedZoom;
216
+ usePageRoom(pageBox, pageLayer, effectiveZoom);
182
217
  const changeZoom = (next) => {
183
218
  const normalized = normalizeZoom(next);
184
219
  if (zoom === void 0) setUncontrolledZoom(normalized);
@@ -254,12 +289,27 @@ function DocxEditorSurface({
254
289
  () => sections === null ? void 0 : sectionPixels(sections),
255
290
  [sections]
256
291
  );
292
+ const notes = useNoteBands({
293
+ state: live?.state ?? null,
294
+ of: opened,
295
+ fontFallbacks: mountedFontFallbacks
296
+ });
297
+ const surface = useStorySurface({
298
+ main: live,
299
+ rows: notes.rows,
300
+ binding: NOTE_SURFACE
301
+ });
257
302
  const overlay = usePageLayout({
258
303
  view: live?.view ?? null,
259
304
  layer: layerRef,
260
305
  enabled: showPageGuides,
261
306
  revision: live?.state.doc,
262
- sections: sectionPapers
307
+ sections: sectionPapers,
308
+ bands: notes.bands,
309
+ trailing: notes.trailing,
310
+ // A page laid out again draws the row the open note stands in wherever it now lands, which
311
+ // would take its composition down with it
312
+ composing: surface.composing
263
313
  });
264
314
  const headersFootersFor = useMemo(() => {
265
315
  if (doc === null || session === null || sections === null) return void 0;
@@ -299,11 +349,11 @@ function DocxEditorSurface({
299
349
  className: [editorClassNames.frame, className].filter(Boolean).join(" "),
300
350
  style,
301
351
  children: [
302
- live && toolbar && /* @__PURE__ */ jsx(
352
+ live && toolbar && surface.active && /* @__PURE__ */ jsx(
303
353
  Toolbar,
304
354
  {
305
- view: live.view,
306
- state: live.state,
355
+ main: live,
356
+ active: surface.active,
307
357
  fontFallbacks: mountedFontFallbacks,
308
358
  presets,
309
359
  commentsOpen,
@@ -333,25 +383,31 @@ function DocxEditorSurface({
333
383
  }
334
384
  ),
335
385
  /* @__PURE__ */ jsxs("div", { ref: rootRef, className: editorClassNames.root, children: [
336
- /* @__PURE__ */ jsxs(
337
- "div",
338
- {
339
- ref: layerRef,
340
- className: editorClassNames.pageLayer,
341
- style: { zoom: effectiveZoom },
342
- children: [
343
- /* @__PURE__ */ jsx("div", { ref: mountRef }),
344
- overlay && /* @__PURE__ */ jsx(
345
- PageGuides,
346
- {
347
- overlay,
348
- headersFootersFor
349
- }
350
- ),
351
- live && opened?.status === "opened" && /* @__PURE__ */ jsx(NotesPanel, { state: live.state, pageWidth: page.pageWidth })
352
- ]
353
- }
354
- ),
386
+ /* @__PURE__ */ jsx("div", { ref: setPageBox, className: editorClassNames.pageBox, children: /* @__PURE__ */ jsxs("div", { ref: holdPageLayer, className: editorClassNames.pageLayer, children: [
387
+ /* @__PURE__ */ jsx("div", { ref: mountRef }),
388
+ overlay && /* @__PURE__ */ jsx(
389
+ PageGuides,
390
+ {
391
+ overlay,
392
+ headersFootersFor
393
+ }
394
+ ),
395
+ /* @__PURE__ */ jsx(
396
+ NotesAroundPage,
397
+ {
398
+ notes,
399
+ overlay,
400
+ page,
401
+ zoom: effectiveZoom,
402
+ open: surface.open,
403
+ editing: surface.editing,
404
+ readOnly: surface.readOnly,
405
+ revision: live?.state,
406
+ onOpen: surface.onOpen,
407
+ onReturn: surface.onReturn
408
+ }
409
+ )
410
+ ] }) }),
355
411
  !commentsOpen && commentsPanel
356
412
  ] }),
357
413
  commentsOpen && commentsPanel
@@ -368,21 +424,23 @@ function DocxEditorSurface({
368
424
  },
369
425
  linkAtCursor.from
370
426
  ),
371
- live && textAnchor && /* @__PURE__ */ jsx(
427
+ live && textAnchor && surface.active && /* @__PURE__ */ jsx(
372
428
  TextMenu,
373
429
  {
374
430
  view: live.view,
375
431
  state: live.state,
376
432
  anchor: textAnchor,
433
+ takes: surface.active.takes,
377
434
  allowLocking: locking
378
435
  }
379
436
  ),
380
- live && tableAnchor && /* @__PURE__ */ jsx(
437
+ live && tableAnchor && surface.active && /* @__PURE__ */ jsx(
381
438
  TableMenu,
382
439
  {
383
440
  view: live.view,
384
441
  state: live.state,
385
442
  anchor: tableAnchor,
443
+ takes: surface.active.takes,
386
444
  allowLocking: locking
387
445
  }
388
446
  )
package/dist/core.d.ts CHANGED
@@ -19,9 +19,10 @@ export type { ParagraphStyleOption } from "./docx/formatting";
19
19
  export type { DocxBytes, ImportOptions } from "./docx/importDocx";
20
20
  /**
21
21
  * What the writer would refuse the document over, asked ahead of the write. The first entry is
22
- * what `exportDocx` throws, so the two cannot disagree.
22
+ * what `exportDocx` throws, so the two cannot disagree. `reason` tells the situations one code
23
+ * covers apart and names the content each is about.
23
24
  */
24
- export type { ExportProblem } from "./docx/invariants";
25
+ export type { ExportPartName, ExportProblem, ExportProblemReason, ExportProblemStory, ExportStoryKind, } from "./docx/invariants";
25
26
  export { exportProblems } from "./docx/invariants";
26
27
  export type { DocxSession } from "./docx/session";
27
28
  export { documentNumbering, documentPartPath } from "./docx/session";
@@ -29,7 +30,7 @@ export type { CellFormat, CellVerticalAlign, DocumentDefaults, HighlightName, Li
29
30
  export { toCellFormat, toParagraphFormat, toRowFormat, toRunFormat, toTableFormat, toTableWidth, } from "./model/format";
30
31
  export type { LevelAlign, LevelIndent, LevelSuffix, NewList, NewListLevel, NumberFormat, Numbering, NumberingLevel, NumberingList, NumberingOptions, NumberingStyleLinks, ReadLevelRun, } from "./numbering/parseNumbering";
31
32
  export { parseNumbering } from "./numbering/parseNumbering";
32
- export type { DocxExportErrorCode, DocxImportErrorCode, } from "./ooxml/errors";
33
+ export type { DocxExportErrorCode, DocxExportErrorOptions, DocxImportErrorCode, } from "./ooxml/errors";
33
34
  export { DocxExportError, DocxImportError } from "./ooxml/errors";
34
35
  /**
35
36
  * An image node's size is written in EMU, the unit the document itself uses. A builder
@@ -4,7 +4,8 @@
4
4
  * The body (document.xml) is always rebuilt from preserved and edited blocks. Every other part is
5
5
  * written by a planner (`./partPlan`) that answers only when the document gives it something to
6
6
  * write: numbering.xml when a list was newly started, the comment parts when comments changed,
7
- * media parts when an image was inserted. Content types and relationships change only when one
7
+ * a header, a footer, or the Footnotes part when a story it holds changed, media parts when an
8
+ * image was inserted. Content types and relationships change only when one
8
9
  * of those additions declares itself through the context every planner shares.
9
10
  *
10
11
  * The body is written before that relationships part, because a link asks for its relationship as
@@ -35,7 +36,7 @@ export declare function exportDocx(doc: PMNode, session: DocxSession, options?:
35
36
  *
36
37
  * The first problem `exportProblems` reports is thrown before anything is written, so a refusal a
37
38
  * caller could have asked about ahead of time arrives with the same code and message it would
38
- * have read there.
39
+ * have read there, and carries that entry as the error's `problem`.
39
40
  */
40
41
  export declare function exportDocxReport(doc: PMNode, session: DocxSession, options?: ExportOptions): {
41
42
  bytes: Uint8Array;
@@ -10,22 +10,20 @@ import {
10
10
  W_NS,
11
11
  withXmlParser
12
12
  } from "../ooxml/xml.js";
13
- import { commentsPlanner } from "./comments.js";
14
13
  import { repackParts } from "./container.js";
15
14
  import {
16
15
  fidelityNotesOf
17
16
  } from "./fidelity.js";
18
- import { headerFooterPlanner } from "./headersFooters.js";
19
17
  import { hyperlinkRefs } from "./hyperlink.js";
20
18
  import { withUniqueIdentities } from "./identities.js";
21
19
  import { problemsOf } from "./invariants.js";
22
20
  import { NO_IMAGE_REFS, planImageMedia } from "./media.js";
23
- import { numberingPlanner } from "./numberingPlanner.js";
24
21
  import { CONTENT_TYPES_PATH, contentTypeWriter } from "./packageParts.js";
25
22
  import {
26
23
  assertPartsParse,
27
24
  runPartPlanners
28
25
  } from "./partPlan.js";
26
+ import { PART_PLANNERS } from "./partPlanners.js";
29
27
  import {
30
28
  readRelationships,
31
29
  relationshipWriter,
@@ -105,11 +103,6 @@ function assertMainPart(documentXml, wroteLinks) {
105
103
  }
106
104
  }
107
105
  var LINK_MARKUP = { namespaces: { r: R_NS } };
108
- var PART_PLANNERS = [
109
- numberingPlanner,
110
- commentsPlanner,
111
- headerFooterPlanner
112
- ];
113
106
  function exportDocx(doc, session, options) {
114
107
  return exportDocxReport(doc, session, options).bytes;
115
108
  }
@@ -123,7 +116,9 @@ function reportThrough(planners, doc, session, options) {
123
116
  return withXmlParser(options?.xmlParser, () => {
124
117
  const store = sessionOf(session);
125
118
  const problem = problemsOf(doc, store)[0];
126
- if (problem) throw new DocxExportError(problem.code, problem.message);
119
+ if (problem) {
120
+ throw new DocxExportError(problem.code, problem.message, { problem });
121
+ }
127
122
  const approximated = [];
128
123
  const bytes = writeDocx(
129
124
  doc,
@@ -141,7 +136,8 @@ function writeDocx(doc, store, notes, planners) {
141
136
  const relsPath = relsPathOf(store.mainPartPath);
142
137
  const context = {
143
138
  relationships: relationshipWriter(readRelationships(store.parts, relsPath)),
144
- contentTypes: contentTypeWriter(store.parts)
139
+ contentTypes: contentTypeWriter(store.parts),
140
+ notes
145
141
  };
146
142
  const media = planImageMedia(doc, store, context);
147
143
  const links = hyperlinkRefs(context.relationships);
@@ -7,10 +7,13 @@ import {
7
7
  import { isOnElement } from "../ooxml/units.js";
8
8
  import { decodeUtf8, encodeUtf8, parseXml, R_NS, W_NS } from "../ooxml/xml.js";
9
9
  import { docxSchema } from "../schema/index.js";
10
- import { sameSource } from "../schema/sourceEquality.js";
11
- import { storyKey, storyNodeOf } from "../schema/stories.js";
10
+ import {
11
+ HEADER_FOOTER_KINDS,
12
+ storyKey
13
+ } from "../schema/stories.js";
12
14
  import { NO_EXPORT_REFS } from "./exportRefs.js";
13
15
  import { fieldSpans, isFieldCharacter } from "./fields.js";
16
+ import { withUniqueStoryIdentities } from "./identities.js";
14
17
  import { relatedPartPath } from "./packageParts.js";
15
18
  import { readRelationships, relsPathOf, resolveTarget } from "./relationships.js";
16
19
  import {
@@ -21,12 +24,16 @@ import {
21
24
  readStory,
22
25
  storyLeafText
23
26
  } from "./story.js";
27
+ import { storyChangesOf } from "./storyParts.js";
24
28
  var EMPTY_VARIANTS = {
25
29
  default: null,
26
30
  first: null,
27
31
  even: null
28
32
  };
29
- var PART_ROOT = { header: "hdr", footer: "ftr" };
33
+ var PART_ROOT = {
34
+ header: "hdr",
35
+ footer: "ftr"
36
+ };
30
37
  function settingsEvenAndOdd(parts, mainPartPath) {
31
38
  const path = relatedPartPath(parts, mainPartPath, `${R_NS}/settings`);
32
39
  const bytes = path === null ? void 0 : parts.get(path);
@@ -36,9 +43,9 @@ function settingsEvenAndOdd(parts, mainPartPath) {
36
43
  return isOnElement(setting);
37
44
  }
38
45
  function kindOf(relationshipType) {
39
- if (relationshipType === `${R_NS}/header`) return "header";
40
- if (relationshipType === `${R_NS}/footer`) return "footer";
41
- return null;
46
+ return HEADER_FOOTER_KINDS.find(
47
+ (kind) => relationshipType === `${R_NS}/${kind}`
48
+ ) ?? null;
42
49
  }
43
50
  function readHeaderFooterStories(parts, mainPartPath, referenced, depsFor) {
44
51
  const keyByRelId = /* @__PURE__ */ new Map();
@@ -166,29 +173,34 @@ function headerFooterText(story, page, totalPages) {
166
173
  return lines.join("\n");
167
174
  }
168
175
  var HEADER_MARKUP = { namespaces: { w: NAMESPACES.w } };
169
- function rewrittenParts(doc, session) {
170
- const parts = /* @__PURE__ */ new Map();
171
- for (const [key, imported] of session.stories) {
172
- if (imported.kind !== "header" && imported.kind !== "footer") continue;
173
- const current = storyNodeOf(doc, key);
174
- if (current === null || sameSource(current, imported.doc)) continue;
175
- const written = serializeStory(current, imported, imported, {
176
+ function writtenPart(imported, current, session, notes) {
177
+ const written = withUniqueStoryIdentities([{ story: current, frozen: false }]).map(
178
+ (story) => serializeStory(story, imported, imported, {
176
179
  ...NO_EXPORT_REFS,
180
+ notes,
177
181
  session
178
- });
179
- const hadBom = decodeUtf8(
180
- session.parts.get(imported.partPath) ?? new Uint8Array()
181
- ).hadBom;
182
- parts.set(
182
+ })
183
+ ).join("");
184
+ const hadBom = decodeUtf8(
185
+ session.parts.get(imported.partPath) ?? new Uint8Array()
186
+ ).hadBom;
187
+ return encodeUtf8(ensureRootDeclarations(written, HEADER_MARKUP), hadBom);
188
+ }
189
+ function rewrittenParts(doc, session, notes) {
190
+ const edited = HEADER_FOOTER_KINDS.flatMap(
191
+ (kind) => storyChangesOf(doc, session, kind)
192
+ ).flatMap((change) => change.change === "edited" ? [change] : []);
193
+ if (edited.length === 0) return null;
194
+ return new Map(
195
+ edited.map(({ imported, current }) => [
183
196
  imported.partPath,
184
- encodeUtf8(ensureRootDeclarations(written, HEADER_MARKUP), hadBom)
185
- );
186
- }
187
- return parts.size === 0 ? null : parts;
197
+ writtenPart(imported, current, session, notes)
198
+ ])
199
+ );
188
200
  }
189
201
  var headerFooterPlanner = {
190
202
  name: "headers and footers",
191
- plan: (doc, session) => rewrittenParts(doc, session)
203
+ plan: (doc, session, context) => rewrittenParts(doc, session, context.notes)
192
204
  };
193
205
  export {
194
206
  displayPageNumber,
@@ -14,6 +14,12 @@
14
14
  * and throws over a block that cannot yield, and `identityProblems` runs the same walk for
15
15
  * `docx/invariants` and lists those blocks instead, so what the query reports and what the write
16
16
  * refuses cannot come apart.
17
+ *
18
+ * A side story is settled by its part rather than by the document: `w14:paraId` is unique within a
19
+ * part (Part 1 §11.3), so `withUniqueStoryIdentities` walks the stories one part holds as one run
20
+ * of blocks, and a name the body or another part holds is not compared. A story the part writes as
21
+ * the bytes it arrived as keeps every name it holds, since no later claimant of one could give it
22
+ * up in bytes nothing rewrites.
17
23
  */
18
24
  import { type Node as PMNode } from "prosemirror-model";
19
25
  import { type DocxExportErrorCode } from "../ooxml/errors";
@@ -55,6 +61,8 @@ export declare const IDENTITY_RULES: readonly IdentityRule[];
55
61
  export interface IdentityProblem {
56
62
  readonly code: DocxExportErrorCode;
57
63
  readonly message: string;
64
+ /** The node type standing twice, which `docx/invariants` names in the problem's reason */
65
+ readonly node: string;
58
66
  readonly pos: number;
59
67
  }
60
68
  /** The document with every later claimant of a name released, in document order */
@@ -65,3 +73,25 @@ export declare function withUniqueIdentities(doc: PMNode, rules?: readonly Ident
65
73
  * document is dropped, so a caller asking ahead of the write reads the same refusal it would throw.
66
74
  */
67
75
  export declare function identityProblems(doc: PMNode, rules?: readonly IdentityRule[]): readonly IdentityProblem[];
76
+ /** A block of a side story that cannot yield */
77
+ export interface StoryIdentityProblem extends IdentityProblem {
78
+ /** The story's place in the list the part was handed over as; `pos` stands inside that story */
79
+ readonly story: number;
80
+ }
81
+ /** One story as its part hands it over to the pass */
82
+ export interface StoryToSettle {
83
+ readonly story: PMNode;
84
+ /**
85
+ * Whether the part writes this story as the bytes it arrived as. A frozen story cannot give a
86
+ * name up, since nothing rewrites what it goes out as, so it claims its names ahead of every
87
+ * story that can.
88
+ */
89
+ readonly frozen: boolean;
90
+ }
91
+ /**
92
+ * The stories one part holds, in the order the part writes them, with every later claimant of a
93
+ * name released. A story holding no such claimant is handed back as the very same node.
94
+ */
95
+ export declare function withUniqueStoryIdentities(entries: readonly StoryToSettle[], rules?: readonly IdentityRule[]): readonly PMNode[];
96
+ /** Every block `withUniqueStoryIdentities` would refuse over, in the order the part writes them */
97
+ export declare function identityProblemsInStories(entries: readonly StoryToSettle[], rules?: readonly IdentityRule[]): readonly StoryIdentityProblem[];
@@ -103,6 +103,7 @@ function refusalOf(block, pos) {
103
103
  return {
104
104
  code: "unsupported-content",
105
105
  message: `a preserved block stands in two places (${block.type.name})`,
106
+ node: block.type.name,
106
107
  pos
107
108
  };
108
109
  }
@@ -130,12 +131,7 @@ function rewriteBlock(block, pos, walk) {
130
131
  });
131
132
  return changed ? visited.copy(Fragment.fromArray(children)) : visited;
132
133
  }
133
- function walkBlocks(doc, rules, refuse) {
134
- const walk = {
135
- rules,
136
- written: rules.map(() => /* @__PURE__ */ new Set()),
137
- refuse
138
- };
134
+ function settle(doc, walk) {
139
135
  const blocks = [];
140
136
  let changed = false;
141
137
  doc.forEach((block, offset) => {
@@ -145,21 +141,58 @@ function walkBlocks(doc, rules, refuse) {
145
141
  });
146
142
  return changed ? doc.copy(Fragment.fromArray(blocks)) : doc;
147
143
  }
144
+ function unwritten(rules) {
145
+ return rules.map(() => /* @__PURE__ */ new Set());
146
+ }
147
+ function refuseExport(problem) {
148
+ throw new DocxExportError(problem.code, problem.message);
149
+ }
148
150
  function withUniqueIdentities(doc, rules = IDENTITY_RULES) {
149
- return walkBlocks(doc, rules, (problem) => {
150
- throw new DocxExportError(problem.code, problem.message);
151
+ return settle(doc, {
152
+ rules,
153
+ written: unwritten(rules),
154
+ refuse: refuseExport
151
155
  });
152
156
  }
153
157
  function identityProblems(doc, rules = IDENTITY_RULES) {
154
158
  const problems = [];
155
- walkBlocks(doc, rules, (problem) => problems.push(problem));
159
+ settle(doc, {
160
+ rules,
161
+ written: unwritten(rules),
162
+ refuse: (problem) => problems.push(problem)
163
+ });
164
+ return problems;
165
+ }
166
+ var CLAIMS_ONLY = () => {
167
+ };
168
+ function settleStories(entries, rules, refuse) {
169
+ const written = unwritten(rules);
170
+ for (const { story, frozen } of entries) {
171
+ if (frozen) settle(story, { rules, written, refuse: CLAIMS_ONLY });
172
+ }
173
+ return entries.map(
174
+ ({ story, frozen }, index) => frozen ? story : settle(story, {
175
+ rules,
176
+ written,
177
+ refuse: (problem) => refuse({ ...problem, story: index })
178
+ })
179
+ );
180
+ }
181
+ function withUniqueStoryIdentities(entries, rules = IDENTITY_RULES) {
182
+ return settleStories(entries, rules, refuseExport);
183
+ }
184
+ function identityProblemsInStories(entries, rules = IDENTITY_RULES) {
185
+ const problems = [];
186
+ settleStories(entries, rules, (problem) => problems.push(problem));
156
187
  return problems;
157
188
  }
158
189
  export {
159
190
  IDENTITY_RULES,
160
191
  controlRule,
161
192
  identityProblems,
193
+ identityProblemsInStories,
162
194
  paragraphIdRule,
163
195
  sourceBlockRule,
164
- withUniqueIdentities
196
+ withUniqueIdentities,
197
+ withUniqueStoryIdentities
165
198
  };