import { panel } from "./component-contract-builders"; 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 immediately after settings transfer, then Background color and finite sizing; Timeline is the final Setup control 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: [ "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.", "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.", "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.", "Infinite exports use canonical world-space scene frames: ToolcraftAppComposition.sceneBoundsProvider contributes exact-state product bounds, runtime image and model bounds are unioned, hidden or suppressed content is excluded, PNG crops tightly, and runtime video unions every scheduled frame state into one envelope.", "Infinite live preview uses that same exact-state product frame: runtime positions one product scene surface from sceneBoundsProvider, and custom raster/WebGL output consumes useToolcraftProductSceneFrame for backing size and world-to-local translation instead of dormant canvas.size.", "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 uses the mandatory headerless Setup controls block; do not add a separate Canvas section label above these fields.", "When the user manually edits Canvas width or Canvas height, the runtime keeps the typed dimension, keeps the other dimension unchanged, switches Aspect ratio to Custom, stores the reduced current ratio in state, and keeps Canvas width and Canvas height as the sole custom numeric editors.", "Aspect ratio presets are the only interaction that may resize both canvas dimensions from a preset; manual size inputs are exact output dimensions.", "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.import", "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.', "Settings import/export is a mandatory runtime preset transfer feature; it must not be used to hide or replace broken 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.", "Do not gate Export Settings / Import Settings behind complexity thresholds, app size, or prompt wording.", "Do not hand-roll settings import/export through app routes, hidden file inputs, or panelActions.", "Settings transfer appears in the first visible headerless Setup controls-panel block; it imports and exports control values, canvas size, and timeline state.", "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: ["settings-import-export"], commands: ["controls.setValue", "timeline.setCurrentTime", "timeline.setDuration"], 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 legacy visibleWhen are runtime compatibility only and fail starter acceptance.', "Conditional applicability combines every predicate with AND, hides controls outside mode/type/source/include/variant/count branches, and preserves their runtime values while hidden.", "A visible product control must prove its existing product outcome in every supported finite sibling-selector branch from its Control Section Inventory, including always controls and selectors omitted from its predicates.", "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.", "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.", "One to seven controls is the normal section size. Eight to ten controls are allowed only for one cohesive entity and require semanticGroup on every control; ten is the hard maximum.", "Every Control Section Inventory entry declares required entityId, entity, exact targets, and groupingReason. One entity with ten or fewer controls stays in one section regardless of control type, visual height, or target namespace.", "An entity above ten controls splits into balanced workflow sections of two to ten controls. Every split section keeps the same entityId and entity and declares a unique workflowStage plus splitReason.", "Section splitting must preserve dependency cohesion: a selector stays with the applicability-gated controls it owns when they share the same target entity or selected branch. Use internal spacing/dividers or a more specific section title before splitting dependent branch controls away.", "Every app-authored controls-panel body section must have a short meaningful visible title. Runtime-created Setup renders as the first visible headerless controls block with no title, reset action, collapse button, or collapsed state; sticky footer action sections use the technical title Export but render without a visible heading.", "Every visible app-authored controls-panel section title renders through the standard 36px collapsible header row with vertically centered text and the runtime collapse icon; 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. Runtime Setup is not collapsible; 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.", "Runtime Setup uses 12px top spacing so its first control row has equal top, left, and right insets. Ordinary controls-panel body sections keep 8px top spacing. Both use 24px bottom spacing; sticky footer action sections keep their dedicated spacing.", "Broad section titles such as Flow, Icon, Shapes, Scene, Text, Typography, or Motion are only valid for small cohesive groups; use specific titles such as Flow Motion, Flow Geometry, Letter Burst, Shape Colors, Logo Glow, Logo Plate, or Text Block for larger groups.", "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"], 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 Setup, and excludes sticky action sections.", ], capabilities: [ ...controlsPanelContract.capabilities, "section-navigation", ], }, layersPanel: { ...panel("layersPanel", "LayersPanel", "left", ["left", "right"], "handle"), aiUsageRules: [ "Enable panels.layers only when the app needs editable layer selection, ordering, grouping, visibility, or multi-object media management.", "Do not enable the layers panel for single-layer apps.", "If the user intent is ambiguous, ask whether layer management is required before enabling panels.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;