@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
@@ -8,6 +8,12 @@ import {
8
8
  } from "../ooxml/xml.js";
9
9
  import { visitPreservedFragments } from "../schema/preservedFragments.js";
10
10
  import { unattributedCommentAuthors } from "../schema/protection.js";
11
+ import {
12
+ HEADER_FOOTER_KINDS,
13
+ NOTE_KINDS,
14
+ splitStoryKey,
15
+ storyKey
16
+ } from "../schema/stories.js";
11
17
  import {
12
18
  commentReferencesIn,
13
19
  commentsChanged,
@@ -22,79 +28,122 @@ import {
22
28
  } from "./comments/parts.js";
23
29
  import { unrecordedAuthors } from "./comments/people.js";
24
30
  import { currentCommentBodies } from "./comments/writing.js";
25
- import { identityProblems } from "./identities.js";
31
+ import { identityProblems, identityProblemsInStories } from "./identities.js";
26
32
  import { insertedImageSrcs } from "./media.js";
27
33
  import { canDefineNewList, newNumIds, startedLists } from "./newLists.js";
34
+ import { firstNoteReferences } from "./notes/references.js";
28
35
  import { CONTENT_TYPES_PATH } from "./packageParts.js";
36
+ import { STORY_ENTRIES_PARTS, STORY_WRITINGS } from "./partPlanners.js";
29
37
  import { lostOriginal } from "./serializePreserved.js";
30
38
  import {
31
39
  originalBlock,
32
40
  sessionOf
33
41
  } from "./session.js";
34
- var bookmarkPairs = {
35
- name: "bookmarkPairs",
36
- check(doc, session) {
37
- const problems = [];
38
- const open = /* @__PURE__ */ new Map();
39
- const used = /* @__PURE__ */ new Set();
40
- visitPreservedFragments(doc, (node, pos, xml) => {
41
- const source = xml ?? originalBlock(node, session)?.xml ?? null;
42
- if (source === null) return;
43
- if (!source.includes("bookmark")) return;
44
- let root;
45
- try {
46
- root = parseXml(
47
- session.documentPrefix + source + session.documentSuffix
48
- ).documentElement;
49
- } catch {
50
- problems.push({
42
+ import {
43
+ rewrittenStories,
44
+ storyChangesOf,
45
+ storyEntriesOf,
46
+ storyEntriesProblems,
47
+ unwrittenStoryChanges
48
+ } from "./storyParts.js";
49
+ function found(scope, pos, problem) {
50
+ return scope.story === null ? { ...problem, pos } : problem;
51
+ }
52
+ function overScopes(name, check) {
53
+ return {
54
+ name,
55
+ check: ({ scopes, session }) => scopes.flatMap((scope) => check(scope, session))
56
+ };
57
+ }
58
+ var bookmarkPairs = overScopes("bookmarkPairs", (scope, session) => {
59
+ const problems = [];
60
+ const open = /* @__PURE__ */ new Map();
61
+ const used = /* @__PURE__ */ new Set();
62
+ visitPreservedFragments(scope.doc, (node, pos, xml) => {
63
+ const source = xml ?? originalBlock(node, session)?.xml ?? null;
64
+ if (source === null) return;
65
+ if (!source.includes("bookmark")) return;
66
+ let root;
67
+ try {
68
+ root = parseXml(
69
+ session.documentPrefix + source + session.documentSuffix
70
+ ).documentElement;
71
+ } catch {
72
+ problems.push(
73
+ found(scope, pos, {
51
74
  code: "malformed-xml",
52
75
  message: "preserved bookmark XML could not be parsed",
53
- pos
54
- });
55
- return;
56
- }
57
- for (const marker of Array.from(root.getElementsByTagName("*"))) {
58
- if (marker.namespaceURI !== W_NS || marker.localName !== "bookmarkStart" && marker.localName !== "bookmarkEnd")
59
- continue;
60
- const kind = marker.localName.slice("bookmark".length);
61
- const value = attributeByLocalName(marker, "id");
62
- if (value === null || value === "") {
63
- problems.push({
76
+ reason: { kind: "unreadable-preserved-xml", story: scope.story }
77
+ })
78
+ );
79
+ return;
80
+ }
81
+ for (const marker of Array.from(root.getElementsByTagName("*"))) {
82
+ if (marker.namespaceURI !== W_NS || marker.localName !== "bookmarkStart" && marker.localName !== "bookmarkEnd")
83
+ continue;
84
+ const kind = marker.localName.slice("bookmark".length);
85
+ const value = attributeByLocalName(marker, "id");
86
+ if (value === null || value === "") {
87
+ problems.push(
88
+ found(scope, pos, {
64
89
  code: "malformed-xml",
65
90
  message: `a bookmark${kind} has no id`,
66
- pos
67
- });
68
- } else if (kind === "Start") {
69
- if (used.has(value)) {
70
- problems.push({
91
+ reason: {
92
+ kind: "unnamed-bookmark",
93
+ marker: kind === "Start" ? "start" : "end",
94
+ story: scope.story
95
+ }
96
+ })
97
+ );
98
+ } else if (kind === "Start") {
99
+ if (used.has(value)) {
100
+ problems.push(
101
+ found(scope, pos, {
71
102
  code: "malformed-xml",
72
103
  message: `bookmark ${value} has more than one start marker`,
73
- pos
74
- });
75
- } else {
76
- open.set(value, pos);
77
- used.add(value);
78
- }
79
- } else if (!open.delete(value)) {
80
- problems.push({
104
+ reason: {
105
+ kind: "repeated-bookmark-start",
106
+ id: value,
107
+ story: scope.story
108
+ }
109
+ })
110
+ );
111
+ } else {
112
+ open.set(value, pos);
113
+ used.add(value);
114
+ }
115
+ } else if (!open.delete(value)) {
116
+ problems.push(
117
+ found(scope, pos, {
81
118
  code: "malformed-xml",
82
119
  message: `bookmark ${value} ends without an earlier start marker`,
83
- pos
84
- });
85
- }
120
+ reason: {
121
+ kind: "unmatched-bookmark",
122
+ id: value,
123
+ marker: "end",
124
+ story: scope.story
125
+ }
126
+ })
127
+ );
86
128
  }
87
- });
88
- for (const [id, pos] of open) {
89
- problems.push({
129
+ }
130
+ });
131
+ for (const [id, pos] of open) {
132
+ problems.push(
133
+ found(scope, pos, {
90
134
  code: "malformed-xml",
91
135
  message: `bookmark ${id} has no end marker`,
92
- pos
93
- });
94
- }
95
- return problems;
136
+ reason: {
137
+ kind: "unmatched-bookmark",
138
+ id,
139
+ marker: "start",
140
+ story: scope.story
141
+ }
142
+ })
143
+ );
96
144
  }
97
- };
145
+ return problems;
146
+ });
98
147
  function mergeReachesPastLastRow(table) {
99
148
  let overlong = false;
100
149
  table.forEach((row, _offset, at) => {
@@ -106,113 +155,190 @@ function mergeReachesPastLastRow(table) {
106
155
  });
107
156
  return overlong;
108
157
  }
109
- var tableGrids = {
110
- name: "tableGrids",
111
- check(doc) {
112
- const problems = [];
113
- doc.descendants((node, pos) => {
114
- if (node.type.name !== "table") return true;
115
- if (mergeReachesPastLastRow(node)) {
116
- problems.push({
158
+ var tableGrids = overScopes("tableGrids", (scope) => {
159
+ const problems = [];
160
+ scope.doc.descendants((node, pos) => {
161
+ if (node.type.name !== "table") return true;
162
+ if (mergeReachesPastLastRow(node)) {
163
+ problems.push(
164
+ found(scope, pos, {
117
165
  code: "invalid-table",
118
166
  message: "a vertical merge in the table reaches past the last row",
119
- pos
120
- });
121
- }
122
- return true;
123
- });
124
- return problems;
125
- }
126
- };
167
+ reason: { kind: "vertical-merge-past-table", story: scope.story }
168
+ })
169
+ );
170
+ }
171
+ return true;
172
+ });
173
+ return problems;
174
+ });
127
175
  var CARRIES_ITS_XML = /* @__PURE__ */ new Set([
128
176
  "rawInline",
129
177
  "rawRunContent"
130
178
  ]);
131
- function lostOriginalOf(node, session) {
179
+ function lostOriginalOf(node, session, story) {
132
180
  if (CARRIES_ITS_XML.has(node.type.name)) {
133
- return typeof node.attrs.xml === "string" ? null : "a preserved element has lost its original XML";
181
+ return typeof node.attrs.xml === "string" ? null : {
182
+ message: "a preserved element has lost its original XML",
183
+ reason: {
184
+ kind: "lost-preserved-xml",
185
+ node: node.type.name,
186
+ story
187
+ }
188
+ };
134
189
  }
135
190
  if (node.type.isInGroup("preserved")) {
136
191
  if (typeof node.attrs.xml === "string") return null;
137
- return originalBlock(node, session) ? null : lostOriginal(node, session);
192
+ return originalBlock(node, session) ? null : lostOriginal(node, session, story);
138
193
  }
139
194
  return null;
140
195
  }
141
- var preservedOriginals = {
142
- name: "preservedOriginals",
143
- check(doc, session) {
196
+ var preservedOriginals = overScopes(
197
+ "preservedOriginals",
198
+ (scope, session) => {
144
199
  const problems = [];
145
- doc.descendants((node, pos) => {
146
- const message = lostOriginalOf(node, session);
147
- if (message !== null)
148
- problems.push({ code: "lost-original", message, pos });
200
+ scope.doc.descendants((node, pos) => {
201
+ const lost = lostOriginalOf(node, session, scope.story);
202
+ if (lost !== null) {
203
+ problems.push(found(scope, pos, { code: "lost-original", ...lost }));
204
+ }
149
205
  return true;
150
206
  });
151
207
  return problems;
152
208
  }
153
- };
209
+ );
154
210
  var uniqueIdentities = {
155
211
  name: "uniqueIdentities",
156
- check(doc) {
157
- return identityProblems(doc);
212
+ check({ doc, session }) {
213
+ const parts = [
214
+ ...HEADER_FOOTER_KINDS.flatMap(
215
+ (kind) => storyChangesOf(doc, session, kind).flatMap(
216
+ (change) => change.change === "edited" ? [
217
+ [
218
+ {
219
+ key: change.imported.key,
220
+ story: change.current,
221
+ frozen: false
222
+ }
223
+ ]
224
+ ] : []
225
+ )
226
+ ),
227
+ ...STORY_ENTRIES_PARTS.map((part) => storyEntriesOf(part, doc, session))
228
+ ];
229
+ return [
230
+ ...identityProblems(doc).map(
231
+ ({ code, message, node, pos }) => ({
232
+ code,
233
+ message,
234
+ reason: { kind: "duplicate-preserved-block", node, story: null },
235
+ pos
236
+ })
237
+ ),
238
+ ...parts.flatMap(
239
+ (entries) => identityProblemsInStories(entries).map(
240
+ ({ code, message, node, story }) => {
241
+ const entry = entries[story];
242
+ return {
243
+ code,
244
+ message,
245
+ reason: {
246
+ kind: "duplicate-preserved-block",
247
+ node,
248
+ story: entry === void 0 ? null : splitStoryKey(entry.key)
249
+ }
250
+ };
251
+ }
252
+ )
253
+ )
254
+ ];
255
+ }
256
+ };
257
+ var storiesHaveWriters = {
258
+ name: "storiesHaveWriters",
259
+ check({ doc, session }) {
260
+ return unwrittenStoryChanges(STORY_WRITINGS, doc, session).map(
261
+ ({ key, change }) => ({
262
+ code: "unsupported-content",
263
+ message: `the ${key} story was ${change}, and no part writer carries that into the file`,
264
+ reason: {
265
+ kind: "unwritten-story-change",
266
+ story: splitStoryKey(key),
267
+ change
268
+ }
269
+ })
270
+ );
158
271
  }
159
272
  };
273
+ function undefinedListProblems(scope, undefinedIds) {
274
+ const problems = [];
275
+ scope.doc.descendants((node, pos) => {
276
+ if (node.type.name !== "paragraph") return true;
277
+ const numId = toParagraphFormat(node.attrs.format)?.numbering?.numId;
278
+ if (numId === void 0 || !undefinedIds.delete(numId)) return false;
279
+ problems.push(
280
+ found(scope, pos, {
281
+ code: "unsupported-content",
282
+ message: `the list numbered ${numId} has no definition to be written`,
283
+ reason: { kind: "undefined-list", numId, story: scope.story }
284
+ })
285
+ );
286
+ return false;
287
+ });
288
+ return problems;
289
+ }
160
290
  var listDefinitions = {
161
291
  name: "listDefinitions",
162
- check(doc, session) {
292
+ check({ doc, session, scopes }) {
163
293
  const undefinedIds = new Set(startedLists(doc, session).unregistered);
164
294
  if (undefinedIds.size === 0) return [];
165
- const problems = [];
166
- doc.descendants((node, pos) => {
167
- if (node.type.name !== "paragraph") return true;
168
- const numId = toParagraphFormat(node.attrs.format)?.numbering?.numId;
169
- if (numId === void 0 || !undefinedIds.delete(numId)) return false;
170
- problems.push({
171
- code: "unsupported-content",
172
- message: `the list numbered ${numId} has no definition to be written`,
173
- pos
174
- });
175
- return false;
176
- });
177
- return problems;
295
+ return scopes.flatMap(
296
+ (scope) => undefinedListProblems(scope, undefinedIds)
297
+ );
178
298
  }
179
299
  };
180
- function addsCommentsPart(doc, session) {
300
+ function addedCommentsPart(doc, session) {
181
301
  const bodyChanged = commentsChanged(doc, session);
182
302
  const threadChanged = extensionsChanged(doc, session);
183
- if (!bodyChanged && !threadChanged) return false;
184
- if (commentsPart.pathIn(session) === null || session.comments.xml === null)
185
- return true;
303
+ if (!bodyChanged && !threadChanged) return null;
304
+ if (commentsPart.pathIn(session) === null || session.comments.xml === null) {
305
+ return "comments";
306
+ }
186
307
  const references = commentReferencesIn(doc);
187
- if (threadChanged && (references.size > 0 || session.comments.extendedPartPath !== null) && (commentsExtendedPart.pathIn(session) === null || session.comments.extendedXml === null))
188
- return true;
308
+ if (threadChanged && (references.size > 0 || session.comments.extendedPartPath !== null) && (commentsExtendedPart.pathIn(session) === null || session.comments.extendedXml === null)) {
309
+ return "commentsExtended";
310
+ }
189
311
  return bodyChanged && (peoplePart.pathIn(session) === null || session.comments.people.xml === null) && unrecordedAuthors(
190
312
  currentCommentBodies(references).values(),
191
313
  session.comments.people,
192
314
  unattributedCommentAuthors(session.comments.ordered)
193
- ).size > 0;
315
+ ).size > 0 ? "people" : null;
194
316
  }
195
317
  var mediaContentTypes = {
196
318
  name: "mediaContentTypes",
197
- check(doc, session) {
319
+ check({ doc, session }) {
198
320
  if (session.parts.has(CONTENT_TYPES_PATH)) return [];
199
321
  const problems = [];
200
322
  if (insertedImageSrcs(doc).length > 0) {
201
323
  problems.push({
202
324
  code: "missing-content-types",
203
- message: `cannot add an image to a package that has no ${CONTENT_TYPES_PATH}`
325
+ message: `cannot add an image to a package that has no ${CONTENT_TYPES_PATH}`,
326
+ reason: { kind: "missing-content-types", part: "media" }
204
327
  });
205
328
  }
206
329
  if (!canDefineNewList(session) && newNumIds(doc, session).length > 0) {
207
330
  problems.push({
208
331
  code: "missing-content-types",
209
- message: `cannot add a part to a package that has no ${CONTENT_TYPES_PATH}`
332
+ message: `cannot add a part to a package that has no ${CONTENT_TYPES_PATH}`,
333
+ reason: { kind: "missing-content-types", part: "numbering" }
210
334
  });
211
335
  }
212
- if (addsCommentsPart(doc, session)) {
336
+ const comments = addedCommentsPart(doc, session);
337
+ if (comments !== null) {
213
338
  problems.push({
214
339
  code: "missing-content-types",
215
- message: `cannot add a part to a package that has no ${CONTENT_TYPES_PATH}`
340
+ message: `cannot add a part to a package that has no ${CONTENT_TYPES_PATH}`,
341
+ reason: { kind: "missing-content-types", part: comments }
216
342
  });
217
343
  }
218
344
  return problems;
@@ -220,37 +346,95 @@ var mediaContentTypes = {
220
346
  };
221
347
  var commentPartRoots = {
222
348
  name: "commentPartRoots",
223
- check(doc, session) {
349
+ check({ doc, session }) {
224
350
  const problems = [];
225
351
  const { xml, extendedXml, extendedPartPath } = session.comments;
226
352
  if (xml !== null && commentsChanged(doc, session)) {
227
353
  const problem = commentsRootProblem(xml);
228
- if (problem !== null)
229
- problems.push({ code: "malformed-xml", message: problem });
354
+ if (problem !== null) {
355
+ problems.push({
356
+ code: "malformed-xml",
357
+ message: problem,
358
+ reason: { kind: "unwritable-part-root", part: "comments" }
359
+ });
360
+ }
230
361
  }
231
362
  if (extendedXml !== null && extensionsChanged(doc, session) && (commentReferencesIn(doc).size > 0 || extendedPartPath !== null)) {
232
363
  const problem = extensionsRootProblem(extendedXml);
233
- if (problem !== null)
234
- problems.push({ code: "malformed-xml", message: problem });
364
+ if (problem !== null) {
365
+ problems.push({
366
+ code: "malformed-xml",
367
+ message: problem,
368
+ reason: { kind: "unwritable-part-root", part: "commentsExtended" }
369
+ });
370
+ }
235
371
  }
236
372
  return problems;
237
373
  }
238
374
  };
375
+ var notePartRoots = {
376
+ name: "notePartRoots",
377
+ check: ({ doc, session }) => STORY_ENTRIES_PARTS.flatMap(
378
+ (part) => storyEntriesProblems(part, doc, session)
379
+ )
380
+ };
381
+ function noteKeyIn(reason) {
382
+ const story = "story" in reason ? reason.story : null;
383
+ if (story === null) return null;
384
+ const kind = NOTE_KINDS.find((candidate) => candidate === story.kind);
385
+ return kind === void 0 ? null : storyKey(kind, story.id);
386
+ }
387
+ function atNoteReferences(problems, doc) {
388
+ let references = null;
389
+ const placed = (key) => {
390
+ references ??= firstNoteReferences(
391
+ doc,
392
+ new Set(
393
+ problems.flatMap(
394
+ ({ pos, reason }) => pos === void 0 ? noteKeyIn(reason) ?? [] : []
395
+ )
396
+ )
397
+ );
398
+ return references.get(key);
399
+ };
400
+ return problems.map((problem) => {
401
+ const key = problem.pos === void 0 ? noteKeyIn(problem.reason) : null;
402
+ const pos = key === null ? void 0 : placed(key);
403
+ return pos === void 0 ? problem : { ...problem, pos };
404
+ });
405
+ }
239
406
  var EXPORT_INVARIANTS = [
240
407
  bookmarkPairs,
241
408
  tableGrids,
242
409
  preservedOriginals,
243
410
  uniqueIdentities,
244
411
  listDefinitions,
412
+ storiesHaveWriters,
245
413
  mediaContentTypes,
246
- commentPartRoots
414
+ commentPartRoots,
415
+ notePartRoots
247
416
  ];
417
+ function exportScopes(doc, session) {
418
+ return [
419
+ { doc, story: null },
420
+ ...rewrittenStories(STORY_WRITINGS, doc, session).map(({ key, story }) => ({
421
+ doc: story,
422
+ story: splitStoryKey(key)
423
+ }))
424
+ ];
425
+ }
248
426
  var answered = /* @__PURE__ */ new WeakMap();
249
427
  function problemsOf(doc, session) {
250
428
  const known = answered.get(doc);
251
429
  if (known && known.session === session) return known.problems;
252
- const problems = EXPORT_INVARIANTS.flatMap(
253
- (invariant) => invariant.check(doc, session)
430
+ const subject = {
431
+ doc,
432
+ session,
433
+ scopes: exportScopes(doc, session)
434
+ };
435
+ const problems = atNoteReferences(
436
+ EXPORT_INVARIANTS.flatMap((invariant) => invariant.check(subject)),
437
+ doc
254
438
  );
255
439
  answered.set(doc, { session, problems });
256
440
  return problems;
@@ -3,6 +3,7 @@ import { toParagraphFormat } from "../model/format.js";
3
3
  import { NEW_LISTS_ATTR, newListsOf } from "../numbering/listRegistry.js";
4
4
  import { parseNumbering } from "../numbering/parseNumbering.js";
5
5
  import { R_NS } from "../ooxml/xml.js";
6
+ import { asStoryKey, storiesOf, storyNodeOf } from "../schema/stories.js";
6
7
  import { CONTENT_TYPES_PATH } from "./packageParts.js";
7
8
  var NUMBERING_REL_TYPE = `${R_NS}/numbering`;
8
9
  var NUMBERING_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml";
@@ -15,20 +16,28 @@ function collectNumIds(node, into) {
15
16
  };
16
17
  if (visit(node)) node.descendants(visit);
17
18
  }
18
- function numIdsIn(node) {
19
+ function numIdsNow(doc) {
19
20
  const used = /* @__PURE__ */ new Set();
20
- collectNumIds(node, used);
21
+ collectNumIds(doc, used);
22
+ for (const key of Object.keys(storiesOf(doc))) {
23
+ const story = asStoryKey(key);
24
+ const node = story === null ? null : storyNodeOf(doc, story);
25
+ if (node !== null) collectNumIds(node, used);
26
+ }
21
27
  return used;
22
28
  }
23
29
  function numIdsAtOpen(session) {
24
30
  const used = /* @__PURE__ */ new Set();
25
31
  for (const block of session.blocks) collectNumIds(block.node, used);
32
+ for (const story of session.stories.values()) {
33
+ for (const block of story.blocks) collectNumIds(block.node, used);
34
+ }
26
35
  return used;
27
36
  }
28
37
  function newNumIds(doc, session) {
29
38
  const defined = parseNumbering(session.numberingXml).lists;
30
39
  const atOpen = numIdsAtOpen(session);
31
- return Array.from(numIdsIn(doc)).filter((numId) => !defined.has(numId) && !atOpen.has(numId)).sort((a, b) => a - b);
40
+ return Array.from(numIdsNow(doc)).filter((numId) => !defined.has(numId) && !atOpen.has(numId)).sort((a, b) => a - b);
32
41
  }
33
42
  function startedLists(doc, session) {
34
43
  const registered = newListsOf(doc.attrs[NEW_LISTS_ATTR]);
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The note an edit puts into a document: a new, empty one, and a copy of one the document holds.
3
+ *
4
+ * Both are side stories (`docx/story`), written back by the notes part writer (`./writing`) as an
5
+ * entry of their own, which is why a copy names none of the blocks its original was read from.
6
+ */
7
+ import { type Node as PMNode } from "prosemirror-model";
8
+ import { type NoteKind, type StoryKey } from "../../schema/stories";
9
+ import type { FormattingContext } from "../formatting";
10
+ import { type SessionStore } from "../session";
11
+ /**
12
+ * The elements that stand for a note's own number inside its entry, which is what Word draws the
13
+ * number from and what a note that keeps its number keeps (`editor/notes/noteSurface`).
14
+ */
15
+ export declare const NOTE_NUMBER_ELEMENTS: readonly string[];
16
+ /**
17
+ * The run properties a note's number is written with: the document's own style for it where the
18
+ * document defines one, and superscript where it does not.
19
+ */
20
+ export declare function noteNumberRunProps(kind: NoteKind, formatting: FormattingContext): string;
21
+ /**
22
+ * The story of a new note holding no text yet: one paragraph in the document's style for note
23
+ * text, opening with the note's number the way Word writes a note it inserts.
24
+ */
25
+ export declare function newNoteStory(kind: NoteKind, formatting: FormattingContext): PMNode;
26
+ /**
27
+ * Every id a note of this kind already answers to: the entries the opened part holds, the stories
28
+ * the document holds, and the ids the references name, whether or not a note stands behind them.
29
+ */
30
+ export declare function takenNoteIds(doc: PMNode, kind: NoteKind, reserved: ReadonlySet<StoryKey>): Set<string>;
31
+ /** The id one above the largest of these, and never below one */
32
+ export declare function nextNoteId(taken: Iterable<string>): string;
33
+ /**
34
+ * The story a copied reference's note is given: the same content, naming none of the blocks the
35
+ * original was read from and none of its paragraph ids, so the export writes it as an entry of its
36
+ * own rather than as the bytes of the original a second time (`docx/cloning`). A preserved block
37
+ * that only names its fragment takes the fragment's XML along.
38
+ */
39
+ export declare function copiedNoteStory(story: PMNode, session: SessionStore | null): PMNode;