# Editor visual harmonization — architecture and UI specification > **Status:** Proposed for final user sign-off (2026-10-23). **Branch:** `feature/editor-visual-harmonization`. **Scope:** Prop Editor and Stickman Editor presentation, responsive workspaces, control feedback, dialogs, and save status. This specification does not change `.prop` or `.stk` formats or Stage inspector behavior. ## 1. Outcome and approved direction The two editors should read as parts of one Stickman Studio product while keeping their distinct authoring tasks. The canvas remains the main work surface. The Prop Editor retains its Arrange, Reshape, and Attach modes, split canvas, Attach preview, and scrolling inspector. The Stickman Editor gains **Overview**, with all ten body-part panels available, and **Focus on Part**, with one larger live panel, a body-part picker, and the Whole Stickman preview. Focus can be chosen at any window size. It is the automatic default only when the minimum widths needed by Overview do not fit. Once a child chooses a view, that choice remains in effect for the session through window resizing. The [Editor Visual Style Guide](../guides/editor_visual_style_guide.md) supplies the visual direction, and [Dialog Design Principles](dialog_design_principles.md) supplies modal behavior. [Stage panel UI system](stage_panel_ui_system.md) remains scoped to in-stage selected-object cards. Neither `StageInspectorPanel` nor its choice/toggle controls are transplanted into the persistent editor workspaces. The implementation proceeds in staged slices under this one contract. Each slice should be reviewable without changing the saved document schema. Final handoff waits for the behavior and rendered visual checks in §10. ## 2. Current architecture and constraints | Area | Current fact | Consequence | | --- | --- | --- | | Prop theme | `PropTheme` reads `prop_theme.json` with defaults, clamping, and typed accessors. `PropEditor` builds a local `Theme`; it also hardcodes some stylebox colors and gives mode buttons `FOCUS_NONE`. | Share chrome roles through one editor token source and restore a distinct keyboard focus state. Keep prop material, primitive, and canvas values under `PropTheme`. | | Prop layout | `prop_editor.tscn` has fixed top-row offsets, outer `HSplitContainer`, scrolling inspector, and an inner Attach split. | Replace brittle top-row positioning with container sizing; keep both splitters and the inspector scroll. | | Stickman state | `StickmanEditor` instantiates ten `BodyPartPanel` scenes, stores them by part name, and reads their live shapes for preview and save. Each panel also owns shape selection, zoom, pan, and popup state. | A view switch must move the same nodes. Calling `set_shape_data()` would reset selection, zoom, and pan. | | Stickman layout | The scene has two five-panel `VBoxContainer` columns and one `WholeStickmanPreview` in a fixed three-column `HBoxContainer`. Each body panel has a 230 × 160 minimum. | Overview needs scroll access; Focus needs a separate host and width-based initial choice. | | Stickman document feedback | Save and load serialize directly; no current path or dirty status is tracked. `WholeStickmanPreview.part_moved` reports translation, while rotation and scale have no commit signal. | Dirty tracking and unsaved-work flows need an explicit controller boundary covering every authored transform. | | Attach drawing order | Anchor `z_index_offset` is an integer clamped to −5…5. `PropAttachPreview` assigns it to the preview prop's `z_index`; held runtime props apply it relative to the rig's index. | Keep the signed value and preview result. Zero means normal ordering, not a universal “front” preset. | ## 3. Theme ownership and visual contract ### 3.1 One shared editor chrome source Add `scripts/shared/editor_ui/editor_visual_theme.gd` as `class_name EditorVisualTheme extends RefCounted` and `res://editor_visual_theme.json`. It owns **shared editor chrome**: background and raised surfaces, primary and muted text, EDIT accent, keyboard focus, primary/destructive actions, borders, typography, spacing, radii, and minimum control targets. Its loader follows the validated `PropTheme`/`SandboxTheme` pattern: immutable defaults, deep merge of the committed JSON, type validation, clamping, a warning once per invalid key, and a safe default when a file or value is missing. Do not read `SandboxTheme` at runtime to style either editor; its values are a product reference and its popup/inspector semantics belong to the Stage. Both editor controllers hold an `EditorVisualTheme` instance and build/apply their own local Godot `Theme` or per-control style overrides. They never mutate `ThemeDB`, the project-wide GUI theme, or a cached shared `Theme` for one selected control. `PropEditor` retains `PropTheme` for material tints, primitive defaults, outline widths, and **prop canvas** colors. Stickman canvas grid, silhouette guide, and selection colors live in a separate `stickman_canvas` namespace with typed accessors; artwork colors are never overridden by chrome styling. Existing `PropTheme.ui` getters may forward during the staged migration, but the final chrome value has one authoritative source in `EditorVisualTheme`. Remove duplicate configuration values when callers have migrated. Suggested typed contract (precise names may change in implementation while preserving ownership): ```gdscript class_name EditorVisualTheme extends RefCounted const DEFAULT_PATH: String = "res://editor_visual_theme.json" func load_from(path: String = DEFAULT_PATH) -> bool: ... func chrome_color(role: StringName, editor: StringName = &"") -> Color: ... func chrome_px(role: StringName, editor: StringName = &"") -> int: ... func stickman_canvas_color(role: StringName) -> Color: ... func control_theme(editor: StringName) -> Theme: ... # return an editor-local instance ``` The JSON payload is organized by semantic chrome and drawing purpose, for example: ```json { "version": "1.0", "chrome": { "colors": {"background": "#171B24F2", "surface": "#252B38FA", "focus": "#8BD9FF"}, "targets": {"button_min_height": 44, "mode_button_min_height": 56} }, "editors": {"prop": {}, "stickman": {}}, "stickman_canvas": {"grid_color": "#FFFFFF26", "selection_core": "#FFFFFF", "selection_halo": "#000000D9"} } ``` The complete committed config and script defaults must contain all roles in §3.2, not only these example keys. Resolution order is script defaults, committed shared JSON, then an explicit editor override for the requested editor. Known roles use named accessors or validated role IDs, never arbitrary dotted strings at call sites. An optional `editors.prop` / `editors.stickman` override is allowed only where density or composition has a documented need; shared meanings stay the same. Document any legacy `PropTheme.ui` accessor forwarding and removal in the implementation slice. ### 3.2 Baseline tokens and states | Role | Starting value or rule | Use | | --- | --- | --- | | Main background / raised surface | Stage references `#171B24F2` / `#252B38FA` | Editor chrome; keep drawing canvases visually distinct. | | Primary / muted text | `#F5F7FA` / `#AAB2C0` | Titles, labels, helper copy, disabled reasons. | | EDIT accent | `#22C6FF` | Active editor mode, part/view choice, and selection accent. | | Keyboard focus | `#8BD9FF`, at least a distinct 2 px outline | Visible over normal, hover, and pressed styles; native Tab/Space/Enter remain usable. | | Primary / destructive action | `#087DBA` / `#E66B75` | One forward dialog action and explicit erase/discard actions. Destructive controls retain words and a cancel path. | | Targets | At least 44 × 44 logical pixels for child-facing interactive controls; mode buttons may stay 56 px high | Increase Prop's current 36 px button/row minima; enlarge click areas without enlarging canvas handles visually. | | Density | Body text about 18–20 px, section headers about 20–24 px; compact Overview part title may be about 16–18 px | Confirm fit at minimum supported viewport and larger type. Focus may use a larger part title. | | Canvas selection | Bright core plus dark backing; handle size and hit area are screen-space | Preserve Prop's existing two-tone halo. Extend the same contrast principle to Stickman panel and preview gizmos. | The final theme must distinguish neutral, hover, pressed/selected, keyboard focus, disabled, dragging/preview, error, saved, and dirty states. Focus and selection may coexist; focus must not paint over or hide the active mode, as the current Prop button style does. Icons or emoji may add cues but each action remains understandable from text alone. The grid, pose guide, authored shape colors, and material colors remain canvas/domain tokens. ## 4. Stickman adaptive workspace ### 4.1 Node hierarchy and state owners ```text StickmanEditor : Control (document, save/load, dirty, active part, view choice) └─ ChromeRoot : VBoxContainer ├─ MenuBar / name and view controls ├─ WorkspaceRow : HSplitContainer │ ├─ BodyPartWorkspace : Control │ │ ├─ OverviewScroll : ScrollContainer │ │ │ └─ OverviewColumns : HBoxContainer │ │ │ ├─ LeftColumn : VBoxContainer (five original panels) │ │ │ └─ CenterColumn : VBoxContainer (five original panels) │ │ └─ FocusHost : VBoxContainer │ │ ├─ PartPicker : native choices, scroll/wrap as needed │ │ └─ FocusPanelHost : Control (one original BodyPartPanel) │ └─ WholeStickmanPreview : PanelContainer (script: WholeStickmanPreview; permanent sibling) └─ StatusBar ``` There is exactly one `BodyPartPanel` instance per part and one `WholeStickmanPreview` instance. The preview remains a permanent sibling of the body-part workspace in `WorkspaceRow`; it is not duplicated or serialized for a view switch. The controller records each panel's original Overview parent and index. Entering Focus reparents the active panel into `FocusPanelHost`; leaving Focus restores it at the original index. Changing focused part restores the outgoing panel before moving the incoming panel. The controller's existing panel dictionary and signal connections remain stable. `StickmanEditor` owns `enum WorkspaceView { OVERVIEW, FOCUS }`, `_active_part_name: String`, `_view_choice_was_manual: bool`, and the pending view/part request. `BodyPartPanel` owns its shapes, selected shape, zoom, and pan; the preview owns its assembly transform and zoom/pan. The part picker changes `_active_part_name` without calling `BodyPartPanel.select()`, which currently selects the topmost shape and would overwrite the child's shape selection. Clicking a panel updates the active part through the existing selection signal. The active part gets a text label and visible border cue in Overview and Focus. Suggested typed additions: ```gdscript # BodyPartPanel signal presentation_safe func can_switch_presentation() -> bool: ... # false during drag, pan, or popup func add_shape(kind: StringName, at_world: Vector2) -> bool: ... func open_selected_color_picker() -> void: ... # StickmanEditor func request_workspace_view(view: WorkspaceView, user_chosen: bool = true) -> void: ... func request_active_part(part_name: String) -> void: ... func _apply_pending_presentation() -> void: ... ``` The picker and view buttons emit typed intent (`view_requested(view: int)`, `part_requested(part_name: String)` if extracted as a small reusable control). The controller validates the part ID against `BODY_PART_NAMES`. No signal bus or saved UI resource is needed for these synchronous presentation decisions. Keep one visible action row tied to the active part in either view. **Add Shape** opens the existing Line/Rectangle/Circle choices and places the new shape at the center of that panel's visible drawing area; **Color** opens the selected shape's existing picker and is disabled with a clear reason until a shape is selected. `BodyPartPanel` exposes small typed methods for those actions and remains the owner of shape mutation. The right-click menu stays available for point editing, ordering, copy/paste, mirroring, and erase; use readable wording and a short on-canvas hint so children can discover those secondary tools. This shared row avoids repeating full 44 px toolbars inside all ten Overview panels and shrinking their drawing areas. ### 4.2 Responsive rules and safe switching The automatic initial view is calculated after the first layout pass from available workspace width and the **Overview** content minimum: both panel columns at their useful minimum widths, container gaps, and the preview's useful minimum width. Use the same stable reference widths when the window resizes; a hidden/expanded view must not alter the threshold. If Overview fits, the default is Overview; otherwise Focus. Automatic reevaluation applies only until a child chooses either view. A manual choice remains for the session, including across later resizing and document load/clear. Do not put it in `.stk` or `user://settings.json` in this phase. Overview keeps all ten panels reachable, with vertical and, when needed, horizontal scrolling rather than shrinking drawing areas below their usable minimums. A manual Overview choice remains available at a narrow width. Focus is available at any width. Wide Focus shows a persistent, resizable Whole Stickman preview in the split beside the enlarged selected panel. Narrow Focus starts with the preview collapsed so the drawing panel keeps its minimum width; a visible **Whole Stickman** button opens and closes it. If both pane minima cannot fit at once, opening the preview gives it the available workspace and offers a visible **Back to Part** action; no control or panel may be clipped. Keep the preview node alive so its zoom, pan, guide, selection, and part transforms survive collapse and return. Do not reparent a panel during vertex/shape drag, pan, context menu, or color popup. Store the requested view/part and apply it when the panel reports a safe point after the gesture or popup closes. This avoids a partial geometry commit and avoids discarding a color choice. A view switch must not call `set_shape_data()`, `clear_shape()`, `reset_view()`, or `select()`; those methods change selection or view context. In particular, `set_shape_data()` currently resets zoom/pan/selection. A presentation change changes no document data, emits no shape-changed signal, and does not mark the document dirty. ### 4.3 Data and event flow ```mermaid flowchart LR View[Overview / Focus request] --> Guard{Panel gesture or popup active?} Guard -->|yes| Pending[Keep request pending] Guard -->|no| Move[Reparent one live panel] Pending -->|presentation_safe| Move Move --> Refresh[Update picker, active cue, preview visibility] Refresh --> Status[Document dirty state unchanged] Edit[Draw or transform a part] --> Owner[Live BodyPartPanel / WholeStickmanPreview] Owner --> Controller[StickmanEditor observes commit] Controller --> Assembly[Refresh assembled preview] Controller --> Dirty[Mark Unsaved changes] ``` For example, when the child focuses the left lower arm, its original panel moves from `LeftColumn` to `FocusPanelHost`. Its current `shapes`, selected shape index, `_zoom`, and `_pan_offset` remain in that node. Editing still triggers the controller's existing shape-changed handler; the preview reads from the same ten-node dictionary. Returning to Overview restores that panel to its original slot with the same drawing context. ## 5. Prop Editor workspace and controls Keep the existing `PropCanvas`, scrolling inspector, outer canvas/inspector `HSplitContainer`, and inner Attach preview split. Convert fixed `TopRow`/`TopBar` offsets into container-driven chrome so Grid/Snap, the name field, and mode controls remain visible at larger fonts and narrow sizes. Tool rows may wrap through `FlowContainer`; keep action order stable. Maintain a useful canvas minimum and allow the inspector to scroll rather than clip. Preserve the clicked control's position when a mode reveals a section or Attach preview. Restore keyboard focus on Arrange, Reshape, and Attach mode buttons. Give the local Button theme a focus outline that composes with the pressed/selected appearance; keep selected state persistent and text stable. Programmatic refresh uses `set_pressed_no_signal()` as today. Disabled controls explain nonobvious reasons near the control or via a clear tooltip. Arrange-only and Attach-only controls retain their existing mode gating, document mutation, and preview-only behavior. Use visible labels and 44 px targets for the primary shape/anchor operations currently hidden in context menus where layout permits. Context menus remain useful shortcuts; the common child path for adding a shape, reshaping a point, and choosing a body part should have an obvious on-screen entry. Preserve Prop's existing two-tone halo and keep its screen-space handles and hit areas distinct. Anchor and preview markers need equivalent contrast against light and dark props. Canvas artwork and material tint values remain unchanged by chrome token migration. ## 6. Copy, dialogs, and save feedback ### 6.1 Child-facing wording | Current wording | Target copy / rule | Behavioral reason | | --- | --- | --- | | Prop `Friction: 0.xx` | **Surface grip: 0.xx**, with optional helper “Higher values make sliding harder.” | Keep the exact `PhysicsMaterial.friction` value and its surface-contact meaning; do not imply hand gripping or change physics presets. | | Attach `Z Offset` | **Held prop layer** with nearby “Negative: behind · 0: normal order · Positive: farther in front.” Keep the −5…5 integer control and show its visual effect in the preview. | `z_index_offset` is a signed render-layer offset in both preview and held runtime behavior. Zero may draw in front in the preview due tree order, so do not relabel zero as a fixed front/behind category. | | Destructive commands | Prefer **Erase Shape**, **Start Over**, and explicit **Discard Changes** in dialogs. For Start Over, state “Start over with a blank stickman?” and keep a separate visible **Cancel** action. | Say which authored work is affected; pair with a visible Cancel path. | | Technical axis labels | Keep X/Y when they are editable coordinates; use plain helper text where the operation is unclear. | Avoid silently changing geometry or transform semantics while updating words. | The Prop Editor's existing dirty title/status and autosave/recovery flow remain authoritative. Improve status wording to clear **Saved** / **Unsaved changes** states; a failed save/load keeps the current document and shows an actionable reason. Preview-only choices, Show Stickman Preview, marker visibility, and mode/view changes do not mark dirty. The Stickman Editor adds controller-owned `_current_path: String` and `_dirty: bool`. Name changes, shape edits, shape order, color edits, and committed Whole Stickman position/rotation/scale changes mark dirty; grid/snap, guide, zoom/pan, selection, part picker, and Overview/Focus do not. Add one transform-commit signal or equivalent controller-observable path to `WholeStickmanPreview` that covers translation, rotation, and scale, because its current `part_moved` signal covers only translation. Only a successful save clears dirty; a failed save preserves it. Successful load/clear initializes the expected clean state, and save/load continue to use the existing `.stk` schema and `ContentPaths.canonicalize()` behavior. Before Stickman **Load**, **Clear**, or window close replaces unsaved work, show the child-facing **Save / Discard Changes / Cancel** choice. Save with no usable path opens the existing Save dialog and resumes the pending action only after successful save. Cancel or a failed save leaves the document and active view untouched. Clear continues to name that it removes the current stickman. Keep Prop's current New/Close/recovery transactions intact while restyling them. Both editors' dialogs follow the dialog guide: rounded shell, readable type, blue primary action, distinct danger action, 44 px targets, visible labeled dismissal, safe viewport fit, and stable anchor when contents change. Adapt `StageDialogStyler` visual rules; its Stage overlay host is not part of either editor. ## 7. Static contracts and document boundaries | Target | Contract | Owner / boundary | | --- | --- | --- | | `EditorVisualTheme : RefCounted` | `load_from(path: String) -> bool`; typed semantic color/size accessors; local `Theme` construction or copies | Validated shared chrome only; no Stage runtime dependency. | | `PropTheme : RefCounted` | Existing material, primitive, outline, and canvas accessors; temporary UI forwards only while callers migrate | Prop-specific presentation and authoring defaults; never physics material mass/friction/bounce values. | | `PropEditor : Control` | Existing mode, file, autosave, inspector signals and handlers; consume shared chrome tokens | Prop document mutation, Save/Load, dirty, mode control, local Theme. | | `PropCanvas : Control` | Existing typed mode and shape/anchor signals; selection drawing uses canvas tokens | Authoring input and visual feedback. | | `StickmanEditor : Control` | Add `class_name StickmanEditor`; `WorkspaceView`, `request_workspace_view`, `request_active_part`; `_dirty`/pending document action | One live panel dictionary, responsive presentation, `.stk` save/load, dirty state. | | `BodyPartPanel : PanelContainer` | Existing `shape_changed(Array)` and `shape_selected()`; `can_switch_presentation() -> bool`, `presentation_safe`, `add_shape(kind: StringName, at_world: Vector2) -> bool`, `open_selected_color_picker() -> void` | One part's shapes, selection, zoom/pan, gesture/popup safety, and shape operations. | | `WholeStickmanPreview : PanelContainer` | Existing `part_moved(String, Vector2)`; add typed `part_transform_committed(part_name: String, position: Vector2, rotation_deg: float, scale: Vector2)` or equivalent | One assembled preview and document transform commit boundary. | No new global bus, serialized `Resource`, duplicate canvas instance, or `.prop`/`.stk` version bump is needed. UI preferences remain session state unless a later feature specifically approves persistence. Saving gathers from the ten live panels and the one live preview, independent of which view is visible. ## 8. Staged implementation slices 1. **Shared foundation.** Add the shared editor chrome theme/config and typed accessors. Set semantic roles, focus/selection/disabled states, minimum targets, and scoped theme application. Keep Prop-specific canvas/material tokens. Confirm both config and defaults match. 2. **Prop Editor pass.** Migrate Prop chrome to shared roles, fix mode-button keyboard focus, raise touch targets, make top controls and inspector fit, refine labels/help, and style existing dialogs. Preserve its file and autosave behavior. 3. **Stickman workspace pass.** Apply the theme to Stickman chrome, introduce Overview/Focus and the body-part picker, move live panel instances safely, add scrolling and narrow preview collapse, preserve drawing context, and expose active-part feedback. 4. **Shared state and recovery pass.** Align canvas contrast and action discovery, add complete Stickman dirty/unsaved transactions, and finish dialog and status consistency. Resolve remaining measured layout and contrast issues from rendered review. Keep one spec so theme roles, view state, and save behavior are reviewed together. Each slice should leave its editor usable and should report which acceptance outcomes below it satisfies. User-guide changes follow the spec-pipeline documentation phase after the user verifies the feature in the editor. ## 9. Architectural trade-offs and risks | Decision | Benefit | Cost / mitigation | | --- | --- | --- | | Shared semantic editor source with local editor Themes | Consistent chrome and focus states without coupling to Stage internals | Migrate Prop's old `ui` values deliberately; prevent two active authorities for one chrome role. | | Reparent one existing BodyPartPanel into Focus | Preserves shapes, selection, zoom/pan, signals, and current save architecture | Node movement during a gesture/popup is unsafe; defer until `presentation_safe`. Keep original parent/index map. | | Permanent WholeStickmanPreview sibling | One source of assembly transform and preview view state | Narrow layout needs collapse/return controls and a minimum-width fallback. | | Session-only manual view choice | A child's chosen workspace does not jump on resize | Automatic layout helps only until the first manual choice; narrow Overview requires scroll access. | | One umbrella specification, staged slices | Prevents token, mode, and state contracts from drifting across separate documents | Each slice needs explicit acceptance accounting and user review. | | Stickman dirty state added with transform commit | Protects edits during Load/Clear/Close | All authored mutation paths must be audited; translation-only signal is insufficient. | Known dependencies: the untracked style guide and design-direction board are source references in this checkout; unrelated dirty Stage work must remain untouched. Theme and layout work can be implemented independently of that Stage work. Prop's in-Stage authoring mode also instantiates `PropEditor`, so chrome changes must preserve its host and hide/disable behavior. Long labels, emoji fallback, larger fonts, and small viewports may expose new minimum-size pressure; resolve by wrapping/scrolling and measured fitting, not by silently shrinking controls. ## 10. Observable acceptance and verification routes | User path | Observable outcome | Verification route | | --- | --- | --- | | Open each editor at a wide window; Tab through menu, primary controls, mode/view buttons, inspector, and dialogs; activate with Space/Enter. | All focusable controls show a distinct focus ring, active mode/view remains visible under focus, and controls meet the agreed target size. | Rendered GUI capture plus real keyboard navigation at ordinary and larger font settings. | | Open Stickman wide, select a part, use Add Shape and Color, draw in a second part, choose Focus on Part, change the focused part, then return to Overview. | The visible actions affect only the active part; Overview has all ten panels; Focus works at wide width; each part retains shapes, selected shape, zoom, and pan; preview updates from edits; no view switch marks dirty. | Interact through production controls; compare panel and preview state before/after switches and inspect rendered captures. | | Open Stickman where Overview's measured minimum does not fit; choose Overview manually, resize wider and narrower, then choose Focus. | Focus is the initial automatic view; Overview remains reachable through scrolling; manual choice remains through resize; Focus preview collapses with visible Whole Stickman access and returns without resetting preview view state. | Rendered narrow/wide GUI session with actual resize and control input. Record viewport dimensions and pane sizes. | | Begin a panel drag or open its color popup, request another view/part, then finish or dismiss that interaction. | The switch waits until the interaction is safe; geometry/color is neither partially committed nor lost; selected shape and pan/zoom remain. | Real input path through the panel and popup; inspect shapes and resulting selection after release/dismissal. | | In Prop, switch Arrange/Reshape/Attach, resize, edit a shape and anchor, and open its preview. | Mode state, inspector sections, top Grid/Snap, canvas, and Attach preview remain reachable without clipped labels or jumping controls; existing document and preview-only dirty rules hold. | Rendered GUI captures of all modes at wide/narrow sizes; check actual edits and status. | | In Prop Attach, set Held prop layer to −1, 0, and +1 and view the preview; save and reopen. | Negative places the held prop behind the rig, zero follows normal ordering, positive places it farther in front; numeric −5…5 values round-trip unchanged. | Preview capture for each value plus a small repeatable save/reopen scenario using an anchor and visible overlapping prop. Confirm runtime held ordering in a Stage scenario where a rig holds the same prop. | | In Prop, choose a material preset and read Surface grip, then switch presets. | The displayed value equals `PhysicsMaterial.friction`; wording describes sliding resistance without changing physics values. | Inspector readback against preset values and rendered text check. | | Edit a Stickman shape, move/rotate/scale a preview part, then Save; edit again and choose Load, Clear, or close. | Every authored mutation sets Unsaved changes; successful Save clears it; Save/Discard Changes/Cancel protects the current document and resumes only the chosen action. | Small repeatable `.stk` scenario: draw and transform two parts, save, reload, compare shape/transform data, then exercise each unsaved branch. Measure status and file content before/after. | | Open save/load/recovery/clear/error dialogs; enlarge type or shorten the viewport. | Primary/destructive actions, visible Cancel/Back/Close, message, and focused control remain accessible; content scrolls or fits without abrupt recentering after a choice. | Rendered dialog states and real pointer/keyboard interactions; capture normal, error, and destructive branches. | | Select artwork with light and dark fills in each editor, drag handles, and use the pose guide/Attach markers. | Selection, active handle, guide, and markers remain legible over both fills and at working zoom; grid stays quieter than the artwork. | Rendered pixel inspection at named scenes/states, with a contrast/color-vision check for text and UI state. | Headless parsing or a successful scene launch alone does not establish these visual or interaction outcomes. During implementation, run targeted Godot import/parse and relevant existing regressions, then use a render-capable editor session and real controls for the visual paths. Record scene/state, viewport size, capture location, observed result, and any outcome that could not be verified. Stop for user verification after agent evidence is complete; update user guides only after the user confirms the feature works.