import { panel } from "./component-contract-builders"; import { TOOLCRAFT_INFINITY_CANVAS_DECISION_RULE_ID } from "./decision-contracts"; import { TOOLCRAFT_PERFORMANCE_VERIFICATION_POLICY, TOOLCRAFT_PERFORMANCE_VERIFICATION_POLICY_TEXT, } from "./performance-verification-policy"; import type { ToolcraftComponentContract } from "./types"; const controlsPanelContract = panel( "controlsPanel", "ControlsPanel", "right", ["left", "right"], "handle", ); const runtimeSetupModeRowRule = "Runtime Setup places the standard Background switch beside Infinity canvas inside Settings, below the separate local defaults section when available, then Background color beside the Blanc/Dots workspace selector and finite sizing; Timeline and optional Lock rotation share the final Setup row when enabled."; const runtimeSetupModeHelpRule = "Timeline and Infinity canvas are self-explanatory runtime mode switches and do not render help icons."; export const TOOLCRAFT_RUNTIME_COMPONENT_CONTRACTS = { canvas: { aiUsageRules: [ "CanvasShell owns Space + primary drag panning when canvas.draggable is enabled, with grab while Space is held and grabbing during the drag. Space-pan takes priority over scene handles and model orbit; plain mouse drag does not pan. Text editors and keyboard-focused controls retain Space input/activation. Pointer-focused panel controls yield Space to the hand tool when the pointer returns to the canvas.", "Trackpad pinch, Ctrl/Meta-wheel, and native gesture events zoom only the canvas under the pointer. ToolcraftRoot suppresses browser zoom across the app, including panels and portaled popups; ordinary panel scrolling remains available. Product code must not recreate these input handlers.", "Choose canvas.sizing.mode from product context instead of copying a universal 1024px artboard.", "Editable-output apps expose the runtime Infinity canvas switch before finite sizing controls; generated apps do not recreate or shadow that mode in product state.", `Follow decision rule ${TOOLCRAFT_INFINITY_CANVAS_DECISION_RULE_ID} as the canonical authority for finite/infinite scene continuity, product bounds, output framing, export crop, and restoration.`, "Infinity canvas removes finite artboard bounds and clipping, hides Aspect ratio, Canvas width, and Canvas height, and preserves the dormant finite canvas size for exact restoration when disabled.", "CanvasShell owns one evaluated Background authority: finite mode renders a pointer-transparent runtime layer below model/image media and product scene content, while Infinity fills the complete viewport without a duplicate finite layer.", "Infinity canvas suppresses the bounded product-rendered preview background so the dormant finite output does not appear as a second canvas; when the standard Background pair is enabled, CanvasShell fills the complete infinite viewport with the selected Background color.", "When the standard Background pair exists, disabling Background atomically restores finite mode and disables Infinity canvas; re-enabling Background restores switch availability without enabling Infinity automatically.", "Custom Canvas 2D, WebGL, and WebGPU output consumes useToolcraftProductSceneFrame for the canonical product rect and keeps its live backing and renderer instance unchanged across finite/infinite mode toggles.", "Do not let canvas.upload choose intrinsic-media by default. Upload without explicit sizing resolves to editable-output so source/background images do not own product output size.", "Use intrinsic-media only for true media-viewer or source-native apps where the natural uploaded/generated media size is the product output; record the reason and prove it with intrinsic-media-size acceptance.", "Use editable-output for generated, exportable, shader, poster, badge, wall, banner, thumbnail, procedural, reference-clone, and product-output apps so users always see Aspect ratio, Canvas width, and Canvas height.", "When an uploaded image is a background/source inside the product canvas, keep the current canvas.size, keep Setup/canvas controls visible, and render the image as cover/crop inside the current canvas bounds without letterbox or aspect distortion.", "When no explicit product size is provided, the runtime default canvas is 16:9 at 1920x1080; do not reset a new product-output app to 1:1 unless the product meaning requires it.", "A user-provided, reference, fixed-format, or base/default size is not a reason to remove size controls; model it as canvas.size plus editable-output so the size is an initial value, not a hidden lock.", "Do not use fixed-output for generated product/output apps with export actions. Reserve fixed-output for non-product internal fixtures where width and height truly must never be user-editable, and prove that lock with canvasSizingCoverage fixed-output-size acceptance.", "A reference or previous app lacking a size editor, or defining a fixed-size baseline, is not a fixed-output reason for a generated product app; product-output clones still use editable-output.", "Resolved canvas.size exists for every canvas app, but visible Canvas width and Canvas height controls are mandatory only for editable-output sizing and live in the mandatory runtime Setup section.", "If canvas.size is provided without an explicit sizing mode, defineToolcraft treats it as editable-output and adds Canvas width and Canvas height controls.", "The runtime Canvas width and Canvas height block belongs to the mandatory Settings section with the standard header; do not add a separate Canvas section label above these fields.", "When the user commits Canvas width or Canvas height, runtime keeps the typed dimension, preserves the selected preset or custom ratio, and recalculates the opposite dimension with integer rounding as one undoable change.", "Aspect ratio presets apply canonical initial dimensions. Custom shows separate Ratio W and Ratio H fields; editing a custom ratio preserves canvas width and recalculates height. Pixel edits retain the active proportion, and repeated values do not introduce rounding drift.", "For non-vector raster, Canvas 2D, WebGL, and WebGPU previews, set canvas.renderScale: true so the runtime adds Resolution scale after canvas sizing. The scale changes backing pixels from 1 to 2 without changing visible canvas size.", TOOLCRAFT_PERFORMANCE_VERIFICATION_POLICY.renderScaleFidelity, "Do not enable canvas.renderScale for DOM/SVG/vector-native previews; preserve vector fidelity through native vector rendering instead of raster supersampling.", ], capabilities: [ "drag", "zoom", "radar", "upload", "editable-size", "infinity-canvas", ], commands: [ "canvas.setMode", "canvas.setSize", "canvas.panBy", "canvas.setOffset", "canvas.center", "canvas.zoomIn", "canvas.zoomOut", "canvas.zoomReset", "media.delete", "media.importBatch", "media.reorder", "media.transform", ], historyPolicy: "patch", id: "canvas", kind: "canvas", schemaType: "canvas", stateMode: "runtime-owned", visualComponent: "CanvasShell", }, persistence: { aiUsageRules: [ "Do not write app state to localStorage directly.", "Use schema persistence policy for app state that should survive reload.", "Persistence may include values, canvas, panels, timeline, layers, and media; history is not persisted.", 'Use persistence include: ["media"] only when runtime media state must survive reload, such as predefined attached files that users can delete, reorder, or transform. Do not use ad hoc storage for media state.', 'Apps with visible runtime panels and localStorage persistence must include "panels" so dragged panel positions survive reload.', "Apps with localStorage persistence must include acceptance coverage for changing a user setting, reloading the browser page, and seeing the restored value or product output.", "Saving source defaults must not hide or replace broken workspace persistence reload behavior.", "Do not store media blobs, files, or large generated images in localStorage.", ], capabilities: ["themePreference", "appStatePolicy"], commands: [], historyPolicy: "never", id: "persistence", kind: "persistence", schemaType: "persistence", stateMode: "runtime-owned", visualComponent: "none", }, settingsTransfer: { aiUsageRules: [ "Generated apps keep a controls panel so runtime Setup is visible from the first run; product controls are added after that mandatory runtime section.", "Local Save State as Default occupies a separate headerless block above Settings; the whole block is absent without host authoring capability. Settings uses standard section Reset and collapse controls; the panel-header Reset remains available in every host.", "Save State as Default captures the full current workspace and reachable binary resources into the project; header Reset restores that saved workspace. Incomplete media prevents publishing partial defaults.", "Do not add settings file actions or source-writing controls in product routes or panelActions.", "The runtime settings codecs support control values, canvas size, timeline playback settings, and attachment paths with durable resource references; they do not expose settings-file actions in Setup. Runtime restores available attachments and settings atomically; missing or invalid attachments are skipped independently. Paths are browser-visible names/relative paths, not permission to read the filesystem or network; JSON does not embed file bytes.", "App-authored sections must not declare runtime Setup targets such as runtime.settingsTransfer, canvas.aspectRatio, canvas.size.width, canvas.size.height, canvas.renderScale, or panels.timeline.extended; those controls never suppress the mandatory runtime Setup controls.", runtimeSetupModeRowRule, runtimeSetupModeHelpRule, "Keep sticky footer panelActions for product delivery actions only, such as Export PNG, Export Video, Copy, Generate, Apply, or Download.", "Typed Export PNG/Video actions consume ToolcraftAppComposition.exportRenderer and typed Export SVG consumes svgExportRenderer; all are runtime-owned, so use onPanelAction only for non-export product actions.", ], capabilities: ["source-defaults-authoring", "settings-import-export"], commands: ["settings.apply"], historyPolicy: "patch", id: "settingsTransfer", kind: "settings", schemaType: "settingsTransfer", stateMode: "runtime-owned", visualComponent: "SettingsTransfer", }, appEntityAcceptance: { aiUsageRules: [ "Every app entity introduced by the AI must have an acceptance test that proves its product responsibility.", "Compound controls must declare controlPartCoverage for every semantic value part required by their control type.", "Compound control browser tests must explicitly exercise each required value part, not only one visible sub-control.", "Acceptance tests must fail when an entity is disconnected from runtime state, renderer output, export output, or command side effects.", "Built-in control values enter runtime state through one canonical codec for defaults, live edits, persistence, settings import, and keyframes.", "Selection-scoped property controls prove two distinct entities: the selected entity changes and the comparison entity remains unchanged in both selection directions.", "Do not accept typecheck, component existence, registered commands, runtime state mutation, renderer input objects, shader uniform presence, or signature strings as final proof.", "Use product-level observables such as rendered pixels, exported image bytes, canvas hash, clipboard payload, cleared media preview, selected layer result, changed viewport, or timeline-rendered frame.", "Every supplied video, GIF, screen-recording, contact-sheet, or extracted-frame reference must be registered as a typed referenceInputs item and preprocessed with pnpm reference:study before product code. The protected study performs a full source scan, reviews dense 12 FPS evidence, partitions reviews above 120 frames, classifies every detected event, decomposes complete contiguous phases into behaviors, and maps each behavior bidirectionally to observable acceptance and browser reference-parity coverage. A no-reference app declares referenceInputs: [] and runs no preprocessing.", "A generic canvas hash difference is not enough for workload or semantic controls; assert the intended direction of the effect.", "Component variants are accepted entities too; tests should fail if a non-default Toolcraft control variant falls back to the default variant or custom markup.", "Conditional entities require fixtures that make the condition observable.", 'Every generated product control declares applicability as mode: "always" or mode: "conditional"; omitted applicability and control-level visibleWhen are rejected before materialization.', "Conditional applicability combines every predicate with AND, hides controls outside mode/type/source/include/variant/count branches, and preserves their runtime values while hidden.", "Every bounded finite product selector is classified exactly once in its Control Section Inventory finiteSelectors array as branch or parameter; continuous controls stay out, and sections without bounded selectors declare finiteSelectors: [].", "A branch lists only the exact always-visible same-entity peers it changes in affectedTargets; explicit applicability predicates add their dependents automatically and are not duplicated, and every predicate owner is a branch so missing predicates remain detectable.", "A parameter still proves its own accepted outcome and exhaustive option coverage without creating peer cases; applicability cases and focused feature closure never use section-wide finite-selector fanout.", "When applicability references a selector for the same product entity or selected branch, keep the selector and its dependent controls in one semantic section. A section that exists only because one selector option is active is not a separate product section just because the branch uses a standalone control.", "When a count/quantity control determines how many sibling controls are applicable, conditional applicability hides unavailable siblings; do not render all possible controls while the renderer reads only the first N.", "Do not use schema disabled: true or disabledWhen for generated product availability; runtime primitives may use disabled styling internally, but product controls outside the active branch are absent.", "Do not leave inactive conditional controls visible while making the renderer ignore them.", "Before controls or canvas interactions, declare typed interactionOwnership for every operation that could plausibly live on either surface. User request, inspected reference, or product usability selects one primary owner.", "Do not mirror one operation across canvas and panel, even with different labels, ids, styling, or custom chrome. The same target and operation capability has one surface owner.", "Allow complementary operations across surfaces when capabilities differ: canvas selection or direct manipulation may coexist with panel properties, modes, constraints, colors, collections, commands, or exact-value editing.", "If an entity cannot be tested against a product-level observable, remove it from the app schema or ask whether it is required.", ], capabilities: ["acceptance-tests", "product-output-verification"], commands: [], historyPolicy: "never", id: "appEntityAcceptance", kind: "composition", schemaType: "appEntityAcceptance", stateMode: "runtime-owned", visualComponent: "none", }, performanceAcceptance: { aiUsageRules: [ "Before renderer code, inventory every reachable control, runtime-state input, and external input, and explicitly mark which ones change workload magnitude; do not infer workload from labels, target names, units, or keywords.", "Map every reachable workload role to exactly one numeric workloadEnvelope dimension with a stable id, unit, source, mapping, defaultValue, and every enforced interactiveMax or batchMax boundary.", "Treat interactiveMax and batchMax as maximum-workload boundaries, not numerical maxima. Every numeric schema source declares workloadBoundary as minimum or maximum, every declared profile boundary equals that schema endpoint, and a batch-only dimension does not invent an interactive boundary.", "Declare only boundary profiles consumed by renderer passes; a control that changes a batch-only dimension keeps targeted control-change coverage without claiming an interactiveMax.", "Derive schema-backed boundaries from the schema and derive non-schema boundaries from the product's enforced runtime or input limits; authored performance fixtures must not redefine those limits.", "Declare fixtureAdapters for every workload dimension so compiled numeric values can be applied and observed exactly; interactive boundaries require a fully guaranteed interactive profile and batch boundaries require batch completion and responsiveness profiles.", "Discrete fixture adapters must declare an exhaustive entries domain that binds every numeric workload value to its applied product value. Schema select/segmented/tabs domains must match every option one-to-one; other finite domains require source-aligned provenance. Development is available only for a reachable combined vector at normalized pressure 0.8 within runtime tolerance; otherwise it is unavailable while maximum remains available. Generic and measured inverse checkpoints are validated against the same exhaustive domain before exact application and observation. Path-level discrete search is deterministic, lazy, and limited by the runtime-owned combination budget; cardinality overflow or an exceeded budget is a planning error, not an unavailable exact vector.", "Before renderer code, declare rendererPipeline.runtimeId plus every render pass's cost dimensions, relationship, frequency, lifecycle, execution location, quality, cache keys, and exact interaction invalidation.", "GPU pass stage/resources/state/surfaces create backend pressure; backend/provider is selected separately for each rendererTechnique.gpu preview/export surface through the renderer-gpu-provider decision rule before renderer code, with detailed policy in docs/toolcraft/renderer-technique.md.", "Run assessToolcraftRenderPlan before implementation. Resolve every required kernel benchmark with measured full-quality candidate evidence; do not choose renderer technology from a hardcoded product category.", "Derive performance paths from rendererPipeline interaction invalidation. Scenarios must reference the canonical pathId and exact coversTargets, and equivalent controls that invalidate the same work share one path.", "Compile development and maximum fixtures centrally from each derived path. Combined fixtures contain every path dimension, and measured inverse full-vector evidence is authored only for a custom dimension mapping or benchmark cost relationship.", "Performance iterations require an exact reachable development fixture at normalized pressure 0.8 and fail with a configuration error when it is unavailable; they never fall back to maximum. Explicit full-performance checkpoints run the complete current matrix with maximum fixtures.", "Browser performance proof must apply compiled fixture values through the real UI, observe every dimension after application, exercise the same preview or export path as the running app, and enforce the path's derived interaction profile.", "Workload acceptance must assert the intended product result, not only a generic hash difference or an isolated helper-state mutation.", "Every export performance scenario must declare the exact panelActions actionValue, visible controlLabel, and completionEvidence it exercises; selectors, unrelated export actions, and injectable completion callbacks are not output evidence. The named browser scenario must resolve to test or it imported from @playwright/test, be statically registered at module scope or inside an executable test.describe, test.describe.serial, or test.describe.parallel suite, and directly await the matching unshadowed protected output-action helper in its root callback block without focus, skip, condition, or nesting; local test runners, uncalled or conditional test declarations, skipped/fixme suites, and runtime skip/fixme/fail annotations are invalid, and generated integrity protects the focused-test Playwright config.", "High-frequency interactions must coalesce redundant work, cancel stale asynchronous work, and preserve retained resources according to the declared pass lifecycle instead of rebuilding upstream resources on every update.", TOOLCRAFT_PERFORMANCE_VERIFICATION_POLICY_TEXT, "When an interaction misses budget, diagnose the assessed passes, update frequency, invalidation, resource lifecycle, scheduling, and execution location before changing output quality or product boundaries.", "Renderer specs must include a Renderer Technique Decision Matrix with sourceRepresentation, productRepresentation, previewRenderer, exportRenderer, rendererStrategy, whyNotAlternativeStrategies, fidelityRisks, and performanceRisks.", "Custom renderer apps must mirror the Renderer Technique Decision Matrix in typed rendererTechnique config so validation can reject contradictory renderer choices.", "Custom renderer specs must include a Renderer Layer Inventory and mirror it in typed rendererTechnique.layers so output, overlays, and export composition remain independently testable.", "Render Pipeline Inventory must explain which runtime targets invalidate each pass; high-frequency interactions must not invalidate retained upstream work unless the declared input actually changes its result.", "Cache-sensitive passes must declare concrete cache keys so tests can reject full recomputation on unrelated interactions.", "Editing handles must be DOM/SVG overlays, excluded from export, and written through runtime state instead of being drawn into the product raster layer.", "Product foreground and editing handle renderer layers must declare uiSelector so browser tests can verify the visible layer exists.", 'productRepresentation "mixed" is valid only when rendererTechnique.layers proves at least two different content families.', "Choose renderer technique from product context, not convenience or novelty. Preserve reference renderer technology in reference-runtime-clone mode unless a concrete blocker and replacement acceptance tests are named.", "Do not switch renderer technology just because it seems more modern or faster. Preview and export may use different renderers only when the decision matrix explains why and export/copy remains product-quality.", "WebGL and WebGPU renderers must initialize contexts, programs, shaders, pipelines, textures, and large buffers once, then update uniforms or stable buffers when controls change.", "For keyframe or playback renderers, texture upload and media decode must be keyed to source media/resource changes, not to timeline time or evaluated settings. Timeline-only updates must reuse decoded media and existing GPU resources.", "Do not create WebGL/WebGPU contexts, shader programs, textures, or requestAnimationFrame loops directly in the React render path.", "Animation loops must cancel scheduled frames during cleanup.", "Animated preview renderers must suspend or coalesce non-essential animation work while the user drags, pans, pinches, zooms, or centers the canvas viewport, then resume from the correct timeline or autonomous time without changing the user's play/pause state.", "Performance matrices must declare rendererStrategy so assessment and benchmark evidence refer to the implementation that actually runs.", "Browser verification must interact with the actual UI, exercise worst-case control values, and fail if the app freezes, creates runaway render loops, drops canvas zoom/offset, or misses the performance budget.", "If a renderer cannot meet the budget, first optimize renderer technique, caching, invalidation, scheduling, and critical-path work. Only change exposed product ranges, work units, or controls after recording measured evidence that the requested quality ceiling is impossible; do not silently reduce product quality to pass budgets.", ], capabilities: ["performance-budgets", "workload-control-tests"], commands: [], historyPolicy: "never", id: "performanceAcceptance", kind: "composition", schemaType: "performanceAcceptance", stateMode: "runtime-owned", visualComponent: "none", }, referenceRuntimeClone: { aiUsageRules: [ 'Use transferMode: "reference-runtime-clone" when the user asks to port, clone, copy, or reproduce an existing app exactly.', "Preserve the reference runtime as the source of truth instead of replacing it with a new renderer or timeline model.", "Port requestAnimationFrame loops, refs, mutable particle/object state, connection state, spawn/update cadence, lifetime rules, pause/resume, export/copy, canvas sizing, and media lifecycle when the reference depends on them.", "Keep Toolcraft as the shell: defineToolcraft, ToolcraftApp, schema controls, canvasContent, fileDrop, panelActions, and runtime commands.", "Reference clone timeline choice is based on timeline behavior, not only on whether the reference draws a timeline-shaped UI.", "If the reference has Play/Pause, Restart from beginning, current time/progress, duration, loop, scrub, selected range, trim handles, or video export timing, write a Reference Timeline Inventory before choosing a timeline mode.", 'Use referenceTimeline.mode "toolcraft-playback" for plain transport behavior such as play/pause, restart, duration/progress, loop, scrub, or export at time.', 'Use referenceTimeline.mode "toolcraft-keyframes" when controls need keyframe diamonds, expanded keyframe rows, easing, or editable keyframes.', 'Use referenceTimeline.mode "none" only when the reference has no user-facing transport behavior at all.', "Reference clone specs must list every detected transport behavior explicitly, including pause-resume, restart, time-progress, export-at-time, playback, scrub, duration, loop, and keyframes when present.", 'Toolcraft reference timelines must declare referenceTimeline.loopDuration with source "reference", "user-request", or "product-derived", plus seconds and evidence; runtime/template fallback 8s is not a valid source.', "panels.timeline.defaultDurationSeconds must match referenceTimeline.loopDuration.seconds for referenceTimeline.mode toolcraft-playback/toolcraft-keyframes.", "Do not create right-panel controls named or targeted as Play, Pause, Paused, Animate, Restart animation, or equivalent app-wide transport toggles.", 'Do not downgrade custom reference timelines to panels.timeline mode "playback". State buttons, trim handles, selected-range playback, or range export require referenceTimeline.mode "custom-reference-timeline" and dedicated acceptance.', "Generated reference clone apps must declare starterTransferMode.referenceTimeline with mode none, toolcraft-playback, toolcraft-keyframes, or custom-reference-timeline.", "Custom reference timeline behavior needs referenceTimelineCoverage entries such as state-jump, trim-range, range-playback, all-range, jump-to-trim-start, and export-range.", "Reference clone acceptance must include referenceCoverage rows for canvas sizing, control mapping, renderer state, and any renderer loop, spawn/update cadence, pause/resume, export/copy, or media lifecycle behavior in the reference.", "Motion reference assets used in a reference clone must use typed starterTransferMode.referenceInputs with nested studies backed by pnpm reference:study evidence; each study covers the fully scanned source through dense phases, classifies every detected event, maps behaviors through acceptanceId and motionReferenceCoverage, and requires browser reference-parity.", "Browser tests must compare reference behavior or a reference-derived baseline, not only Toolcraft state mutation.", ], capabilities: [ "reference-runtime-clone", "reference-behavior-acceptance", "reference-timeline-inventory", "canvasContent-renderer", ], commands: [], historyPolicy: "never", id: "referenceRuntimeClone", kind: "composition", schemaType: "transferMode", stateMode: "runtime-owned", visualComponent: "canvasContent", }, controlLabels: { aiUsageRules: [ "Control labels must be short UI names, usually one to three words.", "Do not put explanations, formulas, units, parenthetical hints, or usage instructions in control labels.", "A concise property label such as Speed, Color, Size, or Opacity is allowed when the nearest visible section or group clearly names the affected product entity.", "When the section is generic, mixed, missing, or otherwise weak context, include the affected entity or role in the label: Pattern color, Background opacity, Wave speed, Stroke width.", "Acceptance validators suggest semantic replacement labels for weak generic labels; fix the schema label instead of relying on runtime fallback rewriting.", "Ten declared controls is a non-blocking density-review threshold, not a section limit. Inspect simultaneously visible controls in reachable modes, compound-editor complexity, panel height, navigation, and reset scope; declaration count alone does not prove visual density.", "Every Control Section Inventory entry declares required entityId, entity, exact targets, and groupingReason. Group by user task and dependency cohesion, not by component type or target namespace. Recommend semanticGroup where helpful; mixed plain-color rows retain their mandatory grouping.", "A coherent section may exceed ten controls, and smaller entities may have justified workflow stages. Every split section keeps the same entityId and entity and declares a unique workflowStage plus concrete splitReason. A complete task may have one control; never split or merge solely to satisfy a count.", "Section splitting must preserve dependency cohesion: keep selectors with their applicability-gated controls by default. A justified workflow split may share the same inventory entity across sections; unique stages and concrete split reasons remain mandatory. A selector option alone is not a separate task.", "Every app-authored controls-panel body section must have a short meaningful visible title. Runtime-created Settings uses the standard visible title, scoped reset action and collapse controls, and remains present before product sections in every mode. Local Save State as Default lives in its own headerless block above Settings; sticky footer action sections use the technical title Export but render without a visible heading.", "Controls-panel section titles normally use one to three words and name only the edited product entity or workflow stage. Four words is the exceptional maximum; starter acceptance rejects more than four semantic words or 32 Unicode code points.", "Use section.description only when the section scope or output relationship is not obvious from its concise title and visible controls. The runtime renders it only behind the standard filled question-mark help icon and never as a visible descriptor or subtitle.", "An actually overflowing legacy or localized section title stays on one line, fades at the right edge, and exposes its full text on hover. This defensive fallback never authorizes a generated app to keep an overlong title.", "Every visible app-authored controls-panel section title renders through the standard 36px collapsible header row with vertically centered, left-aligned text and a separate rightmost 24px design-system collapse icon button; title, help, reset and collapse controls are never nested, and generated apps must not hand-build section headers.", "Controls-panel section expand and collapse uses the standard runtime height/opacity animation; generated apps must not replace it with instant custom section visibility.", "Controls-panel section collapsed/expanded state persists as a runtime UI preference per app. It is not undo/redo state, not settings import/export state, and Reset controls must not clear it. Settings follows ordinary section collapse behavior; the separate defaults action block and sticky footer Export sections are not collapsible.", "Ordinary controls-panel section headers expose the runtime section reset action before the collapse button; it dispatches controls.resetTargets and restores only that section's control targets to their schema defaultValue.", "The local defaults action block uses public technical spacing with 12px on all sides. Settings uses the standard section header and ordinary body spacing, with 8px top and 24px bottom insets; sticky footer action sections keep their dedicated spacing.", "Section titles must identify the edited entity or workflow stage. Use more specific names when the scope is unclear, not merely because the section has many controls.", "Section titles in one controls panel must be unique.", "Use section titles, option labels, tests, or renderer/spec prose for details instead of long field labels.", "Bad: Grid Density (every Nth). Good: Grid Density, with Every 6th as the select option label.", "Use control.description for the short help tooltip shown beside visible labels. It must describe the product behavior or output affected by the control, not restate the label.", "Do not write label-recap descriptions such as Adjusts Opacity, Controls Speed, or Sets Background.", "If there is no useful product-specific explanation, omit control.description; the runtime should not show a help tooltip for that label.", "Do not add control.description to sequential colors such as Color 1, Color 2, or simple palette controls such as Spread when the section title already names the color or palette context.", "For compound controls such as FontPicker, do not use control.description to enumerate the control's owned fields. FontPicker descriptions must not recap font family, weight, size, case, color, opacity, letter spacing, or line height; use description only for non-obvious product scope or omit it.", "The runtime renders a filled Phosphor question icon beside each visible ControlFieldLabel; generated apps must not hand-build their own help icon beside built-in labels.", "If a source label is unavoidably long, keep the visible label concise and rely on the native title tooltip for the full text.", ], capabilities: [ "short-labels", "control-description-tooltip", "native-title-tooltip", "section-description-tooltip", "section-title-overflow-fade", ], commands: [], historyPolicy: "never", id: "controlLabels", kind: "composition", schemaType: "controlLabels", stateMode: "runtime-owned", visualComponent: "ControlFieldLabel", }, controlsPanel: { ...controlsPanelContract, aiUsageRules: [ ...controlsPanelContract.aiUsageRules, "An overflowing controls panel reveals runtime-owned section navigation only after a 300ms pointer dwell within its 12px inner-left padding strip, ending before control content begins. Fast crossings cancel the reveal; while visible, the popup and direct 8px corridor retain it by pointer geometry, remaining outside for 50ms starts a fast 120ms reduced-motion-aware exit before unmount, and reopening requires another dwell. It reuses PanelSurface and the shared ScrollFade, keeps its scrollbar at the right surface edge, shows position-aware top and bottom fades, uses a 12px list inset with zero horizontal item padding, sits 8px to the left centered on the content viewport, enters and exits with opacity and a 2px horizontal offset, renders 13px labels with 8px item gaps at foreground 50%, uses foreground 80% on hover and full foreground for the current item, renders no vertical item markers, jumps immediately to clicked sections, lets focused Arrow Up and Arrow Down wrap, focus, and immediately activate visible sections, includes Settings and the local defaults block when available, and excludes sticky action sections.", "Overflowing navigation labels use the shared right-edge ScrollFade mask without ellipsis and expose the full title on hover. Short labels remain unfaded. Navigation shares the panel reset lifecycle: stale listeners and hover timers are discarded, and a fresh dwell opens it on the replacement panel.", ], capabilities: [...controlsPanelContract.capabilities, "section-navigation"], }, layersPanel: { ...panel("layersPanel", "LayersPanel", "left", ["left", "right"], "handle"), aiUsageRules: [ "Enable layersModule() only when the user explicitly requests a workflow with layers, such as layer selection, ordering, grouping, visibility, or layer-based media management.", "Multiple uploads or editable objects alone do not authorize enabling Layers; without a user-requested layer workflow, leave the module absent.", "Lab layer demonstrations are isolated development fixtures, not generated-product defaults or permission to enable Layers.", "When layers are enabled, layer-specific controls should target selectedLayer.* and apply to the currently selected runtime layer.", "Do not use selectedLayer.* targets when panels.layers is disabled; single-layer apps use app-specific targets.", "Layer-enabled apps need layerCoverage acceptance for selection, visibility, reorder, and grouping.", "Every selectedLayer.* control needs selected-layer-controls acceptance proving it edits the currently selected layer output.", "Every selectedLayer.* control uses the shared two-entity isolation recipe in addition to selected-layer-controls coverage.", "Layer browser coverage must use real LayersPanel rows and buttons, not direct layers.* command dispatch.", "Layer-enabled custom renderers need layers.interactions coverage around real selection, visibility, reorder or grouping, and selected-layer output checks.", ], capabilities: [ "draggable", "snap", "doubleClickReset", "dragMode:handle", "groups", "selection", "visibility", ], commands: [ "layers.add", "layers.delete", "layers.moveToGroup", "layers.rename", "layers.reorder", "layers.select", "layers.toggleCollapsed", "layers.toggleVisibility", ], }, timelinePanel: { ...panel( "timelinePanel", "TimelinePanel", "top", ["top", "bottom"], "panel", ), aiUsageRules: [ "Any product output animation must enable the top Toolcraft timeline; do not ship animated product output with only local requestAnimationFrame playback.", "Before choosing no timeline for any animated product, write an Animation Intent Inventory: product transport, editable keyframes, or autonomous decorative output, plus the user-facing time behaviors present or intentionally absent.", 'User-requested product animation defaults to panels.timeline mode "playback" unless the spec explicitly declares autonomous decorative/self-running output with no play, pause, scrub, duration, loop, export-at-time behavior, or video export.', 'Any product app with Export Video must enable the top Toolcraft timeline: use panels.timeline mode "playback" for product animation transport, or mode "keyframes" when exported animation is driven by keyframes.', 'Export Video is valid only when productReadiness.exportIntent.video is "user-requested" with non-empty explicit user-request evidence; animation, playback, keyframes, and timeline presence never authorize video export.', 'Use panels.timeline: { mode: "playback" } when the product needs user-facing play, pause, scrubbing, duration, loop, restart, time progress, export-at-time controls, or video export.', 'When panels.timeline is enabled for a new Toolcraft app, appTransferMode.animationIntent must match it: mode "timeline-playback" for playback, or mode "timeline-keyframes" for keyframes.', "Playback renderers must consume runtime timeline state; pause freezes output, scrubbing renders a deterministic frame, and the full animation cycle maps to state.timeline.durationSeconds instead of a local fixed duration.", "When the product has a known loop duration, declare it as panels.timeline.defaultDurationSeconds; the runtime timeline duration starts from that loop duration instead of an unrelated 8s default.", 'Timeline animation intent must declare loopDuration with source "reference", "user-request", or "product-derived", plus seconds and evidence; runtime/template fallback 8s is not a valid source.', "panels.timeline.defaultDurationSeconds must match animationIntent.loopDuration.seconds for playback/keyframe animation, so the initial timeline UI shows the declared product loop instead of a generic default.", "Video export frames are rendered deterministically from the runtime-owned 30 FPS timeline schedule; product code must not use autonomous wall-clock time, captureStream, or MediaRecorder as the source or fallback for product duration.", "Playback renderers may compute an initial loop duration default during app initialization or reset, but must not watch state.timeline.durationSeconds and dispatch timeline.setDuration back to a computed local duration. User-edited timeline duration becomes the loop duration source of truth after initialization or reset.", "Playback renderers should use getToolcraftTimelineLoopTime or getToolcraftTimelineLoopProgress to derive product loop phase from state.timeline.currentTimeSeconds and state.timeline.durationSeconds; do not hand-roll wall-clock, fixed-duration, mirror, yoyo, ping-pong, or reverse phase math.", "Product animation loop means a seamless forward-only cycle by default: motion advances in one direction, first and last frames stitch without a visible jump, and mirror/yoyo/ping-pong/reverse loops require explicit user request.", "Changing timeline duration must preserve seamless forward-loop semantics: one full product animation cycle maps from 0 to state.timeline.durationSeconds, first and last frames still stitch, direction does not reverse, and changing duration must not switch the renderer to wall-clock or fixed local speed.", "When non-looping playback reaches the end, pressing Play again restarts playback from time 0.", "Intrinsic-media upload timelines must stay paused at time 0 until source media exists; clearing the last media asset must pause and reset playback.", 'Use panels.timeline: { mode: "keyframes" } or panels.timeline: true only when controls need keyframe diamonds, expanded timeline rows, easing, or keyframe editing.', "In keyframes mode, Toolcraft infers keyframe diamonds from control type; AI must not manually pick a smaller subset of slider/vector/color-style controls.", "Keyframe state stores typed control values; valueLabel is display-only and must never be parsed by renderers or tests as the source of truth.", "Custom renderers with keyframes must consume evaluateToolcraftTimelineValues or useToolcraftEvaluatedValues for keyframed settings instead of raw state.values for those targets.", "Every inferred keyframe-capable control must be evaluated from runtime timeline keyframes and needs acceptance proving diamond creation, row creation, keyframe updates, scrub/playback evaluation, and product output change.", "Keyframe custom renderers must prove zoom, radar, and canvas viewport stability while expanding the timeline, creating keyframes, and scrubbing or playing the timeline.", "Keyframe renderers must not re-decode media or re-upload source textures on timeline ticks, scrubs, playback, or evaluated setting changes.", "Timeline-driven preview renderers must suspend or coalesce non-essential animation work during canvas drag, pan, pinch, zoom, and radar/center interactions without mutating the user's timeline play/pause state.", "Use keyframeable: false only on controls that are structurally unsupported by the shared keyframe capability helper; capable controls cannot opt out to hide broken animation wiring.", "Right-panel animation controls may tune renderer parameters such as mode, intensity, speed, or stagger only after animation intent is declared; they must not replace top timeline transport.", "Do not put Pause or Resume in panelActions; playback belongs to TimelinePanel transport controls.", "Do not replace TimelinePanel with an app-level playback, transport, or timeline panel to avoid runtime performance issues; fix the Toolcraft runtime clock/state path instead.", 'Custom timeline UI is allowed only for explicit referenceTimeline.mode "custom-reference-timeline" transfers with browser-backed referenceTimelineCoverage.', runtimeSetupModeRowRule, runtimeSetupModeHelpRule, "The Timeline Setup switch controls only compact versus extended runtime presentation: off shows the compact Play-only transport, on shows the extended timeline with scrubber, duration, loop, and keyframe UI. It must not stop playback, change keyframes, affect export, enter product values, or be reset by Reset controls/settings transfer.", "Playback-only timelines stay collapsed and do not show keyframe diamonds or expanded keyframe rows.", "If timeline verification fails, wire the renderer to runtime timeline state or remove panels.timeline.", ], capabilities: [ "draggable", "snap", "doubleClickReset", "dragMode:panel", "duration", "keyframes", "playback", ], commands: [ "timeline.changeKeyframeEasing", "timeline.deleteControlKeyframes", "timeline.deleteKeyframe", "timeline.moveKeyframe", "timeline.selectKeyframe", "panels.setHidden", "timeline.setCurrentTime", "timeline.setDuration", "timeline.setPlaying", "timeline.toggleLoop", "timeline.togglePlayback", ], }, toolbar: { ...panel("toolbar", "ToolbarPanel", "bottom", ["top", "bottom"], "panel"), aiUsageRules: [ "Toolbar history owns Undo and Redo buttons plus runtime keyboard shortcuts.", "Do not add app-level Cmd/Ctrl+Z, Cmd/Ctrl+Shift+Z, or Ctrl+Y listeners; use toolbar history and runtime commands.", "Undo/redo keyboard shortcuts stay native while the user edits text-entry inputs, textareas, selects, or contentEditable value labels; focused range, checkbox, and other non-text inputs use Toolcraft history.", ], capabilities: [ "draggable", "snap", "doubleClickReset", "history", "keyboardShortcuts", "zoom", "radar", ], commands: [ "history.undo", "history.redo", "canvas.center", "canvas.zoomIn", "canvas.zoomOut", "canvas.zoomReset", ], kind: "toolbar", }, } as const satisfies Record;