@segnavia/html 0.27.0 → 0.28.0

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 (39) hide show
  1. package/dist/bindings.d.ts +1 -1
  2. package/dist/bindings.js +4 -1
  3. package/dist/book/book-document.js +146 -65
  4. package/dist/book/book-held.js +182 -0
  5. package/dist/book/book-view.d.ts +5 -0
  6. package/dist/book/book-view.js +26 -0
  7. package/dist/book/furniture.d.ts +14 -0
  8. package/dist/book/furniture.js +36 -0
  9. package/dist/book/notes.js +18 -9
  10. package/dist/book/paginate-book.d.ts +11 -0
  11. package/dist/book/paginate-book.js +195 -0
  12. package/dist/book/paginate-columns.d.ts +8 -0
  13. package/dist/book/paginate-columns.js +119 -0
  14. package/dist/book/paginate-keeps.d.ts +4 -0
  15. package/dist/book/paginate-keeps.js +80 -0
  16. package/dist/book/paginate-marks.d.ts +7 -0
  17. package/dist/book/paginate-marks.js +34 -0
  18. package/dist/book/paginate-notes.d.ts +21 -0
  19. package/dist/book/paginate-notes.js +105 -0
  20. package/dist/book/paginate-sequence.d.ts +4 -0
  21. package/dist/book/paginate-sequence.js +267 -0
  22. package/dist/book/paginate-target.d.ts +13 -0
  23. package/dist/book/paginate-target.js +36 -0
  24. package/dist/book/paginate-types.d.ts +170 -0
  25. package/dist/book/types.d.ts +1 -1
  26. package/dist/book-css.d.ts +5 -0
  27. package/dist/book-css.js +14 -0
  28. package/dist/css-tokens.js +189 -0
  29. package/dist/index.d.ts +8 -2
  30. package/dist/index.js +7 -2
  31. package/dist/motifs.d.ts +1 -0
  32. package/dist/motifs.js +5 -1
  33. package/dist/printed-pages.d.ts +18 -4
  34. package/dist/printed-pages.js +41 -19
  35. package/dist/sanitize-css.d.ts +6 -0
  36. package/dist/sanitize-css.js +308 -0
  37. package/dist/sanitize.d.ts +1 -0
  38. package/dist/sanitize.js +37 -15
  39. package/package.json +2 -2
