30 KiB
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.propor.stkformats 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 supplies the visual direction, and Dialog Design Principles supplies modal behavior. Stage panel UI system 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):
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:
{
"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
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 its title inside the panel and a visible border cue in Overview and Focus; Focus does not add a duplicate heading above the canvas.
Suggested typed additions:
# 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
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
- 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.
- 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.
- 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.
- 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.