@peripheries/renderer 0.23.0 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -33
- package/dist/catalog.js +2 -6
- package/dist/chunks/{forms-CCAE7sCu.js → forms-DxA9WzCt.js} +0 -6
- package/dist/chunks/{load-CjPvISzH.js → load-b38vUiZM.js} +0 -4
- package/dist/chunks/{page-bundles-DLRIBP8g.js → page-bundles-CPj_vx-J.js} +1 -5
- package/dist/chunks/{persistence-DCMakM9H.js → persistence-hurkUUQ0.js} +0 -6
- package/dist/chunks/{projection-yvYKzMOS.js → projection-zhu32_KJ.js} +1 -15
- package/dist/chunks/{query-LnTbQghF.js → query-D6xQbTFK.js} +0 -6
- package/dist/chunks/{refs-BLUokra5.js → refs-Csgh6rXq.js} +0 -4
- package/dist/chunks/{structure-BrKSgzRJ.js → structure-CKBtQHJq.js} +1 -7
- package/dist/chunks/{validate-B_g7719l.js → validate-BcB_PA1k.js} +2 -10
- package/dist/core.js +9 -25
- package/dist/forms.js +1 -1
- package/dist/print.js +0 -24
- package/dist/react.js +4 -22
- package/dist/runtime.js +2 -12
- package/dist/styles.css +9 -15
- package/dist/testing.js +5 -21
- package/dist/theme.js +0 -10
- package/dist/types/catalog/check.d.ts +0 -13
- package/dist/types/core/actions.d.ts +0 -24
- package/dist/types/core/bundles/load.d.ts +0 -23
- package/dist/types/core/bundles/page-bundles.d.ts +0 -9
- package/dist/types/core/bundles/refs.d.ts +1 -28
- package/dist/types/core/bundles/resolve.d.ts +0 -10
- package/dist/types/core/cell-props.d.ts +0 -20
- package/dist/types/core/cells.d.ts +0 -21
- package/dist/types/core/data-context.d.ts +0 -22
- package/dist/types/core/diagnostics.d.ts +1 -3
- package/dist/types/core/graph.d.ts +0 -54
- package/dist/types/core/layout.d.ts +0 -9
- package/dist/types/core/load-page.d.ts +0 -111
- package/dist/types/core/query.d.ts +0 -64
- package/dist/types/core/references.d.ts +0 -21
- package/dist/types/core/release.d.ts +0 -10
- package/dist/types/core/rules.d.ts +0 -1
- package/dist/types/core/selected.d.ts +0 -5
- package/dist/types/core/sources.d.ts +5 -64
- package/dist/types/core/store/create-store.d.ts +0 -6
- package/dist/types/core/store/persistence.d.ts +0 -1
- package/dist/types/core/store/projection.d.ts +0 -5
- package/dist/types/core/store/scope-keys.d.ts +0 -1
- package/dist/types/core/store/types.d.ts +0 -6
- package/dist/types/core/structure.d.ts +0 -33
- package/dist/types/core/types.d.ts +0 -13
- package/dist/types/core/validate.d.ts +0 -12
- package/dist/types/core/values.d.ts +0 -6
- package/dist/types/forms/conditions.d.ts +0 -37
- package/dist/types/forms/model.d.ts +0 -44
- package/dist/types/forms/port.d.ts +0 -22
- package/dist/types/print/document-bindings.d.ts +0 -61
- package/dist/types/print/document-profile.d.ts +0 -137
- package/dist/types/print/map-staleness.d.ts +0 -66
- package/dist/types/print/page-map.d.ts +1 -334
- package/dist/types/print/paginate.d.ts +0 -190
- package/dist/types/print/print-layout.d.ts +0 -161
- package/dist/types/print/print-lint.d.ts +0 -109
- package/dist/types/print/print-policy.d.ts +0 -184
- package/dist/types/print/print-stylesheet.d.ts +0 -135
- package/dist/types/print/running-header.d.ts +0 -86
- package/dist/types/print/structure-print.d.ts +1 -243
- package/dist/types/react/cell-states.d.ts +0 -2
- package/dist/types/react/cell-view.d.ts +0 -16
- package/dist/types/react/page-renderer.d.ts +0 -37
- package/dist/types/react/structure-view.d.ts +0 -12
- package/dist/types/react/use-cell-store.d.ts +0 -5
- package/dist/types/react/use-container-size.d.ts +0 -5
- package/dist/types/react/use-forms-runtime.d.ts +1 -55
- package/dist/types/react/use-paging.d.ts +0 -9
- package/dist/types/runtime/boot.d.ts +0 -11
- package/dist/types/runtime/install.d.ts +0 -15
- package/dist/types/runtime/shims.d.ts +0 -8
- package/dist/types/runtime/store-export.d.ts +0 -7
- package/dist/types/testing/base-props.d.ts +0 -1
- package/dist/types/testing/components.d.ts +0 -8
- package/dist/types/testing/form-workbook.d.ts +0 -3
- package/dist/types/testing/golden.d.ts +0 -16
- package/dist/types/testing/ids.d.ts +0 -2
- package/dist/types/testing/sources.d.ts +0 -7
- package/dist/types/theme/check.d.ts +0 -2
- package/dist/types/theme/color.d.ts +0 -12
- package/dist/types/theme/derive.d.ts +0 -12
- package/dist/types/theme/parse.d.ts +0 -7
- package/package.json +2 -2
- package/dist/catalog.js.map +0 -1
- package/dist/chunks/forms-CCAE7sCu.js.map +0 -1
- package/dist/chunks/load-CjPvISzH.js.map +0 -1
- package/dist/chunks/page-bundles-DLRIBP8g.js.map +0 -1
- package/dist/chunks/persistence-DCMakM9H.js.map +0 -1
- package/dist/chunks/projection-yvYKzMOS.js.map +0 -1
- package/dist/chunks/query-LnTbQghF.js.map +0 -1
- package/dist/chunks/refs-BLUokra5.js.map +0 -1
- package/dist/chunks/structure-BrKSgzRJ.js.map +0 -1
- package/dist/chunks/validate-B_g7719l.js.map +0 -1
- package/dist/core.js.map +0 -1
- package/dist/print.js.map +0 -1
- package/dist/react.js.map +0 -1
- package/dist/runtime.js.map +0 -1
- package/dist/testing.js.map +0 -1
- package/dist/theme.js.map +0 -1
|
@@ -1,79 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `@page` geometry, generated from a `DocumentProfile`.
|
|
3
|
-
*
|
|
4
|
-
* ── One source, and it is the profile ───────────────────────────────────────
|
|
5
|
-
*
|
|
6
|
-
* `03-print-strategy.md` §2 specified a `PaperConfig` on the layout contract.
|
|
7
|
-
* That is superseded: `DocumentProfile` already carries `sheet`, `margins`,
|
|
8
|
-
* `measure`, `floorPx` and `regions`, it is authored where the design lives, and
|
|
9
|
-
* it is already lifted off the bundle barrel. A second geometry source would be
|
|
10
|
-
* the exact thing §2 argued against — *"it distributes the page definition
|
|
11
|
-
* across every file that has an opinion"* — with the added twist that the two
|
|
12
|
-
* sources would disagree in a way only a printed page could reveal.
|
|
13
|
-
*
|
|
14
|
-
* ── Why the host injects margin-box content as literals ─────────────────────
|
|
15
|
-
*
|
|
16
|
-
* MEASURED, Chromium 151, this session:
|
|
17
|
-
*
|
|
18
|
-
* CSS.supports('string-set', 'docCode content(text)') -> false
|
|
19
|
-
* CSS.supports('content', 'string(docCode)') -> false
|
|
20
|
-
*
|
|
21
|
-
* and, inserted into a real stylesheet, both are **dropped by the parser**:
|
|
22
|
-
* `h1 { string-set: … }` round-trips as `h1 { }`, and
|
|
23
|
-
* `@page { @bottom-left { content: string(docCode) } }` as `@bottom-left { }`.
|
|
24
|
-
* Literal strings and `counter(page)`/`counter(pages)` both survive.
|
|
25
|
-
*
|
|
26
|
-
* So Chromium does not implement the GCPM `string()` mechanism, and a footer
|
|
27
|
-
* cannot pull text out of the document. **This is a third silent-failure mode**
|
|
28
|
-
* beside the two `03` §4 already lists (`preferCSSPageSize`, margin boxes not
|
|
29
|
-
* inheriting the body font): the declaration is dropped at parse time, the
|
|
30
|
-
* margin box renders empty, and nothing anywhere reports it.
|
|
31
|
-
*
|
|
32
|
-
* The host therefore resolves each region slot to a literal at build time —
|
|
33
|
-
* which it can, because it knows the document — and refuses to emit a slot it
|
|
34
|
-
* cannot resolve rather than emitting one that will silently render blank.
|
|
35
|
-
*/
|
|
36
1
|
import type { DocumentProfile } from './document-profile.js';
|
|
37
|
-
/**
|
|
38
|
-
* Values for the region slots a profile names, keyed by slot name.
|
|
39
|
-
*
|
|
40
|
-
* `instrument` asks for `execution-state` bottom-left and `page-x-of-y`
|
|
41
|
-
* bottom-right. The second is a built-in; the first is document-derived and has
|
|
42
|
-
* to be supplied here.
|
|
43
|
-
*/
|
|
44
2
|
export type PageRegionValues = Readonly<Record<string, string>>;
|
|
45
|
-
/** Test seam. Nothing in the app should need this. */
|
|
46
3
|
export declare function __clearPrintStylesheetWarnings(): void;
|
|
47
|
-
/**
|
|
48
|
-
* Cross-check the bundle's paginator vocabulary against what the host can render.
|
|
49
|
-
*
|
|
50
|
-
* A document bundle exports `PAGINATOR_BINDINGS` — the bindings the host must NOT try to
|
|
51
|
-
* supply, because CSS computes them during pagination. The host holds the other
|
|
52
|
-
* half: the CSS expression each one becomes. Today the two lists agree exactly
|
|
53
|
-
* (`page-x-of-y`, `page-number`, `page-count`), arrived at independently on both
|
|
54
|
-
* sides — which is reassuring and completely unenforced.
|
|
55
|
-
*
|
|
56
|
-
* The divergence that matters is one-directional. If the bundle adds a fourth binding
|
|
57
|
-
* — `page-of-section`, say — the host has no expression for it, `values` will
|
|
58
|
-
* never contain it (correctly: nobody may supply a paginator binding), and the
|
|
59
|
-
* margin box is **silently omitted**. The document loses a footer and nothing
|
|
60
|
-
* reports it, which is the failure this whole module is built to refuse.
|
|
61
|
-
*
|
|
62
|
-
* So the check runs at bundle load, once, and names the gap. The host cannot fix
|
|
63
|
-
* it at runtime — an expression has to be written — but a named gap is a
|
|
64
|
-
* two-minute change and a silent one is a reprint.
|
|
65
|
-
*/
|
|
66
4
|
export declare function checkPaginatorBindings(bindings: unknown, debugLabel: string): string[];
|
|
67
|
-
/**
|
|
68
|
-
* Host fallback geometry, used when no profile resolves. US Letter.
|
|
69
|
-
*
|
|
70
|
-
* **Exported because the page map needs the same numbers.** Most pages declare
|
|
71
|
-
* no profile — the proof document's `layout` is `columns, shell, structure, type`
|
|
72
|
-
* with no `profile` key — so these, not a profile, are what the live `@page`
|
|
73
|
-
* rule is built from. A page map that fell back to its own copy of "US Letter"
|
|
74
|
-
* would be a second source for the geometry the paginator measures against, and
|
|
75
|
-
* the two agreeing today is not the same as them being one number.
|
|
76
|
-
*/
|
|
77
5
|
export declare const DEFAULT_SHEET: {
|
|
78
6
|
width: string;
|
|
79
7
|
height: string;
|
|
@@ -84,49 +12,13 @@ export declare const DEFAULT_MARGINS: {
|
|
|
84
12
|
bottom: string;
|
|
85
13
|
left: string;
|
|
86
14
|
};
|
|
87
|
-
/**
|
|
88
|
-
* Quote a value for use in a CSS `content` string.
|
|
89
|
-
*
|
|
90
|
-
* Not decoration: a document code carrying an apostrophe — or a member name
|
|
91
|
-
* carrying one, which is common — would otherwise terminate the string early
|
|
92
|
-
* and produce a stylesheet that fails to parse from that point on, taking the
|
|
93
|
-
* rest of the `@page` block with it. Backslash first, or it re-escapes the
|
|
94
|
-
* escapes.
|
|
95
|
-
*/
|
|
96
15
|
export declare function cssString(value: string): string;
|
|
97
|
-
/**
|
|
98
|
-
* A profile value (a length, a colour, a rule weight) as a CSS token, or undefined when it isn't
|
|
99
|
-
* one. These come from the bundle's profile, never from document data, but a value that could
|
|
100
|
-
* end a declaration or the style element is dropped all the same: letters, digits, `#`, `.`, `,`,
|
|
101
|
-
* `%`, `-`, `_`, `/`, spaces and parentheses, at most 120 characters.
|
|
102
|
-
*/
|
|
103
16
|
export declare function cssValue(value: unknown): string | undefined;
|
|
104
17
|
export interface BuildPageStylesheetOptions {
|
|
105
18
|
values?: PageRegionValues;
|
|
106
|
-
/** Named in warnings so a blank footer is traceable to a document. */
|
|
107
19
|
debugLabel?: string;
|
|
108
|
-
/**
|
|
109
|
-
* Throw when a binding in `profile.requiredBindings` cannot be filled.
|
|
110
|
-
*
|
|
111
|
-
* **Defaults to `true`, and the default is the whole point.** A caller that
|
|
112
|
-
* forgets this option gets the refusal; a caller that wants to render anyway
|
|
113
|
-
* has to say so at the call site, where a reader can see it and ask why. The
|
|
114
|
-
* inverse default would make "produce a legal instrument that does not say it
|
|
115
|
-
* is a draft" the thing you get by not thinking about it.
|
|
116
|
-
*
|
|
117
|
-
* Screen rendering passes `false` deliberately: `@page` does not apply on
|
|
118
|
-
* screen, so a missing footer binding misrepresents nothing there. The
|
|
119
|
-
* artifact path — where the document becomes paper — must not.
|
|
120
|
-
*/
|
|
121
20
|
strict?: boolean;
|
|
122
21
|
}
|
|
123
|
-
/**
|
|
124
|
-
* Thrown when a document cannot be printed without misrepresenting itself.
|
|
125
|
-
*
|
|
126
|
-
* A distinct class rather than a bare `Error` so an artifact endpoint can
|
|
127
|
-
* answer 422-with-a-reason instead of 500-with-a-stack: this is a document
|
|
128
|
-
* that is not printable yet, not a server that broke.
|
|
129
|
-
*/
|
|
130
22
|
export declare class MissingRequiredBindingError extends Error {
|
|
131
23
|
readonly profileId: string | undefined;
|
|
132
24
|
readonly missing: string[];
|
|
@@ -134,35 +26,8 @@ export declare class MissingRequiredBindingError extends Error {
|
|
|
134
26
|
}
|
|
135
27
|
export interface PageStylesheet {
|
|
136
28
|
css: string;
|
|
137
|
-
/**
|
|
138
|
-
* Unfilled bindings the profile marked required.
|
|
139
|
-
*
|
|
140
|
-
* Only ever non-empty when `strict: false` — strict mode throws instead. It
|
|
141
|
-
* exists so a lenient (screen) caller can still surface the problem before
|
|
142
|
-
* someone hits print, rather than discovering it on paper.
|
|
143
|
-
*/
|
|
144
29
|
missingRequired: string[];
|
|
145
|
-
/**
|
|
146
|
-
* Slots the profile named and the host could not fill.
|
|
147
|
-
*
|
|
148
|
-
* Returned rather than only logged so a caller — a print preflight, or the
|
|
149
|
-
* parity test in stage 9 — can assert on them. A blank margin box is invisible
|
|
150
|
-
* on screen and only wrong on paper, which is the worst place to find it.
|
|
151
|
-
*/
|
|
152
30
|
unresolvedSlots: string[];
|
|
153
31
|
}
|
|
154
|
-
/**
|
|
155
|
-
* Build the `@page` block for one document.
|
|
156
|
-
*
|
|
157
|
-
* @param profile Resolved profile, or `null` for host defaults.
|
|
158
|
-
*/
|
|
159
32
|
export declare function buildPageStylesheet(profile: DocumentProfile | null, options?: BuildPageStylesheetOptions): PageStylesheet;
|
|
160
|
-
/**
|
|
161
|
-
* Profile-derived custom properties for the printed body.
|
|
162
|
-
*
|
|
163
|
-
* Separate from the `@page` block because they cascade to elements, and `@page`
|
|
164
|
-
* is not part of the document tree — a custom property declared inside `@page`
|
|
165
|
-
* is not visible to the body, and one declared on `:root` is not visible to a
|
|
166
|
-
* margin box. Both halves are needed and neither can be dropped into the other.
|
|
167
|
-
*/
|
|
168
33
|
export declare function buildPageVariables(profile: DocumentProfile | null): string;
|
|
@@ -1,105 +1,19 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Running headers and footers that know where they are in the document.
|
|
3
|
-
*
|
|
4
|
-
* ── What this is for ───────────────────────────────────────────────────────
|
|
5
|
-
*
|
|
6
|
-
* The owner: *"I'd like to be able to create a custom print header and footer
|
|
7
|
-
* that updates based on the page layout i.e. page number section continuation
|
|
8
|
-
* etc."*
|
|
9
|
-
*
|
|
10
|
-
* Page numbers were already possible — `counter(page)` in a margin box, and
|
|
11
|
-
* MEASURED to work in Chrome. *"Financial Summary (continued)"* was not, and the
|
|
12
|
-
* reason is recorded in `print-stylesheet.ts`: Chromium drops the GCPM
|
|
13
|
-
* `string()` mechanism at parse time, so a margin box cannot pull text out of
|
|
14
|
-
* the document. Every engine that does this natively (Prince, WeasyPrint) has
|
|
15
|
-
* `string-set`; Chrome has neither.
|
|
16
|
-
*
|
|
17
|
-
* ── Why the host can do it anyway ──────────────────────────────────────────
|
|
18
|
-
*
|
|
19
|
-
* Because the host now knows the pagination. `paginate` assigns every row to a
|
|
20
|
-
* page, and MEASURED this session, Chrome supports **named pages**:
|
|
21
|
-
*
|
|
22
|
-
* @page secA { @top-left { content: "Financial Summary" } }
|
|
23
|
-
* .row { page: secA; }
|
|
24
|
-
*
|
|
25
|
-
* with `counter(page)` staying continuous across them and margin boxes
|
|
26
|
-
* cascading **per box** — a named rule overrides only the slots it redefines,
|
|
27
|
-
* leaving the rest of the default `@page` intact.
|
|
28
|
-
*
|
|
29
|
-
* So the host resolves the running content per page itself and emits one named
|
|
30
|
-
* `@page` rule for each. Nothing is pulled from the document at print time; it
|
|
31
|
-
* is all decided before the stylesheet is written.
|
|
32
|
-
*
|
|
33
|
-
* ── Where a "section" comes from, and why not from config ──────────────────
|
|
34
|
-
*
|
|
35
|
-
* Not from config, because it is not there. The proof page's `layout.structure` is a flat
|
|
36
|
-
* list of `{ cellId }` with no sections and no `sectionId`, and a survey
|
|
37
|
-
* measured 3 `sectionId`s across 2,279 structure nodes with **none** on that
|
|
38
|
-
* document. Its cells are thin refs, so even the component name lives in DSNS
|
|
39
|
-
* rather than the page.
|
|
40
|
-
*
|
|
41
|
-
* It comes from the **rendered document**, on a signal FEC already authors:
|
|
42
|
-
* a row that declares `break-after: avoid` is a row bound to what follows —
|
|
43
|
-
* which is what a section heading is. Measured on the proof document, that selects
|
|
44
|
-
* **exactly** the seven `*-header` rows, all `<h2>`, with no false positives:
|
|
45
|
-
*
|
|
46
|
-
* summary-header · retirement-header · networth-header · tax-header
|
|
47
|
-
* insurance-header · decumulation-header · decisions-header
|
|
48
|
-
*
|
|
49
|
-
* Requiring a heading element as well excludes the cover (`<h1>`, and not bound
|
|
50
|
-
* to anything) and the `<h3>`/`<h4>` sub-headings that live *inside* content
|
|
51
|
-
* rows — those are 6 more heading elements that are emphatically not sections.
|
|
52
|
-
*
|
|
53
|
-
* This module takes the markers already resolved. Finding them in the DOM is
|
|
54
|
-
* the caller's job, for the same reason `collectBlocks` takes an injected
|
|
55
|
-
* measurer: a pure function is testable and a DOM walk is not.
|
|
56
|
-
*/
|
|
57
1
|
import type { PageMap } from './paginate.js';
|
|
58
2
|
import type { Band } from './page-map.js';
|
|
59
|
-
/** A row that begins a section, and the title it carries. */
|
|
60
3
|
export interface SectionMarker {
|
|
61
|
-
/** The cell id of the heading row. */
|
|
62
4
|
cellId: string;
|
|
63
5
|
title: string;
|
|
64
6
|
}
|
|
65
7
|
export interface RunningValues {
|
|
66
|
-
/** 1-based page number. */
|
|
67
8
|
page: number;
|
|
68
|
-
/** The section this page sits in, or undefined before the first heading. */
|
|
69
9
|
sectionTitle?: string;
|
|
70
|
-
/**
|
|
71
|
-
* True when the section started on an EARLIER page.
|
|
72
|
-
*
|
|
73
|
-
* The distinction a reader needs: a page headed *"Retirement Analysis"* that
|
|
74
|
-
* begins the section reads differently from one that carries it on, and
|
|
75
|
-
* without the marker a reader flipping through cannot tell whether they have
|
|
76
|
-
* missed the start.
|
|
77
|
-
*/
|
|
78
10
|
continued: boolean;
|
|
79
11
|
}
|
|
80
12
|
export interface ResolveRunningOptions {
|
|
81
|
-
/** Appended when a section carries over. `' (continued)'` by default. */
|
|
82
13
|
continuedSuffix?: string;
|
|
83
14
|
}
|
|
84
|
-
/**
|
|
85
|
-
* Which section each page is in, and whether it carries over.
|
|
86
|
-
*
|
|
87
|
-
* @param map From `paginate`, whose pages hold band ids in reading order.
|
|
88
|
-
* @param bands From `collectBlocks`, to resolve a band id to its member cells.
|
|
89
|
-
* @param markers The section headings, in document order.
|
|
90
|
-
*/
|
|
91
15
|
export declare function resolveRunningValues(map: PageMap, bands: readonly Band[], markers: readonly SectionMarker[]): RunningValues[];
|
|
92
|
-
/**
|
|
93
|
-
* The per-page slot values, keyed by slot name.
|
|
94
|
-
*
|
|
95
|
-
* Two names, deliberately few. `section-title` is the bare title;
|
|
96
|
-
* `section-title-continued` is the same title with the suffix when it carries
|
|
97
|
-
* over — a profile chooses which behaviour it wants rather than the host
|
|
98
|
-
* deciding for it, because a footer that says *"Retirement Analysis
|
|
99
|
-
* (continued)"* is right for a report and wrong for a form.
|
|
100
|
-
*/
|
|
101
16
|
export declare const RUNNING_SLOTS: readonly ["section-title", "section-title-continued"];
|
|
102
17
|
export type RunningSlot = (typeof RUNNING_SLOTS)[number];
|
|
103
18
|
export declare function isRunningSlot(name: string): name is RunningSlot;
|
|
104
|
-
/** Resolve one page's running slots to literal strings. */
|
|
105
19
|
export declare function runningSlotValues(values: RunningValues, { continuedSuffix }?: ResolveRunningOptions): Partial<Record<RunningSlot, string>>;
|
|
@@ -1,269 +1,46 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Writing a person's page-break decision back into `layout.structure`.
|
|
3
|
-
*
|
|
4
|
-
* ── What this is for ───────────────────────────────────────────────────────
|
|
5
|
-
*
|
|
6
|
-
* The owner: *"hover buttons that allow the user to move components either up or
|
|
7
|
-
* down in the print map … do page mappings get persisted so the next time it
|
|
8
|
-
* respects those changes."*
|
|
9
|
-
*
|
|
10
|
-
* They do, and this is the write. Two things had to be settled before it could
|
|
11
|
-
* exist, and both were settled by the people who own them rather than here:
|
|
12
|
-
*
|
|
13
|
-
* - **The vocabulary is FEC's**. `keepWithPrevious`, not
|
|
14
|
-
* `breakBefore: false`, because an absent boolean and a false one are the same
|
|
15
|
-
* value across a JSON round-trip — so "no opinion" and "explicitly do not
|
|
16
|
-
* break" would have been indistinguishable, which is the declared-vs-default
|
|
17
|
-
* flattening that split a section heading in half.
|
|
18
|
-
* - **The storage is `layout.structure[].print`** (agreed with the agency side, checked
|
|
19
|
-
* against the database). A template re-sync does not touch an instance page's
|
|
20
|
-
* `layout`; the standing rule is never to call sync with `rebuild`.
|
|
21
|
-
*
|
|
22
|
-
* ── Why an intent and not a page number ────────────────────────────────────
|
|
23
|
-
*
|
|
24
|
-
* A stored page ASSIGNMENT — "page 7 holds these four rows" — is wrong the
|
|
25
|
-
* instant any height changes: a data refresh, a webfont, a longer client name.
|
|
26
|
-
* A stored INTENT — "start a page before this row" — survives all of them,
|
|
27
|
-
* because it is a statement about a boundary rather than about a layout.
|
|
28
|
-
*
|
|
29
|
-
* ── Why it is a pure function over the structure array ─────────────────────
|
|
30
|
-
*
|
|
31
|
-
* Because the array is the awkward part, not the write. Entries are strings OR
|
|
32
|
-
* objects, sections nest children arbitrarily deep, and the same cell id must not
|
|
33
|
-
* be matched twice. All of that is testable without a database, and none of it is
|
|
34
|
-
* testable through a mutation hook.
|
|
35
|
-
*/
|
|
36
1
|
import type { LayoutNode as RuntimeLayoutNode } from '@peripheries/runtime';
|
|
37
2
|
import type { PrintConstraints } from './print-policy.js';
|
|
38
|
-
/**
|
|
39
|
-
* Where a structure row renders outside the flow. The runtime types `slot` as an
|
|
40
|
-
* open string; the print writer only knows this one.
|
|
41
|
-
*/
|
|
42
3
|
export type StructureSlot = 'marginalia';
|
|
43
|
-
/**
|
|
44
|
-
* The runtime's `LayoutNode`, with the print block's `blankPageBefore` (a
|
|
45
|
-
* deliberate blank page before the row), which the runtime type does not
|
|
46
|
-
* declare yet. Everything else is the runtime's.
|
|
47
|
-
*/
|
|
48
4
|
export type LayoutNode = Omit<RuntimeLayoutNode, 'print' | 'children'> & {
|
|
49
5
|
children?: Array<LayoutNode | string>;
|
|
50
6
|
print?: NonNullable<RuntimeLayoutNode['print']> & {
|
|
51
7
|
blankPageBefore?: boolean;
|
|
52
8
|
};
|
|
53
9
|
};
|
|
54
|
-
|
|
55
|
-
export type PageIntent =
|
|
56
|
-
/** Start a new page at this row. */
|
|
57
|
-
'break-before'
|
|
58
|
-
/** Keep this row on the page above — it may not open a page. */
|
|
59
|
-
| 'keep-with-previous'
|
|
60
|
-
/** No opinion: the packer decides. Clears any previous intent. */
|
|
61
|
-
| 'auto';
|
|
62
|
-
/** A structure entry, as authored. A bare string is shorthand for `{ cellId }`. */
|
|
10
|
+
export type PageIntent = 'break-before' | 'keep-with-previous' | 'auto';
|
|
63
11
|
export type StructureEntry = string | StructureNode;
|
|
64
|
-
/**
|
|
65
|
-
* A `layout.structure` entry, as the print writer sees it.
|
|
66
|
-
*
|
|
67
|
-
* **Not a second declaration.** Every field lives on `LayoutNode`
|
|
68
|
-
* (`@peripheries/runtime`, plus `blankPageBefore` above) — its header records why there
|
|
69
|
-
* is now one. This alias adds exactly two things, both about writing rather
|
|
70
|
-
* than rendering:
|
|
71
|
-
*
|
|
72
|
-
* - **an index signature**, so an authored key the host has never heard of
|
|
73
|
-
* survives a read-modify-write instead of becoming an excess-property error.
|
|
74
|
-
* The renderer must not have it: it would switch off excess-property checking
|
|
75
|
-
* for every node literal there.
|
|
76
|
-
* - **`children` narrowed to `StructureEntry[]`**, so the recursive walks below
|
|
77
|
-
* stay typed. `LayoutNode.children` is `Array<LayoutNode | string>`, and a
|
|
78
|
-
* `LayoutNode` is not assignable to a type carrying an index signature.
|
|
79
|
-
*
|
|
80
|
-
* **Add a field to `LayoutNode`, never here.** A field added here is invisible
|
|
81
|
-
* to the renderer — which is exactly how `slot` shipped into one of two types
|
|
82
|
-
* and was found by FEC's drift check rather than by either of us.
|
|
83
|
-
*/
|
|
84
12
|
export type StructureNode = Omit<LayoutNode, 'children'> & {
|
|
85
13
|
children?: StructureEntry[];
|
|
86
14
|
[key: string]: unknown;
|
|
87
15
|
};
|
|
88
16
|
export interface SetIntentResult {
|
|
89
17
|
structure: StructureEntry[];
|
|
90
|
-
/** False when no entry named this cell — the caller must not save a no-op. */
|
|
91
18
|
changed: boolean;
|
|
92
19
|
}
|
|
93
|
-
/**
|
|
94
|
-
* Set one row's page intent, returning a new structure.
|
|
95
|
-
*
|
|
96
|
-
* @param structure The page's `layout.structure`, untouched by this call.
|
|
97
|
-
* @param cellId The row to set it on.
|
|
98
|
-
* @param intent What the person asked for.
|
|
99
|
-
*/
|
|
100
20
|
export declare function setRowPageIntent(structure: readonly StructureEntry[] | undefined, cellId: string, intent: PageIntent): SetIntentResult;
|
|
101
|
-
/**
|
|
102
|
-
* The `print` block an intent produces, or `undefined` when it produces none.
|
|
103
|
-
*
|
|
104
|
-
* ── Why this is exported ────────────────────────────────────────────────────
|
|
105
|
-
*
|
|
106
|
-
* Because the same intent now has to land in TWO places, per the owner's ruling
|
|
107
|
-
* on where print configuration lives: *"The honest answer is both. If there is
|
|
108
|
-
* an associated process node synced to the config, then it lives in DSNS. But
|
|
109
|
-
* this should update the project_page record anyways."*
|
|
110
|
-
*
|
|
111
|
-
* So `setRowPageIntent` writes the page row's `layout.structure[].print` and
|
|
112
|
-
* `recordCellPrintIntent` writes the cell's DSNS `content.layout.print`. Two
|
|
113
|
-
* call sites, one set of rules — because two copies of these three lines is
|
|
114
|
-
* exactly how a mirror comes to disagree with its source, which is the defect
|
|
115
|
-
* this whole channel keeps meeting.
|
|
116
|
-
*
|
|
117
|
-
* The rules, all three load-bearing:
|
|
118
|
-
*
|
|
119
|
-
* 1. **Both keys are deleted first.** Leaving a stale `breakBefore: true` under
|
|
120
|
-
* a new `keepWithPrevious: true` produces a node that asks for a break and
|
|
121
|
-
* asks not to break. `StructureRenderer` resolves that to the break and
|
|
122
|
-
* warns, so a person pressing "move up" would watch nothing happen and be
|
|
123
|
-
* told nothing.
|
|
124
|
-
* 2. **`auto` is the ABSENCE of both**, never either set `false`. The
|
|
125
|
-
* vocabulary is FEC's and `nodePrintStyle` reads a `false` as no
|
|
126
|
-
* declaration at all — indistinguishable from working.
|
|
127
|
-
* 3. **An empty block is `undefined`, not `{}`.** To a reader `{}` says
|
|
128
|
-
* "someone had an opinion here", and it would survive every future diff.
|
|
129
|
-
*
|
|
130
|
-
* `breakInside` is never touched: a different decision, authored by someone
|
|
131
|
-
* else, and this function has no opinion about it.
|
|
132
|
-
*/
|
|
133
21
|
export declare function printBlockForIntent(existing: StructureNode['print'] | undefined, intent: PageIntent): StructureNode['print'] | undefined;
|
|
134
|
-
/** What intent a `print` block expresses. The inverse of `printBlockForIntent`. */
|
|
135
22
|
export declare function intentOfPrintBlock(print: StructureNode['print'] | undefined): PageIntent;
|
|
136
|
-
/** What intent a row currently carries, for a control that shows its state. */
|
|
137
23
|
export declare function rowPageIntent(structure: readonly StructureEntry[] | undefined, cellId: string): PageIntent;
|
|
138
|
-
/**
|
|
139
|
-
* Every cell's extrinsic `print` block, keyed by cell id.
|
|
140
|
-
*
|
|
141
|
-
* ── Why this exists, and what it cost not to have it ───────────────────────
|
|
142
|
-
*
|
|
143
|
-
* `layout.structure[].print` was honoured by `StructureRenderer` and by nothing
|
|
144
|
-
* else. `usesStructureLayout` routes a page there only for `type: 'document'`
|
|
145
|
-
* or a section-shaped node — **2 of 198** structured pages — so on every other
|
|
146
|
-
* page the extrinsic half was written, mirrored, and dropped at render.
|
|
147
|
-
*
|
|
148
|
-
* Measured end to end on the proof page: a client pressed *"Start a new page here"*,
|
|
149
|
-
* the intent landed in the cell DSNS AND the page row (both verified by query),
|
|
150
|
-
* and the document did not break there. The button worked, the write worked,
|
|
151
|
-
* and the paper never heard about it — which is indistinguishable from a broken
|
|
152
|
-
* feature and is worse, because two records now claim something the reader
|
|
153
|
-
* cannot see.
|
|
154
|
-
*
|
|
155
|
-
* It is the exact mirror of an earlier finding: that found `StructureRenderer`
|
|
156
|
-
* dropping the INTRINSIC `@printPolicy`; this is `CellsRenderer` dropping the
|
|
157
|
-
* EXTRINSIC `node.print`. Two renderers, each honouring one half.
|
|
158
|
-
*
|
|
159
|
-
* Walks children, because a section's rows carry their own intents and a
|
|
160
|
-
* document-type page routes to the other renderer but a `grid` page with
|
|
161
|
-
* children does not.
|
|
162
|
-
*/
|
|
163
24
|
export declare function printByCellId(structure: readonly StructureEntry[] | undefined): ReadonlyMap<string, NonNullable<StructureNode['print']>>;
|
|
164
|
-
/**
|
|
165
|
-
* A structure node's extrinsic declaration, as `PrintConstraints`.
|
|
166
|
-
*
|
|
167
|
-
* ── Why this is in lib rather than beside the renderer that had it ─────────
|
|
168
|
-
*
|
|
169
|
-
* It lived in `StructureRenderer`, which is fine while one renderer needs it.
|
|
170
|
-
* **Both do:** `CellsRenderer` renders 196 of 198 structured pages and was
|
|
171
|
-
* dropping the extrinsic half entirely. `StructureRenderer` imports
|
|
172
|
-
* `CellRenderer`, so importing back would make the pair circular — and a
|
|
173
|
-
* second copy is how the two renderers would come to disagree about what a
|
|
174
|
-
* node declared, which is the defect this whole file exists to prevent.
|
|
175
|
-
*
|
|
176
|
-
* **It can only ever TIGHTEN**, which is the component repo's invariant for the extrinsic
|
|
177
|
-
* half (`a node may ADD a break, never RELAX one`). `breakInside` is emitted
|
|
178
|
-
* only for `'avoid'`, never `'auto'`, so composing this after a component's
|
|
179
|
-
* intrinsic policy cannot turn an atomic component splittable — a page author
|
|
180
|
-
* cannot switch off what a component declared about itself.
|
|
181
|
-
*
|
|
182
|
-
* `keepWithPrevious` becomes `breakBefore: 'avoid'`, and a `breakBefore`
|
|
183
|
-
* declared alongside it wins, because CSS Fragmentation §3.2 resolves a forced
|
|
184
|
-
* break over an avoid and the composer follows the enforcer rather than
|
|
185
|
-
* inventing a rule.
|
|
186
|
-
*/
|
|
187
25
|
export declare function nodePrintConstraints(node: {
|
|
188
26
|
print?: StructureNode['print'];
|
|
189
27
|
}): PrintConstraints | undefined;
|
|
190
|
-
/**
|
|
191
|
-
* Add or remove a deliberate blank page before a row.
|
|
192
|
-
*
|
|
193
|
-
* Deliberately NOT folded into `setRowPageIntent`: that function owns the
|
|
194
|
-
* break-vocabulary keys and clears the ones it does not set, which is right for
|
|
195
|
-
* three mutually exclusive intents and wrong here. A blank page is an
|
|
196
|
-
* independent decision — a row can both open a page and have a blank one before
|
|
197
|
-
* it — and a person moving a row must not silently delete the blank page they
|
|
198
|
-
* put in front of it.
|
|
199
|
-
*
|
|
200
|
-
* `changed` is false when the row already says this, so a caller can refuse to
|
|
201
|
-
* burn a revision recording nothing.
|
|
202
|
-
*/
|
|
203
28
|
export declare function setRowBlankPage(structure: readonly StructureEntry[] | undefined, cellId: string, present: boolean): {
|
|
204
29
|
structure: StructureEntry[];
|
|
205
30
|
changed: boolean;
|
|
206
31
|
};
|
|
207
|
-
/** Does a deliberate blank page precede this row? */
|
|
208
32
|
export declare function rowHasBlankPage(structure: readonly StructureEntry[] | undefined, cellId: string): boolean;
|
|
209
|
-
/**
|
|
210
|
-
* What one write actually changed, per cell.
|
|
211
|
-
*
|
|
212
|
-
* ── Why a write must carry a diff and not a document ────────────────────────
|
|
213
|
-
*
|
|
214
|
-
* Every layout gesture computes a whole new `layout` from the copy in the
|
|
215
|
-
* client's cache and hands it to the writer, which `update`s the row with it.
|
|
216
|
-
* That is a blind overwrite, and it loses every change the cache had not caught
|
|
217
|
-
* up with — the owner: *"if I don't close the layout mode after adding or
|
|
218
|
-
* deleting a blank page any edits I made to other layout configurations get
|
|
219
|
-
* reverted."*
|
|
220
|
-
*
|
|
221
|
-
* The invalidation bug that made the cache stale is fixed separately, and
|
|
222
|
-
* fixing it is not enough: two clicks land inside one refetch, and the second
|
|
223
|
-
* still reverts the first. The durable answer is for the write to state its
|
|
224
|
-
* own scope. Then the row's untouched entries come from the row as read at
|
|
225
|
-
* submit, and a concurrent change to a different cell survives — the same rule
|
|
226
|
-
* `record-state-patch` follows for authored scalars.
|
|
227
|
-
*
|
|
228
|
-
* `undefined` print means REMOVE the block, which is a real gesture (deleting a
|
|
229
|
-
* page break, deleting a blank page) and must not be confused with "unchanged".
|
|
230
|
-
* That is why the result is a list of explicit entries rather than a partial
|
|
231
|
-
* map: absence here means *not part of this write*, and only `print:
|
|
232
|
-
* undefined` means *take it away*.
|
|
233
|
-
*/
|
|
234
33
|
export interface RowPrintChange {
|
|
235
34
|
cellId: string;
|
|
236
|
-
/** The new block, or `undefined` to remove it. */
|
|
237
35
|
print?: NonNullable<StructureNode['print']>;
|
|
238
36
|
}
|
|
239
37
|
export declare function diffRowPrint(base: readonly StructureEntry[] | undefined, next: readonly StructureEntry[] | undefined): RowPrintChange[];
|
|
240
|
-
/**
|
|
241
|
-
* Apply a diff onto a structure read at submit.
|
|
242
|
-
*
|
|
243
|
-
* `missing` names changes whose cell has no entry in the structure being
|
|
244
|
-
* written to — a renamed cell, or a change aimed at a page that has moved on.
|
|
245
|
-
* Reported rather than silently dropped: a write that lands partially and says
|
|
246
|
-
* nothing is the shape this whole area keeps producing.
|
|
247
|
-
*/
|
|
248
38
|
export declare function applyRowPrint(structure: readonly StructureEntry[] | undefined, changes: readonly RowPrintChange[]): {
|
|
249
39
|
structure: StructureEntry[];
|
|
250
40
|
changed: boolean;
|
|
251
41
|
missing: string[];
|
|
252
42
|
};
|
|
253
|
-
/**
|
|
254
|
-
* The cell ids a structure hoists out of the flow, by slot.
|
|
255
|
-
*
|
|
256
|
-
* Walks nested entries, because a document structure nests
|
|
257
|
-
* (`{sectionId, children: [{cellId}]}`) and a flat scan finds nothing at depth
|
|
258
|
-
* — the exact false negative a review hit and recorded, where a flat
|
|
259
|
-
* comparison reported 7 and 10 cells "missing" from pages that were complete.
|
|
260
|
-
*
|
|
261
|
-
* Returns ids in document order and de-duplicated. Order matters only if a
|
|
262
|
-
* profile ever allows more than one occupant; today the caller takes the first
|
|
263
|
-
* and the rest are a finding, not a layout.
|
|
264
|
-
*/
|
|
265
43
|
export declare function findSlottedCellIds(structure: readonly StructureEntry[] | undefined, slot: StructureSlot): string[];
|
|
266
|
-
/** A page whose cells and structure can be split. Structural minimum, not the full row. */
|
|
267
44
|
type SplittablePage = {
|
|
268
45
|
content?: {
|
|
269
46
|
cells?: Array<{
|
|
@@ -275,28 +52,9 @@ type SplittablePage = {
|
|
|
275
52
|
} | null;
|
|
276
53
|
} & Record<string, unknown>;
|
|
277
54
|
export interface MarginaliaSplit<P> {
|
|
278
|
-
/** What sets in the measure. The SAME reference as the input when nothing moved. */
|
|
279
55
|
bodyPage: P;
|
|
280
|
-
/** What sets beside it, or null when nothing is hoisted. */
|
|
281
56
|
marginaliaPage: P | null;
|
|
282
|
-
/**
|
|
283
|
-
* Why nothing was hoisted, when nothing was. `null` when the split happened.
|
|
284
|
-
* `'dangling'` is the one worth surfacing: the row declares a slot naming a
|
|
285
|
-
* cell the page does not have, which is the dangling-reference class.
|
|
286
|
-
*/
|
|
287
57
|
reason: null | 'no-slot' | 'no-track' | 'dangling';
|
|
288
58
|
}
|
|
289
|
-
/**
|
|
290
|
-
* Split a page into what sets in the measure and what sets in the marginalia.
|
|
291
|
-
*
|
|
292
|
-
* Pure, and separated from the view for one reason: it is the half that can be
|
|
293
|
-
* wrong in a way nobody sees. The DOM placement is visible the moment you open
|
|
294
|
-
* the page; *which cell moved, whether the rest survived, and whether a page
|
|
295
|
-
* that declares nothing is returned untouched* are not.
|
|
296
|
-
*
|
|
297
|
-
* **`bodyPage` is the input reference when nothing moves**, not a shallow copy.
|
|
298
|
-
* A copy would be equal and would still invalidate every downstream memo keyed
|
|
299
|
-
* on identity, on every page in the product — all of which declare no slot.
|
|
300
|
-
*/
|
|
301
59
|
export declare function splitMarginalia<P extends SplittablePage>(page: P | null | undefined, marginaliaCellId: string | null | undefined, hasReservedTrack: boolean): MarginaliaSplit<P>;
|
|
302
60
|
export {};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Component, type ReactNode } from 'react';
|
|
2
|
-
/** A placeholder the size of the cell's slot, so the page doesn't shift when components arrive. */
|
|
3
2
|
export declare function CellPlaceholder({ state }: {
|
|
4
3
|
state: 'loading' | 'pending';
|
|
5
4
|
}): import("react").JSX.Element;
|
|
@@ -9,7 +8,6 @@ export declare function CellNotice({ state, component, onRetry, children }: {
|
|
|
9
8
|
onRetry?: () => void;
|
|
10
9
|
children: ReactNode;
|
|
11
10
|
}): import("react").JSX.Element;
|
|
12
|
-
/** One cell's crash stays in that cell. */
|
|
13
11
|
export declare class CellErrorBoundary extends Component<{
|
|
14
12
|
cellId: string;
|
|
15
13
|
onError?: (cellId: string, error: Error) => void;
|
|
@@ -11,47 +11,32 @@ import type { ScopeContext } from '../core/store/scope-keys.js';
|
|
|
11
11
|
import type { EditCell, PropSourceEntry } from '../core/sources.js';
|
|
12
12
|
import type { PageCell } from '../core/types.js';
|
|
13
13
|
import { type LoadPage } from './use-paging.js';
|
|
14
|
-
/** The slot a parent with `each` repeats (core's Repeat): design/ui-consistency.md §2.8. */
|
|
15
14
|
export declare const REPEAT_SLOT = "item";
|
|
16
15
|
export type CellEnvironment = {
|
|
17
16
|
cellsById: Map<string, PageCell>;
|
|
18
17
|
page: ResolutionContext;
|
|
19
18
|
baseProps: unknown;
|
|
20
|
-
/** null while the page's bundles are loading (and always on the server). */
|
|
21
19
|
bundles: LoadedBundle[] | null;
|
|
22
20
|
bundleFor: (cell: PageCell) => LoadedBundle | null;
|
|
23
|
-
/** The bundle rows couldn't be read, or a bundle didn't import (PageBundles.failed). */
|
|
24
21
|
bundlesFailed?: boolean;
|
|
25
|
-
/** Reads the page again, for a cell's "Try again" (RendererEnvironment.retry). */
|
|
26
22
|
retry?: () => void;
|
|
27
23
|
core: CoreRegistry;
|
|
28
24
|
navigate: (to: string) => void;
|
|
29
25
|
store: StackStoreApi;
|
|
30
26
|
scope: ScopeContext;
|
|
31
27
|
context: Record<string, unknown>;
|
|
32
|
-
/** The signed-in person, for `@viewer.*`. */
|
|
33
28
|
viewer?: unknown;
|
|
34
|
-
/** Each cell's own data, by cell id (loadPageData). */
|
|
35
29
|
cellData?: Record<string, CellData>;
|
|
36
|
-
/** Labels the page declares, which no walk's label replaces. */
|
|
37
30
|
declared?: ReadonlySet<string>;
|
|
38
|
-
/** The actions a cell gets (RendererEnvironment.actionsFor). */
|
|
39
31
|
actionsFor?: (cell: PageCell) => Record<string, CellAction> | undefined;
|
|
40
|
-
/** Runs a declared action (RendererEnvironment.runAction). */
|
|
41
32
|
runAction?: RunAction;
|
|
42
|
-
/** Loads a paged label's next page (RendererEnvironment.loadPage). */
|
|
43
33
|
loadPage?: LoadPage;
|
|
44
34
|
onCellError?: (cellId: string, error: Error) => void;
|
|
45
35
|
onUnresolvedReference?: (cellId: string, ref: string) => void;
|
|
46
|
-
/** Each rendered cell's props, by prop path, with where each value came from (RendererEnvironment.onPropSources). */
|
|
47
36
|
onPropSources?: (cell: EditCell, sources: PropSourceEntry[]) => void;
|
|
48
|
-
/** An embed cell's page (PageRuntime.embeds), rendered by the page renderer. */
|
|
49
37
|
renderEmbed?: (cellId: string) => ReactNode;
|
|
50
|
-
/** Where a render-time diagnostic goes (`COMPONENT_UNKNOWN` from `renderComponent`). */
|
|
51
38
|
onDiagnostic?: (d: Diagnostic) => void;
|
|
52
|
-
/** Extra attributes and style for a cell's frame (RendererEnvironment.frameProps). */
|
|
53
39
|
frameProps?: (cell: PageCell, contract: ComponentContract | undefined) => FrameProps | undefined;
|
|
54
|
-
/** Props from the page's runtime, merged last (RendererEnvironment.cellProps). */
|
|
55
40
|
cellProps?: (cell: PageCell) => Record<string, unknown> | undefined;
|
|
56
41
|
};
|
|
57
42
|
export type FrameProps = {
|
|
@@ -62,6 +47,5 @@ export declare function CellView({ cell, env, nested, injectedProps, }: {
|
|
|
62
47
|
cell: PageCell;
|
|
63
48
|
env: CellEnvironment;
|
|
64
49
|
nested?: boolean;
|
|
65
|
-
/** Host-derived facts merged over the resolved props (a structure's numbering, anchors, references). */
|
|
66
50
|
injectedProps?: Record<string, unknown>;
|
|
67
51
|
}): import("react").JSX.Element | null;
|