--- phase: 1 slug: draft-state-ownership status: draft shadcn_initialized: false preset: none created: 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 | | Font | "Segoe UI Variable", "SF Pro Display", "IBM Plex Sans", system-ui, sans-serif | --- ## Spacing Scale Declared values (must be multiples of 4): | Token | Value | Usage | |-------|-------|-------| | xs | 4px | Status dots, inline separators, badge internals | | sm | 8px | Input-support gaps, chip gaps, tight action spacing | | md | 16px | 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. --- ## Typography | Role | Size | Weight | Line Height | |------|------|--------|-------------| | Body | 16px | 400 | 1.5 | | Label | 12px | 700 | 1.4 | | Heading | 20px | 700 | 1.2 | | Display | 28px | 700 | 1.1 | --- ## Color | Role | Value | Usage | |------|-------|-------| | Dominant (60%) | #0a1016 | App background, drawer shell, page canvas | | Secondary (30%) | #14202d | Cards, grouped sections, saved/live reference surfaces | | Accent (10%) | #48a6ff | 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`. --- ## Registry Safety | Registry | Blocks Used | Safety Gate | |----------|-------------|-------------| | shadcn official | none | not applicable — shadcn not initialized | | third-party | none | not applicable | --- ## Checker Sign-Off - [ ] Dimension 1 Copywriting: PASS - [ ] Dimension 2 Visuals: PASS - [ ] Dimension 3 Color: PASS - [ ] Dimension 4 Typography: PASS - [ ] Dimension 5 Spacing: PASS - [ ] Dimension 6 Registry Safety: PASS **Approval:** pending