@@ -0,0 +1,11 @@
1
+ import { PaginateBookInput, PaginateBookResult } from "./paginate-types.js";
2
+ import { paginateColumns } from "./paginate-columns.js";
3
+ import { paginateKeeps } from "./paginate-keeps.js";
4
+ import { paginateMarks } from "./paginate-marks.js";
5
+ import { paginateNotes } from "./paginate-notes.js";
6
+ import { paginateSequence } from "./paginate-sequence.js";
7
+ //#region src/book/paginate-book.d.ts
8
+ export declare function paginateBook(input: PaginateBookInput): PaginateBookResult;
9
+ export declare const PAGINATE_FUNCTIONS: (typeof paginateColumns | typeof paginateNotes | typeof paginateKeeps | typeof paginateMarks | typeof paginateSequence)[];
10
+ export declare function paginateScript(): string;
11
+ //#endregion
@@ -0,0 +1,195 @@
1
+ import { paginateColumns } from "./paginate-columns.js";
2
+ import { paginateKeeps } from "./paginate-keeps.js";
3
+ import { paginateMarks } from "./paginate-marks.js";
4
+ import { paginateNotes } from "./paginate-notes.js";
5
+ import { paginateSequence } from "./paginate-sequence.js";
6
+ //#region src/book/paginate-book.ts
7
+ function paginateBook(input) {
8
+ const textHeight = input.geometry.textHeight;
9
+ const orphans = input.settings.orphans ?? 2;
10
+ const widows = input.settings.widows ?? 2;
11
+ function used(placed, fragments) {
12
+ return paginateColumns(placed).height + paginateNotes({
13
+ op: "space",
14
+ notes: input.notes,
15
+ geometry: input.geometry,
16
+ fragments
17
+ });
18
+ }
19
+ function withNotes(extra) {
20
+ return {
21
+ notes: input.notes,
22
+ geometry: input.geometry,
23
+ used,
24
+ ...extra
25
+ };
26
+ }
27
+ function refusal(block) {
28
+ const position = `chapter ${block.position.chapter} block ${block.position.block}`;
29
+ return {
30
+ tag: block.tag,
31
+ position: block.position,
32
+ message: `The ${block.tag} at ${position} could not establish a fit in the text area; shorten it or increase the text-area height to clear this refusal.`
33
+ };
34
+ }
35
+ function blockForNote(id) {
36
+ for (const flow of input.flows) {
37
+ if (flow.kind === "fixed") continue;
38
+ for (const candidate of flow.blocks) if (candidate.footnoteRefs?.some((ref) => ref.note === id)) return candidate;
39
+ }
40
+ }
41
+ function noteRefusal(id) {
42
+ const clears = "use a taller text area or a shorter note line to clear this refusal.";
43
+ const found = blockForNote(id);
44
+ if (!found) return {
45
+ note: id,
46
+ message: `Note ${id} could not establish a fit for a note line in the text area; ${clears}`
47
+ };
48
+ const where = `chapter ${found.position.chapter} block ${found.position.block}`;
49
+ return {
50
+ tag: found.tag,
51
+ position: found.position,
52
+ message: `The ${found.tag} at ${where} could not establish a fit for a note line in the text area; ${clears}`
53
+ };
54
+ }
55
+ function lineTooTall(unit) {
56
+ return unit.bottom - unit.top > textHeight;
57
+ }
58
+ function greedyFill(placed, fragments, units, index) {
59
+ let next = index;
60
+ let notes = fragments;
61
+ while (next < units.length) {
62
+ const unit = units[next];
63
+ if (!unit || lineTooTall(unit)) break;
64
+ const allocated = paginateNotes(withNotes({
65
+ op: "allocate",
66
+ placed,
67
+ fragments: notes,
68
+ unit
69
+ }));
70
+ if (!allocated) break;
71
+ placed.push(unit);
72
+ notes = allocated;
73
+ next += 1;
74
+ }
75
+ return {
76
+ placed,
77
+ fragments: notes,
78
+ index: next
79
+ };
80
+ }
81
+ function forcePlace(unit) {
82
+ if (lineTooTall(unit)) return {
83
+ placed: [],
84
+ fragments: [],
85
+ index: 0,
86
+ pending: [],
87
+ refusal: refusal(unit.block)
88
+ };
89
+ const allocated = paginateNotes(withNotes({
90
+ op: "allocate",
91
+ placed: [],
92
+ fragments: [],
93
+ unit
94
+ }));
95
+ if (!allocated) return {
96
+ placed: [],
97
+ fragments: [],
98
+ index: 0,
99
+ pending: [],
100
+ refusal: refusal(unit.block)
101
+ };
102
+ return {
103
+ placed: [unit],
104
+ fragments: allocated,
105
+ index: 0,
106
+ pending: paginateNotes(withNotes({
107
+ op: "leftover",
108
+ fragments: allocated
109
+ }))
110
+ };
111
+ }
112
+ function fillPage(units, index, pending) {
113
+ const held = paginateNotes(withNotes({
114
+ op: "continue",
115
+ pending
116
+ }));
117
+ const incoming = units[index];
118
+ if (held.leftover.length === 0 && incoming && lineTooTall(incoming)) return {
119
+ placed: [],
120
+ fragments: [],
121
+ index,
122
+ pending,
123
+ refusal: refusal(incoming.block)
124
+ };
125
+ const stuck = held.fragments.length === 0 ? held.leftover[0] : void 0;
126
+ if (stuck) return {
127
+ placed: [],
128
+ fragments: [],
129
+ index,
130
+ pending: held.leftover,
131
+ refusal: noteRefusal(stuck.id)
132
+ };
133
+ if (held.leftover.length > 0) return {
134
+ placed: [],
135
+ fragments: held.fragments,
136
+ index,
137
+ pending: held.leftover
138
+ };
139
+ const filled = greedyFill([], held.fragments, units, index);
140
+ const placed = paginateKeeps(filled.placed, units[filled.index], textHeight, orphans, widows, held.fragments.length > 0);
141
+ const nextIndex = filled.index - (filled.placed.length - placed.length);
142
+ const fragments = paginateNotes(withNotes({
143
+ op: "recompute",
144
+ placed,
145
+ seed: held.fragments
146
+ }));
147
+ if (placed.length > 0 || fragments.length > 0) return {
148
+ placed,
149
+ fragments,
150
+ index: nextIndex,
151
+ pending: paginateNotes(withNotes({
152
+ op: "leftover",
153
+ fragments
154
+ }))
155
+ };
156
+ const unit = units[nextIndex];
157
+ const leftoverNote = pending[0];
158
+ if (!unit && leftoverNote) return {
159
+ placed: [],
160
+ fragments: [],
161
+ index: nextIndex,
162
+ pending,
163
+ refusal: noteRefusal(leftoverNote.id)
164
+ };
165
+ if (!unit) return {
166
+ placed: [],
167
+ fragments: [],
168
+ index: nextIndex,
169
+ pending,
170
+ done: true
171
+ };
172
+ const forced = forcePlace(unit);
173
+ if (forced.refusal) return forced;
174
+ return {
175
+ placed: forced.placed,
176
+ fragments: forced.fragments,
177
+ index: nextIndex + 1,
178
+ pending: forced.pending
179
+ };
180
+ }
181
+ return paginateSequence(input, fillPage);
182
+ }
183
+ const PAGINATE_FUNCTIONS = [
184
+ paginateColumns,
185
+ paginateNotes,
186
+ paginateKeeps,
187
+ paginateMarks,
188
+ paginateSequence,
189
+ paginateBook
190
+ ];
191
+ function paginateScript() {
192
+ return `${PAGINATE_FUNCTIONS.map((fn) => fn.toString()).join("\n")}\nvar paginateBook=${paginateBook.name};`;
193
+ }
194
+ //#endregion
195
+ export { PAGINATE_FUNCTIONS, paginateBook, paginateScript };
@@ -0,0 +1,8 @@
1
+ import { PaginateUnit, PaginateWindow } from "./paginate-types.js";
2
+ //#region src/book/paginate-columns.d.ts
3
+ export declare function paginateColumns(placed: PaginateUnit[]): {
4
+ windows: PaginateWindow[];
5
+ height: number;
6
+ headerRepeat: number;
7
+ };
8
+ //#endregion
@@ -0,0 +1,119 @@
1
+ //#region src/book/paginate-columns.ts
2
+ function paginateColumns(placed) {
3
+ function columnCount(block) {
4
+ return block.columns ?? 1;
5
+ }
6
+ function headerFor() {
7
+ const first = placed[0];
8
+ if (first?.block.kind !== "table" || first.index === 0) return 0;
9
+ return first.block.headerHeight ?? 0;
10
+ }
11
+ function sizes(count, columns) {
12
+ const base = Math.floor(count / columns);
13
+ const extra = count % columns;
14
+ return Array.from({ length: columns }, (_, index) => base + (index < extra ? 1 : 0));
15
+ }
16
+ function lineHeight(unit) {
17
+ return unit.bottom - unit.top;
18
+ }
19
+ function edgeStart(unit) {
20
+ return unit.prevBottom === void 0 ? unit.top : (unit.top + unit.prevBottom) / 2;
21
+ }
22
+ function edgeEnd(unit) {
23
+ return unit.nextTop === void 0 ? unit.bottom : (unit.bottom + unit.nextTop) / 2;
24
+ }
25
+ const headerRepeat = headerFor();
26
+ const windows = [];
27
+ let cursorBottom;
28
+ let cursorGalley;
29
+ let lowest = 0;
30
+ function beginY(index) {
31
+ const unit = placed[index];
32
+ if (!unit || cursorBottom === void 0 || cursorGalley === void 0) return 0;
33
+ return cursorBottom + (unit.top - cursorGalley);
34
+ }
35
+ function stack(from, to, startY) {
36
+ let y = startY;
37
+ let tallest = startY;
38
+ let lastBottom = startY;
39
+ for (let index = from; index < to; index += 1) {
40
+ const unit = placed[index];
41
+ const previous = index > from ? placed[index - 1] : void 0;
42
+ if (!unit) break;
43
+ if (previous) y += lineHeight(previous) + (unit.top - previous.bottom);
44
+ lastBottom = y + lineHeight(unit);
45
+ if (lastBottom > tallest) tallest = lastBottom;
46
+ }
47
+ return {
48
+ tallest,
49
+ lastBottom
50
+ };
51
+ }
52
+ function emit(from, to, startY, column, columns) {
53
+ const first = placed[from];
54
+ const last = placed[to - 1];
55
+ if (!first || !last) return;
56
+ const start = edgeStart(first);
57
+ windows.push({
58
+ flow: first.flowId,
59
+ start,
60
+ end: edgeEnd(last),
61
+ top: startY - (first.top - start) + headerRepeat,
62
+ column,
63
+ columns
64
+ });
65
+ }
66
+ function remember(bottom, last) {
67
+ cursorBottom = bottom;
68
+ if (last) cursorGalley = last.bottom;
69
+ }
70
+ function raise(height) {
71
+ if (height > lowest) lowest = height;
72
+ }
73
+ function layoutRun(from, to, columns) {
74
+ const startY = beginY(from);
75
+ if (columns <= 1) {
76
+ const laid = stack(from, to, startY);
77
+ emit(from, to, startY, 0, 1);
78
+ remember(laid.lastBottom, placed[to - 1]);
79
+ raise(laid.tallest);
80
+ return;
81
+ }
82
+ const parts = sizes(to - from, columns);
83
+ let offset = 0;
84
+ let tallest = startY;
85
+ for (let column = 0; column < columns; column += 1) {
86
+ const size = parts[column] ?? 0;
87
+ const start = from + offset;
88
+ const end = start + size;
89
+ offset += size;
90
+ if (size === 0) continue;
91
+ const laid = stack(start, end, startY);
92
+ emit(start, end, startY, column, columns);
93
+ if (laid.tallest > tallest) tallest = laid.tallest;
94
+ }
95
+ remember(tallest, placed[to - 1]);
96
+ raise(tallest);
97
+ }
98
+ let index = 0;
99
+ while (index < placed.length) {
100
+ const unit = placed[index];
101
+ if (!unit) break;
102
+ const columns = columnCount(unit.block);
103
+ let end = index + 1;
104
+ while (end < placed.length) {
105
+ const next = placed[end];
106
+ if (!next || columnCount(next.block) !== columns) break;
107
+ end += 1;
108
+ }
109
+ layoutRun(index, end, columns);
110
+ index = end;
111
+ }
112
+ return {
113
+ windows,
114
+ height: headerRepeat + lowest,
115
+ headerRepeat
116
+ };
117
+ }
118
+ //#endregion
119
+ export { paginateColumns };
@@ -0,0 +1,4 @@
1
+ import { PaginateUnit } from "./paginate-types.js";
2
+ //#region src/book/paginate-keeps.d.ts
3
+ export declare function paginateKeeps(placed: PaginateUnit[], next: PaginateUnit | undefined, textHeight: number, orphans: number, widows: number, allowEmpty?: boolean): PaginateUnit[];
4
+ //#endregion
@@ -0,0 +1,80 @@
1
+ import { paginateColumns } from "./paginate-columns.js";
2
+ //#region src/book/paginate-keeps.ts
3
+ function paginateKeeps(placed, next, textHeight, orphans, widows, allowEmpty) {
4
+ function sameBlock(left, right) {
5
+ return left.position.chapter === right.position.chapter && left.position.block === right.position.block;
6
+ }
7
+ function countOf(held, block) {
8
+ let count = 0;
9
+ for (const unit of held) if (sameBlock(unit.block, block)) count += 1;
10
+ return count;
11
+ }
12
+ function dropBlock(held, block) {
13
+ return held.filter((unit) => !sameBlock(unit.block, block));
14
+ }
15
+ function kept(dropped) {
16
+ if (dropped.length > 0) return dropped;
17
+ return allowEmpty ? dropped : void 0;
18
+ }
19
+ function layoutHeight(block) {
20
+ const units = [];
21
+ for (const [index, line] of block.lines.entries()) units.push({
22
+ flowId: "",
23
+ block,
24
+ index,
25
+ top: line.top,
26
+ bottom: line.bottom,
27
+ refs: []
28
+ });
29
+ return paginateColumns(units).height;
30
+ }
31
+ function keepWithNext(block) {
32
+ return block.kind === "heading" || Boolean(block.keepWithNext);
33
+ }
34
+ function keepTogether(block) {
35
+ if (!(block.kind === "figure" || block.kind === "table" || Boolean(block.keepTogether))) return false;
36
+ return layoutHeight(block) <= textHeight;
37
+ }
38
+ function orphansOf(block) {
39
+ return block.orphans ?? orphans;
40
+ }
41
+ function widowsOf(block) {
42
+ return block.widows ?? widows;
43
+ }
44
+ function rewindTogether(held, last) {
45
+ if (!keepTogether(last.block) || countOf(held, last.block) >= last.block.lines.length) return void 0;
46
+ return kept(dropBlock(held, last.block));
47
+ }
48
+ function rewindOrphans(held, last) {
49
+ const block = last.block;
50
+ if (block.kind === "table") return void 0;
51
+ const heldCount = countOf(held, block);
52
+ if (!held.some((unit) => sameBlock(unit.block, block) && unit.index === 0) || heldCount >= block.lines.length || heldCount >= orphansOf(block)) return void 0;
53
+ return kept(dropBlock(held, block));
54
+ }
55
+ function rewindWidows(held, last) {
56
+ const block = last.block;
57
+ if (block.kind === "table") return void 0;
58
+ const remaining = block.lines.length - last.index - 1;
59
+ const needWidows = widowsOf(block);
60
+ if (remaining <= 0 || remaining >= needWidows) return void 0;
61
+ const need = needWidows - remaining;
62
+ return kept(countOf(held, block) - need <= 0 ? dropBlock(held, block) : held.slice(0, held.length - need));
63
+ }
64
+ function rewindHeading(held, last, following) {
65
+ const other = !following || !sameBlock(following.block, last.block);
66
+ if (!keepWithNext(last.block) || countOf(held, last.block) !== last.block.lines.length || !other) return void 0;
67
+ return kept(dropBlock(held, last.block));
68
+ }
69
+ let result = placed;
70
+ for (let pass = 0; pass < 8; pass += 1) {
71
+ const last = result[result.length - 1];
72
+ if (!last) return result;
73
+ const nextResult = rewindTogether(result, last) ?? rewindOrphans(result, last) ?? rewindWidows(result, last) ?? rewindHeading(result, last, next);
74
+ if (!nextResult) return result;
75
+ result = nextResult;
76
+ }
77
+ return result;
78
+ }
79
+ //#endregion
80
+ export { paginateKeeps };
@@ -0,0 +1,7 @@
1
+ import { PaginatePageMarks, PaginateUnit } from "./paginate-types.js";
2
+ //#region src/book/paginate-marks.d.ts
3
+ export declare function paginateMarks(placed: PaginateUnit[], carried: PaginatePageMarks): {
4
+ marks: PaginatePageMarks;
5
+ carried: PaginatePageMarks;
6
+ };
7
+ //#endregion
@@ -0,0 +1,34 @@
1
+ //#region src/book/paginate-marks.ts
2
+ function paginateMarks(placed, carried) {
3
+ const onPage = Object.create(null);
4
+ for (const unit of placed) for (const mark of unit.block.marks ?? []) {
5
+ if (mark.line !== unit.index) continue;
6
+ const current = onPage[mark.name];
7
+ if (!current) onPage[mark.name] = {
8
+ first: mark.value,
9
+ last: mark.value
10
+ };
11
+ else current.last = mark.value;
12
+ }
13
+ const marks = Object.assign(Object.create(null), onPage);
14
+ for (const name of Object.keys(carried)) {
15
+ if (marks[name] !== void 0) continue;
16
+ const last = carried[name]?.last;
17
+ if (last === void 0) continue;
18
+ marks[name] = {
19
+ first: last,
20
+ last
21
+ };
22
+ }
23
+ const next = Object.assign(Object.create(null), carried);
24
+ for (const name of Object.keys(onPage)) {
25
+ const value = onPage[name];
26
+ if (value) next[name] = value;
27
+ }
28
+ return {
29
+ marks,
30
+ carried: next
31
+ };
32
+ }
33
+ //#endregion
34
+ export { paginateMarks };
@@ -0,0 +1,21 @@
1
+ import { PaginateNotesOp, PaginatePageNote } from "./paginate-types.js";
2
+ //#region src/book/paginate-notes.d.ts
3
+ export declare function paginateNotes(payload: Extract<PaginateNotesOp, {
4
+ op: 'space';
5
+ }>): number;
6
+ export declare function paginateNotes(payload: Extract<PaginateNotesOp, {
7
+ op: 'continue';
8
+ }>): {
9
+ fragments: PaginatePageNote[];
10
+ leftover: PaginatePageNote[];
11
+ };
12
+ export declare function paginateNotes(payload: Extract<PaginateNotesOp, {
13
+ op: 'allocate';
14
+ }>): PaginatePageNote[] | undefined;
15
+ export declare function paginateNotes(payload: Extract<PaginateNotesOp, {
16
+ op: 'recompute';
17
+ }>): PaginatePageNote[];
18
+ export declare function paginateNotes(payload: Extract<PaginateNotesOp, {
19
+ op: 'leftover';
20
+ }>): PaginatePageNote[];
21
+ //#endregion
@@ -0,0 +1,105 @@
1
+ //#region src/book/paginate-notes.ts
2
+ function paginateNotes(payload) {
3
+ const notesById = new Map(payload.notes.map((note) => [note.id, note]));
4
+ const notesGap = payload.geometry.notesGap;
5
+ const textHeight = payload.geometry.textHeight;
6
+ function noteEnd(id) {
7
+ return notesById.get(id)?.lines.length ?? 0;
8
+ }
9
+ function fragmentHeight(fragment) {
10
+ const note = notesById.get(fragment.id);
11
+ const start = note?.lines[fragment.start];
12
+ const end = note?.lines[fragment.end - 1];
13
+ return start && end ? end.bottom - start.top : 0;
14
+ }
15
+ function notesSpace(fragments) {
16
+ if (fragments.length === 0) return 0;
17
+ let height = 0;
18
+ for (const fragment of fragments) height += fragmentHeight(fragment);
19
+ return notesGap + height;
20
+ }
21
+ function measure(placed, fragments) {
22
+ if (payload.op === "continue" || payload.op === "allocate" || payload.op === "recompute") return payload.used(placed, fragments);
23
+ return notesSpace(fragments);
24
+ }
25
+ function makeFragment(id, start, end) {
26
+ return {
27
+ id,
28
+ whole: start === 0 && end === noteEnd(id),
29
+ start,
30
+ end
31
+ };
32
+ }
33
+ function leftoverFrom(fragments) {
34
+ return fragments.flatMap((fragment) => fragment.end < noteEnd(fragment.id) ? [makeFragment(fragment.id, fragment.end, noteEnd(fragment.id))] : []);
35
+ }
36
+ function takeContinued(continued) {
37
+ if (continued.length === 0) return {
38
+ fragments: [],
39
+ leftover: []
40
+ };
41
+ const fragments = [];
42
+ const leftover = [];
43
+ let pending = true;
44
+ for (const item of continued) {
45
+ const total = noteEnd(item.id);
46
+ if (!pending) {
47
+ leftover.push(item);
48
+ continue;
49
+ }
50
+ const whole = makeFragment(item.id, item.start, total);
51
+ if (measure([], [...fragments, whole]) <= textHeight) {
52
+ fragments.push(whole);
53
+ continue;
54
+ }
55
+ let end = item.start;
56
+ while (end < total && measure([], [...fragments, makeFragment(item.id, item.start, end + 1)]) <= textHeight) end += 1;
57
+ if (end > item.start) fragments.push(makeFragment(item.id, item.start, end));
58
+ if (end < total) leftover.push(makeFragment(item.id, end, total));
59
+ pending = false;
60
+ }
61
+ return {
62
+ fragments,
63
+ leftover
64
+ };
65
+ }
66
+ function fitNote(trial, fragments, id) {
67
+ const total = noteEnd(id);
68
+ if (total === 0) return void 0;
69
+ const whole = makeFragment(id, 0, total);
70
+ if (measure(trial, [...fragments, whole]) <= textHeight) return whole;
71
+ const minimum = Math.min(2, total);
72
+ if (measure(trial, [...fragments, makeFragment(id, 0, minimum)]) > textHeight) return void 0;
73
+ let end = minimum;
74
+ while (end < total && measure(trial, [...fragments, makeFragment(id, 0, end + 1)]) <= textHeight) end += 1;
75
+ return makeFragment(id, 0, end);
76
+ }
77
+ function allocateNotes(placed, existing, unit) {
78
+ const trial = [...placed, unit];
79
+ if (measure(trial, existing) > textHeight) return void 0;
80
+ const fragments = existing.slice();
81
+ for (const id of unit.refs) {
82
+ if (fragments.some((fragment) => fragment.id === id)) continue;
83
+ const fragment = fitNote(trial, fragments, id);
84
+ if (!fragment) return void 0;
85
+ fragments.push(fragment);
86
+ }
87
+ return fragments;
88
+ }
89
+ function notesFor(placed, seed) {
90
+ let fragments = seed;
91
+ for (const [offset, unit] of placed.entries()) {
92
+ const allocated = allocateNotes(placed.slice(0, offset), fragments, unit);
93
+ if (!allocated) break;
94
+ fragments = allocated;
95
+ }
96
+ return fragments;
97
+ }
98
+ if (payload.op === "space") return notesSpace(payload.fragments);
99
+ if (payload.op === "continue") return takeContinued(payload.pending);
100
+ if (payload.op === "allocate") return allocateNotes(payload.placed, payload.fragments, payload.unit);
101
+ if (payload.op === "recompute") return notesFor(payload.placed, payload.seed);
102
+ return leftoverFrom(payload.fragments);
103
+ }
104
+ //#endregion
105
+ export { paginateNotes };
@@ -0,0 +1,4 @@
1
+ import { PaginateBookInput, PaginateBookResult, PaginatePageFill, PaginatePageNote, PaginateUnit } from "./paginate-types.js";
2
+ //#region src/book/paginate-sequence.d.ts
3
+ export declare function paginateSequence(input: PaginateBookInput, fillPage: (units: PaginateUnit[], index: number, pending: PaginatePageNote[]) => PaginatePageFill): PaginateBookResult;
4
+ //#endregion