Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ jobs:
DOMFORMAT_BROWSER_NO_SANDBOX: "1"
run: pnpm --filter @layoutit/polycss-domformat test:browser

- name: Certify bounded domformat page preparation
env:
DOMFORMAT_BROWSER_NO_SANDBOX: "1"
run: pnpm --filter @layoutit/polycss-domformat test:page-preparation

certify-gallery-domformat-corpus:
# Gallery assets are byte-bound to this Chromium/sharp runner; changing it requires corpus regeneration.
runs-on: macos-15
Expand Down
48 changes: 42 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Monorepo layout (pnpm workspaces):
| `packages/vue` | `@layoutit/polycss-vue` | Vue 3 mirror of the React package. Owns its own copy of atlas rasterisation. Depends on `core` only. |
| `packages/fonts` | `@layoutit/polycss-fonts` | Fonts + text → extruded 3D `Polygon[]`. Hand-written TrueType (`glyf`) reader + extruder (flat/round/bevel profiles) + Google Fonts loader. Framework-agnostic (returns `Polygon[]`, no React/Vue mirror needed). Depends on `core` + `earcut`. |
| `packages/morph` | `@layoutit/polycss-morph` | Framework-agnostic prepared-model contracts, deterministic Node preparation, browser loading, retained DOM mounting, sparse deformation, controls, springs, animation, joint skinning, and prepared playback. The browser entry uses public `@layoutit/polycss` APIs; Node-only preparation lives at `@layoutit/polycss-morph/prepare`. No React/Vue mirrors. |
| `packages/domformat` | `@layoutit/polycss-domformat` | Private strict-TypeScript `domformat@0` writer, reader, validator, CLI, and browser mount with repository-side conformance. Owns the producer-neutral wire contract; producer lowering stays in producer packages. Runtime installs contain unbundled ESM and declarations but exclude certification material. Not published. |
| `packages/domformat` | `@layoutit/polycss-domformat` | Private strict-TypeScript `domformat@0` writer, reader, validator, CLI, and browser mount with repository-side conformance. Its closed retained-DOM runtime includes sparse playback/surface/variants, typed atlas positions, responsive prepared presentation/layout/timeline profiles, host-selected prepared-bank handoff, fixed orbit input, one bounded digest-bound page lifecycle shared by paged transform/visibility playback and paged variants, and compositor-owned linear transform timing. It owns the producer-neutral wire contract; producer lowering and product input policy stay in producer/host packages. Runtime installs contain code-split ESM and declarations but exclude certification material. Not published. |
| `packages/skills` | `@layoutit/polycss-skills` | Zero-dependency `npx` installer for the PolyCSS agent skill. Owns `skill/SKILL.md` + `skill/docs/*.md` — the source of truth for what agents are told about PolyCSS. No renderer code, no runtime dependency on any other package. |
| `website` | `@layoutit/polycss-website` | Astro + Starlight docs site. Not published. |
| `examples/{html,vanilla,react,vue,fontcss}` | private | Per-framework Vite apps demonstrating the minimal usage for each renderer (`fontcss` demos `@layoutit/polycss-fonts`). Workspace members so they resolve to local `workspace:^` packages. Not published. |
Expand Down Expand Up @@ -97,7 +97,7 @@ This is the load-bearing constraint behind the whole engine. **JavaScript should

The renderer exception is imported skeletal animation. glTF/GLB skinning changes each polygon independently, so the vanilla stable-DOM animation path samples the active clip in JS, keeps the leaf set mounted, caches baked stable-triangle transform frames, and pins each mounted triangle's baked color while transforms animate. Recomputing Lambert from every deformed low-poly face normal creates visible color pumping, so color refresh is internal opt-in rather than the default animation behavior. On WebKit/Safari, where stable CSS triangles fall through to solid atlas `<s>` leaves, same-topology animation updates keep the existing atlas elements and bitmap URLs mounted, cache transform frames once warmed, and hide briefly degenerate atlas triangles only until the next valid frame. That optimized path is the default; do not add a user-facing "baseline vs optimized" toggle or maintain a legacy slow path in product UI.

