@beltype/cicero 0.1.10 → 0.1.11
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.js +2 -2
- package/dist/seam/index.js +1 -1
- package/dist/seam/rewrite.js +7 -1
- package/dist/src/capture.js +7 -20
- package/dist/src/compose-dom.d.ts +8 -0
- package/dist/src/compose-dom.js +60 -7
- package/dist/src/data-beltype.d.ts +6 -0
- package/dist/src/data-beltype.js +14 -0
- package/dist/src/fragmentation.d.ts +3 -3
- package/dist/src/fragmentation.js +3 -3
- package/dist/src/fragments.js +24 -6
- package/dist/src/gcpm-encoding.d.ts +2 -0
- package/dist/src/gcpm-encoding.js +2 -0
- package/dist/src/reads.d.ts +4 -2
- package/dist/src/reads.js +7 -1
- package/dist/src/root.js +2 -2
- package/package.json +1 -1
package/dist/seam/client.js
CHANGED
|
@@ -24,7 +24,7 @@ import { dress, isDressingSheet } from '../src/root.js';
|
|
|
24
24
|
import { isDeliverySheet } from '../src/reads.js';
|
|
25
25
|
import { ForcingMemo } from '../src/forcing-memo.js';
|
|
26
26
|
import { isInlineBox, isFixedBox, holdsFixedBox } from '../src/word-space.js';
|
|
27
|
-
import { FRAGMENT, selectorOf } from '../src/data-beltype.js';
|
|
27
|
+
import { FRAGMENT, decodeFragment, selectorOf } from '../src/data-beltype.js';
|
|
28
28
|
import { present, presenterCount } from '../src/presenters.js';
|
|
29
29
|
import { rewriteDocumentSheets } from './rewrite-document.js';
|
|
30
30
|
import { isLayerName } from './rewrite.js';
|
|
@@ -428,7 +428,7 @@ function show(session, { account, index, total, from, to, resume }) {
|
|
|
428
428
|
seatDoors(blocks);
|
|
429
429
|
if (!session.hashed) {
|
|
430
430
|
// An address that names a place in the document (`…#chapter`) is a place on a page that may not be built; the browser's own scroll to the fragment ran before there were pages. A place not cut to yet waits for the cut that reaches it.
|
|
431
|
-
const named = window.location.hash ? document.getElementById(
|
|
431
|
+
const named = window.location.hash ? document.getElementById(decodeFragment(window.location.hash)) : null;
|
|
432
432
|
if (!named || !root.contains(named))
|
|
433
433
|
session.hashed = true;
|
|
434
434
|
else if (root.pageOf(named) !== 'not-yet') {
|
package/dist/seam/index.js
CHANGED
|
@@ -65,7 +65,7 @@ export const ciceroEngine = {
|
|
|
65
65
|
return ` if (window.__beltypeCicero) {
|
|
66
66
|
try {
|
|
67
67
|
const cicero = window.__beltypeCicero;
|
|
68
|
-
const named = location.hash.length > 1 ? document.getElementById(
|
|
68
|
+
const named = location.hash.length > 1 ? document.getElementById((() => { const h = location.hash.slice(1); try { return decodeURIComponent(h); } catch { return h; } })()) : null;
|
|
69
69
|
let answer = await cicero.run({ until: named || 2 });
|
|
70
70
|
if (!answer.complete) {
|
|
71
71
|
const sheet = document.querySelector('beltype-root')?.shadowRoot?.querySelector('beltype-page');
|
package/dist/seam/rewrite.js
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* @packageDocumentation
|
|
27
27
|
*/
|
|
28
28
|
import { parse, walk } from './csstree.js';
|
|
29
|
-
import { STRING_SET_PROP, RUNNING_PROP, encodeString, encodeElement, encodePageName, BLANK_SUFFIX, encodeRead, parseReadFunction, READ_PREFIX, encodeStringProp } from '../src/gcpm-encoding.js';
|
|
29
|
+
import { STRING_SET_PROP, RUNNING_PROP, HYPHENATE_LIMIT_LINES_PROP, encodeString, encodeElement, encodePageName, BLANK_SUFFIX, encodeRead, parseReadFunction, READ_PREFIX, encodeStringProp } from '../src/gcpm-encoding.js';
|
|
30
30
|
import { MARGIN_BOXES } from '../src/margin-boxes.js';
|
|
31
31
|
import { FRAGMENT_GUARD, HAS_PROPERTY_PREFIX, HAS_REGISTRY_SELECTOR, encodeHasArgument, encodeHasUnion } from '../src/data-beltype.js';
|
|
32
32
|
/** Splice a list of non-overlapping `{ start, end, text }` edits into a source string, right to left so earlier offsets stay valid. */
|
|
@@ -465,6 +465,12 @@ function rewriteText(original, pages = true) {
|
|
|
465
465
|
}
|
|
466
466
|
return;
|
|
467
467
|
}
|
|
468
|
+
if (property === 'hyphenate-limit-lines') { // Chromium drops it at parse, and the Compositor reads the author's limit from the custom property instead
|
|
469
|
+
const colon = source.indexOf(':', node.loc.start.offset);
|
|
470
|
+
if (colon > node.loc.start.offset)
|
|
471
|
+
edits.push({ start: node.loc.start.offset, end: colon, text: HYPHENATE_LIMIT_LINES_PROP });
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
468
474
|
if (property === 'position') {
|
|
469
475
|
const m = /^running\(\s*([^\s)]+)\s*\)$/i.exec(textOf(source, node.value));
|
|
470
476
|
if (m) {
|
package/dist/src/capture.js
CHANGED
|
@@ -932,29 +932,16 @@ function compareSpecificity(a, b) {
|
|
|
932
932
|
return a[i] - b[i];
|
|
933
933
|
return 0;
|
|
934
934
|
}
|
|
935
|
-
/** Margin-box declarations out of a CSSPageRule — via child CSSMarginRule objects
|
|
935
|
+
/** Margin-box declarations out of a CSSPageRule — via the child CSSMarginRule objects (the pinned Chromium has them; `margin-box-padding.browser.ts` asserts it). Returns { 'bottom-center': rawContentValue, ... }. */
|
|
936
936
|
function marginBoxesOf(rule) {
|
|
937
937
|
const out = {};
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
out[name] = content.trim();
|
|
945
|
-
}
|
|
938
|
+
for (const child of (rule.cssRules ?? [])) {
|
|
939
|
+
const name = (child.name ?? child.selectorText ?? '').replace(/^@/, '');
|
|
940
|
+
if (MARGIN_BOXES.includes(name)) {
|
|
941
|
+
const content = child.style?.getPropertyValue('content');
|
|
942
|
+
if (content)
|
|
943
|
+
out[name] = content.trim();
|
|
946
944
|
}
|
|
947
|
-
if (Object.keys(out).length)
|
|
948
|
-
return out;
|
|
949
|
-
}
|
|
950
|
-
const re = /@([a-z-]+)\s*\{([^}]*)\}/g;
|
|
951
|
-
let m;
|
|
952
|
-
while ((m = re.exec(rule.cssText))) {
|
|
953
|
-
if (!MARGIN_BOXES.includes(m[1]))
|
|
954
|
-
continue;
|
|
955
|
-
const c = /content\s*:\s*([^;}]+)/.exec(m[2]);
|
|
956
|
-
if (c)
|
|
957
|
-
out[m[1]] = c[1].trim();
|
|
958
945
|
}
|
|
959
946
|
return out;
|
|
960
947
|
}
|
|
@@ -52,6 +52,10 @@ export type CompositionRecord = {
|
|
|
52
52
|
* It used to ask only the children's display, so a `<pre>`, an SVG's `<text>` and an inline `<code>` were paragraphs. The children's display is asked through `word-space.js`' `isInlineBox`, the procession's one predicate (*the outer display decides*, 2026-09-29): it used to be a second copy of the `inline` prefix test, so a paragraph holding an inline formula, whose `<math>` computes `math`, was refused here while the procession, once fixed, divided it. Every refusal here is silent: a refused block keeps the browser's lines, which are the only lines it has.
|
|
53
53
|
*/
|
|
54
54
|
export declare function isComposable(el: Element): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* A sixth clause: a block in a multi-column container, or itself one, keeps the browser's lines. The Compositor has one measure per block and reads it from the block's own box, not from a column's, so a `columns: 3` box 210px wide was set at the container's measure, the longest line 356px against a column of 70. Refused like a block beside a float, with a console line (`refusedInColumns`); the real fix is a per-column measure and the fragmentation of a multi-column box, which is not this engine's yet.
|
|
57
|
+
*/
|
|
58
|
+
export declare function inColumns(el: Element): boolean;
|
|
55
59
|
/**
|
|
56
60
|
* Words and author-break runs of an element, with DOM points. A word is a maximal non-whitespace run of text not crossing a <br>; nested inlines merge (parts). Returns { words: [{ text, parts: [{node, start, end}] }], runs: [{ start, end }] } where runs index into words, end exclusive.
|
|
57
61
|
*
|
|
@@ -74,6 +78,10 @@ export declare function collectWords(el: Element): {
|
|
|
74
78
|
export declare function hyphenCharOf(style: {
|
|
75
79
|
hyphenateCharacter?: string;
|
|
76
80
|
}): string;
|
|
81
|
+
/**
|
|
82
|
+
* css-text-4 §6.3.5's `hyphenate-limit-lines`, read from the custom property the host rewrote it into (Chromium drops the property itself): `no-limit` is `null`, an integer is the limit, and an absent or unreadable value is the engine's own {@link HYPHENATE_LIMIT_LINES}.
|
|
83
|
+
*/
|
|
84
|
+
export declare function limitLinesOf(style: CSSStyleDeclaration): number | null;
|
|
77
85
|
/** Two border-box rects overlapping in both axes, sub-pixel noise excluded. The geometry half of the beside-a-float refusal below, exported so it is checkable without a browser; what it cannot say is which boxes were handed to it, which is the case runner's job. */
|
|
78
86
|
export declare function overlaps(a: {
|
|
79
87
|
top: number;
|
package/dist/src/compose-dom.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* @packageDocumentation
|
|
11
11
|
*/
|
|
12
12
|
import { compose, HYPHENATE_LIMIT_LINES } from './compositor.js';
|
|
13
|
+
import { HYPHENATE_LIMIT_LINES_PROP } from './gcpm-encoding.js';
|
|
13
14
|
import { addWord, AWAY, BREAK, FILL, hasWord } from './data-beltype.js';
|
|
14
15
|
import { createMeasureContext } from './measure.js';
|
|
15
16
|
import { indexBlocks, pageIntervals, presentationWidths } from './positions.js';
|
|
@@ -47,10 +48,24 @@ export function isComposable(el) {
|
|
|
47
48
|
for (let a = el.parentElement; a; a = a.parentElement)
|
|
48
49
|
if (isAtom(a))
|
|
49
50
|
return false;
|
|
51
|
+
if (inColumns(el))
|
|
52
|
+
return false;
|
|
50
53
|
if (holdsGeneratedText(el, win))
|
|
51
54
|
return false;
|
|
52
55
|
return collectWords(el).words.some((w) => !w.atom);
|
|
53
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* A sixth clause: a block in a multi-column container, or itself one, keeps the browser's lines. The Compositor has one measure per block and reads it from the block's own box, not from a column's, so a `columns: 3` box 210px wide was set at the container's measure, the longest line 356px against a column of 70. Refused like a block beside a float, with a console line (`refusedInColumns`); the real fix is a per-column measure and the fragmentation of a multi-column box, which is not this engine's yet.
|
|
59
|
+
*/
|
|
60
|
+
export function inColumns(el) {
|
|
61
|
+
const win = el.ownerDocument.defaultView;
|
|
62
|
+
for (let a = el; a; a = a.parentElement) {
|
|
63
|
+
const cs = win.getComputedStyle(a);
|
|
64
|
+
if (cs.columnCount !== 'auto' || cs.columnWidth !== 'auto')
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
54
69
|
/**
|
|
55
70
|
* Does this pseudo-element put a box in its element's lines? Its `content` must generate one — `none` and `normal` do not — and its outer display must be inline: a `display: block` `::after` is a block of its own after the lines, not text in them.
|
|
56
71
|
*
|
|
@@ -176,6 +191,16 @@ export function hyphenCharOf(style) {
|
|
|
176
191
|
return raw;
|
|
177
192
|
return raw.slice(1, -1).replace(/\\(.)/g, '$1');
|
|
178
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* css-text-4 §6.3.5's `hyphenate-limit-lines`, read from the custom property the host rewrote it into (Chromium drops the property itself): `no-limit` is `null`, an integer is the limit, and an absent or unreadable value is the engine's own {@link HYPHENATE_LIMIT_LINES}.
|
|
196
|
+
*/
|
|
197
|
+
export function limitLinesOf(style) {
|
|
198
|
+
const raw = style.getPropertyValue(HYPHENATE_LIMIT_LINES_PROP).trim();
|
|
199
|
+
if (raw === 'no-limit')
|
|
200
|
+
return null;
|
|
201
|
+
const n = /^\d+$/.test(raw) ? parseInt(raw, 10) : NaN;
|
|
202
|
+
return Number.isFinite(n) && n >= 1 ? n : HYPHENATE_LIMIT_LINES;
|
|
203
|
+
}
|
|
179
204
|
/**
|
|
180
205
|
* css-text-4 §6.3.4's `hyphenate-limit-chars` as three values, `null` for each `auto`.
|
|
181
206
|
*
|
|
@@ -367,16 +392,27 @@ export function composeDocument(doc, blocks, ctx, cache = new CompositionCache()
|
|
|
367
392
|
}
|
|
368
393
|
// A float's own descendants are laid out in the float's own formatting context at the float's own width, which is exactly what their rect reports — nothing is refused there. What is refused is a block the float reaches into: a sibling it stands beside, or an ancestor whose line boxes it is nested in.
|
|
369
394
|
const intrudedByFloat = (clone, rect) => floats.some((f) => !f.el.contains(clone) && overlaps(rect, f.rect));
|
|
395
|
+
/** The block's content box: a float wholly in the padding or the border (a hanging number) shortens none of its line boxes, so it is the content box the float's rect is compared with. */
|
|
396
|
+
const contentBoxOf = (cs, rect) => {
|
|
397
|
+
const left = rect.left + parseFloat(cs.borderLeftWidth) + parseFloat(cs.paddingLeft);
|
|
398
|
+
const top = rect.top + parseFloat(cs.borderTopWidth) + parseFloat(cs.paddingTop);
|
|
399
|
+
return { left, top, right: rect.right - parseFloat(cs.borderRightWidth) - parseFloat(cs.paddingRight), bottom: rect.bottom - parseFloat(cs.borderBottomWidth) - parseFloat(cs.paddingBottom) };
|
|
400
|
+
};
|
|
370
401
|
const refusedBesideFloat = [];
|
|
371
402
|
/** A second refusal, of the same kind and for the same reason (*a line inside a word is a break point*, 2026-09-23). The Compositor's break opportunities are spaces, hyphenation points and css-text-4 §6.4's emergency break; a block whose text the browser may break anywhere else — `word-break: break-all`, `line-break: anywhere`, or a script written without spaces (`word-space.js`' `UNSPACED_SCRIPT`) — reaches it as words it cannot divide. Measured on `packages/essay/cases/63-the-line-inside-a-word`: a thirty-line `break-all` paragraph was one word to the DP, set on one nowrap line, and twenty-one of its thirty lines were on no page. So such a block keeps the browser's lines, and the console says so; the real fix is the Compositor learning UAX #14's opportunities, which is a later round. */
|
|
372
403
|
const refusedUnspaced = [];
|
|
404
|
+
/** A third refusal: a block in a multi-column container (`inColumns`). */
|
|
405
|
+
const refusedInColumns = [];
|
|
373
406
|
const jobs = [];
|
|
374
407
|
for (const [clone, source] of cloneToSource) {
|
|
375
|
-
if (!isComposable(clone))
|
|
408
|
+
if (!isComposable(clone)) {
|
|
409
|
+
if (inColumns(clone) && PARAGRAPH_DISPLAY.has(win.getComputedStyle(clone).display))
|
|
410
|
+
refusedInColumns.push(source);
|
|
376
411
|
continue;
|
|
412
|
+
}
|
|
377
413
|
const cs = win.getComputedStyle(clone);
|
|
378
414
|
const rect = clone.getBoundingClientRect();
|
|
379
|
-
if (floats.length && intrudedByFloat(clone, rect)) {
|
|
415
|
+
if (floats.length && intrudedByFloat(clone, contentBoxOf(cs, rect))) {
|
|
380
416
|
refusedBesideFloat.push(source);
|
|
381
417
|
continue;
|
|
382
418
|
}
|
|
@@ -393,12 +429,13 @@ export function composeDocument(doc, blocks, ctx, cache = new CompositionCache()
|
|
|
393
429
|
const indent = parseFloat(sourceStyle.textIndent) || 0;
|
|
394
430
|
const hyphenChar = hyphenCharOf(sourceStyle);
|
|
395
431
|
const limits = limitCharsOf(sourceStyle);
|
|
432
|
+
const limitLines = limitLinesOf(sourceStyle);
|
|
396
433
|
const breakAnywhere = ['anywhere', 'break-word'].includes(sourceStyle.overflowWrap);
|
|
397
434
|
const paragraph = paragraphKey({
|
|
398
435
|
path: pathToSource.get(source),
|
|
399
436
|
markup: shapeOf(source.outerHTML),
|
|
400
437
|
width: key,
|
|
401
|
-
inputs: [align, indent, hyphensMode, langAttr ?? null, hyphenChar, limits.total, limits.before, limits.after, breakAnywhere, sourceStyle.wordBreak, sourceStyle.lineBreak, ...WIDTH_INPUTS.map((name) => sourceStyle[name]), ancestryOf(source)]
|
|
438
|
+
inputs: [align, indent, hyphensMode, langAttr ?? null, hyphenChar, limits.total, limits.before, limits.after, limitLines, breakAnywhere, sourceStyle.wordBreak, sourceStyle.lineBreak, ...WIDTH_INPUTS.map((name) => sourceStyle[name]), ancestryOf(source)]
|
|
402
439
|
});
|
|
403
440
|
const hit = lookup(cache, paragraph);
|
|
404
441
|
if (hit) {
|
|
@@ -420,6 +457,7 @@ export function composeDocument(doc, blocks, ctx, cache = new CompositionCache()
|
|
|
420
457
|
// The three css-text-4 properties the line already had and this module was not reading (2026-09-07). All three are inherited, so the block's own computed style is the whole answer.
|
|
421
458
|
hyphenChar,
|
|
422
459
|
limits,
|
|
460
|
+
limitLines,
|
|
423
461
|
breakAnywhere
|
|
424
462
|
});
|
|
425
463
|
}
|
|
@@ -432,8 +470,18 @@ export function composeDocument(doc, blocks, ctx, cache = new CompositionCache()
|
|
|
432
470
|
c.style.textIndent = '0';
|
|
433
471
|
return c;
|
|
434
472
|
});
|
|
473
|
+
// A nested paragraph is measured inside shallow copies of its ancestors up to its top-level block, or it loses what it inherits from them and what a descendant selector reads: a pull quote's `blockquote { font: italic 22px serif }` measured at the measure container's 16px set lines 22% too wide for the box (`composed-pull-quote-overrun`). ponytail: shallow copies carry attributes only, so a sibling selector (`p + p`, `:first-child`) still reads the lone copy; clone the earlier siblings too if one is ever measured wrong.
|
|
474
|
+
const measureRoots = jobs.map((j, i) => {
|
|
475
|
+
let top = nowrapClones[i];
|
|
476
|
+
for (let cur = j.source; !tops.has(cur) && cur.parentElement; cur = cur.parentElement) {
|
|
477
|
+
const wrap = cur.parentElement.cloneNode(false);
|
|
478
|
+
wrap.appendChild(top);
|
|
479
|
+
top = wrap;
|
|
480
|
+
}
|
|
481
|
+
return top;
|
|
482
|
+
});
|
|
435
483
|
const probes = jobs.map((job, i) => seatHyphenProbe(nowrapClones[i], job.hyphenChar));
|
|
436
|
-
ctx.measure(
|
|
484
|
+
ctx.measure(measureRoots);
|
|
437
485
|
// One pass reading, one pass removing: the removal is the last write, so the layout it forces is the last one the composition pays for. `collectWords` runs after it, or a probe's character would be a word.
|
|
438
486
|
const hyphenWidths = probes.map(probeWidth);
|
|
439
487
|
for (const probe of probes)
|
|
@@ -463,7 +511,7 @@ export function composeDocument(doc, blocks, ctx, cache = new CompositionCache()
|
|
|
463
511
|
indent: r === 0 ? job.indent : 0,
|
|
464
512
|
align: job.align,
|
|
465
513
|
breakAnywhere: job.breakAnywhere,
|
|
466
|
-
limitLines:
|
|
514
|
+
limitLines: job.limitLines
|
|
467
515
|
});
|
|
468
516
|
record.runs.push({ start: runs[r].start, ...result });
|
|
469
517
|
record.lineCount += result.lineCount;
|
|
@@ -472,7 +520,7 @@ export function composeDocument(doc, blocks, ctx, cache = new CompositionCache()
|
|
|
472
520
|
resolved.set(job.source, record);
|
|
473
521
|
});
|
|
474
522
|
}
|
|
475
|
-
return { floaty: floats.length > 0, refusedBesideFloat, refusedUnspaced };
|
|
523
|
+
return { floaty: floats.length > 0, refusedBesideFloat, refusedUnspaced, refusedInColumns };
|
|
476
524
|
};
|
|
477
525
|
let todo = blocks.map((_, i) => i).filter((i) => !(trustMemo && memos?.[i]));
|
|
478
526
|
let laid = lay(todo);
|
|
@@ -510,13 +558,18 @@ export function composeDocument(doc, blocks, ctx, cache = new CompositionCache()
|
|
|
510
558
|
tally.blocks.set(memoKeys[i], { records, floaty: laid.floaty, refused: laid.refusedUnspaced.filter((src) => el.contains(src)).length });
|
|
511
559
|
}
|
|
512
560
|
}
|
|
513
|
-
const { refusedBesideFloat, refusedUnspaced } = laid;
|
|
561
|
+
const { refusedBesideFloat, refusedUnspaced, refusedInColumns } = laid;
|
|
514
562
|
// Never in silence — the render-conditions rule, applied to a decision the engine declines to take rather than to one it takes under a relaxation.
|
|
515
563
|
if (refusedBesideFloat.length) {
|
|
516
564
|
win.console?.warn(`BelType/Cicero: ${refusedBesideFloat.length} block(s) sit beside a float and are NOT composed by this engine — ` +
|
|
517
565
|
'their lines are the browser\'s, because a float shortens line boxes and the Compositor has one measure per block, not one per line. ' +
|
|
518
566
|
'The pages are honest; the line breaking in those blocks is not this engine\'s.');
|
|
519
567
|
}
|
|
568
|
+
if (refusedInColumns.length) {
|
|
569
|
+
win.console?.warn(`BelType/Cicero: ${refusedInColumns.length} block(s) sit in a multi-column container and are NOT composed by this engine — ` +
|
|
570
|
+
'their lines are the browser\'s, because the Compositor has one measure per block, not one per column. ' +
|
|
571
|
+
'The pages are honest; the line breaking in those blocks is not this engine\'s.');
|
|
572
|
+
}
|
|
520
573
|
const refusedCount = refusedUnspaced.length + refusedFromMemo;
|
|
521
574
|
if (refusedCount) {
|
|
522
575
|
win.console?.warn(`BelType/Cicero: ${refusedCount} block(s) may break lines inside a word — word-break: break-all, line-break: anywhere, or a script written without spaces — and are NOT composed by this engine; ` +
|
|
@@ -163,3 +163,9 @@ export declare const CUT_OFF: string;
|
|
|
163
163
|
* - `adjoin`: the box a bare-path cut resumes at, and the first-child chain through which its margin collapses, give up the margin that adjoins the break (css-break-3 §5.2) and nothing else — they are not divided.
|
|
164
164
|
*/
|
|
165
165
|
export declare const WORDS_RULES: string;
|
|
166
|
+
/**
|
|
167
|
+
* The id a URL fragment names, percent-decoded; the raw text when it is not valid percent-encoding (`#%E0%A4%A`), where `decodeURIComponent` throws.
|
|
168
|
+
*
|
|
169
|
+
* @param hash - A `location.hash`, with or without its `#`.
|
|
170
|
+
*/
|
|
171
|
+
export declare function decodeFragment(hash: string): string;
|
package/dist/src/data-beltype.js
CHANGED
|
@@ -312,3 +312,17 @@ export const WORDS_RULES = `
|
|
|
312
312
|
${w(ADJOIN)} { margin-top: 0 !important; }
|
|
313
313
|
}
|
|
314
314
|
`;
|
|
315
|
+
/**
|
|
316
|
+
* The id a URL fragment names, percent-decoded; the raw text when it is not valid percent-encoding (`#%E0%A4%A`), where `decodeURIComponent` throws.
|
|
317
|
+
*
|
|
318
|
+
* @param hash - A `location.hash`, with or without its `#`.
|
|
319
|
+
*/
|
|
320
|
+
export function decodeFragment(hash) {
|
|
321
|
+
const raw = hash.startsWith('#') ? hash.slice(1) : hash;
|
|
322
|
+
try {
|
|
323
|
+
return decodeURIComponent(raw);
|
|
324
|
+
}
|
|
325
|
+
catch {
|
|
326
|
+
return raw;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* The fragmentation face: the engine's members a view adapter builds on, `@beltype/cicero/fragmentation`.
|
|
3
3
|
*
|
|
4
4
|
* @remarks
|
|
5
|
-
* Exactly the names the first view adapter imported by deep path on 2026-10-29 (68 of them, from 16 modules), re-exported unchanged, and the names a view has asked for since (the leading-decoration door, `WRAPPED`, `clonesDecoration` and `truncateLeadingDecoration`, 2026-11-07); nothing is un-exported. Which of them stay public, merge or leave is the shape round's question. The face loads in node: `chase.ts`, which defines the `<beltype-page>` element and so needs a DOM at load, is not here; a view imports it for that effect as `@beltype/cicero/
|
|
5
|
+
* Exactly the names the first view adapter imported by deep path on 2026-10-29 (68 of them, from 16 modules), re-exported unchanged, and the names a view has asked for since (the leading-decoration door, `WRAPPED`, `clonesDecoration` and `truncateLeadingDecoration`, 2026-11-07); nothing is un-exported. Which of them stay public, merge or leave is the shape round's question. The face loads in node: `chase.ts`, which defines the `<beltype-page>` element and so needs a DOM at load, is not here; a view imports it for that effect as `@beltype/cicero/page`.
|
|
6
6
|
*
|
|
7
7
|
* @packageDocumentation
|
|
8
8
|
*/
|
|
9
|
-
export { effectivePageName, readGeometryTable, registerGcpmProperties, topLevelReach, transliterateContent } from './capture.ts';
|
|
9
|
+
export { effectivePageName, readGeometryTable, registerGcpmProperties, sheetReach, topLevelReach, transliterateContent } from './capture.ts';
|
|
10
10
|
export { ADJOIN, ARTIFACT, AWAY, END, FRAGMENT, SHELL, SPLIT, START, WRAPPED, addWord, hasWord, removeWord } from './data-beltype.ts';
|
|
11
11
|
export { counterStateAt, documentOpsOf, namedStringsForPage, runningAssignmentsOf, stringAssignmentsOf } from './document-state.ts';
|
|
12
12
|
export { ensureContinuationDressing, ensureDressing } from './dressing.ts';
|
|
@@ -19,6 +19,6 @@ export { STRING_KEYWORDS, encodeStringProp } from './gcpm-encoding.ts';
|
|
|
19
19
|
export { assertRenderConditions, awaitCloneImages, awaitFonts, createMeasureContext } from './measure.ts';
|
|
20
20
|
export { divides, elAt, extensionPx, indexBlocks, normalizePath, pageContaining, pageIntervals, pathOf, placeAt } from './positions.ts';
|
|
21
21
|
export { present, presentation } from './presenters.ts';
|
|
22
|
-
export { cssString, findPageSites, settleReads } from './reads.ts';
|
|
22
|
+
export { cssString, findPageSites, findSites, settleReads } from './reads.ts';
|
|
23
23
|
export { adjudicate, firstBelowTheEdge, fitsPage, isOutOfFlow, neverCutInside, processionBottoms, reachesBelowTheEdge } from './stonehand.ts';
|
|
24
24
|
export { BREAKING_SPACE, clonesDecoration, copyOf, isFixedBox, isInlineBox, isLineBreak, noteSource } from './word-space.ts';
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* The fragmentation face: the engine's members a view adapter builds on, `@beltype/cicero/fragmentation`.
|
|
3
3
|
*
|
|
4
4
|
* @remarks
|
|
5
|
-
* Exactly the names the first view adapter imported by deep path on 2026-10-29 (68 of them, from 16 modules), re-exported unchanged, and the names a view has asked for since (the leading-decoration door, `WRAPPED`, `clonesDecoration` and `truncateLeadingDecoration`, 2026-11-07); nothing is un-exported. Which of them stay public, merge or leave is the shape round's question. The face loads in node: `chase.ts`, which defines the `<beltype-page>` element and so needs a DOM at load, is not here; a view imports it for that effect as `@beltype/cicero/
|
|
5
|
+
* Exactly the names the first view adapter imported by deep path on 2026-10-29 (68 of them, from 16 modules), re-exported unchanged, and the names a view has asked for since (the leading-decoration door, `WRAPPED`, `clonesDecoration` and `truncateLeadingDecoration`, 2026-11-07); nothing is un-exported. Which of them stay public, merge or leave is the shape round's question. The face loads in node: `chase.ts`, which defines the `<beltype-page>` element and so needs a DOM at load, is not here; a view imports it for that effect as `@beltype/cicero/page`.
|
|
6
6
|
*
|
|
7
7
|
* @packageDocumentation
|
|
8
8
|
*/
|
|
9
|
-
export { effectivePageName, readGeometryTable, registerGcpmProperties, topLevelReach, transliterateContent } from './capture.js';
|
|
9
|
+
export { effectivePageName, readGeometryTable, registerGcpmProperties, sheetReach, topLevelReach, transliterateContent } from './capture.js';
|
|
10
10
|
export { ADJOIN, ARTIFACT, AWAY, END, FRAGMENT, SHELL, SPLIT, START, WRAPPED, addWord, hasWord, removeWord } from './data-beltype.js';
|
|
11
11
|
export { counterStateAt, documentOpsOf, namedStringsForPage, runningAssignmentsOf, stringAssignmentsOf } from './document-state.js';
|
|
12
12
|
export { ensureContinuationDressing, ensureDressing } from './dressing.js';
|
|
@@ -18,6 +18,6 @@ export { STRING_KEYWORDS, encodeStringProp } from './gcpm-encoding.js';
|
|
|
18
18
|
export { assertRenderConditions, awaitCloneImages, awaitFonts, createMeasureContext } from './measure.js';
|
|
19
19
|
export { divides, elAt, extensionPx, indexBlocks, normalizePath, pageContaining, pageIntervals, pathOf, placeAt } from './positions.js';
|
|
20
20
|
export { present, presentation } from './presenters.js';
|
|
21
|
-
export { cssString, findPageSites, settleReads } from './reads.js';
|
|
21
|
+
export { cssString, findPageSites, findSites, settleReads } from './reads.js';
|
|
22
22
|
export { adjudicate, firstBelowTheEdge, fitsPage, isOutOfFlow, neverCutInside, processionBottoms, reachesBelowTheEdge } from './stonehand.js';
|
|
23
23
|
export { BREAKING_SPACE, clonesDecoration, copyOf, isFixedBox, isInlineBox, isLineBreak, noteSource } from './word-space.js';
|
package/dist/src/fragments.js
CHANGED
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
* @packageDocumentation
|
|
19
19
|
*/
|
|
20
20
|
import { applyRecordsUnder, hyphenCharOf } from './compose-dom.js';
|
|
21
|
-
import { addWord, ADJOIN, ARTIFACT, AWAY, END, ENGINE_MARKS, hasAnyWord, hasWord, removeWord, SHELL, SPLIT, START, WRAPPED } from './data-beltype.js';
|
|
21
|
+
import { addWord, ADJOIN, ARTIFACT, AWAY, BREAK, END, ENGINE_MARKS, hasAnyWord, hasWord, removeWord, SHELL, SPLIT, START, WRAPPED } from './data-beltype.js';
|
|
22
22
|
import { pairsOf } from './document-state.js';
|
|
23
23
|
import { divides, elAtIn, flowsOf } from './positions.js';
|
|
24
|
-
import { BREAKING_SPACE, clonesDecoration, cutRanges, emptyText, inDisplayOrder, leaveOut, replaceChars, isEngineText, mayBeAutoHyphenated, isFixedBox, isInlineBox, isLineBreak, copyOf, shellOf, noteText, READS_ITS_CHILDREN, sourceOf, sourceStyle } from './word-space.js';
|
|
24
|
+
import { BREAKING_SPACE, clonesDecoration, cutRanges, emptyText, inDisplayOrder, leaveOut, replaceChars, isEngineText, mayBeAutoHyphenated, noteEngineText, splitTextAt, isFixedBox, isInlineBox, isLineBreak, copyOf, shellOf, noteText, READS_ITS_CHILDREN, sourceOf, sourceStyle } from './word-space.js';
|
|
25
25
|
/** The word space, clone side: words are maximal non-whitespace runs of the subtree's joined text — a run crossing a text-node or inline-element seam is one word, exactly as the Gauge reads, and exactly as the position index counts over sources (src/positions.js — the two tokenizations must never drift; counting per node instead shifts every offset by the seam-spanning words before it, which the test-document's inline markup caught). **A non-inline boundary ends the run** (word-space.js), which is where this stopped agreeing with the page for two months. This walk survives on the clone side only: consumption must find the resume word's actual text nodes in the mutated copy, which no source-side index can answer. */
|
|
26
26
|
export function joinedWords(root, isInline = isInlineBox) {
|
|
27
27
|
const words = [];
|
|
@@ -545,6 +545,7 @@ function consumeTrailingWords(clone, n, char = 0, hollow = false) {
|
|
|
545
545
|
for (const [node, ranges] of perNode)
|
|
546
546
|
cutRanges(node, ranges);
|
|
547
547
|
// The hyphen is written at the cut (*the hyphen is written at the cut*, 2026-10-06). A soft hyphen the browser broke the line at is drawn there; one at the end of a fragment's last line is not, because nothing breaks after it. So the last kept character, where it is a soft hyphen, becomes the hyphenation character the browser drew — the box's own `hyphenate-character`, else U+2010 (css-text-4 §6.3.2) — and the empty string, which the property allows, removes it. A replacement: the word keeps its length, and the Compositor's own break, whose hyphen is already written and whose soft hyphen stands after it, cuts before the soft hyphen and never meets this.
|
|
548
|
+
let autoHyphen = null;
|
|
548
549
|
if (char > 0 && words[n].text[char - 1] === '\u00AD') {
|
|
549
550
|
const soft = charRanges(words[n], char - 1, char).find((r) => !r.engine);
|
|
550
551
|
if (soft) {
|
|
@@ -558,7 +559,7 @@ function consumeTrailingWords(clone, n, char = 0, hollow = false) {
|
|
|
558
559
|
const kept = last.find((r) => !r.engine);
|
|
559
560
|
const style = kept && sourceStyle(kept.node.parentElement);
|
|
560
561
|
if (kept && style && !last.some((r) => r.engine) && mayBeAutoHyphenated(words[n].text[char - 1], words[n].text[char], style.hyphens))
|
|
561
|
-
|
|
562
|
+
autoHyphen = { node: kept.node, end: kept.end, char: hyphenCharOf(style) };
|
|
562
563
|
}
|
|
563
564
|
leaveOutAfter(clone, keepNode);
|
|
564
565
|
// Structural pass: an element with no remaining words, standing entirely after the boundary, is spent — its edges adjoin the cut (§5.2) and it leaves the fragment.
|
|
@@ -582,6 +583,19 @@ function consumeTrailingWords(clone, n, char = 0, hollow = false) {
|
|
|
582
583
|
})(clone);
|
|
583
584
|
for (const el of spent)
|
|
584
585
|
putAway(el, hollow);
|
|
586
|
+
// Written last, after the leave-out and the structural pass that would take an element and a text with no source for spent: the hyphen the browser drew stands where the Compositor's stands, a text node of its own, the engine's, before a break mark (*the hyphen is written at the cut*; the printer finds the hyphen by that shape and the letters before it in the source's own run).
|
|
587
|
+
if (autoHyphen && autoHyphen.char) {
|
|
588
|
+
const { node, end } = autoHyphen;
|
|
589
|
+
const parent = node.parentNode;
|
|
590
|
+
const doc = node.parentElement.ownerDocument;
|
|
591
|
+
const before = end < node.data.length ? splitTextAt(node, end) : node.nextSibling;
|
|
592
|
+
const written = doc.createTextNode(autoHyphen.char);
|
|
593
|
+
noteEngineText(written);
|
|
594
|
+
const br = doc.createElement('br');
|
|
595
|
+
addWord(br, BREAK);
|
|
596
|
+
parent.insertBefore(written, before);
|
|
597
|
+
parent.insertBefore(br, before);
|
|
598
|
+
}
|
|
585
599
|
for (let el = inner; el && el !== clone; el = el.parentElement)
|
|
586
600
|
truncateTrailingDecoration(el);
|
|
587
601
|
return inner;
|
|
@@ -843,14 +857,14 @@ export function listOrdinals(ol) {
|
|
|
843
857
|
return out;
|
|
844
858
|
}
|
|
845
859
|
/**
|
|
846
|
-
* Every `<ol>` a cut divides is numbered from the first item its piece keeps: the piece's copy carries a `start` attribute of that item's ordinal, so the browser's own marker counts on from the source's numbering. Without it a continued list numbered from 1 again on every page, and the first piece of a `reversed` list counted down from the items it kept (measured 2026-11-07 against Chromium's print, `test/browser/list-continuation.browser.ts`). Read from the source, so it holds whichever cut ran first.
|
|
860
|
+
* Every `<ol>` (or numbered `<ul>` or `<menu>`) a cut divides is numbered from the first item its piece keeps: the piece's copy carries a `start` attribute of that item's ordinal, so the browser's own marker counts on from the source's numbering. Without it a continued list numbered from 1 again on every page, and the first piece of a `reversed` list counted down from the items it kept (measured 2026-11-07 against Chromium's print, `test/browser/list-continuation.browser.ts`). Read from the source, so it holds whichever cut ran first.
|
|
847
861
|
*/
|
|
848
862
|
function numberDividedLists(root, cut, source, cloneOf) {
|
|
849
863
|
if (!divides(cut) || !source || !cloneOf)
|
|
850
864
|
return;
|
|
851
865
|
const target = elAtIn(source, cut.path);
|
|
852
866
|
for (let a = target?.parentElement ?? null; a; a = a === source ? null : a.parentElement) {
|
|
853
|
-
if (!/^OL$/i.test(a.tagName))
|
|
867
|
+
if (!/^(?:OL|UL|MENU)$/i.test(a.tagName))
|
|
854
868
|
continue;
|
|
855
869
|
const copy = cloneOf.get(a);
|
|
856
870
|
if (!copy)
|
|
@@ -859,7 +873,11 @@ function numberDividedLists(root, cut, source, cloneOf) {
|
|
|
859
873
|
const c = cloneOf.get(li);
|
|
860
874
|
if (!c || !root.contains(c) || hasWord(c, AWAY))
|
|
861
875
|
continue;
|
|
862
|
-
|
|
876
|
+
// HTML's `start` belongs to `<ol>` alone, and Chromium's marker ignores a `counter-reset` of `list-item` (both measured 2026-11-08), so a `ul` or `menu` continues by the first kept item's own `value`.
|
|
877
|
+
if (/^OL$/i.test(a.tagName))
|
|
878
|
+
copy.setAttribute('start', String(n));
|
|
879
|
+
else
|
|
880
|
+
c.setAttribute('value', String(n));
|
|
863
881
|
break;
|
|
864
882
|
}
|
|
865
883
|
}
|
|
@@ -23,6 +23,8 @@ export type ContentFn = {
|
|
|
23
23
|
export declare const STRING_SET_PROP = "--beltype-string-set";
|
|
24
24
|
/** `position: running(head)` survives as this custom property, holding the identifier alone. */
|
|
25
25
|
export declare const RUNNING_PROP = "--beltype-running";
|
|
26
|
+
/** `hyphenate-limit-lines` survives as this custom property, its value text carried verbatim (Chromium drops the property, so the Compositor reads the author's limit from here). It inherits, as the original does, and so is not among {@link CUSTOM_PROPS}. */
|
|
27
|
+
export declare const HYPHENATE_LIMIT_LINES_PROP = "--beltype-hyphenate-limit-lines";
|
|
26
28
|
/** Both, for a host that registers them. `syntax: '*'` because the values are the author's own text and this engine is not their grammar; `inherits: false` because neither original inherits. */
|
|
27
29
|
export declare const CUSTOM_PROPS: string[];
|
|
28
30
|
/**
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
export const STRING_SET_PROP = '--beltype-string-set';
|
|
14
14
|
/** `position: running(head)` survives as this custom property, holding the identifier alone. */
|
|
15
15
|
export const RUNNING_PROP = '--beltype-running';
|
|
16
|
+
/** `hyphenate-limit-lines` survives as this custom property, its value text carried verbatim (Chromium drops the property, so the Compositor reads the author's limit from here). It inherits, as the original does, and so is not among {@link CUSTOM_PROPS}. */
|
|
17
|
+
export const HYPHENATE_LIMIT_LINES_PROP = '--beltype-hyphenate-limit-lines';
|
|
16
18
|
/** Both, for a host that registers them. `syntax: '*'` because the values are the author's own text and this engine is not their grammar; `inherits: false` because neither original inherits. */
|
|
17
19
|
export const CUSTOM_PROPS = [STRING_SET_PROP, RUNNING_PROP];
|
|
18
20
|
/**
|
package/dist/src/reads.d.ts
CHANGED
|
@@ -110,6 +110,8 @@ export type PageSite = {
|
|
|
110
110
|
* @returns null where no rule reads a page fact.
|
|
111
111
|
*/
|
|
112
112
|
export declare function findPageSites(doc: Document, originals?: Element[] | null, keep?: Keep | null): PageSite[] | null;
|
|
113
|
+
/** The flow sites that read the page total, as the rule that reads it names them: what {@link hideReservations} paints invisible while the total is unknown, since a flow site reads `0` until then (`chase.ts`'s `setCounters`). A `counter(page)` and a named string are true from the first page and are not among them. */
|
|
114
|
+
export declare function totalSites(doc: Document): Pick<Site, 'key' | 'pseudos'>[];
|
|
113
115
|
/** What every page of a run publishes, as the measure wears it: the page's number and the document's total, and its named strings' properties (`chase.js`'s `setCounters` and `setStrings`, the same values). */
|
|
114
116
|
export declare function pageFactsOf(account: Array<{
|
|
115
117
|
closes: {
|
|
@@ -143,13 +145,13 @@ export declare const isDeliverySheet: (doc: Document, sheet: CSSStyleSheet) => b
|
|
|
143
145
|
* @param sites - the page reads that are still at the reservation.
|
|
144
146
|
* @param scope - the selector of the element the presented pages stand in.
|
|
145
147
|
*/
|
|
146
|
-
export declare function hideSheetText(sites: Site[], scope?: string): string;
|
|
148
|
+
export declare function hideSheetText(sites: Pick<Site, 'key' | 'pseudos'>[], scope?: string): string;
|
|
147
149
|
/**
|
|
148
150
|
* Paint the reservations invisible: the page numbers the cuts so far do not know yet (*the pages are cut as far as they are asked*, Egildo's ruling 1 of 2026-10-12, on the note's recommendation). A document shown before it is complete must not show `0` where a page number will stand.
|
|
149
151
|
*
|
|
150
152
|
* @param scope - as {@link hideSheetText}.
|
|
151
153
|
*/
|
|
152
|
-
export declare function hideReservations(doc: Document, sites: Site[], scope?: string | undefined): void;
|
|
154
|
+
export declare function hideReservations(doc: Document, sites: Pick<Site, 'key' | 'pseudos'>[], scope?: string | undefined): void;
|
|
153
155
|
/** Take the hiding sheet off: the true numbers are delivered, or there is nothing left to hide. */
|
|
154
156
|
export declare function showReservations(doc: Document): void;
|
|
155
157
|
/** Take the delivery sheet off the document, where one was adopted — a second paginate of a document that no longer declares a read leaves nothing behind. */
|
package/dist/src/reads.js
CHANGED
|
@@ -237,6 +237,12 @@ export function findPageSites(doc, originals = null, keep = null) {
|
|
|
237
237
|
}
|
|
238
238
|
return sites.length ? sites : null;
|
|
239
239
|
}
|
|
240
|
+
/** The flow sites that read the page total, as the rule that reads it names them: what {@link hideReservations} paints invisible while the total is unknown, since a flow site reads `0` until then (`chase.ts`'s `setCounters`). A `counter(page)` and a named string are true from the first page and are not among them. */
|
|
241
|
+
export function totalSites(doc) {
|
|
242
|
+
return contentRules(doc)
|
|
243
|
+
.filter((r) => /counters?\(\s*pages\s*[,)]/.test(r.content))
|
|
244
|
+
.map((r) => ({ key: hostSelector(r.selector), pseudos: r.pseudos }));
|
|
245
|
+
}
|
|
240
246
|
/** What every page of a run publishes, as the measure wears it: the page's number and the document's total, and its named strings' properties (`chase.js`'s `setCounters` and `setStrings`, the same values). */
|
|
241
247
|
export function pageFactsOf(account) {
|
|
242
248
|
return account.map((entry) => {
|
|
@@ -557,7 +563,7 @@ export async function settleReads(doc, { run, blocksOf, originals = null, warm =
|
|
|
557
563
|
else
|
|
558
564
|
withdraw(doc);
|
|
559
565
|
if (hide)
|
|
560
|
-
hideReservations(doc, paged.filter((s) => delivered.get(s.key ?? '')?.get(s.prop) === cssString(RESERVATION)), hide === true ? undefined : hide);
|
|
566
|
+
hideReservations(doc, [...paged.filter((s) => delivered.get(s.key ?? '')?.get(s.prop) === cssString(RESERVATION)), ...(pageSites ? totalSites(doc) : [])], hide === true ? undefined : hide);
|
|
561
567
|
performance.measure('cicero:reads', { start: t, end: (t = performance.now()), detail: { references: sites.length, pageSites: pageSites?.length ?? 0 } });
|
|
562
568
|
let runs = 0;
|
|
563
569
|
let moved = [];
|
package/dist/src/root.js
CHANGED
|
@@ -19,7 +19,7 @@ import { pointShownBy, sourcePointOf } from './source-point.js';
|
|
|
19
19
|
import { copyOf, holdsFixedBox, sourceOf } from './word-space.js';
|
|
20
20
|
import { holdsPresented } from './presenters.js';
|
|
21
21
|
import { transliterateContent, neutralizeMarginBoxes, sheetsUseHas } from './capture.js';
|
|
22
|
-
import { ARTIFACT, addWord, FIXED, FRAGMENT, selectorOf, setValue, wordOf } from './data-beltype.js';
|
|
22
|
+
import { ARTIFACT, decodeFragment, addWord, FIXED, FRAGMENT, selectorOf, setValue, wordOf } from './data-beltype.js';
|
|
23
23
|
import { deliverValues, ensureContinuationDressing, ensureDressing, isWordsSheet } from './dressing.js';
|
|
24
24
|
import { carriedPast, divides, elAtIn, extensionPx, flattenResume, flowsOf, indexBlocks, pageIntervals, pageOfPos, widthKeyOf, wordsBeforeChar } from './positions.js';
|
|
25
25
|
const PRESS_STYLE_ID = '__beltype-press';
|
|
@@ -690,7 +690,7 @@ export class BeltypeRoot extends HTMLElement {
|
|
|
690
690
|
this.#listening = true;
|
|
691
691
|
const view = this.ownerDocument.defaultView;
|
|
692
692
|
const targetOf = (hash) => {
|
|
693
|
-
const id = hash.length > 1 ?
|
|
693
|
+
const id = hash.length > 1 ? decodeFragment(hash) : '';
|
|
694
694
|
const found = id ? this.ownerDocument.getElementById(id) ?? this.ownerDocument.getElementsByName(id)[0] : null;
|
|
695
695
|
return found && this.contains(found) ? found : null;
|
|
696
696
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beltype/cicero",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
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",
|