@portone/docx-editor 0.6.0 → 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.
Files changed (136) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/DocxEditor.js +84 -26
  3. package/dist/docx/exportDocx.d.ts +2 -1
  4. package/dist/docx/exportDocx.js +3 -9
  5. package/dist/docx/headersFooters.js +35 -23
  6. package/dist/docx/identities.d.ts +28 -0
  7. package/dist/docx/identities.js +42 -10
  8. package/dist/docx/importDocx.js +25 -26
  9. package/dist/docx/importParagraph.d.ts +1 -4
  10. package/dist/docx/importParagraph.js +4 -18
  11. package/dist/docx/invariants.d.ts +1 -1
  12. package/dist/docx/invariants.js +45 -4
  13. package/dist/docx/notes/newNote.d.ts +39 -0
  14. package/dist/docx/notes/newNote.js +122 -0
  15. package/dist/docx/notes/numbering.d.ts +27 -0
  16. package/dist/docx/notes/numbering.js +120 -0
  17. package/dist/docx/notes/reading.d.ts +58 -0
  18. package/dist/docx/notes/reading.js +98 -0
  19. package/dist/docx/notes/writing.d.ts +19 -0
  20. package/dist/docx/notes/writing.js +96 -0
  21. package/dist/docx/partPlan.d.ts +3 -0
  22. package/dist/docx/partPlanners.d.ts +13 -0
  23. package/dist/docx/partPlanners.js +48 -0
  24. package/dist/docx/sections.d.ts +5 -0
  25. package/dist/docx/sections.js +7 -2
  26. package/dist/docx/serializeParagraph.js +7 -2
  27. package/dist/docx/session.d.ts +5 -0
  28. package/dist/docx/session.js +6 -0
  29. package/dist/docx/storyParts.d.ts +112 -0
  30. package/dist/docx/storyParts.js +289 -0
  31. package/dist/editor/clipboard/htmlReader.d.ts +24 -4
  32. package/dist/editor/clipboard/htmlReader.js +39 -4
  33. package/dist/editor/clipboard/internalChannel.d.ts +30 -3
  34. package/dist/editor/clipboard/internalChannel.js +43 -3
  35. package/dist/editor/clipboard/normalizers.d.ts +35 -8
  36. package/dist/editor/clipboard/normalizers.js +60 -11
  37. package/dist/editor/clipboard/plugin.js +42 -22
  38. package/dist/editor/commands/index.d.ts +1 -0
  39. package/dist/editor/commands/index.js +18 -0
  40. package/dist/editor/commands/noteCommands.d.ts +47 -0
  41. package/dist/editor/commands/noteCommands.js +95 -0
  42. package/dist/editor/commands/noteQueries.d.ts +22 -3
  43. package/dist/editor/commands/noteQueries.js +21 -13
  44. package/dist/editor/createEditor.js +21 -3
  45. package/dist/editor/documentStyles.d.ts +3 -0
  46. package/dist/editor/documentStyles.js +2 -1
  47. package/dist/editor/editorDocument.d.ts +24 -0
  48. package/dist/editor/editorDocument.js +34 -4
  49. package/dist/editor/notes/noteSurface.d.ts +53 -0
  50. package/dist/editor/notes/noteSurface.js +246 -0
  51. package/dist/editor/plugins/compositionSelection.d.ts +20 -0
  52. package/dist/editor/plugins/compositionSelection.js +19 -0
  53. package/dist/editor/plugins/keymap.d.ts +14 -0
  54. package/dist/editor/plugins/keymap.js +13 -1
  55. package/dist/editor/plugins/noteLifecycle.d.ts +18 -0
  56. package/dist/editor/plugins/noteLifecycle.js +117 -0
  57. package/dist/editor/plugins/noteNavigation.d.ts +45 -0
  58. package/dist/editor/plugins/noteNavigation.js +86 -0
  59. package/dist/editor/plugins/noteNumbering.d.ts +13 -0
  60. package/dist/editor/plugins/noteNumbering.js +58 -0
  61. package/dist/editor/plugins/rowResize.js +2 -7
  62. package/dist/editor/plugins/tabLayout.js +2 -7
  63. package/dist/editor/stories/storyMarkup.d.ts +24 -0
  64. package/dist/editor/stories/storyMarkup.js +35 -0
  65. package/dist/editor/stories/storyState.d.ts +34 -0
  66. package/dist/editor/stories/storyState.js +74 -0
  67. package/dist/editor/stories/storyView.d.ts +115 -0
  68. package/dist/editor/stories/storyView.js +144 -0
  69. package/dist/numbering/spellers.d.ts +1 -1
  70. package/dist/numbering/spellers.js +6 -1
  71. package/dist/page/blockKinds.d.ts +23 -4
  72. package/dist/page/demands/footnoteDemands.d.ts +18 -0
  73. package/dist/page/demands/footnoteDemands.js +51 -0
  74. package/dist/page/demands/index.d.ts +43 -0
  75. package/dist/page/demands/index.js +8 -0
  76. package/dist/page/kinds/paragraphKind.js +7 -1
  77. package/dist/page/kinds/tableKind.js +9 -1
  78. package/dist/page/measureBlocks.d.ts +2 -0
  79. package/dist/page/measureBlocks.js +31 -10
  80. package/dist/page/pageDecorations.d.ts +9 -2
  81. package/dist/page/pageDecorations.js +18 -5
  82. package/dist/page/pageLayout.d.ts +77 -1
  83. package/dist/page/pageLayout.js +186 -9
  84. package/dist/page/usePageLayout.d.ts +57 -2
  85. package/dist/page/usePageLayout.js +50 -5
  86. package/dist/schema/attrRoles.js +3 -2
  87. package/dist/schema/docxSchema.js +5 -2
  88. package/dist/schema/editGuard.d.ts +1 -1
  89. package/dist/schema/guards.js +1 -2
  90. package/dist/schema/preservedGuards.d.ts +4 -6
  91. package/dist/schema/preservedGuards.js +0 -9
  92. package/dist/schema/stories.d.ts +34 -2
  93. package/dist/schema/stories.js +22 -5
  94. package/dist/styles/classNames.d.ts +21 -6
  95. package/dist/styles/classNames.js +21 -6
  96. package/dist/styles/inlineStyle.d.ts +7 -0
  97. package/dist/styles/inlineStyle.js +11 -5
  98. package/dist/styles/visualScale.d.ts +19 -0
  99. package/dist/styles/visualScale.js +26 -0
  100. package/dist/styles.css +89 -32
  101. package/dist/ui/InsertImageButton.d.ts +3 -1
  102. package/dist/ui/InsertImageButton.js +3 -2
  103. package/dist/ui/TableMenu.d.ts +5 -2
  104. package/dist/ui/TableMenu.js +18 -1
  105. package/dist/ui/TextMenu.d.ts +5 -2
  106. package/dist/ui/TextMenu.js +21 -6
  107. package/dist/ui/Toolbar.d.ts +17 -2
  108. package/dist/ui/Toolbar.js +23 -10
  109. package/dist/ui/comments/useCommentRailLayout.js +2 -4
  110. package/dist/ui/noteItems.d.ts +21 -0
  111. package/dist/ui/noteItems.js +30 -0
  112. package/dist/ui/notes/NoteAreas.d.ts +73 -0
  113. package/dist/ui/notes/NoteAreas.js +131 -0
  114. package/dist/ui/notes/NoteList.d.ts +25 -0
  115. package/dist/ui/notes/NoteList.js +58 -0
  116. package/dist/ui/notes/StoryRow.d.ts +75 -0
  117. package/dist/ui/notes/StoryRow.js +122 -0
  118. package/dist/ui/notes/noteBands.d.ts +100 -0
  119. package/dist/ui/notes/noteBands.js +142 -0
  120. package/dist/ui/notes/noteHeights.d.ts +10 -0
  121. package/dist/ui/notes/noteHeights.js +24 -0
  122. package/dist/ui/notes/noteSeparator.d.ts +12 -0
  123. package/dist/ui/notes/noteSeparator.js +9 -0
  124. package/dist/ui/notes/useStorySurface.d.ts +68 -0
  125. package/dist/ui/notes/useStorySurface.js +95 -0
  126. package/dist/ui/runCommand.d.ts +8 -1
  127. package/dist/ui/runCommand.js +2 -2
  128. package/dist/ui/shortcutLabels.d.ts +16 -0
  129. package/dist/ui/shortcutLabels.js +9 -0
  130. package/dist/ui/usePageRoom.d.ts +13 -0
  131. package/dist/ui/usePageRoom.js +31 -0
  132. package/package.json +1 -1
  133. package/dist/docx/notes.d.ts +0 -27
  134. package/dist/docx/notes.js +0 -55
  135. package/dist/ui/NotesPanel.d.ts +0 -6
  136. package/dist/ui/NotesPanel.js +0 -33