The domformat reference mount has one separate, closed exception: it may schedule its validated fixed-rate prepared playback and interaction tables. That scheduler may write only declared sinks on the retained targets, never reconstruct topology or evaluate producer code, expressions, renderer internals, or network resources, and is disabled by `animate: false`. Normal catch-up is bounded to eight due ticks: every due logical animation tick and distinct prepared-effect transition in that window is evaluated in order, but one browser callback may publish only their final retained-DOM state; interaction publishes each such tick separately because input, cursor, grab, and spring state are observable. A larger gap is treated as suspension, discards the stale backlog, advances one tick, and resets the deadline. The one-tick path and public operations remain synchronous. This is a reference implementation of an already-lowered wire profile, not a PolyCSS renderer loop.
The domformat reference mount has one separate, closed exception: it may schedule validated prepared playback and interaction tables. The scheduler sleeps on a deadline timer and requests one paint-aligned animation frame only when a bounded-rate, exact rational-microsecond, or prepared timeline deadline is due; it does not poll display frames continuously. It may write only declared sinks on retained targets, never reconstruct topology or evaluate producer code, expressions, renderer internals, or arbitrary network resources, and is disabled by `animate: false`. The binding selects bounded catch-up (up to eight ordered ticks, then one suspension tick), single-step deadline reset, or animation-only collapsed elapsed reconstruction. Prepared effects and interaction simulate every admitted tick because their state is history-dependent, so they cannot use collapsed elapsed catch-up. Closed compositor timing may instead give a retained model cycle to viewer-owned WAAPI or add viewer-owned linear transform transitions for fixed cadence only; seek, catch-up, restart, pause, and wrap synchronously snap prepared state. This is a reference implementation of an already-lowered wire profile, not a PolyCSS renderer loop.

| Where JS runs | Where JS does NOT run |
|---|---|
Expand Down Expand Up @@ -173,11 +173,47 @@ alias for Morph packages and does not depend on Morph or renderer internals.
- Producers emit the closed writer manifest natively. Source parsing,
preparation, lowering, and product adapters remain in producer packages.
- The only physical form is canonical `.json` plus digest-bound external
sibling resource files. There is no `.dom` packet, gzip transport, embedded
payload, archive, or alternate packaging mode.
sibling resource files. There is no `.dom` packet, gzip document transport,
embedded payload, archive, or alternate packaging mode. One fixed typed
state-page resource kind may use bounded gzip with exact encoded and decoded
identities; this is not a generic binary/custom-codec facility.
- Mounting follows `validate → construct → bind → initialize → publish →
destroy`, with rollback on partial failure and idempotent teardown.
- The package is authored in strict TypeScript, built as unbundled ESM plus
destroy`, with rollback on partial failure and idempotent teardown. Eager
CSS/images verify before construction; initial and fixed-interaction state
pages verify before attachment; playback-initial, interaction-entry, and the
current prepared-bank entry remain pinned beside deferred current/lookahead
pages under an exact hard resident window. Inactive bank entries are not all
retained. Sync
nonresident seek fails without mutation and
`seekAsync` cancels stale generations before exact publication.
- Runtime publication preserves sparse prepared tables and retained identity.
Variant class/effect ownership is explicit, packed full atlas positions are
typed signed-pixel dictionary entries, catch-up coalesces only touched
targets, and class/transform/address writes complete before reveal.
- Root presentation profiles are bounded numeric width breakpoints or one
landscape-first row followed by portrait width bands, with prepared
contain/cover bounds and optional quarter-turns. Same-topology
viewport profiles may override leaf transform/visibility, carry sparse
profile-by-source-frame visibility, and evaluate the closed prepared
viewport-affine coefficient form only on resize/profile change. Playback keeps a
required baseline timeline and may select ordered bounded overrides by those
root profile ids; all use the binding's bounded rate, exact rational interval,
or prepared deadline schedule. Selection precedes initial
publication. Only an animation schedule identity change restarts logical
tick zero and the initial one-based source frame before viewport reveal;
presentation-only band changes preserve the current deadline. Interaction state survives profile changes until
animation re-entry restarts the selected schedule. The typed orbit
operation exposes only finite clamped pitch/yaw/zoom and prepared cyclic
address rows; pointer/wheel/inertia/camera-widget policy stays in the host.
- An optional finite prepared-bank table maps host-selected stable ids to
canonical entry frames and schedules over the same retained topology. Sync
selection requires residency; async selection verifies the complete
cross-channel window before atomic publication. Random, shuffle, catalog,
playlist, and fetch-choice policy stay in the host.
- META may bind multiple exact byte-identity artifacts. Separately inert,
bounded claims never grant fetching, execution, trust, authenticity, or
rights authority.
- The package is authored in strict TypeScript, built as code-split ESM plus
declarations with tsup, `private`, and MIT-licensed. Workspace test/build
commands include it; public version-bump and npm-publish automation must not.
Public Node and browser signatures describe the closed document, resource,
Expand Down
56 changes: 37 additions & 19 deletions packages/domformat/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
`@layoutit/polycss-domformat` is PolyCSS's private, producer-neutral reference
implementation of the experimental `domformat@0` retained-DOM contract. It
stores one canonical UTF-8 JSON document plus integrity-bound sibling resource
files. There is no `.dom` packet, archive, gzip transport, embedded payload, or
alternate packaging mode.
files. There is no `.dom` packet, archive, gzip document transport, embedded
payload, or alternate packaging mode. A fixed typed state-page sibling may use
bounded gzip with exact encoded and decoded identity.

The package is authored in strict TypeScript and builds unbundled ESM plus
declarations with tsup. It has no runtime dependencies and does not depend on
The package is authored in strict TypeScript and builds code-split ESM plus
declarations. It has no runtime dependencies and does not depend on
Morph or PolyCSS renderer internals. It is `private: true` and MIT-licensed; it
is tested and built in the workspace but excluded from public versioning and
publication.
Expand Down Expand Up @@ -53,15 +54,6 @@ Producers must emit the writer manifest natively. Parsing, preparation,
lowering, source provenance, and product adapters remain in producer packages;
domformat contains no producer-specific or legacy conversion layer.

PolyCSS's first producer follows that boundary: website-owned tooling lowers
all 304 Gallery presets through shared preset, loader, camera, animation, and
renderer paths into canonical JSON plus digest-bound CSS and image siblings in
the repository-only `website/gallery-domformat-corpus/` directory. The website
build does not deploy this certification corpus. Its catalog binds the corpus to its
exact Playwright Chromium strategy environment; it is not a browser-neutral
leaf-strategy claim. Neither that producer nor its generated corpus is shipped
in this package.

## Browser API

```js
Expand All @@ -79,6 +71,9 @@ These are the complete browser exports. URL loading fetches the JSON document
and its digest-bound, document-relative, same-origin sibling files with
credentials omitted and redirects rejected. Direct byte loading requires a
logical-id `externalResources` map or a trusted `loadExternalResource` callback.
State pages are never accepted eagerly in the map; they must come from the
callback so declared-size checks and bounded residency happen before the
runtime copies or validates page bytes.

