phase, slug, status, shadcn_initialized, preset, created
phase
slug
status
shadcn_initialized
preset
created
1
draft-state-ownership
draft
false
none
2026-03-28
Phase 1 — UI Design Contract
Visual and interaction contract for frontend phases. Generated by gsd-ui-researcher, verified by gsd-ui-checker.
Design System
Property
Value
Tool
none — existing hand-authored CSS remains the source of truth for Phase 1; shadcn init deferred by default because this phase only defines the contract and does not require component-library adoption
Preset
not applicable
Component library
none — preserve existing runtime drawer, status card, inline chips, segmented controls, and footer actions
Icon library
none required for Phase 1; use text labels and existing native controls before introducing new iconography
Default field spacing, card padding, inline validation offset
lg
24px
Section padding, grouped control spacing
xl
32px
Drawer section breaks, footer-to-body separation
2xl
48px
Major content breaks inside long settings drawers
3xl
64px
Reserved for page-level or modal-level spacing only
Exceptions: minimum 44px input and button height for all primary interactive controls; destructive confirmation rows may use 12px vertical padding inside a 44px container.
Draft-state badges, selected segmented controls, focused fields, primary save CTA, latest-valid async result indicators
Destructive
#f04040
Discard/revert confirmations, inline invalid-state borders, blocking save errors
Accent reserved for: dirty badges, active local-vs-remote selectors, selected model chips, the primary Save changes action, focus-visible outlines, and explicit live-vs-draft comparison labels; never use accent for all neutral buttons.
Copywriting Contract
Element
Copy
Primary CTA
Save changes
Secondary footer action
Discard changes
Confirmation escape action
Keep editing
Empty state heading
No draft changes yet
Empty state body
Edit any runtime setting to start a draft. Live runtime values keep updating until you choose to save or discard.
Error state
Couldn't save your draft. Review the highlighted fields and try again — your unsaved changes are still here.
Destructive confirmation
Discard changes: Discard this draft and return to the last saved settings? / Revert to saved: Replace all unsaved edits with the last saved values.
State Presentation Contract
The sticky drawer header is the first visual anchor on open: Draft settings title, dirty-state summary, and compact Live now status reference appear before any editable fields.
Opening the settings drawer creates a draft from the latest saved baseline and labels the drawer as Draft settings.
The current effective runtime state remains visible as Live now reference content in a secondary surface or read-only inline value for any field changed in the draft.
Dirty fields show three signals together: draft accent border, Draft label, and saved/live reference value below the control; color alone is never the only indicator.
Clean fields render with neutral borders and no draft badge.
Closing the drawer with unsaved edits hides the drawer and preserves the draft for the same app session; do not show a discard prompt on close or backdrop click.
Discard changes is the UI label for the Phase 1 full-form reset behavior locked in 01-CONTEXT.md; Revert to saved is the destructive confirmation label for completing that same reset. Per-field undo is out of scope.
Interaction Contract
Polling continues every 3 seconds for operational state, but polling updates may affect only live status surfaces such as readiness, process state, health messaging, and managed-library metadata.
Polling and async helpers must never overwrite dirty input values, selected folders, provider choice, or selected model while the draft is dirty.
Folder pickers, file pickers, model rescans, and validation passes are field-scoped updates only; they may update the invoked field and its dependent helper text, not the whole form.
If multiple async requests exist for the same field, only the latest response may update draft-facing UI; stale responses are ignored silently.
When a drafted value becomes invalid after rescan or validation, preserve the typed or selected value, mark the field invalid inline, and disable Save changes until fixed.
Resting footer actions for this phase are Save changes and Discard changes; selecting Discard changes expands an inline confirmation row with Keep editing and destructive Revert to saved. Local-runtime apply/start text remains separate and must always reflect whether saving is required first.
Busy states disable the triggering button, preserve current field values, and replace button text with in-place progress copy such as Saving..., Scanning..., or Searching....
Error messaging appears inline near the affected field when possible; fall back to a single drawer-level error banner only for save failures or cross-field failures.
Component Inventory
Drawer shell: right-side fixed drawer, full-height, max width 560px, blurred backdrop, sticky header and footer.
Primary focal point: sticky header block with Draft settings title, dirty-state summary, and Live now chip/status summary before the overview strip.
Overview strip: three read-only summary cells for current status, endpoint, and saved model.
Field groups: card-based sections with 16px internal padding and 18px corner radius.
Segmented controls: two-option mode switch with clear active state and no icon-only affordances.
Draft comparison rows: editable control on top, saved/live helper row beneath when dirty.
Inline validation: destructive border plus one-line message directly under the affected field.
Confirmation pattern: inline footer confirmation row for discard/revert actions instead of a separate modal.
Accessibility and Motion
All interactive controls must meet a 44x44px minimum hit target.
Keyboard order must follow visual order from overview to footer actions.
:focus-visible keeps the 2px accent outline already established in tauri-app/src/index.css.
Body copy and helper text must maintain at least 4.5:1 contrast against their surfaces.
State changes use opacity, border, and background transitions only, with durations between 150ms and 200ms.
Respect prefers-reduced-motion; suppress scale transforms and animated status pulses when reduced motion is enabled.
Source Notes
Locked from D:\Documents\Github\ForgeLM\.planning\ROADMAP.md: Phase 1 goal and success criteria require explicit live-versus-draft separation, explicit save/cancel/revert semantics, and draft-safe failure recovery.
Locked from D:\Documents\Github\ForgeLM\.planning\phases\01-hf-model-discovery\01-CONTEXT.md: draft lifecycle, preserved close behavior, field-scoped async updates, stale-request ignoring, and invalid-draft preservation.
Grounded in existing UI from D:\Documents\Github\ForgeLM\tauri-app\src\index.css, D:\Documents\Github\ForgeLM\tauri-app\src\features\runtime\components\RuntimeSettingsDrawer.css, D:\Documents\Github\ForgeLM\tauri-app\src\features\runtime\components\RuntimeStatusCard.css, and D:\Documents\Github\ForgeLM\tauri-app\src\features\runtime\components\RuntimeSettingsDrawer.tsx.
Brand output conventions referenced from C:\Users\Evandro\.config\opencode\get-shit-done\references\ui-brand.md.