@weasel-js/core 1.7.2 → 1.8.0
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/CHANGELOG.md +64 -0
- package/dist/animation/colorHelpers.js +58 -58
- package/dist/animation/colorHelpers.js.map +1 -1
- package/dist/animation/engine/axes.js +36 -0
- package/dist/animation/engine/axes.js.map +1 -0
- package/dist/animation/engine/codec.js +170 -0
- package/dist/animation/engine/codec.js.map +1 -0
- package/dist/animation/engine/integrator.js +12 -0
- package/dist/animation/engine/integrator.js.map +1 -0
- package/dist/animation/useAnimator.js +199 -157
- package/dist/animation/useAnimator.js.map +1 -1
- package/dist/{builtins-C-gRyVRx.d.ts → builtins-BW32dOXy.d.ts} +74 -8
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js +12 -12
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js.map +1 -1
- package/dist/canvas/pickWalk.js +2 -2
- package/dist/canvas/pickWalk.js.map +1 -1
- package/dist/canvas/sceneSlotWiring.js +1 -1
- package/dist/canvas/sceneSlotWiring.js.map +1 -1
- package/dist/canvas/sceneViewRender.js +1 -1
- package/dist/canvas/sceneViewRender.js.map +1 -1
- package/dist/core/scene/effectivePose.js +1 -1
- package/dist/core/scene/effectivePose.js.map +1 -1
- package/dist/core/scene/poseOverrides.js +42 -22
- package/dist/core/scene/poseOverrides.js.map +1 -1
- package/dist/core/scene/types.js.map +1 -1
- package/dist/index.d.ts +28 -18
- package/dist/renderer/WeaselRenderer.js +4 -1
- package/dist/renderer/WeaselRenderer.js.map +1 -1
- package/dist/renderer/batchClip.js +46 -0
- package/dist/renderer/batchClip.js.map +1 -0
- package/dist/renderer/draw.js +367 -294
- package/dist/renderer/draw.js.map +1 -1
- package/dist/renderer/drawBatch.js +75 -45
- package/dist/renderer/drawBatch.js.map +1 -1
- package/dist/renderer.d.ts +1 -1
- package/dist/tools/builtin/text/useTextTool.js +8 -8
- package/dist/tools/builtin/text/useTextTool.js.map +1 -1
- package/dist/tools/prefs.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +16 -15
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","names":["asNodeId","s"],"sources":["../../../src/core/scene/types.ts"],"sourcesContent":["import type { Path } from '@weasel-js/geom';\nimport type { History, SerializedHistory } from '@weasel-js/history';\nimport type { ParallaxOpts } from '../viewport/parallax';\nimport type { ContainerBounds, LayoutStrategy } from '../../layout/types';\nimport type { PoseComposition } from './composeFrame';\n\n/**\n * Axis-aligned rectangle pose with optional rotation. The canonical pose\n * shape used by `composeRectPose` and the `unionBounds` helper. Rotation is\n * in radians, pivoted on the unrotated AABB center; absent === 0. Kit-side\n * code that consumes rotation already reads `pose.rotation ?? 0`\n * (`wrapNodeOutput`, `rotate/handle.ts`, `pathInWorld.ts`), so\n * the slot exists on every default scene whether or not the consumer\n * populates it.\n */\nexport interface RectPose {\n x: number;\n y: number;\n width: number;\n height: number;\n /** Rotation in radians around the unrotated AABB center. Absent === 0. */\n rotation?: number;\n}\n\n/**\n * # SceneNode — the thing in the scene\n *\n * A `SceneNode` is the single canonical unit of a weasel scene. Everything\n * the user sees on the canvas — a shape, a group, an annotation, a tile —\n * is one of these. Containers and leaves are both nodes; the kit has no\n * other concept of \"scene element.\"\n *\n * ## Three orthogonal slots\n *\n * Every node carries three independent slots, plus its tree position:\n *\n * - **`data: TData`** — app-defined payload. The kit never inspects it.\n * Color, label, kind, glyph, sample-rate, whatever the app's domain\n * calls for. Mutated via `Scene.update(id, { data })`.\n *\n * - **`pose: TPose`** — local transform, relative to the node's direct\n * parent (or world, for root nodes). Default `RectPose` is\n * `{ x, y, width, height }`, but `TPose` is fully generic so apps can\n * use rotated rects, paths, ellipses, etc. The kit composes world\n * poses via `composeWorldPose` when rendering / hit-testing / snapping.\n *\n * - **`layer: TLayer`** — a string tag associating the node with a\n * visual `RenderLayer` at draw time. Separate from `LayerRecord` (the\n * per-layer visible/locked metadata held by the `Scene`).\n *\n * Tree position lives on the node itself: every node has a `parent` (or\n * `null` for roots), and `ContainerNode` adds an ordered `children: NodeId[]`.\n *\n * ## Identity is by `NodeId`, not by reference\n *\n * Nodes are addressed by `NodeId` everywhere outside the scene tree:\n * selection is `NodeId[]`, ops reference `NodeId`s, adapter methods accept\n * `string` ids and look up the node on demand. The `Node` object itself is\n * a snapshot of current state — don't hold references to it across scene\n * updates; look up by id when you need the latest.\n *\n * Picking helpers (`pickBest`, `pickEvery`) likewise return ids, not nodes —\n * they're hit-testing primitives that stay ignorant of node payload shape.\n *\n * ## Vocabulary\n *\n * The kit-internal name is `Node`; the public re-export is `SceneNode`\n * (avoids collision with DOM `Node` at call sites). Adapter methods speak\n * the same vocabulary: `getNode`, `getNodes`, `insertNode`, `removeNode`,\n * `cloneNode`, `addNode`. Older code, demos, and comments may still say\n * \"object\" or \"item\" — those are historical aliases for the same concept.\n */\n\n/** Opaque branded id. Treat as opaque outside the kit. */\nimport type { NodeId } from '@weasel-js/routing';\nexport type { NodeId };\n\n/** Brand a string as a NodeId. */\nexport const asNodeId = (s: string): NodeId => s as NodeId;\n\n/**\n * One dependency, as a derivation reads it: the node itself and the pose it is\n * painted at.\n *\n * The node comes along because a connector legitimately reads more than a box\n * — an edge that thickens with its endpoint's weight, or routes only to nodes\n * on a given layer, is answering off `data` and `layer`. The scene already\n * invalidates dependents on `kit:setData` and `kit:setLayer` for exactly that,\n * so handing over only the pose made the invalidation pay for a read nothing\n * could perform.\n */\nexport interface DerivedDep<TPose> {\n node: Node<unknown, string, TPose>;\n /** Its override when it has one, else its own derived pose, else the pose\n * the document stores — `effectivePose`, the same answer the renderer uses. */\n pose: TPose;\n /** The path it derives, or `null` when it derives none. Read it to place\n * something *along* a dependency rather than beside it — a label on a\n * routed edge, a tick on a curve.\n *\n * Resolved on first read and memoized on the dependency, so a route costs\n * the same whether one node reads it or five, and a dependency nobody asks\n * about costs nothing. */\n readonly path: Path | null;\n}\n\n/** Computes a node's path from its dependencies: the type of a node's\n * `derivePath` and of each entry in `SceneRegistry.derivePath`. */\nexport type DerivePathFn<TPose> = (\n node: Node<unknown, string, TPose>,\n deps: readonly (DerivedDep<TPose> | undefined)[],\n) => Path | null;\n\n/** Computes a node's pose from its dependencies: the type of a node's\n * `derivePose` and of each entry in `SceneRegistry.derivePose`. */\nexport type DerivePoseFn<TPose> = (\n node: Node<unknown, string, TPose>,\n deps: readonly (DerivedDep<TPose> | undefined)[],\n) => TPose | null;\n\ninterface NodeBase<TData, TLayer extends string, TPose> {\n id: NodeId;\n layer: TLayer;\n pose: TPose;\n data: TData;\n parent: NodeId | null;\n /** Nodes whose poses this node's geometry is computed from. Fixed at add\n * time. Absent or empty means the node's geometry is authored, which is the\n * normal case.\n *\n * `'children'` means \"my own children, in child order\" — a container that\n * hugs its contents, which a fixed id list cannot express because\n * reparenting would have to maintain it. The two forms differ in lifetime\n * as well as in membership: deleting a node deletes everything that names\n * it in `dependsOn`, but a container outlives the children it derives\n * from — an emptied group is still a group. */\n dependsOn?: readonly NodeId[] | 'children';\n /** Whether the hit-test walk can return this node. Default `true`; `false`\n * makes it transparent to picking, so a click on it lands on whatever is\n * behind — normally its own container.\n *\n * For content a container owns rather than content the user selects on its\n * own: the label inside a box, a body's rows. Without it the innermost hit\n * wins, and dragging a labeled box moves the label out of the box. It does\n * not hide the node from anything else — it still paints, still clips, and\n * a consumer can still select it by id. */\n pickable?: boolean;\n /** Computes this node's path from its dependencies, in `dependsOn` order —\n * each one the node and the pose it is painted at.\n * A dependency that has been removed arrives as `undefined`.\n * Returning `null` means \"nothing to draw right now\". Re-evaluated when a\n * dependency's world pose changes, never authored. Absolute-pose `Scene`\n * makes that the dependency's own pose, and an ancestor's move reaches it as\n * a `setPose` of its own from the container cascade.\n * `node` is deliberately widened: naming `TData`/`TLayer` here puts them in\n * a contravariant position, making `Scene` invariant in both and breaking\n * assignment kit-wide. The cost is that a `derivePath` casts to read `node.data`. */\n derivePath?: DerivePathFn<TPose>;\n /** Computes this node's pose from its dependencies' poses, the same way\n * `derivePath` computes its path — same `dependsOn` list, same widened\n * `node`, same registry-keyed serialization.\n *\n * Where a derived path is resolved at paint time and reaches only the\n * painter, a derived pose is what the node *is* at: it feeds bounds,\n * hit-testing, selection chrome, snapping and layout, and every reader\n * gets it through `effectivePose`. A pose override still wins over it —\n * that is what lets a drag preview a node the document says is elsewhere.\n *\n * Returning `null` means \"I have nothing to derive from right now\", and\n * the node falls back to its authored `pose`. `setPose` on a derived node\n * still writes that authored pose; it is simply not what anything reads. */\n derivePose?: DerivePoseFn<TPose>;\n}\n\n/** A node with no children — a shape, a label, an image. */\nexport interface LeafNode<TData, TLayer extends string, TPose = RectPose>\n extends NodeBase<TData, TLayer, TPose> {\n kind: 'leaf';\n}\n\n/** A node with an ordered list of children. This is the real group: what\n * Cmd+G creates, what SVG `<g>` round-trips to. A container has its own pose,\n * which its children's poses are relative to, and may optionally clip them. */\nexport interface ContainerNode<TData, TLayer extends string, TPose = RectPose>\n extends NodeBase<TData, TLayer, TPose> {\n kind: 'container';\n children: NodeId[];\n /** Optional clip-path source. Re-evaluated each render. Returning `null`\n * means \"no clip for this container right now\"; an empty / zero-area path\n * means \"clip everything out\" (children render nowhere). When set, the\n * renderer rasterizes the returned path into the stencil buffer and\n * paints descendants only where it covers. */\n clipFromPose?: (pose: TPose) => Path | null;\n /** How this container arranges its children. Whenever the container's\n * child set or order changes, or its bounds change size, the scene hands\n * the change to the strategy — `depart` for children that left it,\n * `arrive` for children that joined it, `childPoses` otherwise — and records the poses it writes in the same\n * undo entry as the change. A drag reads it too: it is the layout `move`\n * reflows and drops against. */\n layout?: LayoutStrategy<TPose>;\n}\n\n/** A pose a container's layout wrote — reported by {@link Scene.onReflow} —\n * in the node's stored frame. */\nexport interface LayoutMove<TPose> {\n id: NodeId;\n from: TPose;\n to: TPose;\n}\n\n/** How a scene reads a laid-out container's bounds and folds poses to the\n * world frame a {@link LayoutStrategy} works in. */\nexport interface LayoutFrame<TPose> {\n /** The container's bounds, from its world pose. Default: the pose's own\n * `x`/`y`/`width`/`height`; a pose without them is not laid out. */\n bounds?(pose: TPose): ContainerBounds | null;\n /** `pose` resized from bounds `from` to `to`, for a layout that grows its\n * container. Default: `to`'s fields written over the pose. */\n remap?(pose: TPose, from: ContainerBounds, to: ContainerBounds): TPose;\n /** Omit for the absolute-pose model. A scene whose canvas composes poses\n * (`<SceneCanvas poseComposition>`) passes the same strategy here. */\n composition?: PoseComposition<TPose>;\n}\n\n/** A node in the scene tree: either a leaf or a container. Re-exported\n * publicly as `SceneNode`, to avoid colliding with the DOM's `Node`. */\nexport type Node<TData, TLayer extends string, TPose = RectPose> =\n | LeafNode<TData, TLayer, TPose>\n | ContainerNode<TData, TLayer, TPose>;\n\ninterface LayerRecordBase<TLayer extends string> {\n id: TLayer;\n visible: boolean;\n locked: boolean;\n /** Draw this layer as a parallax plane: through a view derived from the\n * camera's (see `deriveParallaxView`), and picked through the same one.\n * Absent means the layer moves with the camera. */\n parallax?: ParallaxOpts;\n}\n\n/** A layer declared when the scene was created. Fixed set, no display name —\n * these are the kit's own render bands, not something a user manages. */\nexport interface SystemLayerRecord<TLayer extends string>\n extends LayerRecordBase<TLayer> {\n kind: 'system';\n}\n\n/** A layer the user created and can rename, reorder or delete. */\nexport interface UserLayerRecord<TLayer extends string>\n extends LayerRecordBase<TLayer> {\n kind: 'user';\n name: string;\n}\n\n/** Per-layer metadata held by the scene: whether it is visible and locked,\n * and where it sits in the render stack. Distinct from a node's `layer` tag,\n * which merely names one of these. */\nexport type LayerRecord<TLayer extends string> =\n | SystemLayerRecord<TLayer>\n | UserLayerRecord<TLayer>;\n\n/** What `Scene.add` needs to mint a node. Everything except the id is\n * required; the id is generated unless one is supplied. */\nexport interface AddNodeSpec<TData, TLayer extends string, TPose = RectPose> {\n kind: 'leaf' | 'container';\n layer: TLayer;\n pose: TPose;\n data: TData;\n parent?: NodeId | null;\n index?: number;\n /** Explicit id wins over the Scene's `generateId` and the kit default. */\n id?: NodeId;\n /** Mirrors `SceneNode.pickable`. Omit for the default, which is pickable. */\n pickable?: boolean;\n /** Only meaningful when `kind === 'container'`. Attach a clip-path function\n * to the node; ignored for leaves. Mirrors `ContainerNode.clipFromPose`. */\n clipFromPose?: (pose: TPose) => Path | null;\n /** Mirrors `SceneNode.dependsOn`. */\n dependsOn?: readonly NodeId[] | 'children';\n /** Mirrors `SceneNode.derivePath`. Taken as a live function; its registry key is\n * looked up from it, never passed in. */\n derivePath?: DerivePathFn<TPose>;\n /** Mirrors `SceneNode.derivePose`, on the same terms as `derivePath`. */\n derivePose?: DerivePoseFn<TPose>;\n /** Mirrors `ContainerNode.layout`; ignored for leaves. */\n layout?: LayoutStrategy<TPose>;\n}\n\n/** A custom scene mutation registered with `Scene.registerOp`: how to apply\n * it and how to undo it. The pair is what makes it participate in history. */\nexport interface RegisteredOp<P> {\n apply: (payload: P) => void;\n revert: (payload: P) => void;\n}\n\n/** One of the layers a scene is created with. */\nexport interface SystemLayerSpec<TLayer extends string> {\n id: TLayer;\n visible?: boolean;\n locked?: boolean;\n /** See `LayerRecord.parallax`. */\n parallax?: ParallaxOpts;\n}\n\n/** Argument to `Scene.addLayer`. Always produces a `UserLayerRecord`\n * (`kind: 'user'`). */\nexport interface AddLayerSpec<TLayer extends string> {\n id: TLayer;\n name: string;\n /** Default `true`. */\n visible?: boolean;\n /** Default `false`. */\n locked?: boolean;\n /** See `LayerRecord.parallax`. */\n parallax?: ParallaxOpts;\n /** Render-stack position. Default: top of stack (highest render index). */\n index?: number;\n}\n\n/** One layer as it appears in a snapshot. A snapshot written before layer\n * kind was serialized carries neither field, and loads as a system layer —\n * which is what every layer in such a snapshot was. */\nexport interface SerializedLayer<TLayer extends string>\n extends SystemLayerSpec<TLayer> {\n kind?: 'system' | 'user';\n /** Present on a user layer, absent on a system one. */\n name?: string;\n}\n\n/** JSON-serializable shape of a Scene's current state. Produced by\n * `scene.toJSON()`; consumed by `sceneFromJSON()`. Function fields\n * (e.g., `clipFromPose`) appear as string keys (`clipFromPoseKey`) and\n * are resolved through `SceneRegistry` at load time. */\nexport interface SerializedScene<TData, TLayer extends string, TPose> {\n version: 1;\n /** The whole layer stack, system and user alike — the name predates user\n * layers and is kept so existing snapshots still parse. */\n systemLayers: readonly SerializedLayer<TLayer>[];\n nodes: readonly SerializedNode<TData, TLayer, TPose>[];\n}\n\n/** JSON-serializable shape of a single node. Mirrors `AddNodeSpec` but\n * with function fields replaced by registry keys. */\nexport interface SerializedNode<TData, TLayer extends string, TPose> {\n id: string;\n kind: 'leaf' | 'container';\n layer: TLayer;\n pose: TPose;\n data: TData;\n /** Parent id; omitted for roots. */\n parent?: string;\n /** Registry key for the container's clip-path factory.\n * Containers only; omitted when the container has no clip. */\n clipFromPoseKey?: string;\n /** Ids this node's geometry derives from, or `'children'`. Omitted when it\n * derives from nothing. */\n dependsOn?: readonly string[] | 'children';\n /** Mirrors `SceneNode.pickable`. Omitted when the node is pickable. */\n pickable?: boolean;\n /** Registry key for the node's `derivePath` function. Omitted when it has none. */\n derivePathKey?: string;\n /** Registry key for the node's `derivePose` function. Omitted when it has none. */\n derivePoseKey?: string;\n /** Registry key for the container's layout strategy. Omitted when it has none. */\n layoutKey?: string;\n}\n\n/** Per-scene registry mapping string keys to live function references.\n * Passed to `createScene({ ..., registry })` and `sceneFromJSON(json, { registry })`.\n * Each function-field type has its own keyed map. */\nexport interface SceneRegistry<TPose> {\n /** Maps registry keys to `clipFromPose` factory functions for container nodes. */\n clipFromPose?: Readonly<Record<string, (pose: TPose) => Path | null>>;\n /** Maps registry keys to `derivePath` functions for nodes with `dependsOn`. */\n derivePath?: Readonly<Record<string, DerivePathFn<TPose>>>;\n /** Maps registry keys to `derivePose` functions for nodes with `dependsOn`. */\n derivePose?: Readonly<Record<string, DerivePoseFn<TPose>>>;\n /** Maps registry keys to container layout strategies. */\n layout?: Readonly<Record<string, LayoutStrategy<TPose>>>;\n // A new function field is a row in `NODE_FN_FIELDS` (core/scene/nodeFnFields.ts)\n // plus its entry here; the field name is the registry key by construction.\n}\n\n/**\n * Decides what one edit's changes to containers do to their children.\n * `arrivals` maps each container to the ids that joined it — by an `add`\n * under it or a `move` into it, whatever op or call made them — in the order\n * they arrived. `changed` names every container whose child set or order\n * changed, or whose pose changed size, arrivals included. Both are read once\n * the edit's other mutations have landed.\n *\n * Return the poses to write, in each node's stored frame; they become part of\n * the same edit, and so of the same undo step. An empty map writes nothing.\n * Return `null` to refuse: the whole edit is reverted (see\n * `SceneArrivalRefused`).\n *\n * Runs only for live edits. Undo, redo and a restored history replay the\n * arrangement that was decided the first time. With none installed, a scene\n * holding declared layouts (`ContainerNode.layout`) runs its own.\n */\nexport type SceneArrivalHandler<TPose> = (\n arrivals: ReadonlyMap<NodeId, readonly NodeId[]>,\n changed: ReadonlySet<NodeId>,\n /** The nodes that left each container in the edit, by the container they\n * left — removed, or moved out. */\n departures: ReadonlyMap<NodeId, readonly NodeId[]>,\n) => ReadonlyMap<NodeId, TPose> | null;\n\n/** Options for `useScene` — the layers the scene has, what it starts out\n * holding, and how its history behaves. */\nexport interface UseSceneOptions<TData, TLayer extends string, TPose = RectPose> {\n systemLayers: readonly SystemLayerSpec<TLayer>[];\n initial?: readonly AddNodeSpec<TData, TLayer, TPose>[];\n ops?: Readonly<Record<string, RegisteredOp<unknown>>>;\n historyLimit?: number;\n /** Window (ms) within which consecutive same-shaped mutations merge into\n * the previous undo entry (matching per-op coalesce keys — e.g. repeated\n * `setPose` on the same node, or repeated `applyBatch` calls whose ops\n * carry matching `coalesceKey` multisets). `0` (default) disables\n * coalescing: every mutation is a discrete undo entry. Undo of a\n * coalesced entry returns to the state before the first merged mutation;\n * redo restores the latest. `scene.batch` entries never coalesce. */\n coalesceWindowMs?: number;\n generateId?: () => NodeId;\n /** Per-scene registry for non-serializable function fields (clipFromPose, etc.).\n * Required only when serializing/deserializing scenes that use function fields. */\n registry?: SceneRegistry<TPose>;\n /** How laid-out containers are measured. See {@link LayoutFrame}. */\n layoutFrame?: LayoutFrame<TPose>;\n /** When supplied, `scene.applyOps(ops, label)` consults this on every call.\n * If it returns a non-null `Journal`, ops are routed to the journal's\n * `applyBatch` instead of recording a new parent-history entry. The journal\n * drives adapter mutation internally (via `op.apply(adapter)`), so the\n * scene state changes as normal; only the history tracking differs.\n *\n * The accessor is called on every `applyOps` invocation so the caller can\n * swap the active journal in and out by updating the closure's reference\n * (e.g., an app's mode machine holds `let activeJournal: Journal | null`\n * and the accessor reads that variable).\n *\n * When the accessor isn't known at scene-construction time (typical for\n * mode machines that depend on `scene.history`), pass nothing here and\n * wire it after construction via `scene.setActiveJournalAccessor(fn)`. */\n getActiveJournal?: () => import('@weasel-js/history').Journal | null;\n /** Re-render the host on every scene mutation. Default `true`. Set `false`\n * when the scene is read by a frame loop rather than by a render — a game\n * loop, a simulation — and nothing in the host's DOM derives from it.\n * Read by `useScene`; `createScene` ignores it. */\n subscribe?: boolean;\n}\n\n/**\n * One node's ephemeral presentation override — what a frame loop wants to say\n * about a node without saying it about the document.\n *\n * Not document content: never recorded in history, never in `toJSON`, and\n * writing one does not bump `Scene.getVersion()`. Hoist one entry per node and\n * mutate it in place on a frame loop; `PoseOverrides.commit()` is what makes a\n * mutation visible.\n */\nexport interface PoseOverride<TPose> {\n /** Replaces the node's document pose everywhere the render and hit-test\n * paths read one, including the clip a container derives from its pose, and\n * winning over a `derivePose`. Resolved by `effectivePose` — reading\n * `node.pose` directly is how those paths came to disagree about where a\n * node is. */\n pose?: TPose;\n /** Multiplied into the node's painted alpha, on top of any `alphaFor`. */\n alpha?: number;\n}\n\n/**\n * The scene's ephemeral per-node overrides — see {@link PoseOverride}.\n *\n * The intended shape of a frame is: `set` each node once, mutate the entries\n * in place per frame, `commit()` once. `commit` is not optional bookkeeping —\n * the painter memo keys on pose *reference*, so a mutation without a commit\n * paints the previous frame with no error.\n *\n * To promote a frame to document state (dropping a drag, baking an animation),\n * write it once through `Scene.setPose` and `clear` the override.\n */\nexport interface PoseOverrides<TPose> {\n /** Store `entry` for `id` **by reference**; the caller keeps mutating it. */\n set(id: NodeId, entry: PoseOverride<TPose>): void;\n get(id: NodeId): PoseOverride<TPose> | undefined;\n has(id: NodeId): boolean;\n /** The overridden ids, as a snapshot array. */\n ids(): readonly NodeId[];\n clear(id: NodeId): void;\n clearAll(): void;\n /** Publish this frame's in-place mutations: invalidate the painter memo for\n * every overridden node, then notify subscribers. */\n commit(): void;\n /** Notified after every write. The canvas uses this to repaint without a\n * scene version bump. */\n subscribe(fn: () => void): () => void;\n /** Monotonic write counter. A snapshot for observers that poll. */\n getGeneration(): number;\n}\n\n/**\n * The kit-owned scene tree: nodes, layers, and the undo history over both.\n *\n * A scene is logical, not visual — it says what exists and where, and nothing\n * about how it is painted. Every mutating method is undoable, and reads are\n * snapshots rather than live views. Nodes are addressed by `NodeId`; hold ids\n * across mutations, not node objects.\n *\n * Three type parameters keep it domain-agnostic: `TData` is the app's payload,\n * which the kit never inspects; `TPose` is the transform shape, `RectPose` by\n * default; `TLayer` is the union of layer names.\n */\nexport interface Scene<TData, TLayer extends string, TPose = RectPose> {\n // Reads\n readonly nodes: ReadonlyMap<NodeId, Node<TData, TLayer, TPose>>;\n readonly roots: readonly NodeId[];\n readonly layers: readonly LayerRecord<TLayer>[];\n /** The registry this scene resolves node functions against — the consumer's\n * entries over the kit's. */\n readonly registry: SceneRegistry<TPose>;\n get(id: NodeId): Node<TData, TLayer, TPose> | undefined;\n /** A token for the node `id` names right now, for telling one node from a\n * later one that reuses its id. It holds for as long as the node stays in\n * the scene, whatever edits it takes, and is different every time the id\n * enters the scene — by `add`, by an undo or redo that brings it back, or\n * by `loadState`. `undefined` when `id` is not in the scene. Compare tokens\n * for equality only; their order means nothing. */\n incarnation(id: NodeId): number | undefined;\n childrenOf(id: NodeId): readonly NodeId[];\n ancestorsOf(id: NodeId): readonly NodeId[];\n renderOrder(): Iterable<NodeId>;\n /** The same layer-major sequence as {@link Scene.renderOrder}, as the nodes\n * themselves. Prefer this wherever the ids are only going to be resolved\n * back to nodes: the traversal already holds them, and re-looking each one\n * up was ~40% of the area hit-test's per-node cost. Cached until a\n * structural edit, so repeat calls hand back the same array — a snapshot,\n * not a live view, and not yours to mutate. */\n renderOrderNodes(): readonly Node<TData, TLayer, TPose>[];\n /** The nodes on one layer, in render order. Cached alongside\n * {@link Scene.renderOrderNodes} until a structural edit, so a caller that\n * only cares about one layer pays the tree walk once rather than once per\n * call. A snapshot, not a live view, and not yours to mutate. An unknown\n * layer id yields an empty array. */\n nodesOnLayer(layer: TLayer): readonly Node<TData, TLayer, TPose>[];\n\n // Mutations (all auto-undoable)\n add(spec: AddNodeSpec<TData, TLayer, TPose>): NodeId;\n /** Delete `id`, its **entire subtree**, and **everything that derives from**\n * any of those nodes — a node listing one of them in `dependsOn` goes too,\n * along with its own subtree, transitively. A dependent can live anywhere in\n * the tree, so this deletes nodes the caller never named and may unlink\n * several disjoint subtrees at once. Recorded as one undoable step; `undo()`\n * restores every one of them where it was, child order intact. */\n remove(id: NodeId): void;\n /** {@link remove} over several roots at once, as a **single** undoable step.\n * Ids resolve against the tree as it stands at the call, so an id that\n * another one would cascade away is absorbed rather than removed twice —\n * which is what makes it safe to pass a whole selection. Throws if any id is\n * not in the scene; an empty list does nothing and records no step. */\n removeMany(ids: readonly NodeId[]): void;\n /** Every node {@link removeMany} would take if given `ids`: each id, its\n * subtree, everything deriving from any of those, and those nodes' subtrees\n * in turn. The authoritative answer — the cascade relations live here, and\n * a caller reconstructing them from `dependsOn` and `children` drifts the\n * moment a third one is added. Ids not in the scene come back unchanged.\n *\n * A set, not a restore order: a dependent can be reached before the parent\n * it sits under, which is also in the closure. */\n removalClosure(ids: readonly NodeId[]): readonly NodeId[];\n update(id: NodeId, patch: { data: TData }): void;\n setPose(id: NodeId, pose: TPose): void;\n /** Retag `id` to `layer`. On a **container this cascades**: every descendant\n * is moved to the same layer, recorded as a **single** undo step.\n *\n * Invariants:\n * - **Layer floor** — a child may not render below its parent, so retagging\n * to a layer *below* the node's parent throws. Retagging to the parent's\n * layer or any higher one is allowed; a node with no parent is\n * unconstrained.\n * - **No-op elision** — setting the layer a node already has does nothing\n * and pushes **no** history entry. */\n setLayer(id: NodeId, layer: TLayer): void;\n /** Point `id` at a different set of dependencies — retargeting an edge's end\n * onto another node, or switching a container between an id list and\n * `'children'`. Recorded as one undoable step.\n *\n * Order is significant: a derivation reads its dependencies positionally,\n * so `[b, a]` is not `[a, b]`. Ids need not be in the scene — a declared\n * dependency that appears later invalidates the node when it does, the same\n * as one declared at `add`. Passing `undefined` drops the declaration, after\n * which nothing cascades the node away.\n *\n * **No-op elision** — declaring what the node already declares does nothing\n * and pushes no history entry. */\n setDependsOn(id: NodeId, dependsOn: readonly NodeId[] | 'children' | undefined): void;\n /** Give container `id` a different layout — a strategy, its key in\n * `SceneRegistry.layout`, or `null` for none — and re-arrange its children\n * under it, as one undoable step. Throws for a leaf, or a key the registry\n * does not hold. A registered layout is recorded by its key, so a\n * serialized history restores it; an unregistered one lives only as long\n * as the session.\n *\n * **No-op elision** — setting the layout a container already has does\n * nothing and pushes no history entry. */\n setLayout(id: NodeId, layout: LayoutStrategy<TPose> | string | null): void;\n /** Reparent `id` under `parent` (or to a root when `parent` is `null`) at\n * `index` within the new sibling list, appending when `index` is omitted.\n * Siblings are reindexed. Recorded as one undoable step.\n *\n * Rejected (throws) when:\n * - `parent` exists but is a **leaf**, not a container;\n * - the move would form a **cycle** — `parent` is `id` itself or one of\n * `id`'s own descendants;\n * - it would drop `id` **below its new parent's layer** (child may not\n * render below its parent).\n *\n * `move(id, null)` — detaching to a root — is always allowed regardless of\n * layer, since a root has no parent to render beneath. */\n move(id: NodeId, parent: NodeId | null, index?: number): void;\n /** Shift `id` to `index` within its **current** parent's child list. Unlike\n * {@link move}, the parent never changes — only sibling order. */\n reorder(id: NodeId, index: number): void;\n setLayerVisible(layer: TLayer, visible: boolean): void;\n /** Lock or unlock a layer. A locked layer still paints, but its nodes are\n * out of reach: picking and area selection pass over them, the selection\n * drops them, and every node mutation on them throws. Locking is never\n * blocked by the lock, and neither are the other layer operations. */\n setLayerLocked(layer: TLayer, locked: boolean): void;\n /** Set or clear (`undefined`) a layer's parallax — see\n * `LayerRecord.parallax`. Undoable; an animation tweening it writes inside\n * {@link Scene.untracked} so no frame becomes an undo step. */\n setLayerParallax(layer: TLayer, parallax: ParallaxOpts | undefined): void;\n /** Whether `id` sits on a locked layer or under a container that does.\n * A container's lock covers its whole subtree, whatever layers the\n * descendants are tagged to. An id not in the scene is not locked. */\n isLocked(id: NodeId): boolean;\n /** Run `fn` with the lock guard lifted, for a programmatic edit that has to\n * reach a locked node. Undo and redo never need it. */\n unlocked<T>(fn: () => T): T;\n addLayer(spec: AddLayerSpec<TLayer>): void;\n /** Drop a user layer and every node tagged to it, as one undoable step.\n * Removal cascades, so this also deletes nodes **on other layers** that\n * derive from a node on this one. */\n removeLayer(layer: TLayer): void;\n renameLayer(layer: TLayer, name: string): void;\n moveLayer(layer: TLayer, index: number): void;\n\n // Custom op seam\n registerOp<P>(kind: string, handler: RegisteredOp<P>): void;\n recordOp<P>(op: { kind: string; payload: P }): void;\n\n /** Install (or clear) the active-journal accessor after scene construction.\n * Useful when the journal source (typically a mode machine) is built\n * with `scene.history` as a dependency — a chicken-and-egg situation\n * where the accessor can't be passed in via `UseSceneOptions`.\n *\n * Pass `null` to detach. Overrides any `getActiveJournal` set in\n * `UseSceneOptions`. */\n setActiveJournalAccessor(\n fn: (() => import('@weasel-js/history').Journal | null) | null,\n ): void;\n\n /** Install what the scene does when nodes join a container, replacing any\n * handler installed before; `null` removes it. Returns a function that\n * removes this handler, and does nothing once another has replaced it.\n * See {@link SceneArrivalHandler}. */\n setArrivalHandler(handler: SceneArrivalHandler<TPose> | null): () => void;\n\n /** Apply a batch of ops with journal-aware routing.\n *\n * - **Without active journal** (or no `getActiveJournal` in options):\n * the ops themselves are recorded as one undo entry on the scene's own\n * history, rebound to `adapter` — undo replays each op's `invert()`\n * against that same adapter. Consecutive `applyBatch` entries can\n * coalesce via matching op `coalesceKey`s when the scene opts into\n * `coalesceWindowMs`.\n * - **With active journal**: routes ops to `journal.applyBatch(ops, label)`.\n * The scene's history recording is suppressed for the duration so the\n * journal's inner history — not the scene's undo stack — tracks the batch.\n * Mutations still happen on `adapter` / scene state.\n *\n * `adapter` must be the same adapter the ops expect (typically a\n * `SceneCanvasAdapter`). Pass `this` from `sceneToAdapter` or a compatible\n * adapter. */\n applyBatch(ops: import('core/ops/types').Op[], label: string, adapter: unknown): void;\n\n // Selection\n\n /** The transient set of active ids — \"operate on these N as a unit\".\n * Shared by every view over this scene unless a view supplies its own\n * (see `CanvasView.selection`). Not document content: it never appears\n * in `toJSON`. It does ride on history entries, so undo and redo put\n * back the selection an edit was made under; changing it is never an\n * undo step of its own. */\n getSelection(): readonly NodeId[];\n setSelection(ids: readonly NodeId[]): void;\n\n // Ephemeral presentation state\n\n /** Per-node pose / alpha overrides the render and hit-test paths read\n * through. Like {@link Scene.getSelection} this is not document content:\n * writes are never recorded, never serialized, and do not bump\n * {@link Scene.getVersion}. See {@link PoseOverrides}. */\n readonly overrides: PoseOverrides<TPose>;\n\n // History\n /** The scene's undo/redo engine, in the shape `@weasel-js/history` defines —\n * what the kit's `undo` / `redo` actions resolve their `history` dep to, and\n * what a mode machine opens journals against. Mutating members route through\n * the scene's own wrappers, so an undo driven through this bumps\n * {@link Scene.getVersion} and notifies subscribers exactly as\n * {@link Scene.undo} does; `getVersion` / `subscribe` on it report the\n * engine's history version, not the scene's. */\n readonly history: History;\n undo(): boolean;\n redo(): boolean;\n canUndo(): boolean;\n canRedo(): boolean;\n batch<T>(label: string, fn: () => T): T;\n /** The layout container `id` declares, or `null` for a leaf or a\n * container that declares none. */\n layoutOf(id: NodeId): LayoutStrategy<TPose> | null;\n /** Called with the poses each live edit's layout pass wrote, after the\n * edit lands — never for an undo or redo, which restore poses rather than\n * arrange them. What a reflow animation glides from. */\n onReflow(listener: (moves: readonly LayoutMove<TPose>[]) => void): () => void;\n /** Run `fn` with every mutation applied but none recorded — for writes\n * that are not edits, such as a simulation stepping poses each frame.\n * Undo and redo move between recorded states and never restore an\n * untracked write; the next recorded change starts from whatever the\n * untracked writes left, so undoing it lands there. A `batch` inside\n * records nothing; inside a `batch`, these writes stay out of its entry.\n * Notifies once, reverts `fn`'s writes if it throws, and throws on\n * `applyBatch` and `history.apply` / `applyOps`, which exist to record. */\n untracked<T>(fn: () => T): T;\n /** Read-only snapshot of every history entry currently reachable from\n * the present state. Oldest applied first, then redoable entries in\n * the order they'd be re-applied. Each entry id is stable. */\n historyEntries(): readonly { id: string; label: string }[];\n /** Index of the \"current state\". Equals the count of applied entries;\n * `0` means \"nothing applied\" (initial). */\n historyIndex(): number;\n /** Jump to the given history index by calling undo/redo repeatedly.\n * Clamps to [0, total]. Returns true if any movement occurred. */\n jumpToHistoryIndex(index: number): boolean;\n\n // History persistence\n /** Snapshot the undo/redo history in a JSON-serializable form (the\n * engine's `SerializedHistory`). Entries containing any nameless op are\n * dropped (hand-rolled anonymous ops passed to `applyBatch`); kit and\n * consumer-registered ops always carry names. Payload JSON-safety\n * (e.g. typed arrays inside poses) is the caller's concern. Do not call\n * mid-`batch` — the open batch's ops are not yet recorded. */\n serializeHistory(): SerializedHistory;\n /** Replace the undo/redo history from a `serializeHistory()` snapshot.\n * Call on a scene whose node/layer state already matches the snapshot's\n * head state (i.e. right after `loadState` from the paired scene\n * snapshot); node state is NOT mutated. Ops re-registered via\n * `registerOp` before this call round-trip; unknown kinds become no-op\n * placeholders; external ops rebuild via the global op-factory registry\n * and replay against the `setHistoryAdapter` accessor. Restored entries\n * never coalesce with new ones. Notifies once. Do not call mid-`batch`:\n * the stacks are replaced underneath the open batch, whose eventual\n * flush would graft onto (and evict against) the restored stacks. */\n restoreHistory(snapshot: SerializedHistory): void;\n /** Install (or clear with `null`) the accessor for the adapter that\n * RESTORED external ops (recorded via `applyBatch`, rebuilt from a\n * `restoreHistory` snapshot) apply against on undo/redo. Resolved lazily\n * at each apply, so wiring order relative to `restoreHistory` doesn't\n * matter. Live `applyBatch` entries are unaffected (they bind their\n * call-site adapter). If unset when a restored op applies, the op is a\n * debug-warned no-op. */\n setHistoryAdapter(fn: (() => unknown) | null): void;\n\n // Serialization\n /** Snapshot the current scene state to a JSON-serializable shape.\n * History (undo/redo stacks) is NOT captured. Function fields like\n * `ContainerNode.clipFromPose` are translated to string keys via the\n * scene's registry; throws if any function field has no matching key. */\n toJSON(): SerializedScene<TData, TLayer, TPose>;\n /** Replace this scene's entire node + layer state in place from a snapshot\n * produced by `toJSON()`. Unlike `sceneFromJSON`, the existing Scene\n * instance is preserved — holders such as `<SceneCanvas>` keep their\n * reference. History (undo/redo) is cleared, matching `sceneFromJSON`.\n * Bumps `getVersion()` and notifies subscribers exactly once.\n *\n * Throws on an unsupported version or unknown registry/layer ids; on a\n * malformed snapshot the scene is left empty or partially populated (callers should treat a\n * `loadState` throw as fatal and reload). Snapshots from `toJSON()` are\n * always well-formed. */\n loadState(json: SerializedScene<TData, TLayer, TPose>): void;\n\n // Subscription (used by useScene; also for non-React observers)\n subscribe(listener: () => void): () => void;\n /** Monotonically increasing version. Snapshot for `useSyncExternalStore`. */\n getVersion(): number;\n}\n"],"mappings":";AA8EA,IAAaA,KAAY,MAAsBC"}
|
|
1
|
+
{"version":3,"file":"types.js","names":["asNodeId","s"],"sources":["../../../src/core/scene/types.ts"],"sourcesContent":["import type { Path } from '@weasel-js/geom';\nimport type { History, SerializedHistory } from '@weasel-js/history';\nimport type { ParallaxOpts } from '../viewport/parallax';\nimport type { ContainerBounds, LayoutStrategy } from '../../layout/types';\nimport type { PoseComposition } from './composeFrame';\n\n/**\n * Axis-aligned rectangle pose with optional rotation. The canonical pose\n * shape used by `composeRectPose` and the `unionBounds` helper. Rotation is\n * in radians, pivoted on the unrotated AABB center; absent === 0. Kit-side\n * code that consumes rotation already reads `pose.rotation ?? 0`\n * (`wrapNodeOutput`, `rotate/handle.ts`, `pathInWorld.ts`), so\n * the slot exists on every default scene whether or not the consumer\n * populates it.\n */\nexport interface RectPose {\n x: number;\n y: number;\n width: number;\n height: number;\n /** Rotation in radians around the unrotated AABB center. Absent === 0. */\n rotation?: number;\n}\n\n/**\n * # SceneNode — the thing in the scene\n *\n * A `SceneNode` is the single canonical unit of a weasel scene. Everything\n * the user sees on the canvas — a shape, a group, an annotation, a tile —\n * is one of these. Containers and leaves are both nodes; the kit has no\n * other concept of \"scene element.\"\n *\n * ## Three orthogonal slots\n *\n * Every node carries three independent slots, plus its tree position:\n *\n * - **`data: TData`** — app-defined payload. The kit never inspects it.\n * Color, label, kind, glyph, sample-rate, whatever the app's domain\n * calls for. Mutated via `Scene.update(id, { data })`.\n *\n * - **`pose: TPose`** — local transform, relative to the node's direct\n * parent (or world, for root nodes). Default `RectPose` is\n * `{ x, y, width, height }`, but `TPose` is fully generic so apps can\n * use rotated rects, paths, ellipses, etc. The kit composes world\n * poses via `composeWorldPose` when rendering / hit-testing / snapping.\n *\n * - **`layer: TLayer`** — a string tag associating the node with a\n * visual `RenderLayer` at draw time. Separate from `LayerRecord` (the\n * per-layer visible/locked metadata held by the `Scene`).\n *\n * Tree position lives on the node itself: every node has a `parent` (or\n * `null` for roots), and `ContainerNode` adds an ordered `children: NodeId[]`.\n *\n * ## Identity is by `NodeId`, not by reference\n *\n * Nodes are addressed by `NodeId` everywhere outside the scene tree:\n * selection is `NodeId[]`, ops reference `NodeId`s, adapter methods accept\n * `string` ids and look up the node on demand. The `Node` object itself is\n * a snapshot of current state — don't hold references to it across scene\n * updates; look up by id when you need the latest.\n *\n * Picking helpers (`pickBest`, `pickEvery`) likewise return ids, not nodes —\n * they're hit-testing primitives that stay ignorant of node payload shape.\n *\n * ## Vocabulary\n *\n * The kit-internal name is `Node`; the public re-export is `SceneNode`\n * (avoids collision with DOM `Node` at call sites). Adapter methods speak\n * the same vocabulary: `getNode`, `getNodes`, `insertNode`, `removeNode`,\n * `cloneNode`, `addNode`. Older code, demos, and comments may still say\n * \"object\" or \"item\" — those are historical aliases for the same concept.\n */\n\n/** Opaque branded id. Treat as opaque outside the kit. */\nimport type { NodeId } from '@weasel-js/routing';\nexport type { NodeId };\n\n/** Brand a string as a NodeId. */\nexport const asNodeId = (s: string): NodeId => s as NodeId;\n\n/**\n * One dependency, as a derivation reads it: the node itself and the pose it is\n * painted at.\n *\n * The node comes along because a connector legitimately reads more than a box\n * — an edge that thickens with its endpoint's weight, or routes only to nodes\n * on a given layer, is answering off `data` and `layer`. The scene already\n * invalidates dependents on `kit:setData` and `kit:setLayer` for exactly that,\n * so handing over only the pose made the invalidation pay for a read nothing\n * could perform.\n */\nexport interface DerivedDep<TPose> {\n node: Node<unknown, string, TPose>;\n /** Its override when it has one, else its own derived pose, else the pose\n * the document stores — `effectivePose`, the same answer the renderer uses. */\n pose: TPose;\n /** The path it derives, or `null` when it derives none. Read it to place\n * something *along* a dependency rather than beside it — a label on a\n * routed edge, a tick on a curve.\n *\n * Resolved on first read and memoized on the dependency, so a route costs\n * the same whether one node reads it or five, and a dependency nobody asks\n * about costs nothing. */\n readonly path: Path | null;\n}\n\n/** Computes a node's path from its dependencies: the type of a node's\n * `derivePath` and of each entry in `SceneRegistry.derivePath`. */\nexport type DerivePathFn<TPose> = (\n node: Node<unknown, string, TPose>,\n deps: readonly (DerivedDep<TPose> | undefined)[],\n) => Path | null;\n\n/** Computes a node's pose from its dependencies: the type of a node's\n * `derivePose` and of each entry in `SceneRegistry.derivePose`. */\nexport type DerivePoseFn<TPose> = (\n node: Node<unknown, string, TPose>,\n deps: readonly (DerivedDep<TPose> | undefined)[],\n) => TPose | null;\n\ninterface NodeBase<TData, TLayer extends string, TPose> {\n id: NodeId;\n layer: TLayer;\n pose: TPose;\n data: TData;\n parent: NodeId | null;\n /** Nodes whose poses this node's geometry is computed from. Fixed at add\n * time. Absent or empty means the node's geometry is authored, which is the\n * normal case.\n *\n * `'children'` means \"my own children, in child order\" — a container that\n * hugs its contents, which a fixed id list cannot express because\n * reparenting would have to maintain it. The two forms differ in lifetime\n * as well as in membership: deleting a node deletes everything that names\n * it in `dependsOn`, but a container outlives the children it derives\n * from — an emptied group is still a group. */\n dependsOn?: readonly NodeId[] | 'children';\n /** Whether the hit-test walk can return this node. Default `true`; `false`\n * makes it transparent to picking, so a click on it lands on whatever is\n * behind — normally its own container.\n *\n * For content a container owns rather than content the user selects on its\n * own: the label inside a box, a body's rows. Without it the innermost hit\n * wins, and dragging a labeled box moves the label out of the box. It does\n * not hide the node from anything else — it still paints, still clips, and\n * a consumer can still select it by id. */\n pickable?: boolean;\n /** Computes this node's path from its dependencies, in `dependsOn` order —\n * each one the node and the pose it is painted at.\n * A dependency that has been removed arrives as `undefined`.\n * Returning `null` means \"nothing to draw right now\". Re-evaluated when a\n * dependency's world pose changes, never authored. Absolute-pose `Scene`\n * makes that the dependency's own pose, and an ancestor's move reaches it as\n * a `setPose` of its own from the container cascade.\n * `node` is deliberately widened: naming `TData`/`TLayer` here puts them in\n * a contravariant position, making `Scene` invariant in both and breaking\n * assignment kit-wide. The cost is that a `derivePath` casts to read `node.data`. */\n derivePath?: DerivePathFn<TPose>;\n /** Computes this node's pose from its dependencies' poses, the same way\n * `derivePath` computes its path — same `dependsOn` list, same widened\n * `node`, same registry-keyed serialization.\n *\n * Where a derived path is resolved at paint time and reaches only the\n * painter, a derived pose is what the node *is* at: it feeds bounds,\n * hit-testing, selection chrome, snapping and layout, and every reader\n * gets it through `effectivePose`. A pose override still wins over it —\n * that is what lets a drag preview a node the document says is elsewhere.\n *\n * Returning `null` means \"I have nothing to derive from right now\", and\n * the node falls back to its authored `pose`. `setPose` on a derived node\n * still writes that authored pose; it is simply not what anything reads. */\n derivePose?: DerivePoseFn<TPose>;\n}\n\n/** A node with no children — a shape, a label, an image. */\nexport interface LeafNode<TData, TLayer extends string, TPose = RectPose>\n extends NodeBase<TData, TLayer, TPose> {\n kind: 'leaf';\n}\n\n/** A node with an ordered list of children. This is the real group: what\n * Cmd+G creates, what SVG `<g>` round-trips to. A container has its own pose,\n * which its children's poses are relative to, and may optionally clip them. */\nexport interface ContainerNode<TData, TLayer extends string, TPose = RectPose>\n extends NodeBase<TData, TLayer, TPose> {\n kind: 'container';\n children: NodeId[];\n /** Optional clip-path source. Re-evaluated each render. Returning `null`\n * means \"no clip for this container right now\"; an empty / zero-area path\n * means \"clip everything out\" (children render nowhere). When set, the\n * renderer rasterizes the returned path into the stencil buffer and\n * paints descendants only where it covers. */\n clipFromPose?: (pose: TPose) => Path | null;\n /** How this container arranges its children. Whenever the container's\n * child set or order changes, or its bounds change size, the scene hands\n * the change to the strategy — `depart` for children that left it,\n * `arrive` for children that joined it, `childPoses` otherwise — and records the poses it writes in the same\n * undo entry as the change. A drag reads it too: it is the layout `move`\n * reflows and drops against. */\n layout?: LayoutStrategy<TPose>;\n}\n\n/** A pose a container's layout wrote — reported by {@link Scene.onReflow} —\n * in the node's stored frame. */\nexport interface LayoutMove<TPose> {\n id: NodeId;\n from: TPose;\n to: TPose;\n}\n\n/** How a scene reads a laid-out container's bounds and folds poses to the\n * world frame a {@link LayoutStrategy} works in. */\nexport interface LayoutFrame<TPose> {\n /** The container's bounds, from its world pose. Default: the pose's own\n * `x`/`y`/`width`/`height`; a pose without them is not laid out. */\n bounds?(pose: TPose): ContainerBounds | null;\n /** `pose` resized from bounds `from` to `to`, for a layout that grows its\n * container. Default: `to`'s fields written over the pose. */\n remap?(pose: TPose, from: ContainerBounds, to: ContainerBounds): TPose;\n /** Omit for the absolute-pose model. A scene whose canvas composes poses\n * (`<SceneCanvas poseComposition>`) passes the same strategy here. */\n composition?: PoseComposition<TPose>;\n}\n\n/** A node in the scene tree: either a leaf or a container. Re-exported\n * publicly as `SceneNode`, to avoid colliding with the DOM's `Node`. */\nexport type Node<TData, TLayer extends string, TPose = RectPose> =\n | LeafNode<TData, TLayer, TPose>\n | ContainerNode<TData, TLayer, TPose>;\n\ninterface LayerRecordBase<TLayer extends string> {\n id: TLayer;\n visible: boolean;\n locked: boolean;\n /** Draw this layer as a parallax plane: through a view derived from the\n * camera's (see `deriveParallaxView`), and picked through the same one.\n * Absent means the layer moves with the camera. */\n parallax?: ParallaxOpts;\n}\n\n/** A layer declared when the scene was created. Fixed set, no display name —\n * these are the kit's own render bands, not something a user manages. */\nexport interface SystemLayerRecord<TLayer extends string>\n extends LayerRecordBase<TLayer> {\n kind: 'system';\n}\n\n/** A layer the user created and can rename, reorder or delete. */\nexport interface UserLayerRecord<TLayer extends string>\n extends LayerRecordBase<TLayer> {\n kind: 'user';\n name: string;\n}\n\n/** Per-layer metadata held by the scene: whether it is visible and locked,\n * and where it sits in the render stack. Distinct from a node's `layer` tag,\n * which merely names one of these. */\nexport type LayerRecord<TLayer extends string> =\n | SystemLayerRecord<TLayer>\n | UserLayerRecord<TLayer>;\n\n/** What `Scene.add` needs to mint a node. Everything except the id is\n * required; the id is generated unless one is supplied. */\nexport interface AddNodeSpec<TData, TLayer extends string, TPose = RectPose> {\n kind: 'leaf' | 'container';\n layer: TLayer;\n pose: TPose;\n data: TData;\n parent?: NodeId | null;\n index?: number;\n /** Explicit id wins over the Scene's `generateId` and the kit default. */\n id?: NodeId;\n /** Mirrors `SceneNode.pickable`. Omit for the default, which is pickable. */\n pickable?: boolean;\n /** Only meaningful when `kind === 'container'`. Attach a clip-path function\n * to the node; ignored for leaves. Mirrors `ContainerNode.clipFromPose`. */\n clipFromPose?: (pose: TPose) => Path | null;\n /** Mirrors `SceneNode.dependsOn`. */\n dependsOn?: readonly NodeId[] | 'children';\n /** Mirrors `SceneNode.derivePath`. Taken as a live function; its registry key is\n * looked up from it, never passed in. */\n derivePath?: DerivePathFn<TPose>;\n /** Mirrors `SceneNode.derivePose`, on the same terms as `derivePath`. */\n derivePose?: DerivePoseFn<TPose>;\n /** Mirrors `ContainerNode.layout`; ignored for leaves. */\n layout?: LayoutStrategy<TPose>;\n}\n\n/** A custom scene mutation registered with `Scene.registerOp`: how to apply\n * it and how to undo it. The pair is what makes it participate in history. */\nexport interface RegisteredOp<P> {\n apply: (payload: P) => void;\n revert: (payload: P) => void;\n}\n\n/** One of the layers a scene is created with. */\nexport interface SystemLayerSpec<TLayer extends string> {\n id: TLayer;\n visible?: boolean;\n locked?: boolean;\n /** See `LayerRecord.parallax`. */\n parallax?: ParallaxOpts;\n}\n\n/** Argument to `Scene.addLayer`. Always produces a `UserLayerRecord`\n * (`kind: 'user'`). */\nexport interface AddLayerSpec<TLayer extends string> {\n id: TLayer;\n name: string;\n /** Default `true`. */\n visible?: boolean;\n /** Default `false`. */\n locked?: boolean;\n /** See `LayerRecord.parallax`. */\n parallax?: ParallaxOpts;\n /** Render-stack position. Default: top of stack (highest render index). */\n index?: number;\n}\n\n/** One layer as it appears in a snapshot. A snapshot written before layer\n * kind was serialized carries neither field, and loads as a system layer —\n * which is what every layer in such a snapshot was. */\nexport interface SerializedLayer<TLayer extends string>\n extends SystemLayerSpec<TLayer> {\n kind?: 'system' | 'user';\n /** Present on a user layer, absent on a system one. */\n name?: string;\n}\n\n/** JSON-serializable shape of a Scene's current state. Produced by\n * `scene.toJSON()`; consumed by `sceneFromJSON()`. Function fields\n * (e.g., `clipFromPose`) appear as string keys (`clipFromPoseKey`) and\n * are resolved through `SceneRegistry` at load time. */\nexport interface SerializedScene<TData, TLayer extends string, TPose> {\n version: 1;\n /** The whole layer stack, system and user alike — the name predates user\n * layers and is kept so existing snapshots still parse. */\n systemLayers: readonly SerializedLayer<TLayer>[];\n nodes: readonly SerializedNode<TData, TLayer, TPose>[];\n}\n\n/** JSON-serializable shape of a single node. Mirrors `AddNodeSpec` but\n * with function fields replaced by registry keys. */\nexport interface SerializedNode<TData, TLayer extends string, TPose> {\n id: string;\n kind: 'leaf' | 'container';\n layer: TLayer;\n pose: TPose;\n data: TData;\n /** Parent id; omitted for roots. */\n parent?: string;\n /** Registry key for the container's clip-path factory.\n * Containers only; omitted when the container has no clip. */\n clipFromPoseKey?: string;\n /** Ids this node's geometry derives from, or `'children'`. Omitted when it\n * derives from nothing. */\n dependsOn?: readonly string[] | 'children';\n /** Mirrors `SceneNode.pickable`. Omitted when the node is pickable. */\n pickable?: boolean;\n /** Registry key for the node's `derivePath` function. Omitted when it has none. */\n derivePathKey?: string;\n /** Registry key for the node's `derivePose` function. Omitted when it has none. */\n derivePoseKey?: string;\n /** Registry key for the container's layout strategy. Omitted when it has none. */\n layoutKey?: string;\n}\n\n/** Per-scene registry mapping string keys to live function references.\n * Passed to `createScene({ ..., registry })` and `sceneFromJSON(json, { registry })`.\n * Each function-field type has its own keyed map. */\nexport interface SceneRegistry<TPose> {\n /** Maps registry keys to `clipFromPose` factory functions for container nodes. */\n clipFromPose?: Readonly<Record<string, (pose: TPose) => Path | null>>;\n /** Maps registry keys to `derivePath` functions for nodes with `dependsOn`. */\n derivePath?: Readonly<Record<string, DerivePathFn<TPose>>>;\n /** Maps registry keys to `derivePose` functions for nodes with `dependsOn`. */\n derivePose?: Readonly<Record<string, DerivePoseFn<TPose>>>;\n /** Maps registry keys to container layout strategies. */\n layout?: Readonly<Record<string, LayoutStrategy<TPose>>>;\n // A new function field is a row in `NODE_FN_FIELDS` (core/scene/nodeFnFields.ts)\n // plus its entry here; the field name is the registry key by construction.\n}\n\n/**\n * Decides what one edit's changes to containers do to their children.\n * `arrivals` maps each container to the ids that joined it — by an `add`\n * under it or a `move` into it, whatever op or call made them — in the order\n * they arrived. `changed` names every container whose child set or order\n * changed, or whose pose changed size, arrivals included. Both are read once\n * the edit's other mutations have landed.\n *\n * Return the poses to write, in each node's stored frame; they become part of\n * the same edit, and so of the same undo step. An empty map writes nothing.\n * Return `null` to refuse: the whole edit is reverted (see\n * `SceneArrivalRefused`).\n *\n * Runs only for live edits. Undo, redo and a restored history replay the\n * arrangement that was decided the first time. With none installed, a scene\n * holding declared layouts (`ContainerNode.layout`) runs its own.\n */\nexport type SceneArrivalHandler<TPose> = (\n arrivals: ReadonlyMap<NodeId, readonly NodeId[]>,\n changed: ReadonlySet<NodeId>,\n /** The nodes that left each container in the edit, by the container they\n * left — removed, or moved out. */\n departures: ReadonlyMap<NodeId, readonly NodeId[]>,\n) => ReadonlyMap<NodeId, TPose> | null;\n\n/** Options for `useScene` — the layers the scene has, what it starts out\n * holding, and how its history behaves. */\nexport interface UseSceneOptions<TData, TLayer extends string, TPose = RectPose> {\n systemLayers: readonly SystemLayerSpec<TLayer>[];\n initial?: readonly AddNodeSpec<TData, TLayer, TPose>[];\n ops?: Readonly<Record<string, RegisteredOp<unknown>>>;\n historyLimit?: number;\n /** Window (ms) within which consecutive same-shaped mutations merge into\n * the previous undo entry (matching per-op coalesce keys — e.g. repeated\n * `setPose` on the same node, or repeated `applyBatch` calls whose ops\n * carry matching `coalesceKey` multisets). `0` (default) disables\n * coalescing: every mutation is a discrete undo entry. Undo of a\n * coalesced entry returns to the state before the first merged mutation;\n * redo restores the latest. `scene.batch` entries never coalesce. */\n coalesceWindowMs?: number;\n generateId?: () => NodeId;\n /** Per-scene registry for non-serializable function fields (clipFromPose, etc.).\n * Required only when serializing/deserializing scenes that use function fields. */\n registry?: SceneRegistry<TPose>;\n /** How laid-out containers are measured. See {@link LayoutFrame}. */\n layoutFrame?: LayoutFrame<TPose>;\n /** When supplied, `scene.applyOps(ops, label)` consults this on every call.\n * If it returns a non-null `Journal`, ops are routed to the journal's\n * `applyBatch` instead of recording a new parent-history entry. The journal\n * drives adapter mutation internally (via `op.apply(adapter)`), so the\n * scene state changes as normal; only the history tracking differs.\n *\n * The accessor is called on every `applyOps` invocation so the caller can\n * swap the active journal in and out by updating the closure's reference\n * (e.g., an app's mode machine holds `let activeJournal: Journal | null`\n * and the accessor reads that variable).\n *\n * When the accessor isn't known at scene-construction time (typical for\n * mode machines that depend on `scene.history`), pass nothing here and\n * wire it after construction via `scene.setActiveJournalAccessor(fn)`. */\n getActiveJournal?: () => import('@weasel-js/history').Journal | null;\n /** Re-render the host on every scene mutation. Default `true`. Set `false`\n * when the scene is read by a frame loop rather than by a render — a game\n * loop, a simulation — and nothing in the host's DOM derives from it.\n * Read by `useScene`; `createScene` ignores it. */\n subscribe?: boolean;\n}\n\n/**\n * One node's ephemeral presentation override — what a frame loop wants to say\n * about a node without saying it about the document.\n *\n * Not document content: never recorded in history, never in `toJSON`, and\n * writing one does not bump `Scene.getVersion()`. Hoist one entry per node and\n * mutate it in place on a frame loop; `PoseOverrides.commit()` is what makes a\n * mutation visible.\n */\nexport interface PoseOverride<TPose> {\n /** Replaces the node's document pose everywhere the render and hit-test\n * paths read one, including the clip a container derives from its pose, and\n * winning over a `derivePose`. Resolved by `effectivePose` — reading\n * `node.pose` directly is how those paths came to disagree about where a\n * node is. */\n pose?: TPose;\n /** Multiplied into the node's painted alpha, on top of any `alphaFor`. */\n alpha?: number;\n}\n\n/**\n * The scene's ephemeral per-node overrides — see {@link PoseOverride}.\n *\n * The intended shape of a frame is: `set` each node once, mutate the entries\n * in place per frame, `commit()` once. `commit` is not optional bookkeeping —\n * the painter memo keys on pose *reference*, so a mutation without a commit\n * paints the previous frame with no error.\n *\n * To promote a frame to document state (dropping a drag, baking an animation),\n * write it once through `Scene.setPose` and `clear` the override.\n */\nexport interface PoseOverrides<TPose> {\n /** Store `entry` for `id` **by reference**; the caller keeps mutating it. */\n set(id: NodeId, entry: PoseOverride<TPose>): void;\n /** The entry `set` stored, by reference — what a writer checks to know\n * whether its own entry is still installed. Painting reads `read`. */\n get(id: NodeId): PoseOverride<TPose> | undefined;\n /** What the node is drawn and picked with: every source of overrides for\n * it folded into one, as of the last write or `commit()`. `undefined` for\n * a node nothing overrides. The object is reused per node. */\n read(id: NodeId): PoseOverride<TPose> | undefined;\n has(id: NodeId): boolean;\n /** The overridden ids, as a snapshot array. */\n ids(): readonly NodeId[];\n clear(id: NodeId): void;\n clearAll(): void;\n /** Publish this frame's in-place mutations: invalidate the painter memo for\n * every overridden node, then notify subscribers. */\n commit(): void;\n /** Notified after every write. The canvas uses this to repaint without a\n * scene version bump. */\n subscribe(fn: () => void): () => void;\n /** Monotonic write counter. A snapshot for observers that poll. */\n getGeneration(): number;\n}\n\n/**\n * The kit-owned scene tree: nodes, layers, and the undo history over both.\n *\n * A scene is logical, not visual — it says what exists and where, and nothing\n * about how it is painted. Every mutating method is undoable, and reads are\n * snapshots rather than live views. Nodes are addressed by `NodeId`; hold ids\n * across mutations, not node objects.\n *\n * Three type parameters keep it domain-agnostic: `TData` is the app's payload,\n * which the kit never inspects; `TPose` is the transform shape, `RectPose` by\n * default; `TLayer` is the union of layer names.\n */\nexport interface Scene<TData, TLayer extends string, TPose = RectPose> {\n // Reads\n readonly nodes: ReadonlyMap<NodeId, Node<TData, TLayer, TPose>>;\n readonly roots: readonly NodeId[];\n readonly layers: readonly LayerRecord<TLayer>[];\n /** The registry this scene resolves node functions against — the consumer's\n * entries over the kit's. */\n readonly registry: SceneRegistry<TPose>;\n get(id: NodeId): Node<TData, TLayer, TPose> | undefined;\n /** A token for the node `id` names right now, for telling one node from a\n * later one that reuses its id. It holds for as long as the node stays in\n * the scene, whatever edits it takes, and is different every time the id\n * enters the scene — by `add`, by an undo or redo that brings it back, or\n * by `loadState`. `undefined` when `id` is not in the scene. Compare tokens\n * for equality only; their order means nothing. */\n incarnation(id: NodeId): number | undefined;\n childrenOf(id: NodeId): readonly NodeId[];\n ancestorsOf(id: NodeId): readonly NodeId[];\n renderOrder(): Iterable<NodeId>;\n /** The same layer-major sequence as {@link Scene.renderOrder}, as the nodes\n * themselves. Prefer this wherever the ids are only going to be resolved\n * back to nodes: the traversal already holds them, and re-looking each one\n * up was ~40% of the area hit-test's per-node cost. Cached until a\n * structural edit, so repeat calls hand back the same array — a snapshot,\n * not a live view, and not yours to mutate. */\n renderOrderNodes(): readonly Node<TData, TLayer, TPose>[];\n /** The nodes on one layer, in render order. Cached alongside\n * {@link Scene.renderOrderNodes} until a structural edit, so a caller that\n * only cares about one layer pays the tree walk once rather than once per\n * call. A snapshot, not a live view, and not yours to mutate. An unknown\n * layer id yields an empty array. */\n nodesOnLayer(layer: TLayer): readonly Node<TData, TLayer, TPose>[];\n\n // Mutations (all auto-undoable)\n add(spec: AddNodeSpec<TData, TLayer, TPose>): NodeId;\n /** Delete `id`, its **entire subtree**, and **everything that derives from**\n * any of those nodes — a node listing one of them in `dependsOn` goes too,\n * along with its own subtree, transitively. A dependent can live anywhere in\n * the tree, so this deletes nodes the caller never named and may unlink\n * several disjoint subtrees at once. Recorded as one undoable step; `undo()`\n * restores every one of them where it was, child order intact. */\n remove(id: NodeId): void;\n /** {@link remove} over several roots at once, as a **single** undoable step.\n * Ids resolve against the tree as it stands at the call, so an id that\n * another one would cascade away is absorbed rather than removed twice —\n * which is what makes it safe to pass a whole selection. Throws if any id is\n * not in the scene; an empty list does nothing and records no step. */\n removeMany(ids: readonly NodeId[]): void;\n /** Every node {@link removeMany} would take if given `ids`: each id, its\n * subtree, everything deriving from any of those, and those nodes' subtrees\n * in turn. The authoritative answer — the cascade relations live here, and\n * a caller reconstructing them from `dependsOn` and `children` drifts the\n * moment a third one is added. Ids not in the scene come back unchanged.\n *\n * A set, not a restore order: a dependent can be reached before the parent\n * it sits under, which is also in the closure. */\n removalClosure(ids: readonly NodeId[]): readonly NodeId[];\n update(id: NodeId, patch: { data: TData }): void;\n setPose(id: NodeId, pose: TPose): void;\n /** Retag `id` to `layer`. On a **container this cascades**: every descendant\n * is moved to the same layer, recorded as a **single** undo step.\n *\n * Invariants:\n * - **Layer floor** — a child may not render below its parent, so retagging\n * to a layer *below* the node's parent throws. Retagging to the parent's\n * layer or any higher one is allowed; a node with no parent is\n * unconstrained.\n * - **No-op elision** — setting the layer a node already has does nothing\n * and pushes **no** history entry. */\n setLayer(id: NodeId, layer: TLayer): void;\n /** Point `id` at a different set of dependencies — retargeting an edge's end\n * onto another node, or switching a container between an id list and\n * `'children'`. Recorded as one undoable step.\n *\n * Order is significant: a derivation reads its dependencies positionally,\n * so `[b, a]` is not `[a, b]`. Ids need not be in the scene — a declared\n * dependency that appears later invalidates the node when it does, the same\n * as one declared at `add`. Passing `undefined` drops the declaration, after\n * which nothing cascades the node away.\n *\n * **No-op elision** — declaring what the node already declares does nothing\n * and pushes no history entry. */\n setDependsOn(id: NodeId, dependsOn: readonly NodeId[] | 'children' | undefined): void;\n /** Give container `id` a different layout — a strategy, its key in\n * `SceneRegistry.layout`, or `null` for none — and re-arrange its children\n * under it, as one undoable step. Throws for a leaf, or a key the registry\n * does not hold. A registered layout is recorded by its key, so a\n * serialized history restores it; an unregistered one lives only as long\n * as the session.\n *\n * **No-op elision** — setting the layout a container already has does\n * nothing and pushes no history entry. */\n setLayout(id: NodeId, layout: LayoutStrategy<TPose> | string | null): void;\n /** Reparent `id` under `parent` (or to a root when `parent` is `null`) at\n * `index` within the new sibling list, appending when `index` is omitted.\n * Siblings are reindexed. Recorded as one undoable step.\n *\n * Rejected (throws) when:\n * - `parent` exists but is a **leaf**, not a container;\n * - the move would form a **cycle** — `parent` is `id` itself or one of\n * `id`'s own descendants;\n * - it would drop `id` **below its new parent's layer** (child may not\n * render below its parent).\n *\n * `move(id, null)` — detaching to a root — is always allowed regardless of\n * layer, since a root has no parent to render beneath. */\n move(id: NodeId, parent: NodeId | null, index?: number): void;\n /** Shift `id` to `index` within its **current** parent's child list. Unlike\n * {@link move}, the parent never changes — only sibling order. */\n reorder(id: NodeId, index: number): void;\n setLayerVisible(layer: TLayer, visible: boolean): void;\n /** Lock or unlock a layer. A locked layer still paints, but its nodes are\n * out of reach: picking and area selection pass over them, the selection\n * drops them, and every node mutation on them throws. Locking is never\n * blocked by the lock, and neither are the other layer operations. */\n setLayerLocked(layer: TLayer, locked: boolean): void;\n /** Set or clear (`undefined`) a layer's parallax — see\n * `LayerRecord.parallax`. Undoable; an animation tweening it writes inside\n * {@link Scene.untracked} so no frame becomes an undo step. */\n setLayerParallax(layer: TLayer, parallax: ParallaxOpts | undefined): void;\n /** Whether `id` sits on a locked layer or under a container that does.\n * A container's lock covers its whole subtree, whatever layers the\n * descendants are tagged to. An id not in the scene is not locked. */\n isLocked(id: NodeId): boolean;\n /** Run `fn` with the lock guard lifted, for a programmatic edit that has to\n * reach a locked node. Undo and redo never need it. */\n unlocked<T>(fn: () => T): T;\n addLayer(spec: AddLayerSpec<TLayer>): void;\n /** Drop a user layer and every node tagged to it, as one undoable step.\n * Removal cascades, so this also deletes nodes **on other layers** that\n * derive from a node on this one. */\n removeLayer(layer: TLayer): void;\n renameLayer(layer: TLayer, name: string): void;\n moveLayer(layer: TLayer, index: number): void;\n\n // Custom op seam\n registerOp<P>(kind: string, handler: RegisteredOp<P>): void;\n recordOp<P>(op: { kind: string; payload: P }): void;\n\n /** Install (or clear) the active-journal accessor after scene construction.\n * Useful when the journal source (typically a mode machine) is built\n * with `scene.history` as a dependency — a chicken-and-egg situation\n * where the accessor can't be passed in via `UseSceneOptions`.\n *\n * Pass `null` to detach. Overrides any `getActiveJournal` set in\n * `UseSceneOptions`. */\n setActiveJournalAccessor(\n fn: (() => import('@weasel-js/history').Journal | null) | null,\n ): void;\n\n /** Install what the scene does when nodes join a container, replacing any\n * handler installed before; `null` removes it. Returns a function that\n * removes this handler, and does nothing once another has replaced it.\n * See {@link SceneArrivalHandler}. */\n setArrivalHandler(handler: SceneArrivalHandler<TPose> | null): () => void;\n\n /** Apply a batch of ops with journal-aware routing.\n *\n * - **Without active journal** (or no `getActiveJournal` in options):\n * the ops themselves are recorded as one undo entry on the scene's own\n * history, rebound to `adapter` — undo replays each op's `invert()`\n * against that same adapter. Consecutive `applyBatch` entries can\n * coalesce via matching op `coalesceKey`s when the scene opts into\n * `coalesceWindowMs`.\n * - **With active journal**: routes ops to `journal.applyBatch(ops, label)`.\n * The scene's history recording is suppressed for the duration so the\n * journal's inner history — not the scene's undo stack — tracks the batch.\n * Mutations still happen on `adapter` / scene state.\n *\n * `adapter` must be the same adapter the ops expect (typically a\n * `SceneCanvasAdapter`). Pass `this` from `sceneToAdapter` or a compatible\n * adapter. */\n applyBatch(ops: import('core/ops/types').Op[], label: string, adapter: unknown): void;\n\n // Selection\n\n /** The transient set of active ids — \"operate on these N as a unit\".\n * Shared by every view over this scene unless a view supplies its own\n * (see `CanvasView.selection`). Not document content: it never appears\n * in `toJSON`. It does ride on history entries, so undo and redo put\n * back the selection an edit was made under; changing it is never an\n * undo step of its own. */\n getSelection(): readonly NodeId[];\n setSelection(ids: readonly NodeId[]): void;\n\n // Ephemeral presentation state\n\n /** Per-node pose / alpha overrides the render and hit-test paths read\n * through. Like {@link Scene.getSelection} this is not document content:\n * writes are never recorded, never serialized, and do not bump\n * {@link Scene.getVersion}. See {@link PoseOverrides}. */\n readonly overrides: PoseOverrides<TPose>;\n\n // History\n /** The scene's undo/redo engine, in the shape `@weasel-js/history` defines —\n * what the kit's `undo` / `redo` actions resolve their `history` dep to, and\n * what a mode machine opens journals against. Mutating members route through\n * the scene's own wrappers, so an undo driven through this bumps\n * {@link Scene.getVersion} and notifies subscribers exactly as\n * {@link Scene.undo} does; `getVersion` / `subscribe` on it report the\n * engine's history version, not the scene's. */\n readonly history: History;\n undo(): boolean;\n redo(): boolean;\n canUndo(): boolean;\n canRedo(): boolean;\n batch<T>(label: string, fn: () => T): T;\n /** The layout container `id` declares, or `null` for a leaf or a\n * container that declares none. */\n layoutOf(id: NodeId): LayoutStrategy<TPose> | null;\n /** Called with the poses each live edit's layout pass wrote, after the\n * edit lands — never for an undo or redo, which restore poses rather than\n * arrange them. What a reflow animation glides from. */\n onReflow(listener: (moves: readonly LayoutMove<TPose>[]) => void): () => void;\n /** Run `fn` with every mutation applied but none recorded — for writes\n * that are not edits, such as a simulation stepping poses each frame.\n * Undo and redo move between recorded states and never restore an\n * untracked write; the next recorded change starts from whatever the\n * untracked writes left, so undoing it lands there. A `batch` inside\n * records nothing; inside a `batch`, these writes stay out of its entry.\n * Notifies once, reverts `fn`'s writes if it throws, and throws on\n * `applyBatch` and `history.apply` / `applyOps`, which exist to record. */\n untracked<T>(fn: () => T): T;\n /** Read-only snapshot of every history entry currently reachable from\n * the present state. Oldest applied first, then redoable entries in\n * the order they'd be re-applied. Each entry id is stable. */\n historyEntries(): readonly { id: string; label: string }[];\n /** Index of the \"current state\". Equals the count of applied entries;\n * `0` means \"nothing applied\" (initial). */\n historyIndex(): number;\n /** Jump to the given history index by calling undo/redo repeatedly.\n * Clamps to [0, total]. Returns true if any movement occurred. */\n jumpToHistoryIndex(index: number): boolean;\n\n // History persistence\n /** Snapshot the undo/redo history in a JSON-serializable form (the\n * engine's `SerializedHistory`). Entries containing any nameless op are\n * dropped (hand-rolled anonymous ops passed to `applyBatch`); kit and\n * consumer-registered ops always carry names. Payload JSON-safety\n * (e.g. typed arrays inside poses) is the caller's concern. Do not call\n * mid-`batch` — the open batch's ops are not yet recorded. */\n serializeHistory(): SerializedHistory;\n /** Replace the undo/redo history from a `serializeHistory()` snapshot.\n * Call on a scene whose node/layer state already matches the snapshot's\n * head state (i.e. right after `loadState` from the paired scene\n * snapshot); node state is NOT mutated. Ops re-registered via\n * `registerOp` before this call round-trip; unknown kinds become no-op\n * placeholders; external ops rebuild via the global op-factory registry\n * and replay against the `setHistoryAdapter` accessor. Restored entries\n * never coalesce with new ones. Notifies once. Do not call mid-`batch`:\n * the stacks are replaced underneath the open batch, whose eventual\n * flush would graft onto (and evict against) the restored stacks. */\n restoreHistory(snapshot: SerializedHistory): void;\n /** Install (or clear with `null`) the accessor for the adapter that\n * RESTORED external ops (recorded via `applyBatch`, rebuilt from a\n * `restoreHistory` snapshot) apply against on undo/redo. Resolved lazily\n * at each apply, so wiring order relative to `restoreHistory` doesn't\n * matter. Live `applyBatch` entries are unaffected (they bind their\n * call-site adapter). If unset when a restored op applies, the op is a\n * debug-warned no-op. */\n setHistoryAdapter(fn: (() => unknown) | null): void;\n\n // Serialization\n /** Snapshot the current scene state to a JSON-serializable shape.\n * History (undo/redo stacks) is NOT captured. Function fields like\n * `ContainerNode.clipFromPose` are translated to string keys via the\n * scene's registry; throws if any function field has no matching key. */\n toJSON(): SerializedScene<TData, TLayer, TPose>;\n /** Replace this scene's entire node + layer state in place from a snapshot\n * produced by `toJSON()`. Unlike `sceneFromJSON`, the existing Scene\n * instance is preserved — holders such as `<SceneCanvas>` keep their\n * reference. History (undo/redo) is cleared, matching `sceneFromJSON`.\n * Bumps `getVersion()` and notifies subscribers exactly once.\n *\n * Throws on an unsupported version or unknown registry/layer ids; on a\n * malformed snapshot the scene is left empty or partially populated (callers should treat a\n * `loadState` throw as fatal and reload). Snapshots from `toJSON()` are\n * always well-formed. */\n loadState(json: SerializedScene<TData, TLayer, TPose>): void;\n\n // Subscription (used by useScene; also for non-React observers)\n subscribe(listener: () => void): () => void;\n /** Monotonically increasing version. Snapshot for `useSyncExternalStore`. */\n getVersion(): number;\n}\n"],"mappings":";AA8EA,IAAaA,KAAY,MAAsBC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { s as RenderLayer, u as Dims, w as RedrawSource, x as Scene, y as RectPose, z as InertiaConfig, V as ViewAnimationOptions, A as PoseComposition, B as PoseAdapter, N as Node$1, F as GestureLifecycleOptions, H as SliceDep, J as ToolsApi, K as Tool, L as Contribution, M as LayerHit, D as DrawCommand, O as ChromeState, Q as VertexColorChannel, U as LayerGroup, X as InsertExtras, Y as SerializedScene, Z as GeometryProjection, _ as ContentHandlerEntry, $ as SvgIngestOptions, a0 as AnyTool, a1 as PanBounds, a2 as Animator, a3 as ColorOverrideRegistry, a4 as UseSceneOptions, a5 as SceneRegistry, a6 as RegisteredOp, a7 as PoseOverrides, a8 as DerivedDep, a9 as ViewportDims, P as PathDrawCommand, aa as ViewApi, ab as Affordance, ac as AffordanceRegion, ad as AffordanceBinding, ae as CommonAffordanceScratch, T as TextDrawCommand, af as DerivePoseFn, ag as BooleansAdapter, ah as CreateOutlinesAdapter, ai as UseAnimatorOptions, aj as AnimationHandle, ak as SampledTrack, al as SceneArrivalHandler } from './builtins-
|
|
2
|
-
export { am as AddLayerSpec, an as AddNodeSpec, ao as AnimateToBoundsOptions, ap as AnimationInfo, aq as AnimationKind, ar as AnimatorEvent, as as AnimatorListener, at as AreaSelectDep, au as BooleanOp, av as BooleanOpResult, aw as ClipboardDep, ax as ClipboardIngestCtx, ay as ColorOverride, az as ColorOverrideFn, aA as ContainerNode, aB as ContributionChrome, aC as CreateOutlinesResult, C as CullRect, aD as CustomPaintContext, aE as DecayLoopConfig, aF as DecayOptions, aG as DerivePathFn, aH as EditAnchorsDep, E as Effect, aI as EventBooking, aJ as EventBookingHandle, aK as EventTrack, aL as FitViewToBoundsOptions, G as GroupDrawCommand, aM as HitTestView, aN as IDENTITY_POSE_COMPOSITION, a as ImageDrawCommand, b as ImageMinification, aO as IngestCtx, aP as IngestionDep, aQ as InsertDep, aR as Interpolate, aS as InterpolatorFactory, aT as Keyframe, aU as LassoSelectDep, aV as LayerCommandCache, aW as LayerDrawFailure, aX as LayerDrawSpan, aY as LayerRecord, aZ as LayoutDep, a_ as LayoutFrame, a$ as LayoutMove, b0 as LeafNode, b1 as LiveAnimation, b2 as LoopFactory, b3 as LoopOptions, b4 as NestedTimeline, b5 as NodeAtPointDep, b6 as OutlinePathSpec, b7 as Overlay, b8 as PathInWorldPose, b9 as PhysicsHandle, ba as PhysicsOptions, bb as PointerContextProvider, bc as PointerContextValue, bd as PointerWorldPos, be as PoseClosure, bf as PoseOverride, f as RenderTarget, bg as ResizePolicy, S as SPRITE_STRIDE, bh as SerializedLayer, bi as SerializedNode, g as ShaderDrawCommand, bj as SnapDep, bk as SpringOptions, i as SpriteSheet, j as SpritesDrawCommand, bl as StaggerBuilder, bm as StaggerDelay, bn as StaggerFactory, bo as StaggerOptions, bp as StaggerPerItem, bq as StaggerSpringPoseOptions, br as StaggerTweenOptions, bs as SvgUnpacker, bt as SystemLayerRecord, bu as SystemLayerSpec, bv as TextEditDep, bw as TextNodeSource, bx as TextOutlineSource, by as TextOutlinesError, bz as TextOutlinesFailure, bA as TextPaintPath, bB as TextToPathOptions, bC as TimelineClock, bD as TimelineEvent, bE as TimelineHandle, bF as TimelineOptions, bG as TimelineTrack, bH as ToolDef, bI as Track, bJ as TweenLoopOptions, bK as TweenOptions, bL as UseToolsOptions, bM as UserLayerRecord, bN as VIEW_ANIMATION_KEY, bO as ViewAnimationApi, bP as ViewChannel, bQ as applyBooleanOp, bR as applyCreateOutlines, bS as asNodeId, m as blur, bT as composeWorldPose, bU as createPointerStore, o as cullDrawCommands, bV as depsUnchanged, bW as drawLayers, bX as drawOneLayer, bY as fitViewToBounds, p as frameRect, bZ as isLayerPainted, b_ as isLayerVisible, b$ as loadTextOutlines, c0 as pathInPoseFrame, c1 as pathInWorld, c2 as rebaseLocalPose, c3 as registerContentHandler, r as registerEffect, c4 as resolveStrokeWidth, c5 as subscribeToSources, c6 as textToPath, c7 as textToPathsByPaint, c8 as useDecayLoop, c9 as usePointerContext, ca as usePointerPosition, cb as useTools, cc as useViewAnimation, cd as useViewAnimationOn, v as viewToMat3, q as vignette, ce as worldEditToStorage } from './builtins-
|
|
1
|
+
import { s as RenderLayer, u as Dims, w as RedrawSource, x as Scene, y as RectPose, z as InertiaConfig, V as ViewAnimationOptions, A as PoseComposition, B as PoseAdapter, N as Node$1, F as GestureLifecycleOptions, H as SliceDep, J as ToolsApi, K as Tool, L as Contribution, M as LayerHit, D as DrawCommand, O as ChromeState, Q as VertexColorChannel, U as LayerGroup, X as InsertExtras, Y as SerializedScene, Z as GeometryProjection, _ as ContentHandlerEntry, $ as SvgIngestOptions, a0 as AnyTool, a1 as PanBounds, a2 as Animator, a3 as ColorOverrideRegistry, a4 as UseSceneOptions, a5 as SceneRegistry, a6 as RegisteredOp, a7 as PoseOverrides, a8 as DerivedDep, a9 as ViewportDims, P as PathDrawCommand, aa as ViewApi, ab as Affordance, ac as AffordanceRegion, ad as AffordanceBinding, ae as CommonAffordanceScratch, T as TextDrawCommand, af as DerivePoseFn, ag as BooleansAdapter, ah as CreateOutlinesAdapter, ai as UseAnimatorOptions, aj as AnimationHandle, ak as SampledTrack, al as SceneArrivalHandler } from './builtins-BW32dOXy.js';
|
|
2
|
+
export { am as AddLayerSpec, an as AddNodeSpec, ao as AnimateToBoundsOptions, ap as AnimationInfo, aq as AnimationKind, ar as AnimatorEvent, as as AnimatorListener, at as AreaSelectDep, au as BooleanOp, av as BooleanOpResult, aw as ClipboardDep, ax as ClipboardIngestCtx, ay as ColorOverride, az as ColorOverrideFn, aA as ContainerNode, aB as ContributionChrome, aC as CreateOutlinesResult, C as CullRect, aD as CustomPaintContext, aE as DecayLoopConfig, aF as DecayOptions, aG as DerivePathFn, aH as EditAnchorsDep, E as Effect, aI as EventBooking, aJ as EventBookingHandle, aK as EventTrack, aL as FitViewToBoundsOptions, G as GroupDrawCommand, aM as HitTestView, aN as IDENTITY_POSE_COMPOSITION, a as ImageDrawCommand, b as ImageMinification, aO as IngestCtx, aP as IngestionDep, aQ as InsertDep, aR as Interpolate, aS as InterpolatorFactory, aT as Keyframe, aU as LassoSelectDep, aV as LayerCommandCache, aW as LayerDrawFailure, aX as LayerDrawSpan, aY as LayerRecord, aZ as LayoutDep, a_ as LayoutFrame, a$ as LayoutMove, b0 as LeafNode, b1 as LiveAnimation, b2 as LoopFactory, b3 as LoopOptions, b4 as NestedTimeline, b5 as NodeAtPointDep, b6 as OutlinePathSpec, b7 as Overlay, b8 as PathInWorldPose, b9 as PhysicsHandle, ba as PhysicsOptions, bb as PointerContextProvider, bc as PointerContextValue, bd as PointerWorldPos, be as PoseClosure, bf as PoseOverride, f as RenderTarget, bg as ResizePolicy, S as SPRITE_STRIDE, bh as SerializedLayer, bi as SerializedNode, g as ShaderDrawCommand, bj as SnapDep, bk as SpringOptions, i as SpriteSheet, j as SpritesDrawCommand, bl as StaggerBuilder, bm as StaggerDelay, bn as StaggerFactory, bo as StaggerOptions, bp as StaggerPerItem, bq as StaggerSpringPoseOptions, br as StaggerTweenOptions, bs as SvgUnpacker, bt as SystemLayerRecord, bu as SystemLayerSpec, bv as TextEditDep, bw as TextNodeSource, bx as TextOutlineSource, by as TextOutlinesError, bz as TextOutlinesFailure, bA as TextPaintPath, bB as TextToPathOptions, bC as TimelineClock, bD as TimelineEvent, bE as TimelineHandle, bF as TimelineOptions, bG as TimelineTrack, bH as ToolDef, bI as Track, bJ as TweenLoopOptions, bK as TweenOptions, bL as UseToolsOptions, bM as UserLayerRecord, bN as VIEW_ANIMATION_KEY, bO as ViewAnimationApi, bP as ViewChannel, bQ as applyBooleanOp, bR as applyCreateOutlines, bS as asNodeId, m as blur, bT as composeWorldPose, bU as createPointerStore, o as cullDrawCommands, bV as depsUnchanged, bW as drawLayers, bX as drawOneLayer, bY as fitViewToBounds, p as frameRect, bZ as isLayerPainted, b_ as isLayerVisible, b$ as loadTextOutlines, c0 as pathInPoseFrame, c1 as pathInWorld, c2 as rebaseLocalPose, c3 as registerContentHandler, r as registerEffect, c4 as resolveStrokeWidth, c5 as subscribeToSources, c6 as textToPath, c7 as textToPathsByPaint, c8 as useDecayLoop, c9 as usePointerContext, ca as usePointerPosition, cb as useTools, cc as useViewAnimation, cd as useViewAnimationOn, v as viewToMat3, q as vignette, ce as worldEditToStorage } from './builtins-BW32dOXy.js';
|
|
3
3
|
import { View as View$1, NodeId, PointerSessionCancelReason, DeviceProfile, Bounds, Action, ActionDeps, AffordanceHit, SelectionMode, SelectionExtendKey, SelectionApi, DepSchema, GestureBinding, ToolKeybinding, HotkeyTrigger, ModifierState, RuleCtx, Rule, DebugSink, HitShape, HandleKind, ViewportGestureKind, IngestItem, OngoingOverlay, DepName, ActionsProp, OverlayRole, ResizeAnchor } from '@weasel-js/routing';
|
|
4
4
|
export { ALWAYS, Action, ActionDeps, ActionDisabledReason, ActionDispatch, ActionEnabledResult, ActionEntry, ActionItem, ActionPresentation, ActionVariant, ActionsProp, ActionsRegistry, AffordanceHit, BindingOpts, BindingScope, BindingSource, BoundGesture, Bounds, BuildRuleCtxArgs, COARSE_TARGET_SCALE, ContributionDeps, ContributionRouting, DEFAULT_ALLOWED_CAPABILITIES, DEFAULT_DEVICE_PROFILE, DRAG_THRESHOLD_PX, DebugSink, DepName, DepRegistry, DepSchema, DetectedDeviceFacts, DeviceProfile, DispatchRecord, DispatchRecordInput, Dispatcher, DispatcherContext, DragSample, DroppedCandidate, Eligibility, EligibilityState, GestureBinding, HandleKind, HitShape, HotkeyTrigger, ImmediateInvoker, IngestItem, InputEvent, InvocationCtx, Invoker, KeyBinding, LONG_PRESS_MS, LongPressOptions, LongPressState, LongPressStore, MatchResult, ModeShortcutHandlers, ModifierState, NEVER, NodeId, OngoingHandle, OngoingInvoker, OngoingOverlay, OverlayPosition, OverlayRole, PendingLongPress, PlacedBy, Point2, PointerSession, PointerSessionCallbacks, PointerSessionCancelReason, PointerSessionOptions, RankedCandidate, RecordCandidate, ResizeAnchor, ResolveAllOptions, ResolveOnlyResult, ResolvedCandidate, Rule, RuleCtx, ScopedBinding, ScratchKey, ScratchStore, SelectionApi, SelectionExtendKey, SelectionMode, Selector, SpecificityPart, ToolCtx, ToolKeybinding, ToolLifecycleCtx, ToolModifiers, ToolPresentation, ToolSlot, UiOngoingControl, Point2 as Vec2, View, ViewportGestureKind, WalkStep, actionBindings, actionItems, buildDepsFromRequires, buildRuleCtx, clientToCanvas, createDepRegistry, createDispatcher, createLongPressStore, defineTool, deleteScratch, describeRule, evaluate, evaluateEnabled, getScratch, isEditableTarget, liveScope, matchesKeyBinding, modeShortcutSpec, modeShortcuts, openPointerSession, pastDragThreshold, resolveDeviceProfile, resolveParams, scopeBindings, scratchKey, setScratch, specificity } from '@weasel-js/routing';
|
|
5
5
|
import { a as ParallaxOpts, L as LayoutStrategy, M as MoveAdapter, A as AreaSelectAdapter, e as LayerEnumerableAdapter, f as LassoHitMode, g as LassoSelectAdapter, h as ResizeAdapter, i as RotateAdapter, I as InsertAdapter, j as ClipboardSnapshot, c as LayoutDropTargetMode, d as ReflowTransition, O as OrderedAdapter, P as PlaneMap, b as SceneAdapter } from './types-A37hYkeR.js';
|
|
@@ -24,7 +24,7 @@ export * from '@weasel-js/history';
|
|
|
24
24
|
export { Op } from '@weasel-js/history';
|
|
25
25
|
import { Path, PolygonPath, PlacementAlign, Rect, SpringPresetName, EasingSpec } from '@weasel-js/geom';
|
|
26
26
|
export { BezierEasing, DEFAULT_FLATTEN_TOLERANCE, EASINGS, EasingFn, EasingName, EasingSpec, PATH_C, PATH_CMD_LENGTHS, PATH_L, PATH_M, PATH_Q, PATH_Z, Path, PathBuilder, PathFillRule, PathStation, PointAlongPathOptions, PointInPathOptions, PolygonPath, Rect, RectPath, SPRING_PRESETS, SpringPreset, SpringPresetName, boundsOfPath, composePath, cubicBezierEasing, decomposePath, easeIn, easeInBack, easeInBounce, easeInCirc, easeInCubic, easeInElastic, easeInExpo, easeInOut, easeInOutBack, easeInOutBounce, easeInOutCirc, easeInOutCubic, easeInOutElastic, easeInOutExpo, easeInOutQuad, easeInOutQuart, easeInOutQuint, easeInOutSine, easeInQuad, easeInQuart, easeInQuint, easeInSine, easeOut, easeOutBack, easeOutBounce, easeOutCirc, easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, ellipsePath, flattenCubic, flattenCubicWithArcLen, flattenQuadratic, flattenQuadraticWithArcLen, linePath, linear, pathCommandCoordCount, pathContainsPoint, pathContainsPolygon, pathContainsRect, pathDistanceToPoint, pathFromD, pathIntersectsPolygon, pathIntersectsRect, pathSignedArea, pointAlongPath, pointInPath, polygonFromPoints, polylineFromPoints, rectPath, regularPolygonPath, resolveEasing, reversePath, scalePathToBounds, splitSubpaths, starPath, transformPath, translatePath, unionBoundsPath } from '@weasel-js/geom';
|
|
27
|
-
import { Display, UnitEntry, UnitSystem, Unit } from '@weasel-js/quantity';
|
|
27
|
+
import { Display, UnitEntry, InfinityText, UnitSystem, Unit } from '@weasel-js/quantity';
|
|
28
28
|
export { ANGLE_RADIANS, IMPERIAL_INCHES, METRIC_MM, PIXELS, Unit, UnitEntry, UnitScale, UnitSystem, UnitValue, formatUnit, resolveUnit, unitScale } from '@weasel-js/quantity';
|
|
29
29
|
import { U as UseMoveOptions, O as OriginProjection } from './originProjection-DEklpDc_.js';
|
|
30
30
|
export { A as AUTO_ORIGIN_PROJECTION, R as RECT_ORIGIN_PROJECTION } from './originProjection-DEklpDc_.js';
|
|
@@ -2653,10 +2653,11 @@ interface ToolPrefBase<K extends string, Value> {
|
|
|
2653
2653
|
* on one row labeled with the `pair` string (e.g. `'Position'` for
|
|
2654
2654
|
* `pose.x` / `pose.y`). Purely presentational. */
|
|
2655
2655
|
pair?: string;
|
|
2656
|
-
/**
|
|
2657
|
-
* controls on one line,
|
|
2658
|
-
*
|
|
2659
|
-
|
|
2656
|
+
/** Shorter forms of `name`, longest first. A surface short of room for
|
|
2657
|
+
* `name` — a strip of controls on one line, a segment in a `pair`ed row —
|
|
2658
|
+
* shows the longest of them that fits. `name` stays the accessible name, so
|
|
2659
|
+
* these abbreviate without costing anything. */
|
|
2660
|
+
short?: readonly string[];
|
|
2660
2661
|
}
|
|
2661
2662
|
/** How a schema-driven UI should present a number pref. */
|
|
2662
2663
|
type ToolPrefNumberControl = 'input' | 'slider';
|
|
@@ -2703,6 +2704,10 @@ interface ToolPrefNumber extends ToolPrefBase<'number', number> {
|
|
|
2703
2704
|
* reads `2.00M`. Presentation only: the stored value stays a number. */
|
|
2704
2705
|
display?: Display;
|
|
2705
2706
|
unit?: ToolPrefNumberUnit;
|
|
2707
|
+
/** Which end of a slider stands for infinity: the value there is ±Infinity. */
|
|
2708
|
+
endless?: 'min' | 'max' | 'both';
|
|
2709
|
+
/** The word infinity shows as — `'never'`, `'uncapped'`. Default `∞`. */
|
|
2710
|
+
infinity?: InfinityText;
|
|
2706
2711
|
}
|
|
2707
2712
|
/**
|
|
2708
2713
|
* Stored-value bridge for a boolean leaf whose field is not a boolean — the
|
|
@@ -2756,11 +2761,12 @@ interface ToolPrefEnum<T extends string = string> extends ToolPrefBase<'enum', T
|
|
|
2756
2761
|
* removes the field.
|
|
2757
2762
|
*/
|
|
2758
2763
|
clearable?: boolean;
|
|
2759
|
-
/** `short`
|
|
2760
|
-
*
|
|
2761
|
-
* (weasel-ui resolves it against
|
|
2762
|
-
*
|
|
2763
|
-
*
|
|
2764
|
+
/** `short` holds shorter forms of `label`, longest first, for a segmented
|
|
2765
|
+
* control too narrow for the full one — down to a capital or two. `icon`
|
|
2766
|
+
* names a glyph in the host UI's set (weasel-ui resolves it against
|
|
2767
|
+
* `ICON_PATHS`), drawn in place of any text form where it resolves. It is
|
|
2768
|
+
* a plain string because core ships no icon set and cannot depend on one.
|
|
2769
|
+
* The full `label` stays the accessible name, so
|
|
2764
2770
|
* neither the abbreviation nor the glyph becomes the only thing naming
|
|
2765
2771
|
* the option.
|
|
2766
2772
|
*
|
|
@@ -2771,7 +2777,7 @@ interface ToolPrefEnum<T extends string = string> extends ToolPrefBase<'enum', T
|
|
|
2771
2777
|
options: readonly {
|
|
2772
2778
|
value: T;
|
|
2773
2779
|
label: string;
|
|
2774
|
-
short?: string;
|
|
2780
|
+
short?: readonly string[];
|
|
2775
2781
|
icon?: string;
|
|
2776
2782
|
disabled?: boolean;
|
|
2777
2783
|
}[];
|
|
@@ -3218,7 +3224,7 @@ declare namespace useTextTool {
|
|
|
3218
3224
|
options: {
|
|
3219
3225
|
value: string;
|
|
3220
3226
|
label: string;
|
|
3221
|
-
short: string;
|
|
3227
|
+
short: string[];
|
|
3222
3228
|
}[];
|
|
3223
3229
|
};
|
|
3224
3230
|
fontVariantCaps: {
|
|
@@ -3230,7 +3236,7 @@ declare namespace useTextTool {
|
|
|
3230
3236
|
options: {
|
|
3231
3237
|
value: string;
|
|
3232
3238
|
label: string;
|
|
3233
|
-
short: string;
|
|
3239
|
+
short: string[];
|
|
3234
3240
|
}[];
|
|
3235
3241
|
};
|
|
3236
3242
|
fontFamily: {
|
|
@@ -3256,7 +3262,7 @@ declare namespace useTextTool {
|
|
|
3256
3262
|
letterSpacing: {
|
|
3257
3263
|
kind: "number";
|
|
3258
3264
|
name: string;
|
|
3259
|
-
short: string;
|
|
3265
|
+
short: string[];
|
|
3260
3266
|
description: string;
|
|
3261
3267
|
step: number;
|
|
3262
3268
|
default: number;
|
|
@@ -3264,7 +3270,7 @@ declare namespace useTextTool {
|
|
|
3264
3270
|
baselineShift: {
|
|
3265
3271
|
kind: "number";
|
|
3266
3272
|
name: string;
|
|
3267
|
-
short: string;
|
|
3273
|
+
short: string[];
|
|
3268
3274
|
description: string;
|
|
3269
3275
|
step: number;
|
|
3270
3276
|
unit: {
|
|
@@ -7456,6 +7462,10 @@ declare function useScene<TData, TLayer extends string, TPose = RectPose>(option
|
|
|
7456
7462
|
* the only thing this module needs from the scene, and the reason it doesn't
|
|
7457
7463
|
* import one. `onInvalidate` lets the scene invalidate derived nodes from the
|
|
7458
7464
|
* same chokepoint.
|
|
7465
|
+
*
|
|
7466
|
+
* What `read` answers is a blits mix folded per node. The entries a writer
|
|
7467
|
+
* `set`s are one voice on it, and the nodes holding an entry are the mix's
|
|
7468
|
+
* reach: a node outside it is never probed.
|
|
7459
7469
|
*/
|
|
7460
7470
|
declare function createPoseOverrides<TPose>(getNode: (id: NodeId) => {
|
|
7461
7471
|
data?: unknown;
|
|
@@ -7491,7 +7501,7 @@ interface PosedNode<TPose> {
|
|
|
7491
7501
|
/** What resolving a pose needs: the overrides, and enough of the scene to
|
|
7492
7502
|
* reach a node's dependencies. A `Scene` satisfies it. */
|
|
7493
7503
|
interface PoseSource<TPose> {
|
|
7494
|
-
readonly overrides: Pick<PoseOverrides<TPose>, '
|
|
7504
|
+
readonly overrides: Pick<PoseOverrides<TPose>, 'read'>;
|
|
7495
7505
|
get(id: NodeId): PosedNode<TPose> | undefined;
|
|
7496
7506
|
childrenOf(id: NodeId): readonly NodeId[];
|
|
7497
7507
|
}
|
|
@@ -229,6 +229,7 @@ var ae = {
|
|
|
229
229
|
widthCss: this.widthCss,
|
|
230
230
|
heightCss: this.heightCss,
|
|
231
231
|
clipDepth: 0,
|
|
232
|
+
clipStack: [],
|
|
232
233
|
flattenTolerance: this.flattenTolerance,
|
|
233
234
|
textOutlineMinScreenSize: this.textOutlineMinScreenSize,
|
|
234
235
|
viewMatrix: t,
|
|
@@ -240,7 +241,9 @@ var ae = {
|
|
|
240
241
|
restoreTargetRect: () => this.applyTarget(),
|
|
241
242
|
paintResources: this.paintResources,
|
|
242
243
|
stats: i
|
|
243
|
-
}
|
|
244
|
+
};
|
|
245
|
+
this.drawBatch.clip = null;
|
|
246
|
+
let c = n?.spans ?? [], l = 0;
|
|
244
247
|
for (let t = 0; t < c.length; t++) {
|
|
245
248
|
let n = c[t];
|
|
246
249
|
for (; l < n.start; l++) z(s, e[l]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WeaselRenderer.js","names":["customUniformNames","CUSTOM_KIT_UNIFORMS","extractUniformNames","vertSrc","fragSrc","compileCustomProgram","src","CUSTOM_VERT_SRC","program","ShaderProgram","gl","extractUniformArrays","CUSTOM_ATTRIBUTES","now","GLYPH_PAINT_SOURCES","GLYPH_PATTERN_FRAG_SRC","GLYPH_PATTERN_UNIFORMS","GLYPH_GRAD_FRAG_SRC","GLYPH_GRAD_UNIFORMS","GLYPH_TEXTURE_FRAG_SRC","GLYPH_TEXTURE_UNIFORMS","WeaselRenderer","GroupState","createPaintResources","opts","EffectTargets","PaintScratch","attrs","DEFAULT_BAKE_BUDGET","OUTLINE_MIN_SCREEN_PX","VERT_SRC","FRAG_SRC","PATH_FILL_UNIFORMS","PATH_FILL_ATTRIBUTES","VCOLOR_VERT_SRC","VCOLOR_FRAG_SRC","PATH_FILL_VCOLOR_ATTRIBUTES","IMAGE_VERT_SRC","IMAGE_FRAG_SRC","IMAGE_FILL_UNIFORMS","IMAGE_FILL_ATTRIBUTES","BATCH_VERT_SRC","BATCH_FRAG_SRC","BATCH_FILL_UNIFORMS","BATCH_FILL_ATTRIBUTES","GRAD_VERT_SRC","GRAD_FRAG_SRC","GRAD_FILL_UNIFORMS","GRAD_FILL_ATTRIBUTES","PATTERN_VERT_SRC","PATTERN_FRAG_SRC","PATTERN_FILL_UNIFORMS","PATTERN_FILL_ATTRIBUTES","aPos","GLMeshCache","GLTextureCache","GLImageCache","GradientRampAtlas","DrawBatch","createWhiteTexture","QUAD_VERTICES","QUAD_INDICES","getProgramSource","handle","previous","id","compiled","e","existing","kind","frag","uniforms","GLYPH_PAINT_VERT_SRC","GLYPH_PAINT_ATTRIBUTES","w","h","x","y","target","markAllFontsNotUploaded","prog","t0","counters","spanStats","resetBakeBudget","ctx","spans","next","si","span","commands","drawsBefore","spanT0","flushBatch","tolerance","dims","tex"],"sources":["../../src/renderer/WeaselRenderer.ts"],"sourcesContent":["import { createPaintResources, type OwnedPaintResources } from './paintResources';\nimport { ShaderProgram } from './shaders/ShaderProgram';\nimport {\n VERT_SRC,\n FRAG_SRC,\n VCOLOR_VERT_SRC,\n VCOLOR_FRAG_SRC,\n PATH_FILL_UNIFORMS,\n PATH_FILL_ATTRIBUTES,\n PATH_FILL_VCOLOR_ATTRIBUTES,\n} from './shaders/pathFill';\nimport {\n markAllFontsNotUploaded,\n resetBakeBudget,\n DEFAULT_BAKE_BUDGET,\n} from '@weasel-js/font';\nimport {\n IMAGE_VERT_SRC,\n IMAGE_FRAG_SRC,\n IMAGE_FILL_UNIFORMS,\n IMAGE_FILL_ATTRIBUTES,\n} from './shaders/imageFill';\nimport {\n BATCH_VERT_SRC,\n BATCH_FRAG_SRC,\n BATCH_FILL_UNIFORMS,\n BATCH_FILL_ATTRIBUTES,\n} from './shaders/batchFill';\nimport {\n GRAD_VERT_SRC,\n GRAD_FRAG_SRC,\n GRAD_FILL_UNIFORMS,\n GRAD_FILL_ATTRIBUTES,\n} from './shaders/gradFill';\nimport {\n PATTERN_VERT_SRC,\n PATTERN_FRAG_SRC,\n PATTERN_FILL_UNIFORMS,\n PATTERN_FILL_ATTRIBUTES,\n} from './shaders/patternFill';\nimport {\n GLYPH_PAINT_VERT_SRC,\n GLYPH_PATTERN_FRAG_SRC,\n GLYPH_GRAD_FRAG_SRC,\n GLYPH_TEXTURE_FRAG_SRC,\n GLYPH_PATTERN_UNIFORMS,\n GLYPH_GRAD_UNIFORMS,\n GLYPH_TEXTURE_UNIFORMS,\n GLYPH_PAINT_ATTRIBUTES,\n} from './shaders/glyphPaint';\nimport { PaintScratch } from './paintScratch';\nimport { GLMeshCache } from './cache/GLMeshCache';\nimport { GLTextureCache } from './cache/GLTextureCache';\nimport { GLImageCache, type ImageMinification } from './cache/GLImageCache';\nimport { GradientRampAtlas } from './cache/GradientRampAtlas';\nimport { GroupState } from './state/GroupState';\nimport type { DrawCommand } from './DrawCommand';\nimport type { GlMat3 } from './math/mat3';\nimport {\n dispatch, flushBatch, OUTLINE_MIN_SCREEN_PX, type DrawContext, type GlyphPaintKind,\n} from './draw';\nimport { DrawBatch } from './drawBatch';\nimport {\n CUSTOM_VERT_SRC, CUSTOM_ATTRIBUTES, CUSTOM_KIT_UNIFORMS,\n QUAD_VERTICES, QUAD_INDICES,\n} from './shaders/customPrelude';\nimport { getProgramSource, type ProgramSource, type ShaderProgramHandle } from './shaders/registerProgram';\nimport { EffectTargets } from './effects/EffectTargets';\nimport { extractUniformArrays, extractUniformNames } from './shaders/extractUniformNames';\n\n/** Every uniform a custom program may be written through. Both stages, not\n * just the fragment one: a name never looked up is written through `null`,\n * which GL accepts silently, so the uniform keeps its zero default. */\nfunction customUniformNames(vertSrc: string, fragSrc: string): string[] {\n return [...CUSTOM_KIT_UNIFORMS, ...extractUniformNames(vertSrc), ...extractUniformNames(fragSrc)];\n}\n\n/** Compile a registered program's source against `gl`, with every uniform\n * location and array base it may be written through. */\nfunction compileCustomProgram(gl: WebGL2RenderingContext, src: ProgramSource): ShaderProgram {\n const vertSrc = src.vert === '' ? CUSTOM_VERT_SRC : src.vert;\n const program = new ShaderProgram(gl, vertSrc, src.frag);\n program.lookupUniforms(customUniformNames(vertSrc, src.frag));\n program.lookupUniformArrays(new Map([...extractUniformArrays(vertSrc), ...extractUniformArrays(src.frag)]));\n program.lookupAttributes(CUSTOM_ATTRIBUTES);\n return program;\n}\n\n/** How to construct a `WeaselRenderer`: the GL context or canvas to draw\n * into, the output size, and the quality knobs that separate screen\n * rendering from print. */\n/** A run of top-level commands, `[start, end)` by index, that\n * {@link WeaselRenderer.lastFrameStats} accounts for separately — one render\n * layer's commands, say. Spans are in order and do not overlap. */\nexport interface RenderSpan {\n id: string;\n start: number;\n end: number;\n}\n\n/** Per-frame options for {@link WeaselRenderer.render}. */\nexport interface RenderOptions {\n spans?: readonly RenderSpan[];\n}\n\n/** What one span cost. A batched run is drawn when something closes it, so\n * its draw call counts toward the span that closed it. */\nexport interface RenderSpanStats {\n id: string;\n drawCalls: number;\n ms: number;\n}\n\n/** What one `render()` cost. See {@link WeaselRenderer.lastFrameStats}. */\nexport interface RenderStats {\n drawCalls: number;\n ms: number;\n spans: RenderSpanStats[];\n}\n\nfunction now(): number {\n return typeof performance !== 'undefined' ? performance.now() : Date.now();\n}\n\n/** Construction options for {@link WeaselRenderer}. Pass `gl` or `canvas`;\n * a context passed in must have a stencil buffer. */\nexport interface WeaselRendererOptions {\n /** A WebGL2 context to draw with. Wins over `canvas`. */\n gl?: WebGL2RenderingContext;\n /** A canvas to open a stencil-backed WebGL2 context on, when `gl` is absent. */\n canvas?: HTMLCanvasElement;\n /** Drawing size in CSS pixels. */\n width: number;\n /** Drawing size in CSS pixels. */\n height: number;\n /** Device pixels per CSS pixel. */\n dpr: number;\n /** MIN_FILTER strategy for image/pattern textures (`GLImageCache`).\n * Default `'linear'` — the existing screen behavior. The headless\n * `renderSceneToPixels` path passes `'mipmap'` for print-quality\n * minification. Explicitly passing `'linear'` is always valid. */\n imageMinification?: ImageMinification;\n /** Flatness tolerance for curve tessellation, in WORLD units (see\n * `TessellateOptions.flattenTolerance`). When set, path fills are\n * tessellated fresh at this tolerance per frame (transient pool) instead\n * of served from the Path-identity mesh cache — the cache key does not\n * include tolerance. Default: unset — the existing cached behavior at\n * `DEFAULT_FLATTEN_TOLERANCE`. The headless path derives this from the\n * requested output scale; screen callers normally leave it unset. */\n flattenTolerance?: number;\n /** Per-render synchronous bake budget for dynamic canvas-SDF glyphs.\n * Default DEFAULT_BAKE_BUDGET (16). The headless renderSceneToPixels\n * path passes Infinity so print never defers a glyph. */\n bakeBudget?: number;\n /** On-screen glyph size, in CSS pixels, at or above which text renders from\n * tessellated font outlines instead of a distance field — see\n * `OUTLINE_MIN_SCREEN_PX`. Only faces registered with\n * `registerFontOutlines` are affected; everything else keeps its SDF tier\n * whatever this says. Pass `Infinity` to disable the tier outright, or 0\n * to use outlines wherever they exist (what the headless path does, since\n * print has no reason to sample a field it could evaluate exactly). */\n textOutlineMinScreenSize?: number;\n}\n\n/** Where a renderer draws inside a buffer it does not own.\n *\n * The rect's SIZE is the renderer's own `width`/`height` — `resize()` owns\n * that, and a second copy here could disagree with the one `DrawContext`\n * reports to screen-space layers. */\nexport interface RenderTarget {\n /** Top-left of this renderer's output within the drawing buffer, in CSS\n * pixels, with the origin at the buffer's top-left. GL's bottom-left origin\n * is handled internally. */\n origin: { x: number; y: number };\n /** Clear colour and stencil within the rect before drawing. Default true.\n * Pass false only when the caller clears the whole buffer itself on behalf\n * of every co-tenant — a frame that clears neither inherits its neighbour's\n * stencil bits, and even-odd fills then fill their holes. */\n clear?: boolean;\n}\n\n/** Each glyph-paint program's fragment stage and the uniforms it looks up. */\nconst GLYPH_PAINT_SOURCES: Record<GlyphPaintKind, readonly [string, readonly string[]]> = {\n pattern: [GLYPH_PATTERN_FRAG_SRC, GLYPH_PATTERN_UNIFORMS],\n gradient: [GLYPH_GRAD_FRAG_SRC, GLYPH_GRAD_UNIFORMS],\n texture: [GLYPH_TEXTURE_FRAG_SRC, GLYPH_TEXTURE_UNIFORMS],\n};\n\n/**\n * The WebGL2 renderer: takes a list of draw commands and paints them.\n *\n * It knows nothing about the scene — commands are the whole interface, which\n * is what lets layers, HUD widgets and overlays all draw through the same\n * pipeline. GPU resources (meshes, textures, gradient ramps) are cached across\n * frames and keyed by identity, so re-issuing the same command is cheap.\n */\nexport class WeaselRenderer {\n private readonly gl: WebGL2RenderingContext;\n private stats: RenderStats = { drawCalls: 0, ms: 0, spans: [] };\n private pathFill: ShaderProgram;\n private pathFillVColor: ShaderProgram;\n private imageFill: ShaderProgram;\n private batchFill: ShaderProgram;\n private gradFill: ShaderProgram;\n private patternFill: ShaderProgram;\n /** Compiled on first use: only atlas text under a texture paint needs one. */\n private glyphPaintPrograms = new Map<GlyphPaintKind, ShaderProgram>();\n private meshCache: GLMeshCache;\n private textureCache: GLTextureCache;\n private imageCache: GLImageCache;\n private gradRamps: GradientRampAtlas;\n private programRegistry = new Map<string, ShaderProgram>();\n /** The source each compiled custom program was built from — or, after a\n * failed recompile, the source that failed, so it is not retried every frame. */\n private programSources = new Map<string, ProgramSource>();\n private quadVbo: WebGLBuffer | null = null;\n private quadIbo: WebGLBuffer | null = null;\n private drawBatch: DrawBatch;\n /** 1x1 white, so a batch flush sampling no image still samples something —\n * see `shaders/batchFill.ts`. */\n private whiteTexture: WebGLTexture | null = null;\n private readonly groupState = new GroupState();\n private widthCss: number;\n private heightCss: number;\n private dpr: number;\n private canvas: HTMLCanvasElement | null = null;\n private target: RenderTarget | null = null;\n /** Offscreen buffers for group effects. Allocates nothing until a group\n * with effects asks, so a canvas without them pays no memory. */\n private readonly effectTargets: EffectTargets;\n private readonly paintScratch: PaintScratch;\n private readonly imageMinification: ImageMinification;\n private flattenTolerance?: number;\n private readonly bakeBudget: number;\n private readonly textOutlineMinScreenSize: number;\n private contextLost = false;\n private boundOnLost = (e: Event) => this.onContextLost(e);\n private boundOnRestored = () => this.onContextRestored();\n /** True after `dispose()`. A disposed renderer ignores further `render()`\n * calls and `registerProgram()` calls. */\n private disposed = false;\n /** What registered paint kinds hang their GPU state on. One per context\n * generation: a restore releases it and opens the next. */\n private paintResources: OwnedPaintResources = createPaintResources();\n\n constructor(opts: WeaselRendererOptions) {\n if (!opts.gl && !opts.canvas) {\n throw new Error('WeaselRenderer requires either gl or canvas');\n }\n const gl = opts.gl ?? opts.canvas!.getContext('webgl2', { stencil: true });\n if (!gl) throw new Error('WeaselRenderer: WebGL2 not available');\n this.gl = gl as WebGL2RenderingContext;\n this.effectTargets = new EffectTargets(this.gl);\n this.paintScratch = new PaintScratch(this.gl);\n\n // Bits 0-7 are load-bearing: bit 0 for even-odd fills and stenciled\n // strokes, bits 1-7 for clip depth (`renderer/draw.ts`). Without a stencil\n // buffer both render wrong rather than not at all, so refuse the context\n // instead of painting a plausible lie. A stub that cannot answer is not a\n // stencil-less context — only an explicit `false` is.\n const attrs = typeof this.gl.getContextAttributes === 'function'\n ? this.gl.getContextAttributes()\n : null;\n if (attrs && attrs.stencil === false) {\n throw new Error(\n 'WeaselRenderer: the supplied WebGL2 context has no stencil buffer. '\n + 'Create it with { stencil: true } — clips and even-odd fills need one.',\n );\n }\n\n this.widthCss = opts.width;\n this.heightCss = opts.height;\n this.dpr = opts.dpr;\n this.imageMinification = opts.imageMinification ?? 'linear';\n this.flattenTolerance = opts.flattenTolerance;\n this.bakeBudget = opts.bakeBudget ?? DEFAULT_BAKE_BUDGET;\n this.textOutlineMinScreenSize = opts.textOutlineMinScreenSize ?? OUTLINE_MIN_SCREEN_PX;\n\n this.canvas = opts.canvas ?? null;\n if (this.canvas) {\n this.canvas.width = Math.round(opts.width * opts.dpr);\n this.canvas.height = Math.round(opts.height * opts.dpr);\n if (this.canvas.style) {\n this.canvas.style.width = `${opts.width}px`;\n this.canvas.style.height = `${opts.height}px`;\n }\n this.canvas.addEventListener('webglcontextlost', this.boundOnLost);\n this.canvas.addEventListener('webglcontextrestored', this.boundOnRestored);\n }\n\n this.applyGlState();\n this.applyTarget();\n\n this.pathFill = new ShaderProgram(this.gl, VERT_SRC, FRAG_SRC);\n this.pathFill.lookupUniforms(PATH_FILL_UNIFORMS);\n this.pathFill.lookupAttributes(PATH_FILL_ATTRIBUTES);\n\n this.pathFillVColor = new ShaderProgram(this.gl, VCOLOR_VERT_SRC, VCOLOR_FRAG_SRC);\n this.pathFillVColor.lookupUniforms(PATH_FILL_UNIFORMS);\n this.pathFillVColor.lookupAttributes(PATH_FILL_VCOLOR_ATTRIBUTES);\n\n this.imageFill = new ShaderProgram(this.gl, IMAGE_VERT_SRC, IMAGE_FRAG_SRC);\n this.imageFill.lookupUniforms(IMAGE_FILL_UNIFORMS);\n this.imageFill.lookupAttributes(IMAGE_FILL_ATTRIBUTES);\n\n this.batchFill = new ShaderProgram(this.gl, BATCH_VERT_SRC, BATCH_FRAG_SRC);\n this.batchFill.lookupUniforms(BATCH_FILL_UNIFORMS);\n this.batchFill.lookupAttributes(BATCH_FILL_ATTRIBUTES);\n\n this.gradFill = new ShaderProgram(this.gl, GRAD_VERT_SRC, GRAD_FRAG_SRC);\n this.gradFill.lookupUniforms(GRAD_FILL_UNIFORMS);\n this.gradFill.lookupAttributes(GRAD_FILL_ATTRIBUTES);\n\n this.patternFill = new ShaderProgram(this.gl, PATTERN_VERT_SRC, PATTERN_FRAG_SRC);\n this.patternFill.lookupUniforms(PATTERN_FILL_UNIFORMS);\n this.patternFill.lookupAttributes(PATTERN_FILL_ATTRIBUTES);\n\n const aPos = this.pathFill.attribute('a_position');\n if (aPos === undefined) throw new Error('WeaselRenderer: a_position not found in path-fill shader');\n this.meshCache = new GLMeshCache(this.gl, aPos);\n this.textureCache = new GLTextureCache(this.gl);\n this.imageCache = new GLImageCache(this.gl, this.imageMinification);\n this.gradRamps = new GradientRampAtlas(this.gl);\n this.uploadQuadGeometry();\n this.drawBatch = new DrawBatch(this.gl, this.batchFill);\n this.whiteTexture = createWhiteTexture(this.gl);\n }\n\n private uploadQuadGeometry(): void {\n const gl = this.gl;\n this.quadVbo = gl.createBuffer();\n this.quadIbo = gl.createBuffer();\n if (!this.quadVbo || !this.quadIbo) {\n throw new Error('WeaselRenderer: failed to create quad geometry buffers');\n }\n gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVbo);\n gl.bufferData(gl.ARRAY_BUFFER, QUAD_VERTICES, gl.STATIC_DRAW);\n gl.bindBuffer(gl.ARRAY_BUFFER, null);\n\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quadIbo);\n gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, QUAD_INDICES, gl.STATIC_DRAW);\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);\n }\n\n /**\n * Compile a consumer-registered shader program against this renderer's GL context.\n *\n * Call once per renderer after the module-level `registerProgram()`.\n * Throws `ShaderCompileError` if compilation fails.\n *\n * In dev mode, calling again with the same handle replaces the compiled program.\n *\n * @experimental\n */\n registerProgram(handle: ShaderProgramHandle): void {\n // A disposed renderer must not compile programs into the cleared registry.\n if (this.disposed) return;\n const src = getProgramSource(handle.id);\n if (!src) {\n throw new Error(\n `WeaselRenderer.registerProgram: program \"${handle.id}\" not found in source registry. ` +\n `Call the module-level registerProgram() first.`,\n );\n }\n this.installProgram(handle.id, src, compileCustomProgram(this.gl, src));\n }\n\n private installProgram(id: string, src: ProgramSource, program: ShaderProgram): void {\n const previous = this.programRegistry.get(id);\n if (previous) this.gl.deleteProgram(previous.handle);\n this.programRegistry.set(id, program);\n this.programSources.set(id, src);\n }\n\n /** Recompile every program whose module-level source was re-registered\n * since it was compiled. A source that fails keeps the old program drawing. */\n private refreshPrograms(): void {\n for (const [id, compiled] of this.programSources) {\n const src = getProgramSource(id);\n if (!src || src === compiled) continue;\n if (src.vert === compiled.vert && src.frag === compiled.frag) {\n this.programSources.set(id, src);\n continue;\n }\n try {\n this.installProgram(id, src, compileCustomProgram(this.gl, src));\n } catch (e) {\n this.programSources.set(id, src);\n console.error(`weasel: failed to recompile re-registered program \"${id}\"; still drawing the previous one:`, e);\n }\n }\n }\n\n /**\n * The compiled program for `id`, compiling it against this context on first\n * use. Unlike {@link registerProgram} this neither throws on a missing\n * source nor on a compile failure: a paint kind asking for a program it\n * never registered must decline the paint, not take down the frame.\n */\n private ensureProgram(id: string): ShaderProgram | null {\n const existing = this.programRegistry.get(id);\n if (existing) return existing;\n if (this.disposed || !getProgramSource(id)) return null;\n try {\n this.registerProgram({ id });\n } catch (e) {\n console.error(`weasel: failed to compile paint program \"${id}\":`, e);\n return null;\n }\n return this.programRegistry.get(id) ?? null;\n }\n\n private glyphPaintProgram(kind: GlyphPaintKind): ShaderProgram {\n const existing = this.glyphPaintPrograms.get(kind);\n if (existing) return existing;\n const [frag, uniforms] = GLYPH_PAINT_SOURCES[kind];\n const program = new ShaderProgram(this.gl, GLYPH_PAINT_VERT_SRC, frag);\n program.lookupUniforms(uniforms);\n program.lookupAttributes(GLYPH_PAINT_ATTRIBUTES);\n this.glyphPaintPrograms.set(kind, program);\n return program;\n }\n\n /** The GL state every frame assumes. Applied per `render()` rather than once\n * at construction because a co-tenant sharing this context moves all of it\n * between our frames. */\n private applyGlState(): void {\n const gl = this.gl;\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n gl.disable(gl.DEPTH_TEST);\n gl.disable(gl.CULL_FACE);\n gl.clearColor(0, 0, 0, 0);\n }\n\n /** Viewport and scissor for this frame. Re-applied inside `render()` because\n * a co-tenant on the same context moves both between our frames. */\n private applyTarget(): void {\n const gl = this.gl;\n const w = Math.round(this.widthCss * this.dpr);\n const h = Math.round(this.heightCss * this.dpr);\n if (!this.target) {\n gl.disable(gl.SCISSOR_TEST);\n gl.viewport(0, 0, w, h);\n return;\n }\n const x = Math.round(this.target.origin.x * this.dpr);\n const y = gl.drawingBufferHeight - Math.round(this.target.origin.y * this.dpr) - h;\n gl.viewport(x, y, w, h);\n gl.scissor(x, y, w, h);\n gl.enable(gl.SCISSOR_TEST);\n }\n\n /** Confine this renderer to a rect of its drawing buffer, or pass null to\n * give it the whole buffer back. Takes effect from the next `render()`. */\n setTarget(target: RenderTarget | null): void {\n this.target = target;\n this.applyTarget();\n }\n\n getTarget(): RenderTarget | null {\n return this.target;\n }\n\n isContextLost(): boolean {\n return this.contextLost;\n }\n\n private onContextLost(e: Event): void {\n e.preventDefault();\n this.contextLost = true;\n }\n\n private onContextRestored(): void {\n this.contextLost = false;\n this.applyGlState();\n this.applyTarget();\n // Every framebuffer, texture and renderbuffer the pool held died with the\n // context. `releaseAll` deletes handles that are already gone, which is a\n // no-op, and drops the bookkeeping that would otherwise hand a dead FBO to\n // the next group with effects.\n this.effectTargets.releaseAll();\n this.paintScratch.release();\n this.paintResources.release();\n this.paintResources = createPaintResources();\n this.pathFill = new ShaderProgram(this.gl, VERT_SRC, FRAG_SRC);\n this.pathFill.lookupUniforms(PATH_FILL_UNIFORMS);\n this.pathFill.lookupAttributes(PATH_FILL_ATTRIBUTES);\n this.pathFillVColor = new ShaderProgram(this.gl, VCOLOR_VERT_SRC, VCOLOR_FRAG_SRC);\n this.pathFillVColor.lookupUniforms(PATH_FILL_UNIFORMS);\n this.pathFillVColor.lookupAttributes(PATH_FILL_VCOLOR_ATTRIBUTES); this.imageFill = new ShaderProgram(this.gl, IMAGE_VERT_SRC, IMAGE_FRAG_SRC);\n this.imageFill.lookupUniforms(IMAGE_FILL_UNIFORMS);\n this.imageFill.lookupAttributes(IMAGE_FILL_ATTRIBUTES);\n\n this.batchFill = new ShaderProgram(this.gl, BATCH_VERT_SRC, BATCH_FRAG_SRC);\n this.batchFill.lookupUniforms(BATCH_FILL_UNIFORMS);\n this.batchFill.lookupAttributes(BATCH_FILL_ATTRIBUTES);\n this.gradFill = new ShaderProgram(this.gl, GRAD_VERT_SRC, GRAD_FRAG_SRC);\n this.gradFill.lookupUniforms(GRAD_FILL_UNIFORMS);\n this.gradFill.lookupAttributes(GRAD_FILL_ATTRIBUTES);\n\n this.patternFill = new ShaderProgram(this.gl, PATTERN_VERT_SRC, PATTERN_FRAG_SRC);\n this.patternFill.lookupUniforms(PATTERN_FILL_UNIFORMS);\n this.patternFill.lookupAttributes(PATTERN_FILL_ATTRIBUTES);\n this.glyphPaintPrograms.clear();\n const aPos = this.pathFill.attribute('a_position');\n if (aPos === undefined) throw new Error('a_position missing after restore');\n this.meshCache = new GLMeshCache(this.gl, aPos);\n this.textureCache = new GLTextureCache(this.gl);\n this.imageCache.abandon();\n this.imageCache = new GLImageCache(this.gl, this.imageMinification);\n this.gradRamps = new GradientRampAtlas(this.gl);\n markAllFontsNotUploaded();\n\n this.uploadQuadGeometry();\n this.drawBatch = new DrawBatch(this.gl, this.batchFill);\n this.whiteTexture = createWhiteTexture(this.gl);\n for (const id of this.programRegistry.keys()) {\n const src = getProgramSource(id);\n if (!src) continue;\n try {\n this.programRegistry.set(id, compileCustomProgram(this.gl, src));\n this.programSources.set(id, src);\n } catch (e) {\n console.error(`weasel: failed to recompile program \"${id}\" after context restore:`, e);\n }\n }\n }\n\n /** Free every GL resource this renderer created and detach context-loss\n * listeners when a canvas was supplied. Idempotent.\n *\n * That covers the built-in and consumer-registered programs (their shaders\n * go with them), the shared quad geometry, the batch, the effect targets,\n * and every cache: atlas and gradient-ramp textures, bitmap and pattern\n * textures, and meshes — transient, persistent, and those whose Path was\n * already collected. The context itself is left alone, so a caller-owned\n * context can host renderer after renderer without accumulating anything.\n *\n * A disposed renderer ignores further `render()` and `registerProgram()`\n * calls. */\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n const gl = this.gl;\n if (this.canvas) {\n this.canvas.removeEventListener('webglcontextlost', this.boundOnLost);\n this.canvas.removeEventListener('webglcontextrestored', this.boundOnRestored);\n }\n this.meshCache.dispose();\n this.paintResources.release();\n for (const prog of [\n this.pathFill, this.pathFillVColor, this.imageFill, this.batchFill, this.gradFill, this.patternFill,\n ...this.glyphPaintPrograms.values(),\n ]) {\n gl.deleteProgram(prog.handle);\n }\n this.glyphPaintPrograms.clear();\n for (const prog of this.programRegistry.values()) {\n gl.deleteProgram(prog.handle);\n }\n this.programRegistry.clear();\n this.programSources.clear();\n this.textureCache.free();\n this.imageCache.dispose();\n this.gradRamps.free();\n if (this.quadVbo) gl.deleteBuffer(this.quadVbo);\n if (this.quadIbo) gl.deleteBuffer(this.quadIbo);\n this.drawBatch.dispose();\n if (this.whiteTexture) gl.deleteTexture(this.whiteTexture);\n this.whiteTexture = null;\n this.effectTargets.releaseAll();\n this.paintScratch.release();\n }\n\n /**\n * Draw one frame.\n *\n * `viewMatrix` is the frame's world→screen transform. It is only consulted\n * by `units: 'world'` gradients, which fall back to screen space without\n * it — every other command carries its own transform in the stream, so\n * callers with no view concept can keep calling `render(commands)`.\n */\n render(commands: DrawCommand[], viewMatrix?: GlMat3, opts?: RenderOptions): void {\n if (this.contextLost || this.disposed) return;\n const t0 = now();\n const counters = { drawCalls: 0 };\n const spanStats: RenderSpanStats[] = [];\n this.stats = { drawCalls: 0, ms: 0, spans: spanStats };\n const gl = this.gl;\n // Free GL resources whose Mesh was GC'd since the last frame. Done here\n // (top of render, before any draws) because GL state is known clean —\n // no VAO bound, no draw in flight. Deleting from the FinalizationRegistry\n // callback directly was racy and caused mid-draw crashes.\n this.meshCache.drainPendingDeletes();\n this.refreshPrograms();\n // New frame: refill the dynamic-glyph synchronous bake budget.\n resetBakeBudget(this.bakeBudget);\n this.groupState.reset();\n this.applyGlState();\n this.applyTarget();\n // Ensure all stencil bits are cleared regardless of any mask left over\n // from the previous frame's clip ops.\n if (this.target?.clear !== false) {\n gl.stencilMask(0xFF);\n gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);\n }\n const ctx: DrawContext = {\n gl,\n pathFill: this.pathFill,\n pathFillVColor: this.pathFillVColor,\n imageFill: this.imageFill,\n batchFill: this.batchFill,\n gradFill: this.gradFill,\n patternFill: this.patternFill,\n meshCache: this.meshCache,\n textureCache: this.textureCache,\n imageCache: this.imageCache,\n gradRamps: this.gradRamps,\n programRegistry: this.programRegistry,\n ensureProgram: (id) => this.ensureProgram(id),\n glyphPaintProgram: (kind) => this.glyphPaintProgram(kind),\n quadVbo: this.quadVbo,\n quadIbo: this.quadIbo,\n drawBatch: this.drawBatch,\n whiteTexture: this.whiteTexture,\n state: this.groupState,\n widthCss: this.widthCss,\n heightCss: this.heightCss,\n clipDepth: 0,\n flattenTolerance: this.flattenTolerance,\n textOutlineMinScreenSize: this.textOutlineMinScreenSize,\n viewMatrix,\n effectTargets: this.effectTargets,\n paintScratch: this.paintScratch,\n renderTarget: null,\n deviceWidth: Math.round(this.widthCss * this.dpr),\n deviceHeight: Math.round(this.heightCss * this.dpr),\n restoreTargetRect: () => this.applyTarget(),\n paintResources: this.paintResources,\n stats: counters,\n };\n const spans = opts?.spans ?? [];\n let next = 0;\n for (let si = 0; si < spans.length; si++) {\n const span = spans[si];\n for (; next < span.start; next++) dispatch(ctx, commands[next]);\n const drawsBefore = counters.drawCalls;\n const spanT0 = now();\n for (; next < span.end; next++) dispatch(ctx, commands[next]);\n // The stream's closing flush belongs to whatever it closes.\n if (si === spans.length - 1 && next >= commands.length) flushBatch(ctx);\n spanStats.push({ id: span.id, drawCalls: counters.drawCalls - drawsBefore, ms: now() - spanT0 });\n }\n for (; next < commands.length; next++) dispatch(ctx, commands[next]);\n // The stream ended, so whatever is still staged has nothing left that\n // could merge with it.\n flushBatch(ctx);\n // Free transient resources allocated during this frame (e.g. per-frame\n // stroke ribbons from tessellateStroke). Done after all draws complete\n // so we never delete a buffer that's still bound to a pending draw.\n this.meshCache.freeTransient();\n this.stats.drawCalls = counters.drawCalls;\n this.stats.ms = now() - t0;\n }\n\n /** What the last `render()` cost: GL draw calls issued and CPU milliseconds\n * spent issuing them, in total and per requested span. GPU time is not\n * measured; the GPU runs asynchronously. */\n lastFrameStats(): RenderStats {\n return this.stats;\n }\n\n /** Change the curve-flattening tolerance after construction. `undefined`\n * restores the cached `DEFAULT_FLATTEN_TOLERANCE` route. */\n setFlattenTolerance(tolerance: number | undefined): void {\n this.flattenTolerance = tolerance;\n }\n\n resize(dims: { width: number; height: number; dpr: number }): void {\n this.widthCss = dims.width;\n this.heightCss = dims.height;\n this.dpr = dims.dpr;\n if (this.canvas) {\n this.canvas.width = Math.round(dims.width * dims.dpr);\n this.canvas.height = Math.round(dims.height * dims.dpr);\n if (this.canvas.style) {\n this.canvas.style.width = `${dims.width}px`;\n this.canvas.style.height = `${dims.height}px`;\n }\n }\n this.applyTarget();\n }\n\n /** @internal */ _gl(): WebGL2RenderingContext { return this.gl; }\n /** @internal */ _pathFill(): ShaderProgram { return this.pathFill; }\n /** @internal */ _pathFillVColor(): ShaderProgram { return this.pathFillVColor; }\n /** @internal */ _drawBatch(): DrawBatch { return this.drawBatch; }\n /** @internal */ _imageFill(): ShaderProgram { return this.imageFill; }\n /** @internal */ _batchFill(): ShaderProgram { return this.batchFill; }\n /** @internal */ _gradFill(): ShaderProgram { return this.gradFill; }\n /** @internal */ _patternFill(): ShaderProgram { return this.patternFill; }\n /** @internal */ _meshCache(): GLMeshCache { return this.meshCache; }\n /** @internal */ _textureCache(): GLTextureCache { return this.textureCache; }\n /** @internal */ _imageCache(): GLImageCache { return this.imageCache; }\n /** @internal */ _gradRamps(): GradientRampAtlas { return this.gradRamps; }\n /** @internal */ _groupState(): GroupState { return this.groupState; }\n /** @internal */ _widthCss(): number { return this.widthCss; }\n /** @internal */ _heightCss(): number { return this.heightCss; }\n /** @internal */ _dpr(): number { return this.dpr; }\n}\n\n/** The 1x1 opaque white texture a batch flush binds when its run samples no\n * image. NEAREST both ways: one texel, so there is nothing to filter, and a\n * mipmap filter on a texture with no mipmaps is incomplete. */\nfunction createWhiteTexture(gl: WebGL2RenderingContext): WebGLTexture | null {\n const tex = gl.createTexture();\n if (!tex) return null;\n gl.bindTexture(gl.TEXTURE_2D, tex);\n gl.texImage2D(\n gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,\n new Uint8Array([255, 255, 255, 255]),\n );\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n gl.bindTexture(gl.TEXTURE_2D, null);\n return tex;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WeaselRenderer.js","names":["customUniformNames","CUSTOM_KIT_UNIFORMS","extractUniformNames","vertSrc","fragSrc","compileCustomProgram","src","CUSTOM_VERT_SRC","program","ShaderProgram","gl","extractUniformArrays","CUSTOM_ATTRIBUTES","now","GLYPH_PAINT_SOURCES","GLYPH_PATTERN_FRAG_SRC","GLYPH_PATTERN_UNIFORMS","GLYPH_GRAD_FRAG_SRC","GLYPH_GRAD_UNIFORMS","GLYPH_TEXTURE_FRAG_SRC","GLYPH_TEXTURE_UNIFORMS","WeaselRenderer","GroupState","createPaintResources","opts","EffectTargets","PaintScratch","attrs","DEFAULT_BAKE_BUDGET","OUTLINE_MIN_SCREEN_PX","VERT_SRC","FRAG_SRC","PATH_FILL_UNIFORMS","PATH_FILL_ATTRIBUTES","VCOLOR_VERT_SRC","VCOLOR_FRAG_SRC","PATH_FILL_VCOLOR_ATTRIBUTES","IMAGE_VERT_SRC","IMAGE_FRAG_SRC","IMAGE_FILL_UNIFORMS","IMAGE_FILL_ATTRIBUTES","BATCH_VERT_SRC","BATCH_FRAG_SRC","BATCH_FILL_UNIFORMS","BATCH_FILL_ATTRIBUTES","GRAD_VERT_SRC","GRAD_FRAG_SRC","GRAD_FILL_UNIFORMS","GRAD_FILL_ATTRIBUTES","PATTERN_VERT_SRC","PATTERN_FRAG_SRC","PATTERN_FILL_UNIFORMS","PATTERN_FILL_ATTRIBUTES","aPos","GLMeshCache","GLTextureCache","GLImageCache","GradientRampAtlas","DrawBatch","createWhiteTexture","QUAD_VERTICES","QUAD_INDICES","getProgramSource","handle","previous","id","compiled","e","existing","kind","frag","uniforms","GLYPH_PAINT_VERT_SRC","GLYPH_PAINT_ATTRIBUTES","w","h","x","y","target","markAllFontsNotUploaded","prog","t0","counters","spanStats","resetBakeBudget","ctx","spans","next","si","span","commands","drawsBefore","spanT0","flushBatch","tolerance","dims","tex"],"sources":["../../src/renderer/WeaselRenderer.ts"],"sourcesContent":["import { createPaintResources, type OwnedPaintResources } from './paintResources';\nimport { ShaderProgram } from './shaders/ShaderProgram';\nimport {\n VERT_SRC,\n FRAG_SRC,\n VCOLOR_VERT_SRC,\n VCOLOR_FRAG_SRC,\n PATH_FILL_UNIFORMS,\n PATH_FILL_ATTRIBUTES,\n PATH_FILL_VCOLOR_ATTRIBUTES,\n} from './shaders/pathFill';\nimport {\n markAllFontsNotUploaded,\n resetBakeBudget,\n DEFAULT_BAKE_BUDGET,\n} from '@weasel-js/font';\nimport {\n IMAGE_VERT_SRC,\n IMAGE_FRAG_SRC,\n IMAGE_FILL_UNIFORMS,\n IMAGE_FILL_ATTRIBUTES,\n} from './shaders/imageFill';\nimport {\n BATCH_VERT_SRC,\n BATCH_FRAG_SRC,\n BATCH_FILL_UNIFORMS,\n BATCH_FILL_ATTRIBUTES,\n} from './shaders/batchFill';\nimport {\n GRAD_VERT_SRC,\n GRAD_FRAG_SRC,\n GRAD_FILL_UNIFORMS,\n GRAD_FILL_ATTRIBUTES,\n} from './shaders/gradFill';\nimport {\n PATTERN_VERT_SRC,\n PATTERN_FRAG_SRC,\n PATTERN_FILL_UNIFORMS,\n PATTERN_FILL_ATTRIBUTES,\n} from './shaders/patternFill';\nimport {\n GLYPH_PAINT_VERT_SRC,\n GLYPH_PATTERN_FRAG_SRC,\n GLYPH_GRAD_FRAG_SRC,\n GLYPH_TEXTURE_FRAG_SRC,\n GLYPH_PATTERN_UNIFORMS,\n GLYPH_GRAD_UNIFORMS,\n GLYPH_TEXTURE_UNIFORMS,\n GLYPH_PAINT_ATTRIBUTES,\n} from './shaders/glyphPaint';\nimport { PaintScratch } from './paintScratch';\nimport { GLMeshCache } from './cache/GLMeshCache';\nimport { GLTextureCache } from './cache/GLTextureCache';\nimport { GLImageCache, type ImageMinification } from './cache/GLImageCache';\nimport { GradientRampAtlas } from './cache/GradientRampAtlas';\nimport { GroupState } from './state/GroupState';\nimport type { DrawCommand } from './DrawCommand';\nimport type { GlMat3 } from './math/mat3';\nimport {\n dispatch, flushBatch, OUTLINE_MIN_SCREEN_PX, type DrawContext, type GlyphPaintKind,\n} from './draw';\nimport { DrawBatch } from './drawBatch';\nimport {\n CUSTOM_VERT_SRC, CUSTOM_ATTRIBUTES, CUSTOM_KIT_UNIFORMS,\n QUAD_VERTICES, QUAD_INDICES,\n} from './shaders/customPrelude';\nimport { getProgramSource, type ProgramSource, type ShaderProgramHandle } from './shaders/registerProgram';\nimport { EffectTargets } from './effects/EffectTargets';\nimport { extractUniformArrays, extractUniformNames } from './shaders/extractUniformNames';\n\n/** Every uniform a custom program may be written through. Both stages, not\n * just the fragment one: a name never looked up is written through `null`,\n * which GL accepts silently, so the uniform keeps its zero default. */\nfunction customUniformNames(vertSrc: string, fragSrc: string): string[] {\n return [...CUSTOM_KIT_UNIFORMS, ...extractUniformNames(vertSrc), ...extractUniformNames(fragSrc)];\n}\n\n/** Compile a registered program's source against `gl`, with every uniform\n * location and array base it may be written through. */\nfunction compileCustomProgram(gl: WebGL2RenderingContext, src: ProgramSource): ShaderProgram {\n const vertSrc = src.vert === '' ? CUSTOM_VERT_SRC : src.vert;\n const program = new ShaderProgram(gl, vertSrc, src.frag);\n program.lookupUniforms(customUniformNames(vertSrc, src.frag));\n program.lookupUniformArrays(new Map([...extractUniformArrays(vertSrc), ...extractUniformArrays(src.frag)]));\n program.lookupAttributes(CUSTOM_ATTRIBUTES);\n return program;\n}\n\n/** How to construct a `WeaselRenderer`: the GL context or canvas to draw\n * into, the output size, and the quality knobs that separate screen\n * rendering from print. */\n/** A run of top-level commands, `[start, end)` by index, that\n * {@link WeaselRenderer.lastFrameStats} accounts for separately — one render\n * layer's commands, say. Spans are in order and do not overlap. */\nexport interface RenderSpan {\n id: string;\n start: number;\n end: number;\n}\n\n/** Per-frame options for {@link WeaselRenderer.render}. */\nexport interface RenderOptions {\n spans?: readonly RenderSpan[];\n}\n\n/** What one span cost. A batched run is drawn when something closes it, so\n * its draw call counts toward the span that closed it. */\nexport interface RenderSpanStats {\n id: string;\n drawCalls: number;\n ms: number;\n}\n\n/** What one `render()` cost. See {@link WeaselRenderer.lastFrameStats}. */\nexport interface RenderStats {\n drawCalls: number;\n ms: number;\n spans: RenderSpanStats[];\n}\n\nfunction now(): number {\n return typeof performance !== 'undefined' ? performance.now() : Date.now();\n}\n\n/** Construction options for {@link WeaselRenderer}. Pass `gl` or `canvas`;\n * a context passed in must have a stencil buffer. */\nexport interface WeaselRendererOptions {\n /** A WebGL2 context to draw with. Wins over `canvas`. */\n gl?: WebGL2RenderingContext;\n /** A canvas to open a stencil-backed WebGL2 context on, when `gl` is absent. */\n canvas?: HTMLCanvasElement;\n /** Drawing size in CSS pixels. */\n width: number;\n /** Drawing size in CSS pixels. */\n height: number;\n /** Device pixels per CSS pixel. */\n dpr: number;\n /** MIN_FILTER strategy for image/pattern textures (`GLImageCache`).\n * Default `'linear'` — the existing screen behavior. The headless\n * `renderSceneToPixels` path passes `'mipmap'` for print-quality\n * minification. Explicitly passing `'linear'` is always valid. */\n imageMinification?: ImageMinification;\n /** Flatness tolerance for curve tessellation, in WORLD units (see\n * `TessellateOptions.flattenTolerance`). When set, path fills are\n * tessellated fresh at this tolerance per frame (transient pool) instead\n * of served from the Path-identity mesh cache — the cache key does not\n * include tolerance. Default: unset — the existing cached behavior at\n * `DEFAULT_FLATTEN_TOLERANCE`. The headless path derives this from the\n * requested output scale; screen callers normally leave it unset. */\n flattenTolerance?: number;\n /** Per-render synchronous bake budget for dynamic canvas-SDF glyphs.\n * Default DEFAULT_BAKE_BUDGET (16). The headless renderSceneToPixels\n * path passes Infinity so print never defers a glyph. */\n bakeBudget?: number;\n /** On-screen glyph size, in CSS pixels, at or above which text renders from\n * tessellated font outlines instead of a distance field — see\n * `OUTLINE_MIN_SCREEN_PX`. Only faces registered with\n * `registerFontOutlines` are affected; everything else keeps its SDF tier\n * whatever this says. Pass `Infinity` to disable the tier outright, or 0\n * to use outlines wherever they exist (what the headless path does, since\n * print has no reason to sample a field it could evaluate exactly). */\n textOutlineMinScreenSize?: number;\n}\n\n/** Where a renderer draws inside a buffer it does not own.\n *\n * The rect's SIZE is the renderer's own `width`/`height` — `resize()` owns\n * that, and a second copy here could disagree with the one `DrawContext`\n * reports to screen-space layers. */\nexport interface RenderTarget {\n /** Top-left of this renderer's output within the drawing buffer, in CSS\n * pixels, with the origin at the buffer's top-left. GL's bottom-left origin\n * is handled internally. */\n origin: { x: number; y: number };\n /** Clear colour and stencil within the rect before drawing. Default true.\n * Pass false only when the caller clears the whole buffer itself on behalf\n * of every co-tenant — a frame that clears neither inherits its neighbour's\n * stencil bits, and even-odd fills then fill their holes. */\n clear?: boolean;\n}\n\n/** Each glyph-paint program's fragment stage and the uniforms it looks up. */\nconst GLYPH_PAINT_SOURCES: Record<GlyphPaintKind, readonly [string, readonly string[]]> = {\n pattern: [GLYPH_PATTERN_FRAG_SRC, GLYPH_PATTERN_UNIFORMS],\n gradient: [GLYPH_GRAD_FRAG_SRC, GLYPH_GRAD_UNIFORMS],\n texture: [GLYPH_TEXTURE_FRAG_SRC, GLYPH_TEXTURE_UNIFORMS],\n};\n\n/**\n * The WebGL2 renderer: takes a list of draw commands and paints them.\n *\n * It knows nothing about the scene — commands are the whole interface, which\n * is what lets layers, HUD widgets and overlays all draw through the same\n * pipeline. GPU resources (meshes, textures, gradient ramps) are cached across\n * frames and keyed by identity, so re-issuing the same command is cheap.\n */\nexport class WeaselRenderer {\n private readonly gl: WebGL2RenderingContext;\n private stats: RenderStats = { drawCalls: 0, ms: 0, spans: [] };\n private pathFill: ShaderProgram;\n private pathFillVColor: ShaderProgram;\n private imageFill: ShaderProgram;\n private batchFill: ShaderProgram;\n private gradFill: ShaderProgram;\n private patternFill: ShaderProgram;\n /** Compiled on first use: only atlas text under a texture paint needs one. */\n private glyphPaintPrograms = new Map<GlyphPaintKind, ShaderProgram>();\n private meshCache: GLMeshCache;\n private textureCache: GLTextureCache;\n private imageCache: GLImageCache;\n private gradRamps: GradientRampAtlas;\n private programRegistry = new Map<string, ShaderProgram>();\n /** The source each compiled custom program was built from — or, after a\n * failed recompile, the source that failed, so it is not retried every frame. */\n private programSources = new Map<string, ProgramSource>();\n private quadVbo: WebGLBuffer | null = null;\n private quadIbo: WebGLBuffer | null = null;\n private drawBatch: DrawBatch;\n /** 1x1 white, so a batch flush sampling no image still samples something —\n * see `shaders/batchFill.ts`. */\n private whiteTexture: WebGLTexture | null = null;\n private readonly groupState = new GroupState();\n private widthCss: number;\n private heightCss: number;\n private dpr: number;\n private canvas: HTMLCanvasElement | null = null;\n private target: RenderTarget | null = null;\n /** Offscreen buffers for group effects. Allocates nothing until a group\n * with effects asks, so a canvas without them pays no memory. */\n private readonly effectTargets: EffectTargets;\n private readonly paintScratch: PaintScratch;\n private readonly imageMinification: ImageMinification;\n private flattenTolerance?: number;\n private readonly bakeBudget: number;\n private readonly textOutlineMinScreenSize: number;\n private contextLost = false;\n private boundOnLost = (e: Event) => this.onContextLost(e);\n private boundOnRestored = () => this.onContextRestored();\n /** True after `dispose()`. A disposed renderer ignores further `render()`\n * calls and `registerProgram()` calls. */\n private disposed = false;\n /** What registered paint kinds hang their GPU state on. One per context\n * generation: a restore releases it and opens the next. */\n private paintResources: OwnedPaintResources = createPaintResources();\n\n constructor(opts: WeaselRendererOptions) {\n if (!opts.gl && !opts.canvas) {\n throw new Error('WeaselRenderer requires either gl or canvas');\n }\n const gl = opts.gl ?? opts.canvas!.getContext('webgl2', { stencil: true });\n if (!gl) throw new Error('WeaselRenderer: WebGL2 not available');\n this.gl = gl as WebGL2RenderingContext;\n this.effectTargets = new EffectTargets(this.gl);\n this.paintScratch = new PaintScratch(this.gl);\n\n // Bits 0-7 are load-bearing: bit 0 for even-odd fills and stenciled\n // strokes, bits 1-7 for clip depth (`renderer/draw.ts`). Without a stencil\n // buffer both render wrong rather than not at all, so refuse the context\n // instead of painting a plausible lie. A stub that cannot answer is not a\n // stencil-less context — only an explicit `false` is.\n const attrs = typeof this.gl.getContextAttributes === 'function'\n ? this.gl.getContextAttributes()\n : null;\n if (attrs && attrs.stencil === false) {\n throw new Error(\n 'WeaselRenderer: the supplied WebGL2 context has no stencil buffer. '\n + 'Create it with { stencil: true } — clips and even-odd fills need one.',\n );\n }\n\n this.widthCss = opts.width;\n this.heightCss = opts.height;\n this.dpr = opts.dpr;\n this.imageMinification = opts.imageMinification ?? 'linear';\n this.flattenTolerance = opts.flattenTolerance;\n this.bakeBudget = opts.bakeBudget ?? DEFAULT_BAKE_BUDGET;\n this.textOutlineMinScreenSize = opts.textOutlineMinScreenSize ?? OUTLINE_MIN_SCREEN_PX;\n\n this.canvas = opts.canvas ?? null;\n if (this.canvas) {\n this.canvas.width = Math.round(opts.width * opts.dpr);\n this.canvas.height = Math.round(opts.height * opts.dpr);\n if (this.canvas.style) {\n this.canvas.style.width = `${opts.width}px`;\n this.canvas.style.height = `${opts.height}px`;\n }\n this.canvas.addEventListener('webglcontextlost', this.boundOnLost);\n this.canvas.addEventListener('webglcontextrestored', this.boundOnRestored);\n }\n\n this.applyGlState();\n this.applyTarget();\n\n this.pathFill = new ShaderProgram(this.gl, VERT_SRC, FRAG_SRC);\n this.pathFill.lookupUniforms(PATH_FILL_UNIFORMS);\n this.pathFill.lookupAttributes(PATH_FILL_ATTRIBUTES);\n\n this.pathFillVColor = new ShaderProgram(this.gl, VCOLOR_VERT_SRC, VCOLOR_FRAG_SRC);\n this.pathFillVColor.lookupUniforms(PATH_FILL_UNIFORMS);\n this.pathFillVColor.lookupAttributes(PATH_FILL_VCOLOR_ATTRIBUTES);\n\n this.imageFill = new ShaderProgram(this.gl, IMAGE_VERT_SRC, IMAGE_FRAG_SRC);\n this.imageFill.lookupUniforms(IMAGE_FILL_UNIFORMS);\n this.imageFill.lookupAttributes(IMAGE_FILL_ATTRIBUTES);\n\n this.batchFill = new ShaderProgram(this.gl, BATCH_VERT_SRC, BATCH_FRAG_SRC);\n this.batchFill.lookupUniforms(BATCH_FILL_UNIFORMS);\n this.batchFill.lookupAttributes(BATCH_FILL_ATTRIBUTES);\n\n this.gradFill = new ShaderProgram(this.gl, GRAD_VERT_SRC, GRAD_FRAG_SRC);\n this.gradFill.lookupUniforms(GRAD_FILL_UNIFORMS);\n this.gradFill.lookupAttributes(GRAD_FILL_ATTRIBUTES);\n\n this.patternFill = new ShaderProgram(this.gl, PATTERN_VERT_SRC, PATTERN_FRAG_SRC);\n this.patternFill.lookupUniforms(PATTERN_FILL_UNIFORMS);\n this.patternFill.lookupAttributes(PATTERN_FILL_ATTRIBUTES);\n\n const aPos = this.pathFill.attribute('a_position');\n if (aPos === undefined) throw new Error('WeaselRenderer: a_position not found in path-fill shader');\n this.meshCache = new GLMeshCache(this.gl, aPos);\n this.textureCache = new GLTextureCache(this.gl);\n this.imageCache = new GLImageCache(this.gl, this.imageMinification);\n this.gradRamps = new GradientRampAtlas(this.gl);\n this.uploadQuadGeometry();\n this.drawBatch = new DrawBatch(this.gl, this.batchFill);\n this.whiteTexture = createWhiteTexture(this.gl);\n }\n\n private uploadQuadGeometry(): void {\n const gl = this.gl;\n this.quadVbo = gl.createBuffer();\n this.quadIbo = gl.createBuffer();\n if (!this.quadVbo || !this.quadIbo) {\n throw new Error('WeaselRenderer: failed to create quad geometry buffers');\n }\n gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVbo);\n gl.bufferData(gl.ARRAY_BUFFER, QUAD_VERTICES, gl.STATIC_DRAW);\n gl.bindBuffer(gl.ARRAY_BUFFER, null);\n\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quadIbo);\n gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, QUAD_INDICES, gl.STATIC_DRAW);\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);\n }\n\n /**\n * Compile a consumer-registered shader program against this renderer's GL context.\n *\n * Call once per renderer after the module-level `registerProgram()`.\n * Throws `ShaderCompileError` if compilation fails.\n *\n * In dev mode, calling again with the same handle replaces the compiled program.\n *\n * @experimental\n */\n registerProgram(handle: ShaderProgramHandle): void {\n // A disposed renderer must not compile programs into the cleared registry.\n if (this.disposed) return;\n const src = getProgramSource(handle.id);\n if (!src) {\n throw new Error(\n `WeaselRenderer.registerProgram: program \"${handle.id}\" not found in source registry. ` +\n `Call the module-level registerProgram() first.`,\n );\n }\n this.installProgram(handle.id, src, compileCustomProgram(this.gl, src));\n }\n\n private installProgram(id: string, src: ProgramSource, program: ShaderProgram): void {\n const previous = this.programRegistry.get(id);\n if (previous) this.gl.deleteProgram(previous.handle);\n this.programRegistry.set(id, program);\n this.programSources.set(id, src);\n }\n\n /** Recompile every program whose module-level source was re-registered\n * since it was compiled. A source that fails keeps the old program drawing. */\n private refreshPrograms(): void {\n for (const [id, compiled] of this.programSources) {\n const src = getProgramSource(id);\n if (!src || src === compiled) continue;\n if (src.vert === compiled.vert && src.frag === compiled.frag) {\n this.programSources.set(id, src);\n continue;\n }\n try {\n this.installProgram(id, src, compileCustomProgram(this.gl, src));\n } catch (e) {\n this.programSources.set(id, src);\n console.error(`weasel: failed to recompile re-registered program \"${id}\"; still drawing the previous one:`, e);\n }\n }\n }\n\n /**\n * The compiled program for `id`, compiling it against this context on first\n * use. Unlike {@link registerProgram} this neither throws on a missing\n * source nor on a compile failure: a paint kind asking for a program it\n * never registered must decline the paint, not take down the frame.\n */\n private ensureProgram(id: string): ShaderProgram | null {\n const existing = this.programRegistry.get(id);\n if (existing) return existing;\n if (this.disposed || !getProgramSource(id)) return null;\n try {\n this.registerProgram({ id });\n } catch (e) {\n console.error(`weasel: failed to compile paint program \"${id}\":`, e);\n return null;\n }\n return this.programRegistry.get(id) ?? null;\n }\n\n private glyphPaintProgram(kind: GlyphPaintKind): ShaderProgram {\n const existing = this.glyphPaintPrograms.get(kind);\n if (existing) return existing;\n const [frag, uniforms] = GLYPH_PAINT_SOURCES[kind];\n const program = new ShaderProgram(this.gl, GLYPH_PAINT_VERT_SRC, frag);\n program.lookupUniforms(uniforms);\n program.lookupAttributes(GLYPH_PAINT_ATTRIBUTES);\n this.glyphPaintPrograms.set(kind, program);\n return program;\n }\n\n /** The GL state every frame assumes. Applied per `render()` rather than once\n * at construction because a co-tenant sharing this context moves all of it\n * between our frames. */\n private applyGlState(): void {\n const gl = this.gl;\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n gl.disable(gl.DEPTH_TEST);\n gl.disable(gl.CULL_FACE);\n gl.clearColor(0, 0, 0, 0);\n }\n\n /** Viewport and scissor for this frame. Re-applied inside `render()` because\n * a co-tenant on the same context moves both between our frames. */\n private applyTarget(): void {\n const gl = this.gl;\n const w = Math.round(this.widthCss * this.dpr);\n const h = Math.round(this.heightCss * this.dpr);\n if (!this.target) {\n gl.disable(gl.SCISSOR_TEST);\n gl.viewport(0, 0, w, h);\n return;\n }\n const x = Math.round(this.target.origin.x * this.dpr);\n const y = gl.drawingBufferHeight - Math.round(this.target.origin.y * this.dpr) - h;\n gl.viewport(x, y, w, h);\n gl.scissor(x, y, w, h);\n gl.enable(gl.SCISSOR_TEST);\n }\n\n /** Confine this renderer to a rect of its drawing buffer, or pass null to\n * give it the whole buffer back. Takes effect from the next `render()`. */\n setTarget(target: RenderTarget | null): void {\n this.target = target;\n this.applyTarget();\n }\n\n getTarget(): RenderTarget | null {\n return this.target;\n }\n\n isContextLost(): boolean {\n return this.contextLost;\n }\n\n private onContextLost(e: Event): void {\n e.preventDefault();\n this.contextLost = true;\n }\n\n private onContextRestored(): void {\n this.contextLost = false;\n this.applyGlState();\n this.applyTarget();\n // Every framebuffer, texture and renderbuffer the pool held died with the\n // context. `releaseAll` deletes handles that are already gone, which is a\n // no-op, and drops the bookkeeping that would otherwise hand a dead FBO to\n // the next group with effects.\n this.effectTargets.releaseAll();\n this.paintScratch.release();\n this.paintResources.release();\n this.paintResources = createPaintResources();\n this.pathFill = new ShaderProgram(this.gl, VERT_SRC, FRAG_SRC);\n this.pathFill.lookupUniforms(PATH_FILL_UNIFORMS);\n this.pathFill.lookupAttributes(PATH_FILL_ATTRIBUTES);\n this.pathFillVColor = new ShaderProgram(this.gl, VCOLOR_VERT_SRC, VCOLOR_FRAG_SRC);\n this.pathFillVColor.lookupUniforms(PATH_FILL_UNIFORMS);\n this.pathFillVColor.lookupAttributes(PATH_FILL_VCOLOR_ATTRIBUTES); this.imageFill = new ShaderProgram(this.gl, IMAGE_VERT_SRC, IMAGE_FRAG_SRC);\n this.imageFill.lookupUniforms(IMAGE_FILL_UNIFORMS);\n this.imageFill.lookupAttributes(IMAGE_FILL_ATTRIBUTES);\n\n this.batchFill = new ShaderProgram(this.gl, BATCH_VERT_SRC, BATCH_FRAG_SRC);\n this.batchFill.lookupUniforms(BATCH_FILL_UNIFORMS);\n this.batchFill.lookupAttributes(BATCH_FILL_ATTRIBUTES);\n this.gradFill = new ShaderProgram(this.gl, GRAD_VERT_SRC, GRAD_FRAG_SRC);\n this.gradFill.lookupUniforms(GRAD_FILL_UNIFORMS);\n this.gradFill.lookupAttributes(GRAD_FILL_ATTRIBUTES);\n\n this.patternFill = new ShaderProgram(this.gl, PATTERN_VERT_SRC, PATTERN_FRAG_SRC);\n this.patternFill.lookupUniforms(PATTERN_FILL_UNIFORMS);\n this.patternFill.lookupAttributes(PATTERN_FILL_ATTRIBUTES);\n this.glyphPaintPrograms.clear();\n const aPos = this.pathFill.attribute('a_position');\n if (aPos === undefined) throw new Error('a_position missing after restore');\n this.meshCache = new GLMeshCache(this.gl, aPos);\n this.textureCache = new GLTextureCache(this.gl);\n this.imageCache.abandon();\n this.imageCache = new GLImageCache(this.gl, this.imageMinification);\n this.gradRamps = new GradientRampAtlas(this.gl);\n markAllFontsNotUploaded();\n\n this.uploadQuadGeometry();\n this.drawBatch = new DrawBatch(this.gl, this.batchFill);\n this.whiteTexture = createWhiteTexture(this.gl);\n for (const id of this.programRegistry.keys()) {\n const src = getProgramSource(id);\n if (!src) continue;\n try {\n this.programRegistry.set(id, compileCustomProgram(this.gl, src));\n this.programSources.set(id, src);\n } catch (e) {\n console.error(`weasel: failed to recompile program \"${id}\" after context restore:`, e);\n }\n }\n }\n\n /** Free every GL resource this renderer created and detach context-loss\n * listeners when a canvas was supplied. Idempotent.\n *\n * That covers the built-in and consumer-registered programs (their shaders\n * go with them), the shared quad geometry, the batch, the effect targets,\n * and every cache: atlas and gradient-ramp textures, bitmap and pattern\n * textures, and meshes — transient, persistent, and those whose Path was\n * already collected. The context itself is left alone, so a caller-owned\n * context can host renderer after renderer without accumulating anything.\n *\n * A disposed renderer ignores further `render()` and `registerProgram()`\n * calls. */\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n const gl = this.gl;\n if (this.canvas) {\n this.canvas.removeEventListener('webglcontextlost', this.boundOnLost);\n this.canvas.removeEventListener('webglcontextrestored', this.boundOnRestored);\n }\n this.meshCache.dispose();\n this.paintResources.release();\n for (const prog of [\n this.pathFill, this.pathFillVColor, this.imageFill, this.batchFill, this.gradFill, this.patternFill,\n ...this.glyphPaintPrograms.values(),\n ]) {\n gl.deleteProgram(prog.handle);\n }\n this.glyphPaintPrograms.clear();\n for (const prog of this.programRegistry.values()) {\n gl.deleteProgram(prog.handle);\n }\n this.programRegistry.clear();\n this.programSources.clear();\n this.textureCache.free();\n this.imageCache.dispose();\n this.gradRamps.free();\n if (this.quadVbo) gl.deleteBuffer(this.quadVbo);\n if (this.quadIbo) gl.deleteBuffer(this.quadIbo);\n this.drawBatch.dispose();\n if (this.whiteTexture) gl.deleteTexture(this.whiteTexture);\n this.whiteTexture = null;\n this.effectTargets.releaseAll();\n this.paintScratch.release();\n }\n\n /**\n * Draw one frame.\n *\n * `viewMatrix` is the frame's world→screen transform. It is only consulted\n * by `units: 'world'` gradients, which fall back to screen space without\n * it — every other command carries its own transform in the stream, so\n * callers with no view concept can keep calling `render(commands)`.\n */\n render(commands: DrawCommand[], viewMatrix?: GlMat3, opts?: RenderOptions): void {\n if (this.contextLost || this.disposed) return;\n const t0 = now();\n const counters = { drawCalls: 0 };\n const spanStats: RenderSpanStats[] = [];\n this.stats = { drawCalls: 0, ms: 0, spans: spanStats };\n const gl = this.gl;\n // Free GL resources whose Mesh was GC'd since the last frame. Done here\n // (top of render, before any draws) because GL state is known clean —\n // no VAO bound, no draw in flight. Deleting from the FinalizationRegistry\n // callback directly was racy and caused mid-draw crashes.\n this.meshCache.drainPendingDeletes();\n this.refreshPrograms();\n // New frame: refill the dynamic-glyph synchronous bake budget.\n resetBakeBudget(this.bakeBudget);\n this.groupState.reset();\n this.applyGlState();\n this.applyTarget();\n // Ensure all stencil bits are cleared regardless of any mask left over\n // from the previous frame's clip ops.\n if (this.target?.clear !== false) {\n gl.stencilMask(0xFF);\n gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);\n }\n const ctx: DrawContext = {\n gl,\n pathFill: this.pathFill,\n pathFillVColor: this.pathFillVColor,\n imageFill: this.imageFill,\n batchFill: this.batchFill,\n gradFill: this.gradFill,\n patternFill: this.patternFill,\n meshCache: this.meshCache,\n textureCache: this.textureCache,\n imageCache: this.imageCache,\n gradRamps: this.gradRamps,\n programRegistry: this.programRegistry,\n ensureProgram: (id) => this.ensureProgram(id),\n glyphPaintProgram: (kind) => this.glyphPaintProgram(kind),\n quadVbo: this.quadVbo,\n quadIbo: this.quadIbo,\n drawBatch: this.drawBatch,\n whiteTexture: this.whiteTexture,\n state: this.groupState,\n widthCss: this.widthCss,\n heightCss: this.heightCss,\n clipDepth: 0,\n clipStack: [],\n flattenTolerance: this.flattenTolerance,\n textOutlineMinScreenSize: this.textOutlineMinScreenSize,\n viewMatrix,\n effectTargets: this.effectTargets,\n paintScratch: this.paintScratch,\n renderTarget: null,\n deviceWidth: Math.round(this.widthCss * this.dpr),\n deviceHeight: Math.round(this.heightCss * this.dpr),\n restoreTargetRect: () => this.applyTarget(),\n paintResources: this.paintResources,\n stats: counters,\n };\n // A frame that threw part-way left its clip here.\n this.drawBatch.clip = null;\n const spans = opts?.spans ?? [];\n let next = 0;\n for (let si = 0; si < spans.length; si++) {\n const span = spans[si];\n for (; next < span.start; next++) dispatch(ctx, commands[next]);\n const drawsBefore = counters.drawCalls;\n const spanT0 = now();\n for (; next < span.end; next++) dispatch(ctx, commands[next]);\n // The stream's closing flush belongs to whatever it closes.\n if (si === spans.length - 1 && next >= commands.length) flushBatch(ctx);\n spanStats.push({ id: span.id, drawCalls: counters.drawCalls - drawsBefore, ms: now() - spanT0 });\n }\n for (; next < commands.length; next++) dispatch(ctx, commands[next]);\n // The stream ended, so whatever is still staged has nothing left that\n // could merge with it.\n flushBatch(ctx);\n // Free transient resources allocated during this frame (e.g. per-frame\n // stroke ribbons from tessellateStroke). Done after all draws complete\n // so we never delete a buffer that's still bound to a pending draw.\n this.meshCache.freeTransient();\n this.stats.drawCalls = counters.drawCalls;\n this.stats.ms = now() - t0;\n }\n\n /** What the last `render()` cost: GL draw calls issued and CPU milliseconds\n * spent issuing them, in total and per requested span. GPU time is not\n * measured; the GPU runs asynchronously. */\n lastFrameStats(): RenderStats {\n return this.stats;\n }\n\n /** Change the curve-flattening tolerance after construction. `undefined`\n * restores the cached `DEFAULT_FLATTEN_TOLERANCE` route. */\n setFlattenTolerance(tolerance: number | undefined): void {\n this.flattenTolerance = tolerance;\n }\n\n resize(dims: { width: number; height: number; dpr: number }): void {\n this.widthCss = dims.width;\n this.heightCss = dims.height;\n this.dpr = dims.dpr;\n if (this.canvas) {\n this.canvas.width = Math.round(dims.width * dims.dpr);\n this.canvas.height = Math.round(dims.height * dims.dpr);\n if (this.canvas.style) {\n this.canvas.style.width = `${dims.width}px`;\n this.canvas.style.height = `${dims.height}px`;\n }\n }\n this.applyTarget();\n }\n\n /** @internal */ _gl(): WebGL2RenderingContext { return this.gl; }\n /** @internal */ _pathFill(): ShaderProgram { return this.pathFill; }\n /** @internal */ _pathFillVColor(): ShaderProgram { return this.pathFillVColor; }\n /** @internal */ _drawBatch(): DrawBatch { return this.drawBatch; }\n /** @internal */ _imageFill(): ShaderProgram { return this.imageFill; }\n /** @internal */ _batchFill(): ShaderProgram { return this.batchFill; }\n /** @internal */ _gradFill(): ShaderProgram { return this.gradFill; }\n /** @internal */ _patternFill(): ShaderProgram { return this.patternFill; }\n /** @internal */ _meshCache(): GLMeshCache { return this.meshCache; }\n /** @internal */ _textureCache(): GLTextureCache { return this.textureCache; }\n /** @internal */ _imageCache(): GLImageCache { return this.imageCache; }\n /** @internal */ _gradRamps(): GradientRampAtlas { return this.gradRamps; }\n /** @internal */ _groupState(): GroupState { return this.groupState; }\n /** @internal */ _widthCss(): number { return this.widthCss; }\n /** @internal */ _heightCss(): number { return this.heightCss; }\n /** @internal */ _dpr(): number { return this.dpr; }\n}\n\n/** The 1x1 opaque white texture a batch flush binds when its run samples no\n * image. NEAREST both ways: one texel, so there is nothing to filter, and a\n * mipmap filter on a texture with no mipmaps is incomplete. */\nfunction createWhiteTexture(gl: WebGL2RenderingContext): WebGLTexture | null {\n const tex = gl.createTexture();\n if (!tex) return null;\n gl.bindTexture(gl.TEXTURE_2D, tex);\n gl.texImage2D(\n gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,\n new Uint8Array([255, 255, 255, 255]),\n );\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n gl.bindTexture(gl.TEXTURE_2D, null);\n return tex;\n}\n"],"mappings":"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"}
|