Mounting follows:

Expand All @@ -88,10 +83,28 @@ validate → construct → bind → initialize → publish → destroy

Partial failures roll back DOM, style elements, listeners, observers, object
URLs, and host mutations. `destroy()` is idempotent. The runtime controller is
closed to `lifecycle`, `mode`, `sourceFrame`, `seek`, `setMode`, and `destroy`.
Automatic playback may defer transforms for paint-hidden leaves; `seek(frame)`
is the synchronous barrier that publishes every current prepared transform
before returning, including for a same-frame seek.
closed to `lifecycle`, `mode`, `sourceFrame`, optional `bankId`, `seek`,
`seekAsync`, `selectBank`, `selectBankAsync`, `setMode`, the fixed `setInput`
operation, and `destroy`.
Automatic playback may defer transforms and atlas addresses for paint-hidden
leaves; every reveal flushes transform then address before visibility.
`seek(frame)` is the synchronous barrier that restores transforms plus the
canonical variant/surface row before returning, including for a same-frame
seek after interaction changed only surface state.

Large prepared playback and class schedules may use fixed typed state pages.
One document-wide coordinator verifies entry pages before attach, enforces
encoded, decoded, resident, lookahead, and request-generation ceilings, and
commits cross-channel seeks atomically. Prepared bank selection changes state
on the same retained topology; random/catalog policy remains host-owned.

Playback accepts bounded rational or explicit cadence, declared catch-up, and a
closed transform-only compositor strategy. Presentation supports bounded
responsive roots and same-topology leaf profiles. Surface addresses, sparse
classes, transforms, and visibility always publish in the validated order
before reveal. The fixed orbit input exposes prepared data semantics but leaves
pointer, wheel, inertia, and widget policy to the host. Exact wire contracts,
limits, and lifecycle rules live in the repository specifications.

## Contract and security

