@portone/docx-editor 0.5.1 → 0.6.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.
- package/CHANGELOG.md +47 -0
- package/CONTRIBUTING.md +8 -2
- package/README.md +1 -1
- package/dist/DocxEditor.js +87 -29
- package/dist/docx/exportDocx.d.ts +2 -1
- package/dist/docx/exportDocx.js +3 -9
- package/dist/docx/headersFooters.js +35 -23
- package/dist/docx/identities.d.ts +28 -0
- package/dist/docx/identities.js +42 -10
- package/dist/docx/importDocx.js +25 -26
- package/dist/docx/importParagraph.d.ts +1 -4
- package/dist/docx/importParagraph.js +4 -18
- package/dist/docx/invariants.d.ts +1 -1
- package/dist/docx/invariants.js +45 -4
- package/dist/docx/notes/newNote.d.ts +39 -0
- package/dist/docx/notes/newNote.js +122 -0
- package/dist/docx/notes/numbering.d.ts +27 -0
- package/dist/docx/notes/numbering.js +120 -0
- package/dist/docx/notes/reading.d.ts +58 -0
- package/dist/docx/notes/reading.js +98 -0
- package/dist/docx/notes/writing.d.ts +19 -0
- package/dist/docx/notes/writing.js +96 -0
- package/dist/docx/partPlan.d.ts +3 -0
- package/dist/docx/partPlanners.d.ts +13 -0
- package/dist/docx/partPlanners.js +48 -0
- package/dist/docx/sections.d.ts +5 -0
- package/dist/docx/sections.js +7 -2
- package/dist/docx/serializeParagraph.js +7 -2
- package/dist/docx/session.d.ts +5 -0
- package/dist/docx/session.js +6 -0
- package/dist/docx/storyParts.d.ts +112 -0
- package/dist/docx/storyParts.js +289 -0
- package/dist/editor/clipboard/htmlReader.d.ts +24 -4
- package/dist/editor/clipboard/htmlReader.js +39 -4
- package/dist/editor/clipboard/internalChannel.d.ts +30 -3
- package/dist/editor/clipboard/internalChannel.js +43 -3
- package/dist/editor/clipboard/normalizers.d.ts +35 -8
- package/dist/editor/clipboard/normalizers.js +60 -11
- package/dist/editor/clipboard/plugin.js +42 -22
- package/dist/editor/commands/comments/editing.d.ts +4 -1
- package/dist/editor/commands/comments/editing.js +1 -3
- package/dist/editor/commands/comments/model.d.ts +7 -0
- package/dist/editor/commands/index.d.ts +1 -0
- package/dist/editor/commands/index.js +18 -0
- package/dist/editor/commands/noteCommands.d.ts +47 -0
- package/dist/editor/commands/noteCommands.js +95 -0
- package/dist/editor/commands/noteQueries.d.ts +22 -3
- package/dist/editor/commands/noteQueries.js +21 -13
- package/dist/editor/createEditor.js +25 -3
- package/dist/editor/documentStyles.d.ts +3 -0
- package/dist/editor/documentStyles.js +2 -1
- package/dist/editor/editorDocument.d.ts +24 -0
- package/dist/editor/editorDocument.js +34 -4
- package/dist/editor/notes/noteSurface.d.ts +53 -0
- package/dist/editor/notes/noteSurface.js +246 -0
- package/dist/editor/plugins/commentDecorations.js +5 -4
- package/dist/editor/plugins/commentRestoration.d.ts +20 -0
- package/dist/editor/plugins/commentRestoration.js +168 -0
- package/dist/editor/plugins/compositionSelection.d.ts +20 -0
- package/dist/editor/plugins/compositionSelection.js +19 -0
- package/dist/editor/plugins/keymap.d.ts +14 -0
- package/dist/editor/plugins/keymap.js +13 -1
- package/dist/editor/plugins/noteLifecycle.d.ts +18 -0
- package/dist/editor/plugins/noteLifecycle.js +117 -0
- package/dist/editor/plugins/noteNavigation.d.ts +45 -0
- package/dist/editor/plugins/noteNavigation.js +86 -0
- package/dist/editor/plugins/noteNumbering.d.ts +13 -0
- package/dist/editor/plugins/noteNumbering.js +58 -0
- package/dist/editor/plugins/rowResize.js +2 -7
- package/dist/editor/plugins/tabLayout.js +2 -7
- package/dist/editor/stories/storyMarkup.d.ts +24 -0
- package/dist/editor/stories/storyMarkup.js +35 -0
- package/dist/editor/stories/storyState.d.ts +34 -0
- package/dist/editor/stories/storyState.js +74 -0
- package/dist/editor/stories/storyView.d.ts +115 -0
- package/dist/editor/stories/storyView.js +144 -0
- package/dist/numbering/spellers.d.ts +1 -1
- package/dist/numbering/spellers.js +6 -1
- package/dist/page/blockKinds.d.ts +23 -4
- package/dist/page/demands/footnoteDemands.d.ts +18 -0
- package/dist/page/demands/footnoteDemands.js +51 -0
- package/dist/page/demands/index.d.ts +43 -0
- package/dist/page/demands/index.js +8 -0
- package/dist/page/kinds/paragraphKind.js +7 -1
- package/dist/page/kinds/tableKind.js +9 -1
- package/dist/page/measureBlocks.d.ts +2 -0
- package/dist/page/measureBlocks.js +31 -10
- package/dist/page/pageDecorations.d.ts +9 -2
- package/dist/page/pageDecorations.js +18 -5
- package/dist/page/pageLayout.d.ts +77 -1
- package/dist/page/pageLayout.js +186 -9
- package/dist/page/usePageLayout.d.ts +57 -2
- package/dist/page/usePageLayout.js +50 -5
- package/dist/schema/attrRoles.js +3 -2
- package/dist/schema/docxSchema.js +5 -2
- package/dist/schema/editGuard.d.ts +1 -1
- package/dist/schema/guards.js +1 -2
- package/dist/schema/preservedGuards.d.ts +4 -6
- package/dist/schema/preservedGuards.js +0 -9
- package/dist/schema/stories.d.ts +34 -2
- package/dist/schema/stories.js +22 -5
- package/dist/styles/classNames.d.ts +23 -6
- package/dist/styles/classNames.js +23 -6
- package/dist/styles/inlineStyle.d.ts +7 -0
- package/dist/styles/inlineStyle.js +11 -5
- package/dist/styles/visualScale.d.ts +19 -0
- package/dist/styles/visualScale.js +26 -0
- package/dist/styles.css +96 -36
- package/dist/ui/CommentsPanel.d.ts +6 -1
- package/dist/ui/CommentsPanel.js +26 -8
- package/dist/ui/InsertImageButton.d.ts +3 -1
- package/dist/ui/InsertImageButton.js +3 -2
- package/dist/ui/TableMenu.d.ts +5 -2
- package/dist/ui/TableMenu.js +18 -1
- package/dist/ui/TextMenu.d.ts +5 -2
- package/dist/ui/TextMenu.js +21 -6
- package/dist/ui/Toolbar.d.ts +17 -2
- package/dist/ui/Toolbar.js +23 -10
- package/dist/ui/comments/useCommentRailLayout.js +2 -4
- package/dist/ui/noteItems.d.ts +21 -0
- package/dist/ui/noteItems.js +30 -0
- package/dist/ui/notes/NoteAreas.d.ts +73 -0
- package/dist/ui/notes/NoteAreas.js +131 -0
- package/dist/ui/notes/NoteList.d.ts +25 -0
- package/dist/ui/notes/NoteList.js +58 -0
- package/dist/ui/notes/StoryRow.d.ts +75 -0
- package/dist/ui/notes/StoryRow.js +122 -0
- package/dist/ui/notes/noteBands.d.ts +100 -0
- package/dist/ui/notes/noteBands.js +142 -0
- package/dist/ui/notes/noteHeights.d.ts +10 -0
- package/dist/ui/notes/noteHeights.js +24 -0
- package/dist/ui/notes/noteSeparator.d.ts +12 -0
- package/dist/ui/notes/noteSeparator.js +9 -0
- package/dist/ui/notes/useStorySurface.d.ts +68 -0
- package/dist/ui/notes/useStorySurface.js +95 -0
- package/dist/ui/runCommand.d.ts +8 -1
- package/dist/ui/runCommand.js +2 -2
- package/dist/ui/shortcutLabels.d.ts +16 -0
- package/dist/ui/shortcutLabels.js +9 -0
- package/dist/ui/usePageRoom.d.ts +13 -0
- package/dist/ui/usePageRoom.js +31 -0
- package/package.json +2 -1
- package/dist/docx/notes.d.ts +0 -27
- package/dist/docx/notes.js +0 -55
- package/dist/ui/NotesPanel.d.ts +0 -6
- package/dist/ui/NotesPanel.js +0 -33
package/dist/docx/importDocx.js
CHANGED
|
@@ -37,7 +37,12 @@ import { readLinkTargets } from "./hyperlink.js";
|
|
|
37
37
|
import { NO_IMPORT_SOURCES } from "./importParagraph.js";
|
|
38
38
|
import { readImageSources } from "./media.js";
|
|
39
39
|
import { NUMBERING_REL_TYPE } from "./newLists.js";
|
|
40
|
-
import {
|
|
40
|
+
import { withNoteLabels } from "./notes/numbering.js";
|
|
41
|
+
import {
|
|
42
|
+
readNoteNumbering,
|
|
43
|
+
readNotes,
|
|
44
|
+
specialNotesOf
|
|
45
|
+
} from "./notes/reading.js";
|
|
41
46
|
import { readPart, relatedPartPath } from "./packageParts.js";
|
|
42
47
|
import { A4_PORTRAIT } from "./pageGeometry.js";
|
|
43
48
|
import { readRelationships } from "./relationships.js";
|
|
@@ -215,25 +220,13 @@ function readDocx(input) {
|
|
|
215
220
|
);
|
|
216
221
|
const comments = readComments(parts, mainPartPath);
|
|
217
222
|
const notes = readNotes(parts, mainPartPath);
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
endnote: /* @__PURE__ */ new Map()
|
|
221
|
-
};
|
|
222
|
-
const noteLabel = (kind, id) => {
|
|
223
|
-
const labels = noteLabels[kind];
|
|
224
|
-
const existing = labels.get(id);
|
|
225
|
-
if (existing) return existing;
|
|
226
|
-
const label = `${labels.size + 1}`;
|
|
227
|
-
labels.set(id, label);
|
|
228
|
-
return label;
|
|
229
|
-
};
|
|
223
|
+
const noteNumbering = readNoteNumbering(settingsDom);
|
|
224
|
+
const specialNotes = specialNotesOf(notes);
|
|
230
225
|
const sources = {
|
|
231
226
|
images: readImageSources(parts, mainPartPath),
|
|
232
227
|
themeFonts,
|
|
233
228
|
links: readLinkTargets(parts, mainPartPath),
|
|
234
|
-
comments
|
|
235
|
-
notes,
|
|
236
|
-
noteLabel
|
|
229
|
+
comments
|
|
237
230
|
};
|
|
238
231
|
const sessionId = newSessionId();
|
|
239
232
|
const headerFooters = readHeaderFooterStories(
|
|
@@ -275,14 +268,18 @@ function readDocx(input) {
|
|
|
275
268
|
)
|
|
276
269
|
);
|
|
277
270
|
}
|
|
278
|
-
const doc =
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
271
|
+
const doc = withNoteLabels(
|
|
272
|
+
docxSchema.nodes.doc.create(
|
|
273
|
+
{
|
|
274
|
+
sectPr: scan.sectPr,
|
|
275
|
+
stories: Object.fromEntries(
|
|
276
|
+
Array.from(stories, ([key, story]) => [key, story.doc.toJSON()])
|
|
277
|
+
)
|
|
278
|
+
},
|
|
279
|
+
blockNodes
|
|
280
|
+
),
|
|
281
|
+
noteNumbering,
|
|
282
|
+
specialNotes
|
|
286
283
|
);
|
|
287
284
|
return {
|
|
288
285
|
doc,
|
|
@@ -294,7 +291,7 @@ function readDocx(input) {
|
|
|
294
291
|
documentPrefix: scan.prefix,
|
|
295
292
|
documentSuffix: scan.suffix,
|
|
296
293
|
documentHadBom: hadBom,
|
|
297
|
-
blocks:
|
|
294
|
+
blocks: doc.children.map((node, i) => ({
|
|
298
295
|
xml: scan.blocks[i]?.xml ?? "",
|
|
299
296
|
node
|
|
300
297
|
})),
|
|
@@ -308,7 +305,9 @@ function readDocx(input) {
|
|
|
308
305
|
comments,
|
|
309
306
|
commentReferenceIds: new Set(commentReferencesIn(doc).keys()),
|
|
310
307
|
headerFooterStories: headerFooters.refs,
|
|
311
|
-
stories
|
|
308
|
+
stories,
|
|
309
|
+
noteNumbering,
|
|
310
|
+
specialNotes
|
|
312
311
|
})
|
|
313
312
|
};
|
|
314
313
|
}
|
|
@@ -19,11 +19,10 @@ import type { Node as PMNode } from "prosemirror-model";
|
|
|
19
19
|
import { type ImportedComments } from "./comments/reading";
|
|
20
20
|
import { type LinkTargets } from "./hyperlink";
|
|
21
21
|
import type { ImageSources } from "./media";
|
|
22
|
-
import { type ImportedNotes, type NoteKind } from "./notes";
|
|
23
22
|
import { type ThemeFonts } from "./theme";
|
|
24
23
|
/**
|
|
25
24
|
* What the body is read with: the images its drawings can point at, the fonts a theme reference
|
|
26
|
-
* resolves to, the addresses its links can point at, and the
|
|
25
|
+
* resolves to, the addresses its links can point at, and the comments its references name.
|
|
27
26
|
* They travel together from `docx/importDocx` down through the tables to here.
|
|
28
27
|
*/
|
|
29
28
|
export interface ImportSources {
|
|
@@ -31,8 +30,6 @@ export interface ImportSources {
|
|
|
31
30
|
themeFonts: ThemeFonts;
|
|
32
31
|
links: LinkTargets;
|
|
33
32
|
comments: ImportedComments;
|
|
34
|
-
notes: ImportedNotes;
|
|
35
|
-
noteLabel: (kind: NoteKind, id: string) => string;
|
|
36
33
|
}
|
|
37
34
|
export declare const NO_IMPORT_SOURCES: ImportSources;
|
|
38
35
|
/**
|
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
preservationOf
|
|
21
21
|
} from "./importPreserved.js";
|
|
22
22
|
import { NO_IMAGES } from "./media.js";
|
|
23
|
-
import { NO_NOTES, noteById } from "./notes.js";
|
|
24
23
|
import { NO_THEME_FONTS } from "./theme.js";
|
|
25
24
|
import { wrapperFits, wrapperKindFor } from "./wrappers.js";
|
|
26
25
|
function runMark(run, themeFonts) {
|
|
@@ -47,7 +46,7 @@ function buildImage(el, marks, images) {
|
|
|
47
46
|
marks
|
|
48
47
|
);
|
|
49
48
|
}
|
|
50
|
-
function buildModelledRunChild(el, marks, images, comments
|
|
49
|
+
function buildModelledRunChild(el, marks, images, comments) {
|
|
51
50
|
switch (el.localName) {
|
|
52
51
|
case "t": {
|
|
53
52
|
const text = el.textContent ?? "";
|
|
@@ -102,7 +101,6 @@ function buildModelledRunChild(el, marks, images, comments, notes, noteLabel) {
|
|
|
102
101
|
const id = annotationId(el);
|
|
103
102
|
if (id === null) return null;
|
|
104
103
|
const kind = el.localName === "footnoteReference" ? "footnote" : "endnote";
|
|
105
|
-
const note = noteById(notes, kind, id);
|
|
106
104
|
const written = attributeByLocalName(el, "customMarkFollows");
|
|
107
105
|
const customMarkFollows = written !== null && (ST_OnOff.parse(written) ?? true);
|
|
108
106
|
return [
|
|
@@ -110,7 +108,6 @@ function buildModelledRunChild(el, marks, images, comments, notes, noteLabel) {
|
|
|
110
108
|
{
|
|
111
109
|
kind,
|
|
112
110
|
id,
|
|
113
|
-
label: note ? noteLabel(kind, id) : "?",
|
|
114
111
|
customMarkFollows,
|
|
115
112
|
referenceXml: serializeXml(el)
|
|
116
113
|
},
|
|
@@ -123,7 +120,7 @@ function buildModelledRunChild(el, marks, images, comments, notes, noteLabel) {
|
|
|
123
120
|
return null;
|
|
124
121
|
}
|
|
125
122
|
}
|
|
126
|
-
function buildRunNodes(run, images, themeFonts, comments,
|
|
123
|
+
function buildRunNodes(run, images, themeFonts, comments, wrappers = []) {
|
|
127
124
|
const mark = runMark(run, themeFonts);
|
|
128
125
|
const marks = wrappers.reduce(
|
|
129
126
|
(set, wrapper) => wrapper.addToSet(set),
|
|
@@ -134,14 +131,7 @@ function buildRunNodes(run, images, themeFonts, comments, notes, noteLabel, wrap
|
|
|
134
131
|
const policy = policyFor(child, "r");
|
|
135
132
|
if (policy.tier === "model") {
|
|
136
133
|
if (child.localName === "rPr") continue;
|
|
137
|
-
const built = buildModelledRunChild(
|
|
138
|
-
child,
|
|
139
|
-
marks,
|
|
140
|
-
images,
|
|
141
|
-
comments,
|
|
142
|
-
notes,
|
|
143
|
-
noteLabel
|
|
144
|
-
);
|
|
134
|
+
const built = buildModelledRunChild(child, marks, images, comments);
|
|
145
135
|
if (built !== null) {
|
|
146
136
|
nodes.push(...built);
|
|
147
137
|
continue;
|
|
@@ -176,9 +166,7 @@ var NO_IMPORT_SOURCES = {
|
|
|
176
166
|
images: NO_IMAGES,
|
|
177
167
|
themeFonts: NO_THEME_FONTS,
|
|
178
168
|
links: NO_LINK_TARGETS,
|
|
179
|
-
comments: NO_COMMENTS
|
|
180
|
-
notes: NO_NOTES,
|
|
181
|
-
noteLabel: () => "?"
|
|
169
|
+
comments: NO_COMMENTS
|
|
182
170
|
};
|
|
183
171
|
function buildModelledInline(child, sources, depth, wrappers) {
|
|
184
172
|
if (child.localName === "r") {
|
|
@@ -187,8 +175,6 @@ function buildModelledInline(child, sources, depth, wrappers) {
|
|
|
187
175
|
sources.images,
|
|
188
176
|
sources.themeFonts,
|
|
189
177
|
sources.comments,
|
|
190
|
-
sources.notes,
|
|
191
|
-
sources.noteLabel,
|
|
192
178
|
wrappers
|
|
193
179
|
);
|
|
194
180
|
}
|
|
@@ -17,7 +17,7 @@ import { type DocxSession, type SessionStore } from "./session";
|
|
|
17
17
|
export interface ExportProblem {
|
|
18
18
|
readonly code: DocxExportErrorCode;
|
|
19
19
|
readonly message: string;
|
|
20
|
-
/** Where the problem stands in the document. Absent for a problem of the package
|
|
20
|
+
/** Where the problem stands in the document. Absent for a problem of the package, of the session, or of a side story */
|
|
21
21
|
readonly pos?: number;
|
|
22
22
|
}
|
|
23
23
|
/** The problems as the writer sees them, for a caller already inside the writer's parser scope */
|
package/dist/docx/invariants.js
CHANGED
|
@@ -8,6 +8,7 @@ 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 { HEADER_FOOTER_KINDS } from "../schema/stories.js";
|
|
11
12
|
import {
|
|
12
13
|
commentReferencesIn,
|
|
13
14
|
commentsChanged,
|
|
@@ -22,15 +23,25 @@ import {
|
|
|
22
23
|
} from "./comments/parts.js";
|
|
23
24
|
import { unrecordedAuthors } from "./comments/people.js";
|
|
24
25
|
import { currentCommentBodies } from "./comments/writing.js";
|
|
25
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
identityProblems,
|
|
28
|
+
identityProblemsInStories
|
|
29
|
+
} from "./identities.js";
|
|
26
30
|
import { insertedImageSrcs } from "./media.js";
|
|
27
31
|
import { canDefineNewList, newNumIds, startedLists } from "./newLists.js";
|
|
28
32
|
import { CONTENT_TYPES_PATH } from "./packageParts.js";
|
|
33
|
+
import { STORY_ENTRIES_PARTS, STORY_WRITINGS } from "./partPlanners.js";
|
|
29
34
|
import { lostOriginal } from "./serializePreserved.js";
|
|
30
35
|
import {
|
|
31
36
|
originalBlock,
|
|
32
37
|
sessionOf
|
|
33
38
|
} from "./session.js";
|
|
39
|
+
import {
|
|
40
|
+
storyChangesOf,
|
|
41
|
+
storyEntriesOf,
|
|
42
|
+
storyEntriesProblems,
|
|
43
|
+
unwrittenStoryChanges
|
|
44
|
+
} from "./storyParts.js";
|
|
34
45
|
var bookmarkPairs = {
|
|
35
46
|
name: "bookmarkPairs",
|
|
36
47
|
check(doc, session) {
|
|
@@ -153,8 +164,30 @@ var preservedOriginals = {
|
|
|
153
164
|
};
|
|
154
165
|
var uniqueIdentities = {
|
|
155
166
|
name: "uniqueIdentities",
|
|
156
|
-
check(doc) {
|
|
157
|
-
|
|
167
|
+
check(doc, session) {
|
|
168
|
+
const parts = [
|
|
169
|
+
...HEADER_FOOTER_KINDS.flatMap(
|
|
170
|
+
(kind) => storyChangesOf(doc, session, kind)
|
|
171
|
+
).flatMap(
|
|
172
|
+
(change) => change.change === "edited" ? [[{ story: change.current, frozen: false }]] : []
|
|
173
|
+
),
|
|
174
|
+
...STORY_ENTRIES_PARTS.map((part) => storyEntriesOf(part, doc, session))
|
|
175
|
+
];
|
|
176
|
+
return [
|
|
177
|
+
...identityProblems(doc),
|
|
178
|
+
...parts.flatMap((entries) => identityProblemsInStories(entries)).map(({ code, message }) => ({ code, message }))
|
|
179
|
+
];
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
var storiesHaveWriters = {
|
|
183
|
+
name: "storiesHaveWriters",
|
|
184
|
+
check(doc, session) {
|
|
185
|
+
return unwrittenStoryChanges(STORY_WRITINGS, doc, session).map(
|
|
186
|
+
({ key, change }) => ({
|
|
187
|
+
code: "unsupported-content",
|
|
188
|
+
message: `the ${key} story was ${change}, and no part writer carries that into the file`
|
|
189
|
+
})
|
|
190
|
+
);
|
|
158
191
|
}
|
|
159
192
|
};
|
|
160
193
|
var listDefinitions = {
|
|
@@ -236,14 +269,22 @@ var commentPartRoots = {
|
|
|
236
269
|
return problems;
|
|
237
270
|
}
|
|
238
271
|
};
|
|
272
|
+
var notePartRoots = {
|
|
273
|
+
name: "notePartRoots",
|
|
274
|
+
check: (doc, session) => STORY_ENTRIES_PARTS.flatMap(
|
|
275
|
+
(part) => storyEntriesProblems(part, doc, session)
|
|
276
|
+
)
|
|
277
|
+
};
|
|
239
278
|
var EXPORT_INVARIANTS = [
|
|
240
279
|
bookmarkPairs,
|
|
241
280
|
tableGrids,
|
|
242
281
|
preservedOriginals,
|
|
243
282
|
uniqueIdentities,
|
|
244
283
|
listDefinitions,
|
|
284
|
+
storiesHaveWriters,
|
|
245
285
|
mediaContentTypes,
|
|
246
|
-
commentPartRoots
|
|
286
|
+
commentPartRoots,
|
|
287
|
+
notePartRoots
|
|
247
288
|
];
|
|
248
289
|
var answered = /* @__PURE__ */ new WeakMap();
|
|
249
290
|
function problemsOf(doc, session) {
|
|
@@ -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;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// src/docx/notes/newNote.ts
|
|
2
|
+
import { Fragment } from "prosemirror-model";
|
|
3
|
+
import { elementXml } from "../../ooxml/element.js";
|
|
4
|
+
import { wName } from "../../ooxml/names.js";
|
|
5
|
+
import { docxSchema } from "../../schema/index.js";
|
|
6
|
+
import {
|
|
7
|
+
NOTE_KINDS,
|
|
8
|
+
storiesOf,
|
|
9
|
+
storyKey
|
|
10
|
+
} from "../../schema/stories.js";
|
|
11
|
+
import { CLONE_POLICIES } from "../cloning.js";
|
|
12
|
+
import { originalBlock } from "../session.js";
|
|
13
|
+
import { withStyleFormats } from "../story.js";
|
|
14
|
+
var NOTE_MARKUP = {
|
|
15
|
+
footnote: {
|
|
16
|
+
textStyle: "FootnoteText",
|
|
17
|
+
referenceStyle: "FootnoteReference",
|
|
18
|
+
numberElement: "footnoteRef"
|
|
19
|
+
},
|
|
20
|
+
endnote: {
|
|
21
|
+
textStyle: "EndnoteText",
|
|
22
|
+
referenceStyle: "EndnoteReference",
|
|
23
|
+
numberElement: "endnoteRef"
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var NOTE_NUMBER_ELEMENTS = NOTE_KINDS.map(
|
|
27
|
+
(kind) => NOTE_MARKUP[kind].numberElement
|
|
28
|
+
);
|
|
29
|
+
function noteNumberRunProps(kind, formatting) {
|
|
30
|
+
const { referenceStyle } = NOTE_MARKUP[kind];
|
|
31
|
+
const property = formatting.styles.get(referenceStyle)?.type === "character" ? elementXml(wName("rStyle"), [[wName("val"), referenceStyle]]) : elementXml(wName("vertAlign"), [[wName("val"), "superscript"]]);
|
|
32
|
+
return elementXml(wName("rPr"), [], [property]);
|
|
33
|
+
}
|
|
34
|
+
function noteTextProps(kind, formatting) {
|
|
35
|
+
const { textStyle } = NOTE_MARKUP[kind];
|
|
36
|
+
return formatting.styles.get(textStyle)?.type === "paragraph" ? elementXml(
|
|
37
|
+
wName("pPr"),
|
|
38
|
+
[],
|
|
39
|
+
[elementXml(wName("pStyle"), [[wName("val"), textStyle]])]
|
|
40
|
+
) : null;
|
|
41
|
+
}
|
|
42
|
+
function newNoteStory(kind, formatting) {
|
|
43
|
+
const { numberElement } = NOTE_MARKUP[kind];
|
|
44
|
+
const number = docxSchema.nodes.rawRunContent.create(
|
|
45
|
+
{
|
|
46
|
+
xml: elementXml(wName(numberElement), []),
|
|
47
|
+
element: numberElement,
|
|
48
|
+
display: "chip",
|
|
49
|
+
text: null,
|
|
50
|
+
guarded: false
|
|
51
|
+
},
|
|
52
|
+
null,
|
|
53
|
+
[
|
|
54
|
+
docxSchema.marks.run.create({
|
|
55
|
+
rPr: noteNumberRunProps(kind, formatting)
|
|
56
|
+
})
|
|
57
|
+
]
|
|
58
|
+
);
|
|
59
|
+
const paragraph = docxSchema.nodes.paragraph.create(
|
|
60
|
+
{ pPr: noteTextProps(kind, formatting) },
|
|
61
|
+
number
|
|
62
|
+
);
|
|
63
|
+
return withStyleFormats(
|
|
64
|
+
docxSchema.nodes.doc.create(null, [paragraph]),
|
|
65
|
+
formatting
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
var DECIMAL = /^-?\d+$/;
|
|
69
|
+
function takenNoteIds(doc, kind, reserved) {
|
|
70
|
+
const prefix = storyKey(kind, "");
|
|
71
|
+
const ids = new Set(
|
|
72
|
+
[...reserved, ...Object.keys(storiesOf(doc))].flatMap(
|
|
73
|
+
(key) => key.startsWith(prefix) ? [key.slice(prefix.length)] : []
|
|
74
|
+
)
|
|
75
|
+
);
|
|
76
|
+
doc.descendants((node) => {
|
|
77
|
+
const id = node.attrs.id;
|
|
78
|
+
if (node.type === docxSchema.nodes.noteReference && node.attrs.kind === kind && typeof id === "string") {
|
|
79
|
+
ids.add(id);
|
|
80
|
+
}
|
|
81
|
+
return true;
|
|
82
|
+
});
|
|
83
|
+
return ids;
|
|
84
|
+
}
|
|
85
|
+
function nextNoteId(taken) {
|
|
86
|
+
const largest = Array.from(taken).filter((id) => DECIMAL.test(id)).map((id) => BigInt(id)).reduce((max, id) => id > max ? id : max, 0n);
|
|
87
|
+
return (largest + 1n).toString();
|
|
88
|
+
}
|
|
89
|
+
function copiedBlock(node, session) {
|
|
90
|
+
if (node.isInline) return node;
|
|
91
|
+
if (node.type === docxSchema.nodes.paragraph) {
|
|
92
|
+
return node.type.create(
|
|
93
|
+
CLONE_POLICIES.paragraph.attrs(node, "copy"),
|
|
94
|
+
node.content,
|
|
95
|
+
node.marks
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
if (node.type === docxSchema.nodes.rawBlock) {
|
|
99
|
+
const held = node.attrs.xml;
|
|
100
|
+
const xml = typeof held === "string" ? held : session === null ? void 0 : originalBlock(node, session)?.xml;
|
|
101
|
+
return xml === void 0 ? node : node.type.create({ ...node.attrs, xml, srcId: null }, null, node.marks);
|
|
102
|
+
}
|
|
103
|
+
const attrs = node.attrs.srcId === void 0 || node.attrs.srcId === null ? node.attrs : { ...node.attrs, srcId: null };
|
|
104
|
+
return node.type.create(
|
|
105
|
+
attrs,
|
|
106
|
+
Fragment.fromArray(
|
|
107
|
+
node.children.map((child) => copiedBlock(child, session))
|
|
108
|
+
),
|
|
109
|
+
node.marks
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
function copiedNoteStory(story, session) {
|
|
113
|
+
return copiedBlock(story, session);
|
|
114
|
+
}
|
|
115
|
+
export {
|
|
116
|
+
NOTE_NUMBER_ELEMENTS,
|
|
117
|
+
copiedNoteStory,
|
|
118
|
+
newNoteStory,
|
|
119
|
+
nextNoteId,
|
|
120
|
+
noteNumberRunProps,
|
|
121
|
+
takenNoteIds
|
|
122
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type Node as PMNode } from "prosemirror-model";
|
|
2
|
+
import { type StoryKey } from "../../schema/stories";
|
|
3
|
+
import { type NoteNumbering } from "./reading";
|
|
4
|
+
/**
|
|
5
|
+
* The label each note reference of the body is drawn with, by position.
|
|
6
|
+
*
|
|
7
|
+
* Each kind is counted on its own, in the order its references stand: a repeated id takes the
|
|
8
|
+
* label of its first reference since the count last started, a reference whose custom mark follows
|
|
9
|
+
* takes neither a label nor a number (§17.11.14), and one calling a separator entry is no note to
|
|
10
|
+
* count. A section's own properties lay over the settings, and a section that restarts each section
|
|
11
|
+
* counts from its start again (§17.11.19, §17.11.20), so a note referred to in an earlier section
|
|
12
|
+
* and again in this one is drawn with this section's own number, as Word draws it. A restart on
|
|
13
|
+
* each page is counted straight through, since the label would then depend on the pages, which
|
|
14
|
+
* depend in turn on how wide the labels are drawn.
|
|
15
|
+
*
|
|
16
|
+
* It reads the document and what it is handed and nothing else, so the file an edited document
|
|
17
|
+
* exports opens with the labels the editor drew.
|
|
18
|
+
*/
|
|
19
|
+
export declare function noteLabelsIn(doc: PMNode, numbering: NoteNumbering, special: ReadonlySet<StoryKey>): ReadonlyMap<number, string>;
|
|
20
|
+
/**
|
|
21
|
+
* What `noteLabelsIn` reads off the document, as one string: the kind, id, and custom mark of each
|
|
22
|
+
* reference, which section it stands in, and what each section lays over the settings. Two
|
|
23
|
+
* documents with the same signature are labelled alike under the same numbering.
|
|
24
|
+
*/
|
|
25
|
+
export declare function noteLabelSignature(doc: PMNode): string;
|
|
26
|
+
/** The document with every note reference labelled, and the same node where every label already stood */
|
|
27
|
+
export declare function withNoteLabels(doc: PMNode, numbering: NoteNumbering, special: ReadonlySet<StoryKey>): PMNode;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// src/docx/notes/numbering.ts
|
|
2
|
+
import { Fragment } from "prosemirror-model";
|
|
3
|
+
import { spellNumber } from "../../numbering/spellers.js";
|
|
4
|
+
import {
|
|
5
|
+
NOTE_KINDS,
|
|
6
|
+
storyKey
|
|
7
|
+
} from "../../schema/stories.js";
|
|
8
|
+
import { sectionsOf } from "../sections.js";
|
|
9
|
+
import {
|
|
10
|
+
withNoteProps
|
|
11
|
+
} from "./reading.js";
|
|
12
|
+
var MAX_LABEL_CHARS = 64;
|
|
13
|
+
function referencesBySection(doc) {
|
|
14
|
+
const found = [];
|
|
15
|
+
doc.forEach((block, offset, index) => {
|
|
16
|
+
block.descendants((node, pos) => {
|
|
17
|
+
if (node.type.name === "noteReference") {
|
|
18
|
+
found.push({
|
|
19
|
+
block: index,
|
|
20
|
+
reference: { pos: offset + 1 + pos, node }
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
if (found.length === 0) return [];
|
|
27
|
+
const sections = sectionsOf(doc);
|
|
28
|
+
const references = sections.map(() => []);
|
|
29
|
+
let at = 0;
|
|
30
|
+
for (const { block, reference } of found) {
|
|
31
|
+
while (block > sections[at].lastBlock) at += 1;
|
|
32
|
+
references[at].push(reference);
|
|
33
|
+
}
|
|
34
|
+
return sections.map((section, index) => ({
|
|
35
|
+
section,
|
|
36
|
+
references: references[index]
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
function kindOf(node) {
|
|
40
|
+
return node.attrs.kind === "endnote" ? "endnote" : "footnote";
|
|
41
|
+
}
|
|
42
|
+
function idOf(node) {
|
|
43
|
+
const id = node.attrs.id;
|
|
44
|
+
return typeof id === "string" ? id : null;
|
|
45
|
+
}
|
|
46
|
+
function propsIn(section, numbering) {
|
|
47
|
+
return {
|
|
48
|
+
footnote: withNoteProps(numbering.footnote, section.props.footnotePr),
|
|
49
|
+
endnote: withNoteProps(numbering.endnote, section.props.endnotePr)
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function restarted(count, props) {
|
|
53
|
+
count.next = props.start;
|
|
54
|
+
count.taken.clear();
|
|
55
|
+
}
|
|
56
|
+
function labelOf(node, count, props, special) {
|
|
57
|
+
if (node.attrs.customMarkFollows === true) return "";
|
|
58
|
+
const id = idOf(node);
|
|
59
|
+
if (id === null || special.has(storyKey(kindOf(node), id))) return "?";
|
|
60
|
+
const taken = count.taken.get(id);
|
|
61
|
+
if (taken !== void 0) return taken;
|
|
62
|
+
const label = spellNumber(count.next, props.format, MAX_LABEL_CHARS);
|
|
63
|
+
count.next += 1;
|
|
64
|
+
count.taken.set(id, label);
|
|
65
|
+
return label;
|
|
66
|
+
}
|
|
67
|
+
function noteLabelsIn(doc, numbering, special) {
|
|
68
|
+
const labels = /* @__PURE__ */ new Map();
|
|
69
|
+
const counts = {
|
|
70
|
+
footnote: { next: 1, taken: /* @__PURE__ */ new Map() },
|
|
71
|
+
endnote: { next: 1, taken: /* @__PURE__ */ new Map() }
|
|
72
|
+
};
|
|
73
|
+
referencesBySection(doc).forEach(({ section, references }, index) => {
|
|
74
|
+
const props = propsIn(section, numbering);
|
|
75
|
+
for (const kind of NOTE_KINDS) {
|
|
76
|
+
if (index === 0 || props[kind].restart === "eachSect") {
|
|
77
|
+
restarted(counts[kind], props[kind]);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
for (const { pos, node } of references) {
|
|
81
|
+
const kind = kindOf(node);
|
|
82
|
+
labels.set(pos, labelOf(node, counts[kind], props[kind], special));
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
return labels;
|
|
86
|
+
}
|
|
87
|
+
function noteLabelSignature(doc) {
|
|
88
|
+
return JSON.stringify(
|
|
89
|
+
referencesBySection(doc).map(({ section, references }) => [
|
|
90
|
+
section.props.footnotePr,
|
|
91
|
+
section.props.endnotePr,
|
|
92
|
+
references.map(({ node }) => [
|
|
93
|
+
kindOf(node),
|
|
94
|
+
idOf(node),
|
|
95
|
+
node.attrs.customMarkFollows === true
|
|
96
|
+
])
|
|
97
|
+
])
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
function relabelled(node, contentStart, labels) {
|
|
101
|
+
const children = [];
|
|
102
|
+
let changed = false;
|
|
103
|
+
node.forEach((child, offset) => {
|
|
104
|
+
const at = contentStart + offset;
|
|
105
|
+
const label = labels.get(at);
|
|
106
|
+
const next = label !== void 0 && child.attrs.label !== label ? child.type.create({ ...child.attrs, label }, null, child.marks) : child.isLeaf ? child : relabelled(child, at + 1, labels);
|
|
107
|
+
changed ||= next !== child;
|
|
108
|
+
children.push(next);
|
|
109
|
+
});
|
|
110
|
+
return changed ? node.copy(Fragment.from(children)) : node;
|
|
111
|
+
}
|
|
112
|
+
function withNoteLabels(doc, numbering, special) {
|
|
113
|
+
const labels = noteLabelsIn(doc, numbering, special);
|
|
114
|
+
return labels.size === 0 ? doc : relabelled(doc, 0, labels);
|
|
115
|
+
}
|
|
116
|
+
export {
|
|
117
|
+
noteLabelSignature,
|
|
118
|
+
noteLabelsIn,
|
|
119
|
+
withNoteLabels
|
|
120
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads the footnotes and endnotes parts the main part relates to: the entry each holds, which of
|
|
3
|
+
* them lays the page out rather than says anything, and how each kind is numbered.
|
|
4
|
+
*
|
|
5
|
+
* What a note says is a story of its own, read by `docx/story` out of the same part text.
|
|
6
|
+
*/
|
|
7
|
+
import { type NumberFormat } from "../../numbering/spellers";
|
|
8
|
+
import { type NoteKind, type StoryKey } from "../../schema/stories";
|
|
9
|
+
export type { NoteKind } from "../../schema/stories";
|
|
10
|
+
/**
|
|
11
|
+
* One entry of a notes part as it arrived. What the note says is a story of its own, held on the
|
|
12
|
+
* document node under `footnote:<id>` or `endnote:<id>` (`docx/story`).
|
|
13
|
+
*/
|
|
14
|
+
export interface ImportedNote {
|
|
15
|
+
kind: NoteKind;
|
|
16
|
+
id: string;
|
|
17
|
+
type: string | null;
|
|
18
|
+
}
|
|
19
|
+
export interface ImportedNotePart {
|
|
20
|
+
partPath: string | null;
|
|
21
|
+
/** The raw text of the part, which is what the note stories are sliced out of. null for none */
|
|
22
|
+
xml: string | null;
|
|
23
|
+
ordered: readonly ImportedNote[];
|
|
24
|
+
byId: ReadonlyMap<string, ImportedNote>;
|
|
25
|
+
}
|
|
26
|
+
export interface ImportedNotes {
|
|
27
|
+
footnotes: ImportedNotePart;
|
|
28
|
+
endnotes: ImportedNotePart;
|
|
29
|
+
}
|
|
30
|
+
/** Reads both note parts without modifying their original package bytes. */
|
|
31
|
+
export declare function readNotes(parts: Map<string, Uint8Array>, mainPartPath: string): ImportedNotes;
|
|
32
|
+
/**
|
|
33
|
+
* The entries a notes part lays the page out with rather than numbers - a separator, a
|
|
34
|
+
* continuation separator, a continuation notice (§17.11.1, §17.11.23) - as the keys their stories
|
|
35
|
+
* stand under.
|
|
36
|
+
*/
|
|
37
|
+
export declare function specialNotesOf(notes: ImportedNotes): ReadonlySet<StoryKey>;
|
|
38
|
+
/** Where the count of one kind of note goes back to its start (§17.18.74) */
|
|
39
|
+
export type NoteRestart = "continuous" | "eachSect" | "eachPage";
|
|
40
|
+
/** How one kind of note is counted (§17.11.17-20) */
|
|
41
|
+
export interface NoteNumberingProps {
|
|
42
|
+
readonly format: NumberFormat;
|
|
43
|
+
readonly start: number;
|
|
44
|
+
readonly restart: NoteRestart;
|
|
45
|
+
}
|
|
46
|
+
export type NoteNumbering = Readonly<Record<NoteKind, NoteNumberingProps>>;
|
|
47
|
+
export declare const DEFAULT_NOTE_NUMBERING: NoteNumbering;
|
|
48
|
+
/**
|
|
49
|
+
* What one `w:footnotePr` or `w:endnotePr` names, leaving out every property it does not, and null
|
|
50
|
+
* for no element at all.
|
|
51
|
+
*
|
|
52
|
+
* A format this editor has no speller for is counted in decimal, as a list marker is.
|
|
53
|
+
*/
|
|
54
|
+
export declare function readNoteProps(props: Element | null): Partial<NoteNumberingProps> | null;
|
|
55
|
+
/** The properties with whatever an override names laid over them (§17.11.5, §17.11.11) */
|
|
56
|
+
export declare function withNoteProps(base: NoteNumberingProps, override: Partial<NoteNumberingProps> | null): NoteNumberingProps;
|
|
57
|
+
/** The document-wide note properties settings.xml names (§17.11.4, §17.11.12) */
|
|
58
|
+
export declare function readNoteNumbering(settings: Document | null): NoteNumbering;
|