@beltype/cicero 0.1.10 → 0.1.12
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/dist/seam/client.d.ts +5 -1
- package/dist/seam/client.js +23 -5
- package/dist/seam/index.js +1 -1
- package/dist/seam/rewrite.js +7 -1
- package/dist/seam/rim.d.ts +2 -1
- package/dist/seam/rim.js +3 -1
- package/dist/src/capture.js +7 -20
- package/dist/src/compose-dom.d.ts +8 -0
- package/dist/src/compose-dom.js +65 -9
- package/dist/src/data-beltype.d.ts +8 -0
- package/dist/src/data-beltype.js +18 -0
- package/dist/src/document-state.d.ts +13 -2
- package/dist/src/document-state.js +18 -5
- package/dist/src/driver.d.ts +25 -8
- package/dist/src/driver.js +141 -39
- package/dist/src/fragmentation.d.ts +3 -3
- package/dist/src/fragmentation.js +3 -3
- package/dist/src/fragments.js +31 -6
- package/dist/src/gauge.js +22 -0
- package/dist/src/gcpm-encoding.d.ts +2 -0
- package/dist/src/gcpm-encoding.js +2 -0
- package/dist/src/measure.d.ts +3 -1
- package/dist/src/measure.js +29 -3
- package/dist/src/reads.d.ts +17 -9
- package/dist/src/reads.js +51 -32
- package/dist/src/root.js +2 -2
- package/dist/src/scope.d.ts +23 -0
- package/dist/src/scope.js +60 -0
- package/dist/src/stonehand.d.ts +1 -1
- package/dist/src/stonehand.js +20 -9
- package/dist/src/word-space.d.ts +11 -0
- package/dist/src/word-space.js +40 -0
- package/package.json +1 -1
package/dist/src/stonehand.js
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
*
|
|
12
12
|
* @packageDocumentation
|
|
13
13
|
*/
|
|
14
|
-
import { isAtom, READS_ITS_CHILDREN } from './word-space.js';
|
|
15
|
-
import { ARTIFACT, hasWord } from './data-beltype.js';
|
|
14
|
+
import { isAtom, READS_ITS_CHILDREN, sourceHasWord } from './word-space.js';
|
|
15
|
+
import { ARTIFACT, ONE_COLUMN, hasWord } from './data-beltype.js';
|
|
16
16
|
/**
|
|
17
17
|
* THE MAKE-UP ORDER (*breaks are classified*, I3, amended in the fifth round) — which constraint yields first, and the standing fact that a constraint which has yielded still pulls.
|
|
18
18
|
*
|
|
@@ -75,13 +75,13 @@ export function isMonolith(el, style, live = el) {
|
|
|
75
75
|
return notYetDivided(style, live) !== null;
|
|
76
76
|
}
|
|
77
77
|
/**
|
|
78
|
-
* **A fourth ground, and a temporary one: what the engine does not yet divide, where dividing it loses words** (*every branch gets a place*, 2026-09-21). Two arrangements lay content side by side in ways a row of sibling boxes does not describe, and each lost words at a cut until it was held whole (`packages/essay/probes/conservation.js`): a multi-column container, whose columns are fragmentainers of their own; and an inline-level block container, which is an atom of a line, with paragraphs inside it that the procession used to see. Held as a monolith each moves whole, or is sliced by the edge with its remainder painted — css-break-3's own remedy. **The remedy has a price, measured the same day, and it is why the list is this short**: a slice through text leaves the line the edge crosses half on each page, and the print's text layer reads it on both. That is better than words on no page and worse than anything else, so a grid with sized tracks or placed items — which divides without losing a word and merely opens its next page on the spent track's blank — was tried here and taken out again. Each entry leaves this list when it has a design of its own.
|
|
78
|
+
* **A fourth ground, and a temporary one: what the engine does not yet divide, where dividing it loses words** (*every branch gets a place*, 2026-09-21). Two arrangements lay content side by side in ways a row of sibling boxes does not describe, and each lost words at a cut until it was held whole (`packages/essay/probes/conservation.js`): a multi-column container, whose columns are fragmentainers of their own; and an inline-level block container, which is an atom of a line, with paragraphs inside it that the procession used to see. Held as a monolith each moves whole, or is sliced by the edge with its remainder painted — css-break-3's own remedy. **The remedy has a price, measured the same day, and it is why the list is this short**: a slice through text leaves the line the edge crosses half on each page, and the print's text layer reads it on both. A multi-column box taller than a page no longer reaches this list: it is laid out as one column (`src/scope.ts`), and the slice is left to a box that is a monolith for another reason. That is better than words on no page and worse than anything else, so a grid with sized tracks or placed items — which divides without losing a word and merely opens its next page on the spent track's blank — was tried here and taken out again. Each entry leaves this list when it has a design of its own.
|
|
79
79
|
*
|
|
80
80
|
* `live` is the element a measured clone stands for, kept in the signature for the day a ground needs to ask the element rather than its computed style — a grid's declared tracks are such a question: `grid-template-rows` resolves to the used tracks, implicit ones included, and only `computedStyleMap()` tells a declared list from none (measured in Chromium).
|
|
81
81
|
*/
|
|
82
82
|
export function notYetDivided(style, live) {
|
|
83
|
-
if ((style.columnCount ?? 'auto') !== 'auto' || (style.columnWidth ?? 'auto') !== 'auto')
|
|
84
|
-
return 'multicol';
|
|
83
|
+
if (((style.columnCount ?? 'auto') !== 'auto' || (style.columnWidth ?? 'auto') !== 'auto') && !sourceHasWord(live, ONE_COLUMN))
|
|
84
|
+
return 'multicol'; // a source the run laid out as one column (`src/scope.ts`) is an ordinary block, though its own style still says columns
|
|
85
85
|
if (/^inline-(block|flex|grid|table)$/.test(style.display ?? ''))
|
|
86
86
|
return 'inline block container';
|
|
87
87
|
return null;
|
|
@@ -331,8 +331,16 @@ function collect(w, { boxes, limit, pageStart, eps, stats }) {
|
|
|
331
331
|
plan.push({ item, state: 'held', verdict: null }); // a monolith the height crosses resumes whole: the stated limit, as a float's (the item is the graphic, and nothing inside it is a place)
|
|
332
332
|
else {
|
|
333
333
|
const v = flowVerdict(w, item, h + lift, eps);
|
|
334
|
-
if (!v)
|
|
335
|
-
|
|
334
|
+
if (!v) {
|
|
335
|
+
// Content crosses the height and the item offers no place above it: it shows nothing here. In the first table row of the group that withdraws the offer (case 91). In a later row of a group a `rowspan` makes, that row moves whole with every row after it, the boundary before it being the break in its column, and the spanning cell is still divided at the height, as Chromium prints it (the essay's case 92).
|
|
336
|
+
if (item.parent === row[0].parent)
|
|
337
|
+
return null;
|
|
338
|
+
const from = row.findIndex((r) => r.parent === item.parent);
|
|
339
|
+
plan.splice(from);
|
|
340
|
+
for (const rest of row.slice(from))
|
|
341
|
+
plan.push({ item: rest, state: 'below', verdict: null });
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
336
344
|
plan.push({ item, state: 'divided', verdict: v });
|
|
337
345
|
}
|
|
338
346
|
}
|
|
@@ -358,7 +366,9 @@ function collect(w, { boxes, limit, pageStart, eps, stats }) {
|
|
|
358
366
|
const pair = siblings[si - 1];
|
|
359
367
|
const anchor = last(siblings.slice(0, si).filter((b) => !parallelBox(b))) ?? null;
|
|
360
368
|
// Before the rows of a continuation only the repeated header stands, and a copy is furniture (*an artifact is never read*): a boundary there takes nothing of the box, and would send the page back to where the box began.
|
|
361
|
-
|
|
369
|
+
// A table row a `rowspan` from above reaches is inside its group's row: the boundary before it would leave the spanning cell's remainder on no page, and the group's own cut is where that boundary is taken (`rowPlan`).
|
|
370
|
+
const spanned = box.blockChildren.some((c) => { const r = rowOf(c); return !!r && r[0].parent !== box; });
|
|
371
|
+
if (anchor && !spanned && !siblings.slice(0, si).every((b) => parallelBox(b) || copy(b))) {
|
|
362
372
|
candidates.push({
|
|
363
373
|
kind: 'A',
|
|
364
374
|
cls: 'A',
|
|
@@ -454,7 +464,8 @@ function collect(w, { boxes, limit, pageStart, eps, stats }) {
|
|
|
454
464
|
// The place it names is before the box's first in-flow child, past the gap (*the cut divides its ancestors*, 2026-09-29): the page before carries the box's border, its padding and whatever stands in the gap — an empty float, the clearance under it — and the page after resumes the box divided, its first child whole. So the position is the child's outer top, which is the content edge plus the gap; it used to be the box's top plus its border, which put the padding on the next page and offered a break point §4.1 does not provide (`packages/essay/cases/71-the-box-that-begins-a-page`).
|
|
455
465
|
const startGap = box.blockChildren.length ? report.gapAt({ box, edge: 'start' }) : 0;
|
|
456
466
|
const firstInFlow = startGap > eps ? box.blockChildren.find((c) => !parallelBox(c)) : undefined;
|
|
457
|
-
|
|
467
|
+
// A box that opens the page offers no start gap: the page would hold its block-start decoration and nothing else, and the next page would offer the same gap again wherever the box clones its decoration, so the cut is no progress (css-break-3 §4.4 drops an avoid that leaves only this, and Chromium's print never takes it: a row taller than the page under `break-inside: avoid` left a blank first page, and WPT's `table-fragmentation-001c` refused, assessment 2026-11-07, `outer-row-taller-than-page-avoid`).
|
|
468
|
+
if (firstInFlow && !copy(firstInFlow) && box.rect.top > pageStart + eps) {
|
|
458
469
|
const prevSibling = si > 0 ? siblings[si - 1] : null;
|
|
459
470
|
const bs = win.getComputedStyle(box.el);
|
|
460
471
|
candidates.push({
|
package/dist/src/word-space.d.ts
CHANGED
|
@@ -49,6 +49,10 @@ export declare function isLineBreak(el: Element): boolean;
|
|
|
49
49
|
* @param map - the run's words, by source element.
|
|
50
50
|
*/
|
|
51
51
|
export declare function setSourceWords(map: WeakMap<Element, string[]>): void;
|
|
52
|
+
/** Give a source a word for the copies this run makes from now on; the source itself is not written (C5). */
|
|
53
|
+
export declare function addSourceWord(source: Element, word: string): void;
|
|
54
|
+
/** Does the run's record give this source the word? */
|
|
55
|
+
export declare const sourceHasWord: (source: Element, word: string) => boolean;
|
|
52
56
|
/**
|
|
53
57
|
* Write the words onto a copy and every element under it, in lockstep with the source tree, without registering the copy against its source — for a measuring clone that is read and thrown away (the Compositor's), where registering would change what an unmapped element is treated as.
|
|
54
58
|
*
|
|
@@ -186,6 +190,13 @@ export declare const SIDE_BY_SIDE: Set<string>;
|
|
|
186
190
|
* **The answer is read once per source element and kept** (*the page asks the index*, 2026-09-25). `stripFixedBoxes` asks it of every element of every feed clone and every presentation clone, about 300 000 style reads a run on the BGD manual for an answer that cannot change: the source is never written (*the source document is never written*, C5), and a host calls `run()` once per frame, so nothing moves a `position` within a run — the essay's client runs its judge and its view over one document whose values do not change between them. Only this predicate is kept: `isInlineBox` and `sourceStyle` are read far less often per clone, and a wider cache would be a wider claim.
|
|
187
191
|
*/
|
|
188
192
|
export declare function isFixedBox(el: Element): boolean;
|
|
193
|
+
/**
|
|
194
|
+
* Is this child a float standing wholly in its parent's padding or border, a hanging number (Codeberg issue 7)? Such a float shortens none of the parent's line boxes, so it is no part of the text being set: the composed mode skips it, in the child check and in the word walk, and leaves it where the browser put it. Any other float still makes the parent a block of floats and refuses.
|
|
195
|
+
*
|
|
196
|
+
* @remarks
|
|
197
|
+
* Asked through the registered source and kept per source, as {@link isFixedBox} is, for the same reasons: a clone detached from the document has no rect, and the source's layout does not move within a run. An unregistered, disconnected element answers false.
|
|
198
|
+
*/
|
|
199
|
+
export declare function isOutsideMarker(el: Element): boolean;
|
|
189
200
|
/** Does this box hold a `position: fixed` box anywhere inside it? The question `root.js` asks to know whether a block may be presented as its original or must be presented as a stripped clone — asked through `isFixedBox`, so the two can never disagree about which boxes count. */
|
|
190
201
|
export declare function holdsFixedBox(el: Element): boolean;
|
|
191
202
|
/**
|
package/dist/src/word-space.js
CHANGED
|
@@ -67,6 +67,16 @@ let WORDS_OF = new WeakMap();
|
|
|
67
67
|
export function setSourceWords(map) {
|
|
68
68
|
WORDS_OF = map;
|
|
69
69
|
}
|
|
70
|
+
/** Give a source a word for the copies this run makes from now on; the source itself is not written (C5). */
|
|
71
|
+
export function addSourceWord(source, word) {
|
|
72
|
+
const list = WORDS_OF.get(source);
|
|
73
|
+
if (!list)
|
|
74
|
+
WORDS_OF.set(source, [word]);
|
|
75
|
+
else if (!list.includes(word))
|
|
76
|
+
list.push(word);
|
|
77
|
+
}
|
|
78
|
+
/** Does the run's record give this source the word? */
|
|
79
|
+
export const sourceHasWord = (source, word) => !!WORDS_OF.get(source)?.includes(word);
|
|
70
80
|
/** Write the words a source carries onto a copy of it — added to whatever the copy inherited from the source's own attribute, never replacing it. */
|
|
71
81
|
function dressCopy(clone, source) {
|
|
72
82
|
const words = WORDS_OF.get(source);
|
|
@@ -430,6 +440,36 @@ export function isFixedBox(el) {
|
|
|
430
440
|
}
|
|
431
441
|
return fixed;
|
|
432
442
|
}
|
|
443
|
+
const OUTSIDE = new WeakMap();
|
|
444
|
+
/**
|
|
445
|
+
* Is this child a float standing wholly in its parent's padding or border, a hanging number (Codeberg issue 7)? Such a float shortens none of the parent's line boxes, so it is no part of the text being set: the composed mode skips it, in the child check and in the word walk, and leaves it where the browser put it. Any other float still makes the parent a block of floats and refuses.
|
|
446
|
+
*
|
|
447
|
+
* @remarks
|
|
448
|
+
* Asked through the registered source and kept per source, as {@link isFixedBox} is, for the same reasons: a clone detached from the document has no rect, and the source's layout does not move within a run. An unregistered, disconnected element answers false.
|
|
449
|
+
*/
|
|
450
|
+
export function isOutsideMarker(el) {
|
|
451
|
+
const live = SOURCE_OF.get(el) ?? el;
|
|
452
|
+
const win = live.ownerDocument?.defaultView;
|
|
453
|
+
const parent = live.parentElement;
|
|
454
|
+
if (!win || !parent || live.isConnected !== true)
|
|
455
|
+
return false;
|
|
456
|
+
let outside = OUTSIDE.get(live);
|
|
457
|
+
if (outside === undefined) {
|
|
458
|
+
outside = false;
|
|
459
|
+
if (win.getComputedStyle(live).float !== 'none') {
|
|
460
|
+
const ps = win.getComputedStyle(parent);
|
|
461
|
+
const p = parent.getBoundingClientRect();
|
|
462
|
+
const f = live.getBoundingClientRect();
|
|
463
|
+
const left = p.left + parseFloat(ps.borderLeftWidth) + parseFloat(ps.paddingLeft);
|
|
464
|
+
const right = p.right - parseFloat(ps.borderRightWidth) - parseFloat(ps.paddingRight);
|
|
465
|
+
const top = p.top + parseFloat(ps.borderTopWidth) + parseFloat(ps.paddingTop);
|
|
466
|
+
const bottom = p.bottom - parseFloat(ps.borderBottomWidth) - parseFloat(ps.paddingBottom);
|
|
467
|
+
outside = !(f.left < right && f.right > left && f.top < bottom && f.bottom > top);
|
|
468
|
+
}
|
|
469
|
+
OUTSIDE.set(live, outside);
|
|
470
|
+
}
|
|
471
|
+
return outside;
|
|
472
|
+
}
|
|
433
473
|
/** Does this box hold a `position: fixed` box anywhere inside it? The question `root.js` asks to know whether a block may be presented as its original or must be presented as a stripped clone — asked through `isFixedBox`, so the two can never disagree about which boxes count. */
|
|
434
474
|
export function holdsFixedBox(el) {
|
|
435
475
|
for (const child of el.children) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beltype/cicero",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "Cicero, a paged-media engine for the live browser: it cuts a document into pages under CSS Paged Media and presents them as real frames. Beta.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|