Expand All @@ -105,13 +118,18 @@ The format excludes executable package code, generic expressions, arbitrary
network resources, custom interpreters, arbitrary HTML, ZIP/container formats,
compatibility aliases, and browser-generated identity. CSS is parsed against a
closed semantic subset, scoped to a viewer-owned instance, and rewritten only
for validated asset tokens.
for validated asset tokens. Prepared class effects use a separate exact
owner/descendant/property table materialized by the viewer; package CSS cannot
attach rules to dynamic class tokens or use priority annotations.

The normative specifications, independent Python producer/reader and N-version
JavaScript reader, fixtures, alternate mount shell, and certification tests
remain repository-side and are intentionally absent from the install tarball. See the
[domformat source directory](https://github.com/LayoutitStudio/polycss/tree/main/packages/domformat)
for that material.
for that material. The repository also carries
[pinned cssGraphics compatibility evidence](https://github.com/LayoutitStudio/polycss/blob/main/packages/domformat/spec/cssgraphics-compatibility.md)
for the complete stable browser-demo technique mapping at the audited source
revision.

## Release gate

Expand Down
34 changes: 27 additions & 7 deletions packages/domformat/conformance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ runtime and not a public producer API.

`producer.py` is an independent Python producer. It imports no domformat
implementation, fixture, or source module. It emits one executable
`domformat@0` / `polycss-3d@0` JSON document and two external sibling resources
with deterministic bytes.
`domformat@0` / `polycss-3d@0` JSON document and deterministic external sibling
resources.

`reader.py` is an independent Python reader. It imports no production
JavaScript and validates bounded JSON, the closed six-member envelope, safe
Expand All @@ -27,7 +27,10 @@ teardown, while importing the compiled reference lifecycle, input adapter, and
profile interpreters from `dist/`. Comparing it with the public browser mount
tests shell composition, stable retained DOM, ordered writes, animation,
interaction, CSS materialization, and rollback without maintaining copied
state machines.
state machines. It independently owns eager/deferred resource identity checks,
bounded page decoding, initial-before-attach readiness, rational/explicit
deadline scheduling and declared catch-up policy,
and transactional shell cleanup.

## Commands

Expand Down Expand Up @@ -61,6 +64,16 @@ readers from the same positive JSON document and sibling resources. It covers:
- terminal visual nodes missing the required `aria-hidden="true"` contract;
- missing resources and digest corruption.

Targeted cross-reader matrices additionally cover responsive presentation and
profile-selected playback timelines, same-topology viewport profiles, typed orbit input, closed compositor timing,
inline and paged variants, exact artifacts versus inert claims, C0 style
controls, boolean packet-version confusion, and state-page encoded/decoded
identity, canonical payload, and decompression failures. The paged-limit matrix
admits a descriptor-only 64,000-frame/500-page closure without page loading and
rejects excessive total pages, total frames, per-page frames, aggregate encoded
bytes, and resident materialized-byte products before resource loading in the
production, N-version, and Python readers.

The canonical JSON and binary32 corpora pin writer-form encoding and prepared
arithmetic. The CSS security corpus pins the fail-closed selector, declaration,
function, URL, token, and scope rules. Seeded and targeted differential
Expand All @@ -77,13 +90,20 @@ The release gate verifies:
implementations;
- exact stable-tree and DOM-write transcripts across public and alternate mount
shells that share the reference interpreters;
- real Chromium mounts the reference fixture at its only prepared frame and
the animated Python-produced fixture at noninitial source frame 2;
- real Chromium runs from a clean-installed tarball and proves ordered
class/address/reveal publication, deferred page fetch plus eviction/refetch,
combined paged playback/variant lazy seeks, width and landscape-first
portrait-band roots, playback-timeline, and per-leaf profiles, including
playback-initial page residency after disjoint lookahead; typed orbit input, compositor logical
timing, and `animate: false` across reference, alternate-shell, and
N-version-reader paths; it also mounts the independently produced animation
at a noninitial source frame;
- a retained `<i>` strategy leaf in the reference-writer Chromium fixture,
complementing Gallery strategy counts from its pinned feature profile;
- separately captured zero model-pixel deltas for public versus alternate
mount-shell paths and production-reader versus N-version-reader paths;
- exact runtime tarball allowlisting and a clean-installed runtime/CLI smoke.

No package mode, embedded payload path, archive, gzip transport, or `.dom`
extension is part of this conformance contract.
No package mode, embedded payload path, archive, gzip document transport, or
`.dom` extension is part of this conformance contract. Bounded gzip is tested
only for the fixed typed state-page resource.
Loading
Loading