161 lines
8.7 KiB
Markdown
161 lines
8.7 KiB
Markdown
---
|
|
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
|