@@ -19,6 +19,7 @@
19
19
  import { type PageGeometry } from "../docx/pageGeometry";
20
20
  import type { DocumentSection, SectionProperties } from "../docx/sections";
21
21
  import type { MeasuredBlock, PageCut } from "./blockKinds";
22
+ import type { DemandBand } from "./demands";
22
23
  export type { MeasuredBlock };
23
24
  /** The paper measured in the pixels the sheet is drawn with */
24
25
  export interface PagePixels {
@@ -128,6 +129,51 @@ export interface PageStart {
128
129
  */
129
130
  crossed: boolean;
130
131
  }
132
+ /** The room one page keeps at the foot of its body for one band (`page/demands`) */
133
+ export interface PageReservation {
134
+ page: number;
135
+ band: string;
136
+ /** The ids the page keeps, in the order it took them: those carried over from the page before first */
137
+ ids: readonly string[];
138
+ /**
139
+ * Where the band starts, measured from the top of the body like a split. The bands a page keeps
140
+ * stand one under the next in the order it took them, from the end of its body above the room.
141
+ * A page that could not give its bands all the room they ask for gives them what lies under the
142
+ * text that has to stay on it, so a band starts no higher than that text ends
143
+ */
144
+ top: number;
145
+ /**
146
+ * The band's overhead and the height of each id, which the page's body ends above. A demand
147
+ * taller than an empty page's body is counted whole, though the page has less to give it
148
+ */
149
+ height: number;
150
+ }
151
+ /**
152
+ * Rows laid after the last block of the document rather than beside any place in it
153
+ * (`page/demands`), which is where the endnotes stand.
154
+ *
155
+ * They are given whatever the last page has left below its text, and go on down pages of their
156
+ * own from there. Nothing in the layout knows what they hold: a row is an id and a height, and
157
+ * what draws them is told back which page each landed on.
158
+ */
159
+ export interface TrailingRows {
160
+ /** Height set aside once, above the first row, wherever the rows begin */
161
+ readonly overhead: number;
162
+ readonly rows: readonly {
163
+ readonly id: string;
164
+ readonly height: number;
165
+ }[];
166
+ }
167
+ /** The trailing rows one page holds, top to bottom */
168
+ export interface TrailingPlacement {
169
+ page: number;
170
+ /** Where the rows begin on this page, measured from the top of the body like a split */
171
+ top: number;
172
+ /** The rows this page holds, in the order they are laid */
173
+ ids: readonly string[];
174
+ /** The rows' heights, and the overhead on the page the rows begin on */
175
+ height: number;
176
+ }
131
177
  export interface PageLayout {
132
178
  /** Only the blocks to be moved down to the next page */
133
179
  pushes: BlockPush[];
@@ -144,6 +190,10 @@ export interface PageLayout {
144
190
  * margin needs the sheet drawn down to this.
145
191
  */
146
192
  marginBottom: number;
193
+ /** What each page keeps at its foot, page by page, one entry per band a page holds */
194
+ reserved: readonly PageReservation[];
195
+ /** Where the rows laid after the last block landed, one entry per page holding any */
196
+ trailing: readonly TrailingPlacement[];
147
197
  }
148
198
  export interface PageLayoutInput {
149
199
  blocks: readonly MeasuredBlock[];
@@ -153,6 +203,16 @@ export interface PageLayoutInput {
153
203
  * unless that section is `continuous`.
154
204
  */
155
205
  sections: readonly SectionPixels[];
206
+ /**
207
+ * The bands the blocks' demands are kept in, by name. A demand naming a band not given here is
208
+ * kept nowhere, so with none given the pages are the ones the blocks alone come to
209
+ */
210
+ bands?: ReadonlyMap<string, DemandBand>;
211
+ /**
212
+ * The rows laid after the last block, which open pages of their own where the last page has no
213
+ * room left for them. Left out, the pages end with the blocks
214
+ */
215
+ trailing?: TrailingRows;
156
216
  }
157
217
  /**
158
218
  * Distributes the blocks across pages.
@@ -169,5 +229,21 @@ export interface PageLayoutInput {
169
229
  * after it and the first piece of the block the keeps end at.
170
230
  * Each block is measured against the paper of its own section, and the first block of a section
171
231
  * opens a page of its own unless the section it opens declares `continuous`.
232
+ *
233
+ * A demand (`page/demands`) asks the page its place lands on for room at the foot, and that page's
234
+ * body ends above the room: whatever would stand in it is pushed, parted or crossed onto the next
235
+ * page exactly as at the end of the paper, the demand's own piece included when the room it asks
236
+ * for does not fit beneath that piece. The page ends and page tops stay where the paper puts them,
237
+ * except the place a block taller than a page crosses, which is the end of the body above the room.
238
+ * A demand is never parted. Where the content that has to stay on a page leaves no room for it, it
239
+ * is carried whole to the top of the next page's band with every demand after it on that page, and
240
+ * past the last block a page is opened for them; one taller than an empty page's body could fit
241
+ * nowhere, so it stays where it landed. A block that opens its page is never pushed off it to make
242
+ * room, which would leave a page holding nothing but what was carried onto it.
243
+ *
244
+ * The trailing rows are laid last, under the last block on the page it ends on, and go on down
245
+ * pages of their own where that page has no room left. They stand above whatever that page keeps
246
+ * at its foot, the way the text does, and a page opened for them belongs to the section the last
247
+ * block belongs to, so it carries that section's header and footer.
172
248
  */
173
- export declare function pageLayout({ blocks, sections }: PageLayoutInput): PageLayout;
249
+ export declare function pageLayout({ blocks, sections, bands, trailing: trailingRows, }: PageLayoutInput): PageLayout;
@@ -58,6 +58,60 @@ var TOLERANCE_PX = 0.5;
58
58
  function round(value) {
59
59
  return Math.round(value * 100) / 100;
60
60
  }
61
+ var NO_BANDS = /* @__PURE__ */ new Map();
62
+ var NO_DEMANDS = [];
63
+ function emptyRoom() {
64
+ return { bands: /* @__PURE__ */ new Map(), kept: [], height: 0 };
65
+ }
66
+ function keep(bands, room, taken) {
67
+ const { demand } = taken;
68
+ const band = bands.get(demand.band);
69
+ if (!band || room.bands.get(demand.band)?.has(demand.id)) return;
70
+ const ids = room.bands.get(demand.band);
71
+ if (ids) {
72
+ ids.add(demand.id);
73
+ } else {
74
+ room.bands.set(demand.band, /* @__PURE__ */ new Set([demand.id]));
75
+ room.height += band.overhead;
76
+ }
77
+ room.kept.push(taken);
78
+ room.height += band.heights.get(demand.id) ?? 0;
79
+ }
80
+ function letGo(bands, room) {
81
+ const taken = room.kept.pop();
82
+ const band = taken && bands.get(taken.demand.band);
83
+ const ids = taken && room.bands.get(taken.demand.band);
84
+ if (!taken || !band || !ids) return void 0;
85
+ ids.delete(taken.demand.id);
86
+ room.height -= band.heights.get(taken.demand.id) ?? 0;
87
+ if (ids.size === 0) {
88
+ room.bands.delete(taken.demand.band);
89
+ room.height -= band.overhead;
90
+ }
91
+ if (room.kept.length === 0) room.height = 0;
92
+ return taken;
93
+ }
94
+ function bandHeight(band, ids) {
95
+ let height = band.overhead;
96
+ for (const id of ids) height += band.heights.get(id) ?? 0;
97
+ return height;
98
+ }
99
+ function roomAdded(bands, room, demands) {
100
+ if (demands.length === 0) return 0;
101
+ const trial = {
102
+ bands: new Map(
103
+ Array.from(room.bands, ([name, ids]) => [name, new Set(ids)])
104
+ ),
105
+ kept: [...room.kept],
106
+ height: room.height
107
+ };
108
+ for (const demand of demands) keep(bands, trial, { demand, order: 0 });
109
+ return trial.height - room.height;
110
+ }
111
+ function demandsInOrder(block) {
112
+ const demands = block.demands ?? NO_DEMANDS;
113
+ return demands.length < 2 ? demands : [...demands].sort((a, b) => a.offset - b.offset);
114
+ }
61
115
  function withinSections(pages, sections) {
62
116
  let section = null;
63
117
  let within = 0;
@@ -101,10 +155,17 @@ function keptExtents(blocks, laid) {
101
155
  settle();
102
156
  return extents;
103
157
  }
104
- function pageLayout({ blocks, sections }) {
158
+ function pageLayout({
159
+ blocks,
160
+ sections,
161
+ bands = NO_BANDS,
162
+ trailing: trailingRows
163
+ }) {
105
164
  const pushes = [];
106
165
  const cuts = [];
107
166
  const splits = [];
167
+ const reserved = [];
168
+ const trailing = [];
108
169
  let blockPos = blocks[0]?.pos ?? 0;
109
170
  const opened = [
110
171
  { page: 1, bodyStart: 0, pos: blockPos, crossed: false }
@@ -119,7 +180,9 @@ function pageLayout({ blocks, sections }) {
119
180
  splits,
120
181
  pages: withinSections(opened, sections),
121
182
  bodyHeight: 0,
122
- marginBottom: 0
183
+ marginBottom: 0,
184
+ reserved,
185
+ trailing
123
186
  };
124
187
  }
125
188
  const blockSections = blocks.map(
@@ -136,16 +199,82 @@ function pageLayout({ blocks, sections }) {
136
199
  let cursor = 0;
137
200
  let paper = laid[0]?.paper ?? roomy;
138
201
  const stepTo = (opening) => paper.pageStep - paper.marginTop + opening.marginTop;
202
+ let room = emptyRoom();
203
+ let carried = [];
204
+ let reached = 0;
205
+ let held = 0;
206
+ const reservedHere = () => Math.min(room.height, Math.max(0, pageStart + paper.bodyHeight - held));
207
+ const bodyEnd = () => pageStart + paper.bodyHeight - reservedHere();
208
+ const endsOnPage = (bottom) => bottom <= pageStart + paper.bodyHeight + TOLERANCE_PX;
209
+ const settle = (floor, onPaper = paper) => {
210
+ held = Math.max(held, floor);
211
+ while (held + room.height > pageStart + onPaper.bodyHeight + TOLERANCE_PX) {
212
+ const last = room.kept.at(-1);
213
+ if (last === void 0 || roomAdded(bands, emptyRoom(), [last.demand]) > onPaper.bodyHeight + TOLERANCE_PX) {
214
+ return;
215
+ }
216
+ letGo(bands, room);
217
+ carried.push(last);
218
+ }
219
+ };
220
+ const recordRoom = (page) => {
221
+ const held2 = [...room.bands].flatMap(([name, ids]) => {
222
+ const band = bands.get(name);
223
+ return band ? [{ name, ids, band }] : [];
224
+ }).sort((a, b) => b.band.order - a.band.order);
225
+ let top = bodyEnd();
226
+ for (const { name, ids, band } of held2) {
227
+ const height = bandHeight(band, ids);
228
+ reserved.push({
229
+ page,
230
+ band: name,
231
+ ids: [...ids],
232
+ top: round(top),
233
+ height: round(height)
234
+ });
235
+ top += height;
236
+ }
237
+ };
139
238
  const split = (y, crossed, forced, opening = paper) => {
140
239
  const page = splits.length + 2;
240
+ recordRoom(page - 1);
141
241
  splits.push({ y: round(y), page, forced, crossed });
142
242
  pageStart = crossed ? y : y + stepTo(opening);
143
243
  opened.push({ page, bodyStart: round(pageStart), pos: blockPos, crossed });
244
+ room = emptyRoom();
245
+ held = pageStart;
246
+ const waiting = carried;
247
+ carried = [];
248
+ waiting.sort((a, b) => a.order - b.order);
249
+ for (const taken of waiting) keep(bands, room, taken);
250
+ settle(pageStart, opening);
144
251
  };
145
252
  const crossTo = (y) => {
146
- while (y > pageStart + paper.bodyHeight + TOLERANCE_PX) {
147
- split(pageStart + paper.bodyHeight, true, false);
253
+ while (y > bodyEnd() + TOLERANCE_PX) {
254
+ const end = bodyEnd();
255
+ split(
256
+ end > pageStart + TOLERANCE_PX ? end : pageStart + paper.bodyHeight,
257
+ true,
258
+ false
259
+ );
260
+ }
261
+ };
262
+ const place = (bottom, demands, placeOf) => {
263
+ for (const demand of demands) {
264
+ if (!bands.has(demand.band)) continue;
265
+ if (!endsOnPage(bottom)) crossTo(placeOf(demand));
266
+ const taken = { demand, order: reached };
267
+ reached += 1;
268
+ const alreadyKept = room.bands.get(demand.band)?.has(demand.id) === true;
269
+ if (carried.length > 0 && !alreadyKept) {
270
+ carried.push(taken);
271
+ } else {
272
+ keep(bands, room, taken);
273
+ settle(placeOf(demand));
274
+ }
148
275
  }
276
+ if (endsOnPage(bottom)) settle(bottom);
277
+ crossTo(bottom);
149
278
  };
150
279
  const kept = keptExtents(blocks, laid);
151
280
  let breakAfterPrevious = false;
@@ -159,7 +288,10 @@ function pageLayout({ blocks, sections }) {
159
288
  const first = kept.get(index) ?? block.minFirstPiece;
160
289
  const overflows = top + first > pageEnd + TOLERANCE_PX;
161
290
  const fits = first <= opening.bodyHeight + TOLERANCE_PX;
162
- const push = startsPage || overflows && fits ? Math.max(0, pageEnd + stepTo(opening) - top) : 0;
291
+ const demands = demandsInOrder(block);
292
+ const firstDemands = demands.length === 0 ? NO_DEMANDS : demands.filter((demand) => demand.offset < first);
293
+ const crowded = top > pageStart + TOLERANCE_PX && top + first + roomAdded(bands, room, firstDemands) > bodyEnd() + TOLERANCE_PX && first + roomAdded(bands, emptyRoom(), firstDemands) <= opening.bodyHeight + TOLERANCE_PX;
294
+ const push = startsPage || overflows && fits || crowded ? Math.max(0, pageEnd + stepTo(opening) - top) : 0;
163
295
  if (push > 0 || startsPage) split(pageEnd, false, startsPage, opening);
164
296
  paper = opening;
165
297
  if (push > 0) {
@@ -173,15 +305,23 @@ function pageLayout({ blocks, sections }) {
173
305
  const pageBodyHeight = paper.bodyHeight;
174
306
  let added = 0;
175
307
  let pieceStart = 0;
308
+ let untaken = 0;
176
309
  for (let index2 = 0; index2 <= block.candidates.length; index2 += 1) {
177
310
  const cut = index2 === 0 ? null : block.candidates[index2 - 1];
178
- const pieceEnd = block.candidates[index2]?.offset ?? block.height;
311
+ const next = block.candidates[index2];
312
+ const pieceEnd = next?.offset ?? block.height;
313
+ const taken = untaken;
314
+ while (untaken < demands.length && (next === void 0 || (demands[untaken]?.offset ?? 0) < pieceEnd)) {
315
+ untaken += 1;
316
+ }
317
+ const pieceDemands = taken === untaken ? NO_DEMANDS : demands.slice(taken, untaken);
179
318
  const pieceTop = contentTop + pieceStart + added;
180
319
  let pieceBottom = contentTop + pieceEnd + added;
181
320
  if (cut) {
182
321
  if (cut.forced) crossTo(pieceTop);
183
322
  const pageBottom = pageStart + pageBodyHeight;
184
- const cutHere = cut.forced || pieceBottom > pageBottom + TOLERANCE_PX && pieceTop > pageStart + TOLERANCE_PX && pieceEnd - pieceStart <= pageBodyHeight + TOLERANCE_PX;
323
+ const size = pieceEnd - pieceStart;
324
+ const cutHere = cut.forced || pieceTop > pageStart + TOLERANCE_PX && (pieceBottom > pageBottom + TOLERANCE_PX && size <= pageBodyHeight + TOLERANCE_PX || pieceBottom + roomAdded(bands, room, pieceDemands) > bodyEnd() + TOLERANCE_PX && size + roomAdded(bands, emptyRoom(), pieceDemands) <= pageBodyHeight + TOLERANCE_PX);
185
325
  if (cutHere) {
186
326
  split(pageBottom, false, cut.forced);
187
327
  const height = Math.max(0, pageStart - pieceTop);
@@ -190,12 +330,47 @@ function pageLayout({ blocks, sections }) {
190
330
  pieceBottom += height + cut.repeatHeight;
191
331
  }
192
332
  }
193
- crossTo(pieceBottom);
333
+ const shift = contentTop + added;
334
+ place(pieceBottom, pieceDemands, (demand) => shift + demand.offset);
194
335
  pieceStart = pieceEnd;
195
336
  }
196
337
  cursor = contentTop + block.height + added;
197
338
  breakAfterPrevious = block.breakAfter;
198
339
  }
340
+ const drainCarried = () => {
341
+ while (carried.length > 0) {
342
+ split(pageStart + paper.bodyHeight, false, false);
343
+ }
344
+ };
345
+ drainCarried();
346
+ let laidHere;
347
+ let below = Math.max(cursor, pageStart);
348
+ for (const [at, row] of (trailingRows?.rows ?? []).entries()) {
349
+ const above = at === 0 ? trailingRows?.overhead ?? 0 : 0;
350
+ const opens = below + above + row.height > bodyEnd() + TOLERANCE_PX && below > pageStart + TOLERANCE_PX;
351
+ if (opens) {
352
+ laidHere = void 0;
353
+ split(pageStart + paper.bodyHeight, false, false);
354
+ below = pageStart;
355
+ }
356
+ const top = below + above;
357
+ if (laidHere === void 0) {
358
+ laidHere = {
359
+ page: splits.length + 1,
360
+ top: round(below),
361
+ ids: [row.id],
362
+ height: round(above + row.height)
363
+ };
364
+ trailing.push(laidHere);
365
+ } else {
366
+ laidHere.ids = [...laidHere.ids, row.id];
367
+ laidHere.height = round(laidHere.height + row.height);
368
+ }
369
+ below = top + row.height;
370
+ settle(below);
371
+ }
372
+ drainCarried();
373
+ recordRoom(splits.length + 1);
199
374
  return {
200
375
  pushes,
201
376
  cuts,
@@ -203,7 +378,9 @@ function pageLayout({ blocks, sections }) {
203
378
  pages: withinSections(opened, sections),
204
379
  // The last page is filled out in full on the paper of the section it opens with
205
380
  bodyHeight: round(pageStart + paper.bodyHeight),
206
- marginBottom: paper.marginBottom
381
+ marginBottom: paper.marginBottom,
382
+ reserved,
383
+ trailing
207
384
  };
208
385
  }
209
386
  export {
@@ -10,7 +10,8 @@
10
10
  */
11
11
  import type { EditorView } from "prosemirror-view";
12
12
  import { type RefObject } from "react";
13
- import { type SectionPixels } from "./pageLayout";
13
+ import type { DemandBand } from "./demands";
14
+ import { type SectionPixels, type TrailingRows } from "./pageLayout";
14
15
  /** One place where a page parts from the next. The position is measured on the sheet */
15
16
  export interface PageMark {
16
17
  page: number;
@@ -19,6 +20,33 @@ export interface PageMark {
19
20
  height: number;
20
21
  crossed: boolean;
21
22
  }
23
+ /** The room one page keeps at the foot of its body for one band, measured on the sheet */
24
+ export interface ReservedRoom {
25
+ band: string;
26
+ /** What the page keeps there, in the order it took them (`page/pageLayout`) */
27
+ ids: readonly string[];
28
+ top: number;
29
+ /**
30
+ * The height the page gives the band, down to the end of its body. Less than the band asks for
31
+ * where it holds more than the page could give it
32
+ */
33
+ height: number;
34
+ /** Whether the band asks for more than that height */
35
+ clipped: boolean;
36
+ }
37
+ /** The rows laid after the last block that one page holds, measured on the sheet */
38
+ export interface TrailingRoom {
39
+ /** What the page holds, in the order they are laid (`page/pageLayout`) */
40
+ ids: readonly string[];
41
+ top: number;
42
+ /**
43
+ * The height the page gives the rows, down to the end of its body. Less than they ask for where
44
+ * one of them is taller than the room the page had left
45
+ */
46
+ height: number;
47
+ /** Whether the rows ask for more than that height */
48
+ clipped: boolean;
49
+ }
22
50
  /** The paper area of one visual page, used to place its header and footer stories. */
23
51
  export interface PageFace {
24
52
  page: number;
@@ -34,6 +62,13 @@ export interface PageFace {
34
62
  left: number;
35
63
  width: number;
36
64
  crossed: boolean;
65
+ /** What the page keeps at the foot of its body, one entry per band, top to bottom */
66
+ reserved: readonly ReservedRoom[];
67
+ /**
68
+ * The rows laid after the last block that landed on this page, measured on the sheet, and null
69
+ * for a page holding none (`page/pageLayout`)
70
+ */
71
+ trailing: TrailingRoom | null;
37
72
  }
38
73
  export interface PageOverlay {
39
74
  left: number;
@@ -55,6 +90,26 @@ interface PageLayoutOptions {
55
90
  * change by the caller. A4 where a document names none, and where none is open
56
91
  */
57
92
  sections?: readonly SectionPixels[];
93
+ /**
94
+ * The bands the demands of the text are kept in (`page/demands`), by name. Left out, the pages
95
+ * are the ones the blocks alone come to, which is also what a document asking for no room
96
+ * should be laid out through. A new value lays the pages out again
97
+ */
98
+ bands?: ReadonlyMap<string, DemandBand>;
99
+ /**
100
+ * The rows laid after the last block of the document, which open pages of their own where the
101
+ * last page has no room left for them. A new value lays the pages out again
102
+ */
103
+ trailing?: TrailingRows;
104
+ /**
105
+ * Whether anything drawn over the sheet besides the body is composing, such as the view over a
106
+ * note being edited.
107
+ *
108
+ * A measurement moves the room a band is kept in, and what is drawn in that room is drawn again
109
+ * wherever it lands, which would take an open composition down with it. So the frame is taken
110
+ * again until that composition is over, the way it is for the body's own.
111
+ */
112
+ composing?: () => boolean;
58
113
  }
59
- export declare function usePageLayout({ view, layer, enabled, revision, sections, }: PageLayoutOptions): PageOverlay | null;
114
+ export declare function usePageLayout({ view, layer, enabled, revision, sections, bands, trailing, composing, }: PageLayoutOptions): PageOverlay | null;
60
115
  export {};
@@ -14,6 +14,18 @@ import {
14
14
  pageLayout,
15
15
  sectionPaperAt
16
16
  } from "./pageLayout.js";
17
+ var NO_ROOM = [];
18
+ function trailingOn(placement, contentTop, bodyBottom) {
19
+ if (placement === void 0) return null;
20
+ const top = contentTop + placement.top;
21
+ const height = Math.max(0, bodyBottom - top);
22
+ return {
23
+ ids: placement.ids,
24
+ top,
25
+ height: Math.min(placement.height, height),
26
+ clipped: placement.height > height
27
+ };
28
+ }
17
29
  function useFrameThrottle(run, hold) {
18
30
  const latest = useRef({ run, hold });
19
31
  const frame = useRef(0);
@@ -51,7 +63,10 @@ function usePageLayout({
51
63
  layer,
52
64
  enabled,
53
65
  revision,
54
- sections
66
+ sections,
67
+ bands,
68
+ trailing,
69
+ composing
55
70
  }) {
56
71
  const [overlay, setOverlay] = useState(null);
57
72
  const papers = sections === void 0 || sections.length === 0 ? A4_SECTION_PIXELS : sections;
@@ -60,7 +75,19 @@ function usePageLayout({
60
75
  const box = layer.current;
61
76
  if (!view || !box || !enabled) return;
62
77
  const measured = measureSheet(view, box);
63
- const layout = pageLayout({ blocks: measured.blocks, sections: papers });
78
+ const layout = pageLayout({
79
+ blocks: measured.blocks,
80
+ sections: papers,
81
+ bands,
82
+ trailing
83
+ });
84
+ const roomOn = /* @__PURE__ */ new Map();
85
+ for (const room of layout.reserved) {
86
+ roomOn.set(room.page, [...roomOn.get(room.page) ?? [], room]);
87
+ }
88
+ const laidOn = new Map(
89
+ layout.trailing.map((placement) => [placement.page, placement])
90
+ );
64
91
  const paperOf = (pos) => sectionPaperAt(papers, pos);
65
92
  const sheet = paperOf(0);
66
93
  setPageMarks(view, { pushes: layout.pushes, cuts: layout.cuts });
@@ -83,6 +110,7 @@ function usePageLayout({
83
110
  pages: layout.pages.map((start) => {
84
111
  const paper = paperOf(start.pos);
85
112
  const paperTop = measured.contentTop + start.bodyStart - (start.crossed ? 0 : paper.marginTop);
113
+ const bodyBottom = measured.contentTop + start.bodyStart + paper.bodyHeight;
86
114
  return {
87
115
  page: start.page,
88
116
  pos: start.pos,
@@ -91,19 +119,36 @@ function usePageLayout({
91
119
  footerTop: paperTop + paper.pageHeight - paper.marginBottom / 2,
92
120
  left: sheet.marginLeft,
93
121
  width: sheet.bodyWidth,
94
- crossed: start.crossed
122
+ crossed: start.crossed,
123
+ trailing: trailingOn(
124
+ laidOn.get(start.page),
125
+ measured.contentTop,
126
+ bodyBottom
127
+ ),
128
+ reserved: roomOn.get(start.page)?.map((room) => {
129
+ const top = measured.contentTop + room.top;
130
+ const height = Math.max(0, bodyBottom - top);
131
+ return {
132
+ band: room.band,
133
+ ids: room.ids,
134
+ top,
135
+ height: Math.min(room.height, height),
136
+ clipped: room.height > height
137
+ };
138
+ }) ?? NO_ROOM
95
139
  };
96
140
  })
97
141
  };
98
142
  setOverlay((previous) => sameOverlay(previous, next) ? previous : next);
99
143
  },
100
- () => view?.composing === true
144
+ () => view?.composing === true || composing?.() === true
101
145
  );
102
146
  useEffect(() => {
103
147
  if (enabled) remeasure();
104
- }, [remeasure, revision, enabled]);
148
+ }, [remeasure, revision, enabled, bands, trailing]);
105
149
  useEffect(() => {
106
150
  if (enabled) return;
151
+ setOverlay(null);
107
152
  layer.current?.style.removeProperty(editorCssVariables.sheetHeight);
108
153
  if (!view) return;
109
154
  setPageMarks(view, { pushes: [], cuts: [] });
@@ -103,8 +103,9 @@ var NODE_ATTR_ROLES = {
103
103
  id: { role: "source", class: "identity" },
104
104
  customMarkFollows: { role: "source", class: "model" },
105
105
  referenceXml: { role: "source", class: "preserved" },
106
- // Worked out from the notes part as the document was opened, and the writer puts back the
107
- // reference alone: a note renumbered around an edit is the same reference it was
106
+ // Worked out again from the reference order and the numbering properties after every edit that
107
+ // moves them (`docx/notes/numbering`), and the writer puts back the reference alone: a note
108
+ // renumbered around an edit is the same reference it was
108
109
  label: { role: "display", class: "derived" }
109
110
  },
110
111
  rawRunContent: {
@@ -35,6 +35,7 @@ import {
35
35
  tableStyle
36
36
  } from "../styles/inlineStyle.js";
37
37
  import { imageNodeSpec, runMarkSpec } from "./rendering.js";
38
+ import { noteName } from "./stories.js";
38
39
  import { WRAPPER_ATTRS, WRAPPER_GROUP } from "./wrappers.js";
39
40
  import { imageNodeSpec as imageNodeSpec2, runMarkSpec as runMarkSpec2 } from "./rendering.js";
40
41
  var NO_STORIES = Object.freeze({});
@@ -790,7 +791,6 @@ var docxSchema = new Schema({
790
791
  referenceXml: { default: null }
791
792
  },
792
793
  toDOM(node) {
793
- const kind = node.attrs.kind === "endnote" ? "Endnote" : "Footnote";
794
794
  const label = text(node.attrs.label) ?? "?";
795
795
  return [
796
796
  "sup",
@@ -801,7 +801,10 @@ var docxSchema = new Schema({
801
801
  "data-note-label": label,
802
802
  "data-custom-mark-follows": node.attrs.customMarkFollows === true ? "1" : void 0,
803
803
  "data-reference-xml": text(node.attrs.referenceXml),
804
- "aria-label": `${kind} ${label}`
804
+ "aria-label": noteName(
805
+ node.attrs.kind === "endnote" ? "endnote" : "footnote",
806
+ label
807
+ )
805
808
  },
806
809
  node.attrs.customMarkFollows === true ? "" : label
807
810
  ];
@@ -46,7 +46,7 @@ export type EditIntent =
46
46
  * against the places it puts that guard to the test, and a place naming a guard that no longer
47
47
  * stands has to be a mistake the compiler catches rather than an annotation answering for nothing.
48
48
  */
49
- export type EditGuardName = "protection" | "lock" | "preserved" | "note" | "section";
49
+ export type EditGuardName = "protection" | "lock" | "preserved" | "section";
50
50
  /** What every guard answers, whichever of the two judgements it is written as */
51
51
  interface GuardCommon {
52
52
  readonly name: EditGuardName;
@@ -4,7 +4,7 @@ import {
4
4
  transactionReaches
5
5
  } from "./editGuard.js";
6
6
  import { lockGuard } from "./locks.js";
7
- import { noteGuard, preservedGuard, sectionGuard } from "./preservedGuards.js";
7
+ import { preservedGuard, sectionGuard } from "./preservedGuards.js";
8
8
  import {
9
9
  isCommentNode,
10
10
  protectionAllows
@@ -37,7 +37,6 @@ var EDIT_GUARDS = [
37
37
  protectionGuard,
38
38
  lockGuard,
39
39
  preservedGuard,
40
- noteGuard,
41
40
  sectionGuard
42
41
  ];
43
42
  function judgesSteps(guard) {
@@ -1,15 +1,14 @@
1
1
  /**
2
2
  * The guards over what a document is opened with and the editor only preserves: the fragments a
3
- * file falls apart without, the reference standing where a footnote or an endnote is called, and
4
- * the paragraph that ends a section.
3
+ * file falls apart without, and the paragraph that ends a section.
5
4
  *
6
5
  * The first of those is the two ends of a bookmark range, the two ends of a permission or move
7
6
  * range, and the pieces of a field, which say what they say only in the order they stand in. None
8
7
  * of it is content the editor writes, so nothing in it can put such a thing back once it is gone,
9
8
  * and a document that lost one of a bookmark's two ends cannot be written back as a file at all
10
- * (`docx/exportDocx` refuses it). What the first two guards hold is therefore the whole list of
11
- * the nodes they answer for, in the order the document carries them; a section break, which an
12
- * edit may legitimately move from one paragraph to another, is held by its number instead.
9
+ * (`docx/exportDocx` refuses it). What that guard holds is therefore the whole list of the nodes
10
+ * it answers for, in the order the document carries them; a section break, which an edit may
11
+ * legitimately move from one paragraph to another, is held by its number instead.
13
12
  */
14
13
  import type { Node as PMNode } from "prosemirror-model";
15
14
  import { type ChangeGuard, type EditGuardName } from "./editGuard";
@@ -30,7 +29,6 @@ type Signature = (node: PMNode) => string;
30
29
  */
31
30
  export declare function preservedNodeGuard(name: EditGuardName, holds: (node: PMNode) => boolean, signature: Signature): ChangeGuard;
32
31
  export declare const preservedGuard: ChangeGuard;
33
- export declare const noteGuard: ChangeGuard;
34
32
  /**
35
33
  * A guard that lets a change through only while it leaves at least as many section breaks standing
36
34
  * as it found.