@huukhanhnguyen/model / schema/sheets
schema/sheets
ComposeArgs
ComposeArgs = {
facts:Record<string,string>;operations: (Operation|Group)[];options:SheetResolveOptions;parameters?: (Operation|Group)[];sheetKey:string;sheetTitle:string;size: {height:number;width:number; };template:Template; }
What composeSheet takes — one call's argument list (resolveSheet is its only caller). It stays a declared type rather than a module-local one because every type in this package is declared under schema/ (the declaration-path law, enforced by types/tests/typeDeclarationLaw.test.ts).
facts
facts:
Record<string,string>
operations
options
options:
SheetResolveOptions
parameters?
The sheet's own signature — forwarded to resolveFlatOperations so the isolated scope can bind it (see the hook's doc).
sheetKey
sheetKey:
string
sheetTitle
sheetTitle:
string
size
size: {
height:number;width:number; }
height
height:
number
width
width:
number
template
template:
Template
CropFractions
CropFractions = {
bottom:number;left:number;right:number;top:number; }
Viewport crop — fractions 0–1 of the uncropped drawing.
bottom
bottom:
number
left
left:
number
right
right:
number
top
top:
number
CropRegion
CropRegion = {
curve:Curve2d;json:string;rings:Point2d[][]; }
The crop AREA, as the region it is — never a box. crop.ts builds one from the fractions above; every clipper reads only this, so a non-rectangular crop is a different argument rather than a different code path.
json and rings are curve in the two forms the kernel doors want. They ride along because the clippers run per segment and per point, and deriving them there would be one wasm call apiece.
curve
curve:
Curve2d
The crop boundary. Closed rings; nested rings punch holes (even-odd).
json
json:
string
curve as the Curve2d kernel payload, for the boolean door.
rings
rings:
Point2d[][]
curve sampled to polygon rings, for the segment / point clippers.
SheetEvaluation
SheetEvaluation = {
sheet:Resolved<Page>;status:"complete"; } | {sheet:Resolved<Page>;status:"partial";warnings:StepFailure[]; }
What Model.evaluateSheet answers with.
A UNION, not a warnings field beside the sheet, because the failure mode being fixed is a consumer that reads .value.data and renders a page that LOOKS finished. A field is ignorable by doing nothing; this shape is not reachable at all without naming which case you are in, so "I forgot to check" becomes a compile error rather than a drawing with a view missing.
Measured (parashape sheetSurface fixture): a literal window drew 80 documents, 6 of them from the group1 step; the same window with an undefined variable drew 74. Those 6 documents vanish, the host then skips a placement that drew nothing, and the placed view is gone from the sheet with a console line as its only trace.
partial still carries the sheet: the drawing that DID compose is worth showing beside "this view could not be placed because: …". What it must not do is arrive looking whole.
SheetResolveOptions
SheetResolveOptions = {
evaluateExpression?: (expr) =>unknown;materials?:Material[];resolveComponent?: (key) =>Component|undefined;resolveDetail?: (source) =>ObjectData[];resolveFlatOperations?: (operations,parameters?) =>ObjectData[];templates?:Template[];titleBlock?:Record<string,string>;views?:View[]; }
evaluateExpression?
optionalevaluateExpression?: (expr) =>unknown
Evaluates sheet.title — an expression like every other authored string (titleValue law: eval, else the literal). Absent (a bare-engine caller with no registry) leaves the title literal.
Parameters
expr
string
Returns
unknown
materials?
optionalmaterials?:Material[]
resolveComponent?
optionalresolveComponent?: (key) =>Component|undefined
Looks up a components[] entry by key for the template's furniture placeComponent (the same hook sheet operations already use). Absent, only the kernel default title-block component is visible.
Parameters
key
string
Returns
Component | undefined
resolveDetail?
optionalresolveDetail?: (source) =>ObjectData[]
Forwarded verbatim to each auto-tiled view's own resolveView call — a viewDetail head reads its flat component through it (see ResolveViewOptions.resolveDetail in views.ts). A POSITIONED viewObject (one with at) goes the other way round, through resolveFlatOperations → Model's own view node.
Parameters
source
string
Returns
resolveFlatOperations?
optionalresolveFlatOperations?: (operations,parameters?) =>ObjectData[]
Resolves the sheet's own operations (everything past the extracted auto-tile view nodes — flat lane, authored in sheet mm, y UP from the paper's bottom-left, CAD paper-space convention: viewObject/ tableEntity/imageEntity place shared content by key with their own at/scale) — Model's flat evaluator bound. parameters is the SHEET'S OWN signature, forwarded so the isolated scope those operations evaluate in can bind it (2026-07-26 — a free sheet parameter used to be inert). Passed from here rather than closed over by the caller because drawingSetToSvg runs one callback across every sheet in the set.
Parameters
operations
parameters?
Returns
templates?
optionaltemplates?:Template[]
Templates the sheet may name. Absent + omitted sheet.template = ISO default.
titleBlock?
optionaltitleBlock?:Record<string,string>
Leftover detector — title-block facts belong on the template or the sheet's placeComponent args. Passing this fails loud.
views?
optionalviews?:View[]
The model's named views[] lane — a top-level viewObject node with no at arg (the auto-tile bucket, see autoTileViewSpecs) resolves its source key against this.
TiledViewport
TiledViewport = {
crop?:CropFractions;scale:number|"auto";spec:View; }
One auto-tiled view placement on a sheet.
crop?
optionalcrop?:CropFractions
scale
scale:
number|"auto"
spec
spec:
View