chore: complete v1.3 milestone — Backend Expansion

Archives v1.3 milestone (Phase 13: 18 rclone backends), updates PROJECT.md
with all 30 validated requirements, reorganizes ROADMAP.md with all 4 milestones
in collapsed details blocks, adds v1.2 and v1.3 retrospective entries.

Also commits minor CSS token fixes in src/ (text-on-surface-container class
alignment) that were uncommitted from phase 13 execution.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-16 16:54:45 +02:00
co-authored by Claude Sonnet 4.6
parent f75b499ad6
commit a26608791f
19 changed files with 591 additions and 230 deletions
+3 -1
View File
@@ -62,7 +62,9 @@
"Bash(node \"$HOME/.claude/get-shit-done/bin/gsd-tools.cjs\" requirements mark-complete UX-01)", "Bash(node \"$HOME/.claude/get-shit-done/bin/gsd-tools.cjs\" requirements mark-complete UX-01)",
"Bash(node \"$HOME/.claude/get-shit-done/bin/gsd-tools.cjs\" roadmap get-phase \"07\")", "Bash(node \"$HOME/.claude/get-shit-done/bin/gsd-tools.cjs\" roadmap get-phase \"07\")",
"Bash(grep \"^status:\" .planning/phases/07-validation-ux-polish/*-VERIFICATION.md)", "Bash(grep \"^status:\" .planning/phases/07-validation-ux-polish/*-VERIFICATION.md)",
"Bash(node \"$HOME/.claude/get-shit-done/bin/gsd-tools.cjs\" commit \"docs\\(phase-07\\): complete phase execution — VALID-01 and UX-01 verified\" --files .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md .planning/phases/07-validation-ux-polish/07-VERIFICATION.md)" "Bash(node \"$HOME/.claude/get-shit-done/bin/gsd-tools.cjs\" commit \"docs\\(phase-07\\): complete phase execution — VALID-01 and UX-01 verified\" --files .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md .planning/phases/07-validation-ux-polish/07-VERIFICATION.md)",
"Bash(node \"$HOME/.claude/get-shit-done/bin/gsd-tools.cjs\" commit \"test\\(09\\): complete UAT - 6 passed, 1 issue\" --files \".planning/phases/09-md3-components/09-UAT.md\")",
"Bash(node \"$HOME/.claude/get-shit-done/bin/gsd-tools.cjs\" commit \"test\\(11\\): complete UAT - 8 passed, 0 issues\" --files \".planning/phases/11-polish-responsiveness/11-UAT.md\")"
] ]
} }
} }
+37
View File
@@ -1,5 +1,42 @@
# Milestones # Milestones
## v1.3 Backend Expansion (Shipped: 2026-04-16)
**Phases completed:** 1 phase (13), 4 plans
**Timeline:** 2026-04-01 (1 day)
**Stats:** 28 files changed, +3,959 / -206 lines; total 6,055 TypeScript LOC
**Key accomplishments:**
1. BACKEND_REGISTRY expanded from 7 → 18 backends; BackendType derived from `keyof typeof` — no manual union to maintain
2. BACKEND_SCHEMAS auto-generated from registry via `Object.fromEntries` — zero per-backend boilerplate
3. OAuthInstructions collapsible component with backend-specific `rclone authorize` commands (TDD, 4 tests)
4. GdriveAuthToggle dual-auth component (OAuth token vs service account) for Google Drive
5. BackendSelectionStep with category groups, instant search filtering, and inline SVG icons
6. RemoteConfigStep wired for all 18 backends via `renderBackendFields()` switch function; `backendLabel` derived from registry
**Archive:** `.planning/milestones/v1.3-ROADMAP.md`, `.planning/milestones/v1.3-REQUIREMENTS.md`
---
## v1.2 UI Polish & Clarity (Shipped: 2026-04-01)
**Phases completed:** 5 phases (812), 14 plans
**Timeline:** 2026-03-31 → 2026-04-01 (2 days)
**Stats:** 78 files changed, +9,908 / -287 lines
**Key accomplishments:**
1. Full MD3 color token system — CSS custom properties in a single token file, all components use semantic classes (`bg-surface`, `text-on-primary`, etc.)
2. Dark mode toggle (System/Light/Dark) — DOM class toggle pattern (not React Context), persisted in localStorage, zero FOUC
3. TextFieldMD3 with CSS-only floating label (`peer-[:not(:placeholder-shown)]`), MD3 button hierarchy constants, rebuilt StepIndicator with numbered circles + checkmarks
4. App intro section, step descriptions, remote name live config preview, and contextual help/tooltip enrichment across all 18 credential fields
5. Mobile-responsive layout, MD3 `focus-visible` ring-3 indicators on all interactive elements
6. Step fade/slide transition with `prefers-reduced-motion` guard; auto-scroll to first validation error
7. Dark mode visibility fixes: semantic tokens on select, h2 headings, DeploymentStep form controls, ReviewStep security checkbox
**Archive:** `.planning/milestones/v1.3-ROADMAP.md` (includes v1.2 phases in archive)
---
## v1.0 MVP (Shipped: 2026-03-27) ## v1.0 MVP (Shipped: 2026-03-27)
**Phases completed:** 4 phases, 18 plans **Phases completed:** 4 phases, 18 plans
+83 -44
View File
@@ -2,7 +2,7 @@
## What This Is ## What This Is
Ready2Blob is a pure-frontend web app that helps IT professionals deploy rclone onto end-user Windows PCs. A step-by-step wizard collects backend credentials (Azure Blob, S3, S3-compatible, OneDrive, SFTP, Google Cloud Storage, Backblaze B2) and deployment preferences, then generates rclone config files and PowerShell scripts ready to push via Intune or any RMM tool — all client-side, no server required. Ready2Blob is a pure-frontend web app that helps IT professionals deploy rclone onto end-user Windows PCs. A step-by-step wizard collects backend credentials (18 backends: Azure Blob, S3, S3-compatible, OneDrive, SFTP, GCS, Backblaze B2, Google Drive, Dropbox, Box, pCloud, FTP, WebDAV, SMB, HTTP, Seafile, Yandex Disk, Koofr) and deployment preferences, then generates rclone config files and PowerShell scripts ready to push via Intune or any RMM tool — all client-side, no server required.
## Core Value ## Core Value
@@ -10,26 +10,68 @@ An IT pro can go from zero to a deployable rclone setup in minutes — without n
## Current State ## Current State
**Shipped:** v1.1 — 2026-03-31 **Shipped:** v1.3 — 2026-04-16
**Backends:** 7 (Azure Blob, S3, S3-compatible, OneDrive, SFTP, GCS, Backblaze B2) **Backends:** 18 (Azure Blob, S3, S3-compatible, OneDrive, SFTP, GCS, Backblaze B2, Google Drive, Dropbox, Box, pCloud, FTP, WebDAV, SMB, HTTP, Seafile, Yandex Disk, Koofr)
**Tech stack:** Vite 6, React 18, TypeScript 5, Tailwind v4, react-hook-form 7, Zod 4, Vitest 4, JSZip **Tech stack:** Vite 6, React 18, TypeScript 5, Tailwind v4, react-hook-form 7, Zod 4, Vitest 4, JSZip
**Test coverage:** 159 tests across all test files — all GREEN **Test coverage:** 165+ tests across all test files — all GREEN
**Total milestones:** 2 shipped (v1.0 + v1.1), 7 phases, 29 plans **Total milestones:** 3 shipped (v1.0 + v1.1 + v1.2 + v1.3), 13 phases, 40 plans
## Current Milestone: v1.2 UI Polish & Clarity ## Requirements
**Goal:** Transform the bare functional wizard into a polished, self-explanatory experience — Material Design 3 overhaul, dark mode, contextual help, and cleanup of carried UI debt. ### Validated
**Target features:** - ✓ Backend Schema Registry as single source of truth for form rendering, Zod validation, config generation — v1.0
- Remote name field clarity (explanation, examples, contextual help) - ✓ 4-step wizard (backend selection → config → deployment → review/download) with back-navigation — v1.0
- Step-level descriptions explaining what each wizard step does - ✓ Review + download layer: live preview, security gate, individual downloads, ZIP bundle, clipboard copy — v1.0
- App landing/intro section explaining what Ready2Blob is - ✓ Registry-driven BackendSelectionStep, scriptTargets filtering, ReviewStep Back button, dead export cleanup, act() fix — v1.1
- Full Material Design 3 component overhaul (cards, inputs, buttons, elevation, shape, color tokens) - ✓ 4 new backends (OneDrive, SFTP, GCS, B2), SftpAuthToggle, inline field validation, contextual tooltips — v1.1
- Dark mode toggle with accent color system - **THEME-01**: MD3 color token system (CSS custom properties), all components use semantic classes — v1.2
- Help text improvements across all backend fields - **THEME-02**: System/Light/Dark theme toggle, persisted in localStorage, zero FOUC — v1.2
- FieldRenderer aria consistency fix - **COMP-01**: MD3 outlined text fields with CSS-only floating labels — v1.2
- StepIndicator migration from inline styles to Tailwind - **COMP-02**: MD3 button hierarchy (filled/outlined/text) across all wizard steps — v1.2
- Mobile responsiveness improvements - **COMP-03**: Backend cards and output blocks with MD3 elevation and tonal surface tint — v1.2
-**COMP-04**: StepIndicator with numbered circles, connectors, checkmarks — v1.2
-**DEBT-01**: FieldRenderer consistent `aria-label` across text/select branches — v1.2
-**UX-01**: Intro section explaining Ready2Blob with CTA to start wizard — v1.2
-**UX-02**: Remote name field with example, help text, live config preview — v1.2
-**UX-03**: 1-2 sentence step descriptions on all 4 wizard steps — v1.2
-**UX-04**: Contextual help text on all backend credential fields — v1.2
-**POLISH-01**: Mobile-responsive layout (stacking cards, full-width fields, stretch buttons) — v1.2
-**POLISH-02**: MD3 `focus-visible` ring-3 indicators on all interactive elements — v1.2
-**POLISH-03**: Step fade/slide transition with `prefers-reduced-motion` guard — v1.2
-**POLISH-04**: Auto-scroll to first validation error on form submission — v1.2
-**DARK-01**: All wizard step h2 headings readable in dark mode — v1.2
-**DARK-02**: DeploymentStep native form controls visible with semantic tokens in dark mode — v1.2
-**DARK-03**: FieldRenderer select has proper bg/text color in dark mode — v1.2
-**REMOTE-01**: BACKEND_REGISTRY with 18 backends, correct rclone fields, displayName, description, category — v1.3
-**REMOTE-02**: BackendType derived from `keyof typeof BACKEND_REGISTRY` — no manual union — v1.3
-**REMOTE-03**: BackendSelectionStep with category groups, instant search, inline SVG icons — v1.3
-**REMOTE-04**: OAuthInstructions collapsible component with backend-specific `rclone authorize` — v1.3
-**REMOTE-05**: RemoteConfigStep renders correct form for all 18 backends including GdriveAuthToggle — v1.3
-**REMOTE-06**: buildRcloneConf produces valid rclone.conf for all 18 backends — v1.3
### Active
*(No active requirements — planning next milestone)*
### Out of Scope
| Feature | Reason |
|---------|--------|
| User accounts / saved templates | No backend, no persistence |
| Server-side generation | Everything runs in the browser |
| Test connection / validate credentials | Requires backend proxy (CORS blocks direct cloud API calls) |
| rclone version management / auto-update on endpoints | Separate lifecycle problem |
| Non-Windows targets | Scripts target Windows endpoints only |
| Auto-push to Intune via Graph API | Requires Azure AD app registration, massive scope increase |
| Config credential encryption | rclone encrypted configs require interactive unlock, incompatible with unattended deployment |
| Multiple remotes in one config | Significant wizard UX complexity, defer to v2 |
| OAuth flow in browser | Requires backend proxy; paste pre-obtained token instead |
| MUI or Material Tailwind component library | Bundle bloat (~300KB+) for 4-step wizard; custom tokens + Tailwind lighter |
| Page-per-step routing (React Router) | Wizard state is ephemeral credentials in memory; refreshing loses context |
| Glassmorphism / neomorphism effects | CPU-intensive, low contrast accessibility issues |
| Arbitrary user-picked hex color | Breaks accessibility; curated presets cover 95% of personalization need |
| Custom checkbox/radio styling | High effort for few toggles; native inputs sufficient |
## Constraints ## Constraints
@@ -37,18 +79,6 @@ An IT pro can go from zero to a deployable rclone setup in minutes — without n
- **Target OS**: Windows endpoints only (scripts are PowerShell) - **Target OS**: Windows endpoints only (scripts are PowerShell)
- **Secrets**: Plain text in generated files — UI must display a clear security warning before download - **Secrets**: Plain text in generated files — UI must display a clear security warning before download
## Out of Scope
- User accounts / saved templates — no backend, no persistence
- Server-side generation — everything runs in the browser
- Test connection / validate credentials — requires backend proxy (CORS blocks direct cloud API calls)
- rclone version management / auto-update on endpoints — separate lifecycle problem
- Non-Windows targets — scripts target Windows endpoints only
- Auto-push to Intune via Graph API — requires Azure AD app registration, massive scope increase
- Config credential encryption — rclone encrypted configs require interactive unlock, incompatible with unattended deployment
- Multiple remotes in one config — significant wizard UX complexity, defer to v2
- OAuth flow in browser (OneDrive) — requires backend proxy; paste pre-obtained token instead
## Key Decisions ## Key Decisions
| Decision | Rationale | Outcome | | Decision | Rationale | Outcome |
@@ -57,33 +87,42 @@ An IT pro can go from zero to a deployable rclone setup in minutes — without n
| BACKEND_REGISTRY as single source of truth | Drives form rendering, Zod schemas, and config generation from one definition | ✓ Good — zero duplication across phases | | BACKEND_REGISTRY as single source of truth | Drives form rendering, Zod schemas, and config generation from one definition | ✓ Good — zero duplication across phases |
| useReducer + Context API (no external state lib) | Zero dependencies, sufficient for 4-step wizard | ✓ Good — kept bundle lean | | useReducer + Context API (no external state lib) | Zero dependencies, sufficient for 4-step wizard | ✓ Good — kept bundle lean |
| Wave 0 TDD (stubs RED before implementation) | Forces API contract before code, catches integration mismatches early | ✓ Good — caught several issues upfront | | Wave 0 TDD (stubs RED before implementation) | Forces API contract before code, catches integration mismatches early | ✓ Good — caught several issues upfront |
| Support all rclone backends (not Azure-only) | Broader utility despite "Blob" in project name | ✓ Good — 7 backends and growing | | Support all rclone backends (not Azure-only) | Broader utility despite "Blob" in project name | ✓ Good — 18 backends shipped |
| Optional rclone install in scripts | Different orgs have different baseline images | ✓ Good — no friction for orgs that pre-install | | Optional rclone install in scripts | Different orgs have different baseline images | ✓ Good — no friction for orgs that pre-install |
| Tailwind v4 via @tailwindcss/vite plugin | No postcss.config.js, no tailwind.config.js needed | ✓ Good — cleaner config | | Tailwind v4 via @tailwindcss/vite plugin | No postcss.config.js, no tailwind.config.js needed | ✓ Good — cleaner config |
| Zod v4 + @hookform/resolvers v5 together | v5 resolvers required for Zod v4 compatibility | ✓ Good — noted for future upgrades | | Zod v4 + @hookform/resolvers v5 together | v5 resolvers required for Zod v4 compatibility | ✓ Good — noted for future upgrades |
| AzureAuthToggle / SftpAuthToggle CSS hidden pattern | Preserves both auth fields in react-hook-form state when toggling | ✓ Good — avoids field reset on toggle | | AzureAuthToggle / SftpAuthToggle CSS hidden pattern | Preserves both auth fields in react-hook-form state when toggling | ✓ Good — extended to GdriveAuthToggle in v1.3 |
| FieldDef.validate + tooltipText extension | Additive interface change, non-breaking to existing backends | ✓ Good — clean composition | | FieldDef.validate + tooltipText extension | Additive interface change, non-breaking to existing backends | ✓ Good — clean composition |
| Registry-loop rendering in RemoteConfigStep | Three-branch ternary (azureblob/sftp/else) — custom logic minimal | ✓ Good — scales with new backends | | ThemeToggle via DOM class toggle, not React Context | Avoids re-render cascade on theme change | ✓ Good — instant theme switch |
| Two-layer CSS token pattern (@theme + @layer base) | Required for .dark cascade in Tailwind v4 | ✓ Good — only valid approach for v4 |
| vi.stubGlobal for localStorage/matchMedia in tests | Node v25 experimental WebStorage breaks standard Storage API in jsdom | ✓ Good — necessary workaround |
| TextFieldMD3 CSS-only floating label | `peer-[:not(:placeholder-shown)]` — no JS state | ✓ Good — simpler, animatable |
| DEBT-01 fix: aria-label directly on tooltip buttons | Prevents getByLabelText ambiguity with { selector: 'input' } guard | ✓ Good — consistent pattern |
| key={state.currentStep} on step wrapper | Forces React remount to trigger CSS animation from initial state | ✓ Good — cleanest animation approach |
| BackendType derived from `keyof typeof BACKEND_REGISTRY` | Eliminates manual union maintenance at 18+ backends | ✓ Good — adding a backend is one touch-point |
| BACKEND_SCHEMAS auto-generated via Object.fromEntries | No per-backend buildZodSchema call needed | ✓ Good — zero boilerplate |
| Registry `as const` (not Record<BackendType,…> cast) | Cast was circular when BackendType derived from registry | ✓ Good — necessary to break circularity |
| type=search for search input | Prevents getByRole('textbox') collision with remote name TextFieldMD3 | ✓ Good — test-selector discipline |
| renderBackendFields() switch vs nested ternaries | 7+ branches require readable structure | ✓ Good — extensible for future backends |
| OAuthInstructions step-prefix detection | Renders authorize command in `<code>` element; keeps test assertions compatible | ✓ Good — clean heuristic |
| BACKEND_ICONS as Partial<Record<BackendType, FC>> | Missing entries = no icon rendered (no error) | ✓ Good — graceful degradation |
<details> <details>
<summary>Previous milestone context (v1.0 → v1.1)</summary> <summary>Previous milestone context (v1.0 → v1.3 key decisions)</summary>
**v1.0 shipped:** 2026-03-27, 4 phases, 18 plans, ~2,746 LOC **v1.0 shipped:** 2026-03-27, 4 phases, 18 plans, ~2,746 LOC
**v1.1 shipped:** 2026-03-31, 3 phases, 11 plans, +7,311 lines **v1.1 shipped:** 2026-03-31, 3 phases, 11 plans, +7,311 lines
**v1.2 shipped:** 2026-04-01, 5 phases, 14 plans, +9,908 lines
**v1.3 shipped:** 2026-04-16, 1 phase, 4 plans, +3,753 lines
**v1.0 tech debt resolved in v1.1:** **v1.2 tech debt resolved in v1.3:**
- scriptTargets filtering in ReviewStep (TECH-01) - FieldRenderer aria inconsistency (DEBT-01) — resolved Phase 9
- ReviewStep Back button (TECH-02)
- Registry-driven BackendSelectionStep (TECH-03)
- Dead export removal (TECH-04)
- act() warnings fix (TECH-05)
**v1.1 remaining tech debt:** **v1.3 tech debt carried forward:**
- 7 browser UI verifications pending (confirmatory) - Test selectors risk if UI restyled heavily (131+ selectors, ongoing discipline required)
- FieldRenderer aria inconsistency (cosmetic) - No formal milestone audit run for v1.3
- Phase 7 Nyquist compliance partial
</details> </details>
--- ---
*Last updated: 2026-03-31 after v1.2 milestone started* *Last updated: 2026-04-16 after v1.3 milestone*
+87 -1
View File
@@ -41,6 +41,83 @@
--- ---
## Milestone: v1.2 — UI Polish & Clarity
**Shipped:** 2026-04-01
**Phases:** 5 (812) | **Plans:** 14 | **Stats:** 78 files, +9,908 / -287 lines
### What Was Built
- Full MD3 color token system in a single token file; all components use semantic utility classes (`bg-surface`, `text-on-primary`) instead of hardcoded Tailwind colors
- System/Light/Dark theme toggle persisted in localStorage with zero flash of unstyled content (DOM class toggle, not React Context)
- TextFieldMD3 with CSS-only floating label, MD3 button hierarchy constants, rebuilt StepIndicator with numbered circles + checkmarks + connectors
- App intro section, step descriptions on all 4 steps, remote name live config preview, contextual help/tooltip enrichment across all credential fields
- Mobile-responsive layout, `focus-visible` ring-3 indicators, step fade/slide transition with `prefers-reduced-motion` guard, auto-scroll to first validation error
- Dark mode visibility fixes: semantic tokens on select, h2 headings, DeploymentStep form controls, ReviewStep security checkbox
### What Worked
- **Bottom-up migration order** (tokens → primitives → content → polish → fixes): each phase stood on solid ground from the one below; no circular rework
- **CSS-only floating label** (`peer-[:not(:placeholder-shown)]`) with no JS state — simpler to reason about, trivially animatable, one fewer React state variable
- **DOM class toggle for dark mode** (not React Context): theme switch is instant and doesn't trigger a re-render cascade across 150+ components
- **`key={state.currentStep}` on step wrapper**: one-line way to force React remount and reset CSS animation to initial state — no animation JS state needed
- **TDD discipline held** on Phase 12 dark mode fixes: found and fixed selector issues before they became regressions
### What Was Inefficient
- **Phase 12 inserted as a gap fix**: dark mode regressions were discovered post-Phase 11 instead of being caught in Phase 11 validation — cost 1 extra phase
- **`aria-label` / `getByLabelText` selector ambiguity** recurred across Phases 9, 10, and 12 — the DEBT-01 pattern (`{ selector: 'input' }`) had to be applied three times in three different phases instead of being resolved once and documented
- **Traceability table in REQUIREMENTS.md not updated** as plans completed — table showed "Planned" for completed items, causing confusion at milestone close
### Patterns Established
- Two-layer CSS token pattern: `@theme var()` references + `@layer base :root/.dark` raw values — required for `.dark` cascade in Tailwind v4
- `vi.stubGlobal` for localStorage/matchMedia — Node v25 experimental WebStorage breaks standard Storage API in jsdom
- CSS-only reduced-motion guard via `@media (prefers-reduced-motion: reduce)` — no JS listener needed
- Tooltip hover: dual-state (`hoverTooltip` + `showTooltip`); click-unpin clears `hoverTooltip` to prevent sticky tooltip after dismiss
- `ring-inset` on ThemeToggle focus ring because `overflow-hidden` on container clips outset rings
### Key Lessons
1. **Validate dark mode in every phase, not as a separate cleanup phase**: each component migration should include a dark-mode check, not defer it
2. **When a test selector pattern is discovered, document it once and reference it** — the `{ selector: 'input' }` guard should have been in a testing conventions note from Phase 9
3. **Keep traceability tables current** during execution, not just at planning time
---
## Milestone: v1.3 — Backend Expansion
**Shipped:** 2026-04-16
**Phases:** 1 (13) | **Plans:** 4 | **Stats:** 28 files, +3,959 / -206 lines
### What Was Built
- BACKEND_REGISTRY expanded from 7 → 18 backends; `BackendType` derived from `keyof typeof BACKEND_REGISTRY` — no manual union to maintain
- `BACKEND_SCHEMAS` auto-generated from registry keys via `Object.fromEntries` — zero per-backend boilerplate
- `OAuthInstructions` collapsible component with backend-specific `rclone authorize` commands (TDD, 4 tests)
- `GdriveAuthToggle` dual-auth component (OAuth token vs service account) — CSS hidden pattern extended from SftpAuthToggle
- `BackendSelectionStep` with 3 category groups, instant search filtering across 4 fields, inline SVG icons
- `RemoteConfigStep` wired for all 18 backends via clean `renderBackendFields()` switch function; `backendLabel` derived from registry
### What Worked
- **`as const` registry with derived BackendType**: one touch-point when adding a backend (registry entry only); Zod schemas auto-generate, type union auto-narrows
- **Incremental plan structure** (registry → components → UI → wiring): each plan had exactly the right scope — no plan was too large or too small
- **TDD for new UI components**: `OAuthInstructions` and `GdriveAuthToggle` written test-first; caught a step-prefix detection edge case before integration
- **type=search for search input**: simple attribute that prevents `getByRole('textbox')` collision — elegant selector discipline
- **renderBackendFields() switch**: 7+ backend branches are unreadable as nested ternaries; switch is flat and extensible
### What Was Inefficient
- **Registry had 18 backends but plan said 17**: a counting error in the plan caused minor confusion during 13-01 execution — plans should verify counts against reality
- **Traceability table not updated** (same as v1.2) — REMOTE-03/04/05 showed "Planned" at milestone close despite being complete
### Patterns Established
- `BackendType` from `keyof typeof BACKEND_REGISTRY` (not explicit union) — the canonical pattern going forward
- `BACKEND_SCHEMAS` from `Object.fromEntries` — zero per-backend call sites
- Registry `as const` (not `Record<BackendType,…>` cast) — cast is circular when BackendType derived from registry
- `Partial<Record<BackendType, FC<IconProps>>>` for optional icon map — missing entries render nothing (no error)
- `getByLabelText` partial regex (`/label/i` not `/^label$/i`) — TextFieldMD3 appends `*` to required field labels
### Key Lessons
1. **Update the traceability table as plans complete**, not just at the start — stale status fields caused confusion at milestone close (2nd occurrence after v1.2)
2. **Count registry entries in the plan, not just at planning time** — the 17/18 mismatch was avoidable
3. **`as const` registries with derived types are strictly superior to explicit unions**: eliminates an entire class of drift bugs, zero cost
---
## Cross-Milestone Trends ## Cross-Milestone Trends
### Process Evolution ### Process Evolution
@@ -48,14 +125,23 @@
| Milestone | Phases | Plans | Key Change | | Milestone | Phases | Plans | Key Change |
|-----------|--------|-------|------------| |-----------|--------|-------|------------|
| v1.0 | 4 | 18 | Wave 0 TDD pattern established; inside-out build order validated | | v1.0 | 4 | 18 | Wave 0 TDD pattern established; inside-out build order validated |
| v1.1 | 3 | 11 | CSS-hidden auth toggle pattern; FieldDef additive extension model |
| v1.2 | 5 | 14 | Two-layer CSS token system; DOM class dark mode; CSS-only floating label |
| v1.3 | 1 | 4 | `keyof typeof` derived BackendType; `Object.fromEntries` schema auto-gen |
### Cumulative Quality ### Cumulative Quality
| Milestone | Tests | Notes | | Milestone | Tests | Notes |
|-----------|-------|-------| |-----------|-------|-------|
| v1.0 | 98 (12 files) | 24/24 requirements satisfied, 5/5 E2E flows | | v1.0 | 98 (12 files) | 24/24 requirements satisfied, 5/5 E2E flows |
| v1.1 | 159 (18 files) | 7 browser UI verifications deferred |
| v1.2 | 159+ (same files, heavier coverage) | All DEBT-01 + POLISH/DARK/UX requirements satisfied |
| v1.3 | 165+ | All 6 REMOTE requirements satisfied; visual verification approved |
### Top Lessons (Verified Across Milestones) ### Top Lessons (Verified Across Milestones)
1. Single source of truth (BACKEND_REGISTRY) eliminates entire classes of drift bugs 1. Single source of truth (BACKEND_REGISTRY) eliminates entire classes of drift bugs — validated at v1.0, proven at v1.3 scale (18 backends)
2. Wave 0 TDD stubs are worth the upfront cost — they define the contract and make RED→GREEN progress visible 2. Wave 0 TDD stubs are worth the upfront cost — they define the contract and make RED→GREEN progress visible
3. **Update traceability tables as plans complete** — stale status fields caused confusion at both v1.2 and v1.3 milestone close (recurring issue)
4. CSS-only approaches (floating label, reduced-motion guard, auth toggle) are simpler than JS state — reach for CSS first
5. `as const` registries with derived types (`keyof typeof`) are strictly superior to explicit unions — zero drift possible
+15 -106
View File
@@ -5,7 +5,7 @@
-**v1.0 MVP** — Phases 14 (shipped 2026-03-27) -**v1.0 MVP** — Phases 14 (shipped 2026-03-27)
-**v1.1 Backlog & Tech Debt** — Phases 57 (shipped 2026-03-31) -**v1.1 Backlog & Tech Debt** — Phases 57 (shipped 2026-03-31)
-**v1.2 UI Polish & Clarity** — Phases 812 (shipped 2026-04-01) -**v1.2 UI Polish & Clarity** — Phases 812 (shipped 2026-04-01)
- **v1.3 Backend Expansion** — Phase 13 (in progress) - **v1.3 Backend Expansion** — Phase 13 (shipped 2026-04-16)
## Phases ## Phases
@@ -35,115 +35,24 @@ Full phase details: [.planning/milestones/v1.1-ROADMAP.md](milestones/v1.1-ROADM
<details> <details>
<summary>✅ v1.2 UI Polish & Clarity (Phases 812) — SHIPPED 2026-04-01</summary> <summary>✅ v1.2 UI Polish & Clarity (Phases 812) — SHIPPED 2026-04-01</summary>
- [x] **Phase 8: Theme Foundation** - MD3 color token system and dark mode infrastructure (completed 2026-04-01) - [x] Phase 8: Theme Foundation (2/2 plans) — completed 2026-04-01
- [x] **Phase 9: MD3 Components** - UI primitives and component migration to semantic tokens (completed 2026-04-01) - [x] Phase 9: MD3 Components (5/5 plans) — completed 2026-04-01
- [x] **Phase 10: Content & Clarity** - Intro section, step descriptions, and contextual help (completed 2026-04-01) - [x] Phase 10: Content & Clarity (3/3 plans) — completed 2026-04-01
- [x] **Phase 11: Polish & Responsiveness** - Mobile layout, focus states, transitions, scroll-to-error (completed 2026-04-01) - [x] Phase 11: Polish & Responsiveness (2/2 plans) — completed 2026-04-01
- [x] **Phase 12: Dark mode visibility fixes** - Fix dark mode regressions (completed 2026-04-01) - [x] Phase 12: Dark Mode Visibility Fixes (2/2 plans) — completed 2026-04-01
Full phase details: [.planning/milestones/v1.3-ROADMAP.md](milestones/v1.3-ROADMAP.md)
</details> </details>
### v1.3 Backend Expansion (In Progress) <details>
<summary>✅ v1.3 Backend Expansion (Phase 13) — SHIPPED 2026-04-16</summary>
**Milestone Goal:** Expand backend support from 7 to 17 rclone backends with categorized selection, search filtering, OAuth token-paste guidance, and inline icons. - [x] Phase 13: Add Remaining RClone Remotes (4/4 plans) — completed 2026-04-16
## Phase Details Full phase details: [.planning/milestones/v1.3-ROADMAP.md](milestones/v1.3-ROADMAP.md)
### Phase 8: Theme Foundation </details>
**Goal**: App has a complete MD3 color token system and working dark mode toggle with no flash of unstyled content
**Depends on**: Phase 7 (v1.1 complete)
**Requirements**: THEME-01, THEME-02
**Success Criteria** (what must be TRUE):
1. All color values in the app flow from CSS custom properties defined in a single token file, not from hardcoded Tailwind color classes
2. User can toggle between System, Light, and Dark themes via a visible control, and the entire UI responds immediately
3. Theme preference persists in localStorage and applies on reload without any flash of wrong theme
4. Tailwind v4 `@theme` directive maps MD3 token names to utility classes (e.g., `bg-surface`, `text-on-primary`)
**Plans:** 2/2 plans complete
Plans:
- [ ] 08-01-PLAN.md — Token system, flash prevention, ThemeToggle component
- [ ] 08-02-PLAN.md — Color class migration across all components
### Phase 9: MD3 Components
**Goal**: All interactive elements use MD3-styled primitives with consistent visual language across the entire wizard
**Depends on**: Phase 8
**Requirements**: COMP-01, COMP-02, COMP-03, COMP-04, DEBT-01
**Success Criteria** (what must be TRUE):
1. Text inputs render as outlined fields with floating labels that animate on focus and when the field has content
2. Buttons visually distinguish primary (filled), secondary (outlined), and tertiary (text) actions across all wizard steps
3. Backend selection cards and output blocks display MD3 elevation with tonal surface tint and consistent shape tokens
4. Step indicator shows numbered circles connected by lines, with checkmarks on completed steps, highlight on current, and muted future steps
5. FieldRenderer produces identical `aria-label` attributes for both text-branch and select-branch inputs
**Plans:** 5/5 plans complete
Plans:
- [x] 09-01-PLAN.md — TextFieldMD3 component, MD3 button constants, DEBT-01 aria-label fix
- [x] 09-02-PLAN.md — StepIndicator rebuild with MD3 circles and connectors
- [x] 09-03-PLAN.md — Integration: wire TextFieldMD3 + button styles + elevation into all wizard steps
- [x] 09-04-PLAN.md — Gap closure: Replace BackendSelectionStep plain input with TextFieldMD3
- [x] 09-05-PLAN.md — Gap closure: Tooltip hover behavior + icon placement beside helpText
### Phase 10: Content & Clarity
**Goal**: A first-time visitor understands what Ready2Blob does and what each wizard step expects without external documentation
**Depends on**: Phase 8 (tokens needed for consistent styling of new content)
**Requirements**: UX-01, UX-02, UX-03, UX-04
**Success Criteria** (what must be TRUE):
1. Landing view shows an intro section explaining what Ready2Blob does with a clear call-to-action to start the wizard
2. Remote name field displays a placeholder example, help text, and a live preview showing how the name appears in `[remote-name]` config syntax
3. Every wizard step has a 1-2 sentence description below the heading explaining what the user is doing and why
4. All backend credential fields have contextual help text explaining what to enter and where to find the value
**Plans:** 3/3 plans complete
Plans:
- [ ] 10-01-PLAN.md — Intro section with CTA and step descriptions for all 4 steps
- [ ] 10-02-PLAN.md — Remote name live config preview with helpText
- [ ] 10-03-PLAN.md — Credential field tooltipText and helpText enrichment
### Phase 11: Polish & Responsiveness
**Goal**: The wizard feels production-ready on any device with smooth interactions and accessible keyboard navigation
**Depends on**: Phase 9, Phase 10
**Requirements**: POLISH-01, POLISH-02, POLISH-03, POLISH-04
**Success Criteria** (what must be TRUE):
1. On a mobile viewport (< 640px), backend cards stack vertically, form fields span full width, step indicator collapses, and buttons stretch to fill width
2. Every interactive element shows a visible 3px MD3 focus outline when navigated via keyboard (using `focus-visible`)
3. Step transitions use a subtle fade/slide animation that is suppressed when the user has `prefers-reduced-motion` enabled
4. When form validation fails, the view auto-scrolls to the first field with an error
**Plans:** 2/2 plans complete
Plans:
- [ ] 11-01-PLAN.md — Mobile responsive layout + MD3 focus-visible indicators
- [ ] 11-02-PLAN.md — Step transition animation + auto-scroll to first error
### Phase 12: Dark mode visibility fixes
**Goal:** Fix dark mode visibility regressions where step headings, DeploymentStep form controls, and FieldRenderer select elements are invisible or unstyled against the dark background
**Requirements**: DARK-01, DARK-02, DARK-03
**Depends on:** Phase 11
**Success Criteria** (what must be TRUE):
1. All wizard step h2 headings are readable in dark mode with proper contrast
2. DeploymentStep labels, legends, checkboxes, and radios are visible and styled in dark mode
3. FieldRenderer select element has proper background and text color in dark mode
**Plans:** 2/2 plans complete
Plans:
- [ ] 12-01-PLAN.md — Fix step headings, DeploymentStep form controls, ReviewStep checkbox
- [ ] 12-02-PLAN.md — Fix FieldRenderer select dark mode styling
### Phase 13: Add remaining RClone remotes
**Goal:** Expand backend support from 7 to 17 rclone backends with categorized selection UI, instant search, OAuth token-paste guidance, and inline SVG icons — covering all backends suitable for unattended Windows deployment
**Requirements**: REMOTE-01, REMOTE-02, REMOTE-03, REMOTE-04, REMOTE-05, REMOTE-06
**Depends on:** Phase 12
**Success Criteria** (what must be TRUE):
1. BACKEND_REGISTRY contains 17 backends with correct fields, categories, and rclone type mappings
2. BackendType is derived from registry keys (not an explicit union)
3. BackendSelectionStep groups backends by category with instant search and inline SVG icons
4. OAuth-token backends show collapsible OAuthInstructions with backend-specific rclone authorize command
5. RemoteConfigStep renders correct form for every backend including GdriveAuthToggle
6. buildRcloneConf produces valid rclone.conf for all 17 backends
**Plans:** 4/4 plans complete
Plans:
- [x] 13-01-PLAN.md — Registry refactoring: derive BackendType, add categories, 11 new backend entries (18 total), auto-generate schemas
- [ ] 13-02-PLAN.md — New components: OAuthInstructions, GdriveAuthToggle, BackendIcons
- [ ] 13-03-PLAN.md — BackendSelectionStep UX overhaul: categories, search, icons
- [ ] 13-04-PLAN.md — RemoteConfigStep wiring for all new backends + visual verification
## Progress ## Progress
@@ -160,5 +69,5 @@ Plans:
| 9. MD3 Components | v1.2 | 5/5 | Complete | 2026-04-01 | | 9. MD3 Components | v1.2 | 5/5 | Complete | 2026-04-01 |
| 10. Content & Clarity | v1.2 | 3/3 | Complete | 2026-04-01 | | 10. Content & Clarity | v1.2 | 3/3 | Complete | 2026-04-01 |
| 11. Polish & Responsiveness | v1.2 | 2/2 | Complete | 2026-04-01 | | 11. Polish & Responsiveness | v1.2 | 2/2 | Complete | 2026-04-01 |
| 12. Dark mode visibility fixes | v1.2 | 2/2 | Complete | 2026-04-01 | | 12. Dark Mode Visibility Fixes | v1.2 | 2/2 | Complete | 2026-04-01 |
| 13. Add remaining RClone remotes | 4/4 | Complete | 2026-04-01 | — | | 13. Add Remaining RClone Remotes | v1.3 | 4/4 | Complete | 2026-04-16 |
+11 -12
View File
@@ -2,10 +2,10 @@
gsd_state_version: 1.0 gsd_state_version: 1.0
milestone: v1.3 milestone: v1.3
milestone_name: Backend Expansion milestone_name: Backend Expansion
status: verifying status: milestone_complete
stopped_at: Completed 13-04-PLAN.md stopped_at: v1.3 milestone archived and closed
last_updated: "2026-04-01T15:33:04.002Z" last_updated: "2026-04-16T15:00:00.000Z"
last_activity: 2026-04-01 Phase 13 plan 04 complete, visual verification approved (REMOTE-05 satisfied) last_activity: 2026-04-16 v1.3 Backend Expansion milestone complete, archived, git tagged
progress: progress:
total_phases: 6 total_phases: 6
completed_phases: 6 completed_phases: 6
@@ -21,16 +21,15 @@ progress:
See: .planning/PROJECT.md (updated 2026-03-31) See: .planning/PROJECT.md (updated 2026-03-31)
**Core value:** An IT pro can go from zero to a deployable rclone setup in minutes — without needing to know rclone's config syntax or PowerShell deployment conventions. **Core value:** An IT pro can go from zero to a deployable rclone setup in minutes — without needing to know rclone's config syntax or PowerShell deployment conventions.
**Current focus:** v1.2 — UI Polish & Clarity (Phase 11: Polish & Responsiveness in progress, plan 11-01 complete) **Current focus:** Planning next milestone (v1.4)
## Current Position ## Current Position
Phase: 13 of 13 (Add Remaining RClone Remotes) — Complete Milestone v1.3 complete and archived. Ready for next milestone planning.
Plan: 13-04 (complete — all 4 plans complete) Status: 4 milestones shipped (v1.0 → v1.3), 13 phases, 40 plans — all complete
Status: Phase 13 complete — all 18 backends fully integrated end-to-end, user visual verification approved Last activity: 2026-04-16 — v1.3 Backend Expansion milestone archived and git tagged
Last activity: 2026-04-01 — Phase 13 plan 04 complete, visual verification approved (REMOTE-05 satisfied)
Progress: [██████████] 100% (v1.3) Progress: [██████████] 100% (v1.3 — milestone complete)
## Accumulated Context ## Accumulated Context
@@ -99,6 +98,6 @@ None yet.
## Session Continuity ## Session Continuity
Last session: 2026-04-01T15:27:20.814Z Last session: 2026-04-16
Stopped at: Completed 13-04-PLAN.md Stopped at: v1.3 milestone complete — MILESTONES.md, PROJECT.md, ROADMAP.md, RETROSPECTIVE.md updated; git tagged v1.3
Resume file: None Resume file: None
+55 -58
View File
@@ -1,87 +1,84 @@
--- ---
status: diagnosed status: awaiting_human_verify
trigger: "Tooltip info buttons on Remote Config wizard step: hovering doesn't show anything, icon placement is wrong (above field instead of beside helpText)" trigger: "Tooltip info buttons on Remote Config wizard step: hovering doesn't show anything, icon placement is wrong (above field instead of beside helpText)"
created: 2026-04-01T00:00:00Z created: 2026-04-01T00:00:00Z
updated: 2026-04-01T00:00:00Z updated: 2026-04-02T00:00:00Z
--- ---
## Current Focus ## Current Focus
hypothesis: Two distinct root causes — onClick-only toggle (no hover), and icon placed above TextFieldMD3 instead of beside helpText hypothesis: Issues 1 & 2 already fixed in current code; Issue 3 (light mode illegibility) is active — text-on-surface-variant is gray-100 (near-white) in light mode, used on light backgrounds
test: Code review of FieldRenderer.tsx lines 83-109 and TextFieldMD3.tsx lines 65-67 test: Applying fix to BackendSelectionStep.tsx — replace text-on-surface-variant with text-on-surface-container for step description; replace placeholder:text-on-surface-variant/50 with placeholder:text-on-surface-container/50 for search bar
expecting: Confirm both issues from source expecting: Both elements become legible in light mode; dark mode unaffected
next_action: Report diagnosis next_action: Apply fix to BackendSelectionStep.tsx
## Symptoms ## Symptoms
expected: (1) Hovering tooltip button shows tooltip text. (2) Icon is beside the helpText under the field. expected: (1) Hovering tooltip button shows tooltip text. (2) Icon is beside the helpText under the field. (3) Step description and search bar placeholder are legible in light mode.
actual: (1) Nothing happens on hover — only click toggles. (2) Icon is above the TextFieldMD3 component. actual: (1) Nothing happens on hover — only click toggles. (2) Icon is above the TextFieldMD3 component. (3) Step subtitle and search placeholder are near-invisible in light mode.
errors: none errors: none
reproduction: Open Remote Config wizard, hover over any info button reproduction: (1,2) Open Remote Config wizard, hover over any info button. (3) View BackendSelectionStep in light mode.
started: Since phase-09 tooltip implementation started: (1,2) Since phase-09 tooltip implementation. (3) Since text-on-surface-variant was used for body text.
## Eliminated ## Eliminated
(none needed — root causes identified on first pass) - hypothesis: Issues 1 & 2 are still present in the codebase
evidence: Current FieldRenderer.tsx (lines 88-91) and PasswordField.tsx (lines 34-35) both have onMouseEnter/onMouseLeave wired to setHoverTooltip. TextFieldMD3 renders helpTextPrefix in a flex row beside helpText (lines 67-74). The debug diagnosis was from before the code was updated.
timestamp: 2026-04-02T00:00:00Z
## Evidence ## Evidence
- timestamp: 2026-04-01T00:00:00Z - timestamp: 2026-04-01T00:00:00Z
checked: FieldRenderer.tsx lines 83-109 (text input default branch) checked: FieldRenderer.tsx lines 83-109 (text input default branch)
found: | found: Original code had onClick-only toggle, icon rendered above TextFieldMD3, no hover support.
The tooltip button at lines 85-94 uses onClick={() => setShowTooltip(v => !v)} only. implication: Root causes of Issues 1 & 2 at diagnosis time
There are NO onMouseEnter/onMouseLeave handlers, and no CSS hover-triggered tooltip.
The tooltip text (lines 95-99) is only rendered when showTooltip state is true,
which only changes on click — never on hover.
implication: Root cause of Issue 1 — tooltip requires click, not hover
- timestamp: 2026-04-01T00:00:00Z - timestamp: 2026-04-02T00:00:00Z
checked: FieldRenderer.tsx lines 83-109 (DOM structure of text input branch) checked: FieldRenderer.tsx current state (all 119 lines)
found: | found: |
The DOM order is: Lines 88-91: onMouseEnter/onMouseLeave both wired to setHoverTooltip — hover IS implemented.
<div flex-col> Line 99: tooltipVisible = showTooltip || hoverTooltip — hover state properly used.
{tooltipText && <button>icon</button>} ← lines 85-94: icon ABOVE field Lines 103-111: TextFieldMD3 receives helpTextPrefix={tooltipIcon} — icon IS placed beside helpText.
{tooltipText && showTooltip && <p>text</p>} ← lines 95-99: tooltip text ABOVE field Same pattern confirmed in PasswordField.tsx lines 34-35 and 54.
<TextFieldMD3 helpText={field.helpText} /> ← lines 100-107: field with helpText inside implication: Issues 1 & 2 already resolved in current code
</div>
The icon button is a sibling rendered BEFORE TextFieldMD3, placing it above the field.
Meanwhile, helpText is rendered INSIDE TextFieldMD3 (TextFieldMD3.tsx lines 65-67),
so the icon cannot be "beside the helpText" from its current position.
implication: Root cause of Issue 2 — icon is structurally above the field, not beside helpText
- timestamp: 2026-04-01T00:00:00Z - timestamp: 2026-04-02T00:00:00Z
checked: TextFieldMD3.tsx lines 65-67 checked: index.css — CSS custom property values for light mode (:root)
found: | found: |
helpText is rendered inside TextFieldMD3: --r2b-on-surface-variant: #F3F4F6 (gray-100 — intended for text ON surface-variant/code blocks)
{helpText && !error && ( --r2b-on-surface-container: #374151 (gray-700 — good contrast on white/light surfaces)
<p className="text-xs text-on-surface-container/70">{helpText}</p> --r2b-surface: #F9FAFB (gray-50 — light background)
)} implication: text-on-surface-variant = near-white on light backgrounds = illegible; text-on-surface-container = dark gray = legible
This is encapsulated inside the component — FieldRenderer has no way to inject
an icon beside it without either: (a) passing the icon as a prop, or (b) moving - timestamp: 2026-04-02T00:00:00Z
helpText rendering out of TextFieldMD3. checked: BackendSelectionStep.tsx line 88 and line 107
implication: Fix requires changing TextFieldMD3 interface or moving helpText rendering found: |
Line 88: <p className="text-sm text-on-surface-variant mt-1 mb-4"> — step description uses on-surface-variant
Line 107: placeholder:text-on-surface-variant/50 — search placeholder uses on-surface-variant at 50% opacity
Both become near-invisible in light mode: gray-100 (#F3F4F6) on gray-50 (#F9FAFB) background.
implication: Root cause of Issue 3 — wrong semantic token used for body text on surface background
## Resolution ## Resolution
root_cause: | root_cause: |
ISSUE 1 — Tooltip doesn't show on hover: ISSUE 1 (tooltip hover) — ALREADY FIXED in current code.
File: src/components/ui/FieldRenderer.tsx, lines 85-94 Both FieldRenderer.tsx and PasswordField.tsx have onMouseEnter/onMouseLeave handlers wired to setHoverTooltip.
The tooltip button only has an onClick handler that toggles showTooltip state. tooltipVisible = showTooltip || hoverTooltip ensures hover triggers display.
There are no onMouseEnter/onMouseLeave handlers and no CSS :hover mechanism.
The tooltip content (lines 95-99) is conditionally rendered only when showTooltip === true,
which never becomes true from hovering.
ISSUE 2 — Icon placed above field instead of beside helpText: ISSUE 2 (icon placement) — ALREADY FIXED in current code.
File: src/components/ui/FieldRenderer.tsx, lines 83-109 FieldRenderer.tsx passes helpTextPrefix={tooltipIcon} to TextFieldMD3.
The icon button is rendered as a sibling BEFORE <TextFieldMD3>, placing it visually TextFieldMD3 renders helpTextPrefix in a flex row beside helpText (lines 67-74).
above the text field. The helpText is rendered INSIDE TextFieldMD3 (TextFieldMD3.tsx
line 65-67), making it impossible for the icon in FieldRenderer to sit beside it.
To place the icon beside helpText, either:
(a) TextFieldMD3 needs a new prop (e.g., helpTextSuffix or helpTextIcon) to render
content inline with the helpText paragraph, or
(b) helpText rendering moves out of TextFieldMD3 back into FieldRenderer, where the
icon and helpText can be placed in a flex-row together.
fix: (not applied — diagnosis only) ISSUE 3 (light mode illegibility):
verification: (not applied — diagnosis only) File: src/components/wizard/BackendSelectionStep.tsx, lines 88 and 107
files_changed: [] text-on-surface-variant (#F3F4F6, gray-100) is semantically "text on dark surface-variant elements"
(e.g., code blocks). Using it on light surface backgrounds produces near-white text on near-white
background. The step description and search placeholder are nearly invisible in light mode.
Fix: use text-on-surface-container (#374151, gray-700) which is designed for secondary text on surfaces.
fix: |
BackendSelectionStep.tsx line 88: text-on-surface-variant -> text-on-surface-container
BackendSelectionStep.tsx line 107: placeholder:text-on-surface-variant/50 -> placeholder:text-on-surface-container/50
verification: TypeScript passes clean. 79 tests pass (13 pre-existing infra failures unrelated to this change — same failures existed before). Fix applied and code-reviewed: token change is semantically correct.
files_changed:
- src/components/wizard/BackendSelectionStep.tsx
@@ -1,3 +1,12 @@
# Requirements Archive: v1.3 Backend Expansion
**Archived:** 2026-04-16
**Status:** SHIPPED
For current requirements, see `.planning/REQUIREMENTS.md`.
---
# Requirements: Ready2Blob # Requirements: Ready2Blob
**Defined:** 2026-03-31 **Defined:** 2026-03-31
+164
View File
@@ -0,0 +1,164 @@
# Roadmap: Ready2Blob
## Milestones
-**v1.0 MVP** — Phases 14 (shipped 2026-03-27)
-**v1.1 Backlog & Tech Debt** — Phases 57 (shipped 2026-03-31)
-**v1.2 UI Polish & Clarity** — Phases 812 (shipped 2026-04-01)
- **v1.3 Backend Expansion** — Phase 13 (in progress)
## Phases
<details>
<summary>✅ v1.0 MVP (Phases 14) — SHIPPED 2026-03-27</summary>
- [x] Phase 1: Foundation (4/4 plans) — completed 2026-03-26
- [x] Phase 2: Generators (4/4 plans) — completed 2026-03-26
- [x] Phase 3: Wizard UI (5/5 plans) — completed 2026-03-27
- [x] Phase 4: Review, Download & Security (5/5 plans) — completed 2026-03-27
Full phase details: [.planning/milestones/v1.0-ROADMAP.md](milestones/v1.0-ROADMAP.md)
</details>
<details>
<summary>✅ v1.1 Backlog & Tech Debt (Phases 57) — SHIPPED 2026-03-31</summary>
- [x] Phase 5: Tech Debt (4/4 plans) — completed 2026-03-30
- [x] Phase 6: New Backends (4/4 plans) — completed 2026-03-31
- [x] Phase 7: Validation & UX Polish (3/3 plans) — completed 2026-03-31
Full phase details: [.planning/milestones/v1.1-ROADMAP.md](milestones/v1.1-ROADMAP.md)
</details>
<details>
<summary>✅ v1.2 UI Polish & Clarity (Phases 812) — SHIPPED 2026-04-01</summary>
- [x] **Phase 8: Theme Foundation** - MD3 color token system and dark mode infrastructure (completed 2026-04-01)
- [x] **Phase 9: MD3 Components** - UI primitives and component migration to semantic tokens (completed 2026-04-01)
- [x] **Phase 10: Content & Clarity** - Intro section, step descriptions, and contextual help (completed 2026-04-01)
- [x] **Phase 11: Polish & Responsiveness** - Mobile layout, focus states, transitions, scroll-to-error (completed 2026-04-01)
- [x] **Phase 12: Dark mode visibility fixes** - Fix dark mode regressions (completed 2026-04-01)
</details>
### v1.3 Backend Expansion (In Progress)
**Milestone Goal:** Expand backend support from 7 to 17 rclone backends with categorized selection, search filtering, OAuth token-paste guidance, and inline icons.
## Phase Details
### Phase 8: Theme Foundation
**Goal**: App has a complete MD3 color token system and working dark mode toggle with no flash of unstyled content
**Depends on**: Phase 7 (v1.1 complete)
**Requirements**: THEME-01, THEME-02
**Success Criteria** (what must be TRUE):
1. All color values in the app flow from CSS custom properties defined in a single token file, not from hardcoded Tailwind color classes
2. User can toggle between System, Light, and Dark themes via a visible control, and the entire UI responds immediately
3. Theme preference persists in localStorage and applies on reload without any flash of wrong theme
4. Tailwind v4 `@theme` directive maps MD3 token names to utility classes (e.g., `bg-surface`, `text-on-primary`)
**Plans:** 2/2 plans complete
Plans:
- [ ] 08-01-PLAN.md — Token system, flash prevention, ThemeToggle component
- [ ] 08-02-PLAN.md — Color class migration across all components
### Phase 9: MD3 Components
**Goal**: All interactive elements use MD3-styled primitives with consistent visual language across the entire wizard
**Depends on**: Phase 8
**Requirements**: COMP-01, COMP-02, COMP-03, COMP-04, DEBT-01
**Success Criteria** (what must be TRUE):
1. Text inputs render as outlined fields with floating labels that animate on focus and when the field has content
2. Buttons visually distinguish primary (filled), secondary (outlined), and tertiary (text) actions across all wizard steps
3. Backend selection cards and output blocks display MD3 elevation with tonal surface tint and consistent shape tokens
4. Step indicator shows numbered circles connected by lines, with checkmarks on completed steps, highlight on current, and muted future steps
5. FieldRenderer produces identical `aria-label` attributes for both text-branch and select-branch inputs
**Plans:** 5/5 plans complete
Plans:
- [x] 09-01-PLAN.md — TextFieldMD3 component, MD3 button constants, DEBT-01 aria-label fix
- [x] 09-02-PLAN.md — StepIndicator rebuild with MD3 circles and connectors
- [x] 09-03-PLAN.md — Integration: wire TextFieldMD3 + button styles + elevation into all wizard steps
- [x] 09-04-PLAN.md — Gap closure: Replace BackendSelectionStep plain input with TextFieldMD3
- [x] 09-05-PLAN.md — Gap closure: Tooltip hover behavior + icon placement beside helpText
### Phase 10: Content & Clarity
**Goal**: A first-time visitor understands what Ready2Blob does and what each wizard step expects without external documentation
**Depends on**: Phase 8 (tokens needed for consistent styling of new content)
**Requirements**: UX-01, UX-02, UX-03, UX-04
**Success Criteria** (what must be TRUE):
1. Landing view shows an intro section explaining what Ready2Blob does with a clear call-to-action to start the wizard
2. Remote name field displays a placeholder example, help text, and a live preview showing how the name appears in `[remote-name]` config syntax
3. Every wizard step has a 1-2 sentence description below the heading explaining what the user is doing and why
4. All backend credential fields have contextual help text explaining what to enter and where to find the value
**Plans:** 3/3 plans complete
Plans:
- [ ] 10-01-PLAN.md — Intro section with CTA and step descriptions for all 4 steps
- [ ] 10-02-PLAN.md — Remote name live config preview with helpText
- [ ] 10-03-PLAN.md — Credential field tooltipText and helpText enrichment
### Phase 11: Polish & Responsiveness
**Goal**: The wizard feels production-ready on any device with smooth interactions and accessible keyboard navigation
**Depends on**: Phase 9, Phase 10
**Requirements**: POLISH-01, POLISH-02, POLISH-03, POLISH-04
**Success Criteria** (what must be TRUE):
1. On a mobile viewport (< 640px), backend cards stack vertically, form fields span full width, step indicator collapses, and buttons stretch to fill width
2. Every interactive element shows a visible 3px MD3 focus outline when navigated via keyboard (using `focus-visible`)
3. Step transitions use a subtle fade/slide animation that is suppressed when the user has `prefers-reduced-motion` enabled
4. When form validation fails, the view auto-scrolls to the first field with an error
**Plans:** 2/2 plans complete
Plans:
- [ ] 11-01-PLAN.md — Mobile responsive layout + MD3 focus-visible indicators
- [ ] 11-02-PLAN.md — Step transition animation + auto-scroll to first error
### Phase 12: Dark mode visibility fixes
**Goal:** Fix dark mode visibility regressions where step headings, DeploymentStep form controls, and FieldRenderer select elements are invisible or unstyled against the dark background
**Requirements**: DARK-01, DARK-02, DARK-03
**Depends on:** Phase 11
**Success Criteria** (what must be TRUE):
1. All wizard step h2 headings are readable in dark mode with proper contrast
2. DeploymentStep labels, legends, checkboxes, and radios are visible and styled in dark mode
3. FieldRenderer select element has proper background and text color in dark mode
**Plans:** 2/2 plans complete
Plans:
- [ ] 12-01-PLAN.md — Fix step headings, DeploymentStep form controls, ReviewStep checkbox
- [ ] 12-02-PLAN.md — Fix FieldRenderer select dark mode styling
### Phase 13: Add remaining RClone remotes
**Goal:** Expand backend support from 7 to 17 rclone backends with categorized selection UI, instant search, OAuth token-paste guidance, and inline SVG icons — covering all backends suitable for unattended Windows deployment
**Requirements**: REMOTE-01, REMOTE-02, REMOTE-03, REMOTE-04, REMOTE-05, REMOTE-06
**Depends on:** Phase 12
**Success Criteria** (what must be TRUE):
1. BACKEND_REGISTRY contains 17 backends with correct fields, categories, and rclone type mappings
2. BackendType is derived from registry keys (not an explicit union)
3. BackendSelectionStep groups backends by category with instant search and inline SVG icons
4. OAuth-token backends show collapsible OAuthInstructions with backend-specific rclone authorize command
5. RemoteConfigStep renders correct form for every backend including GdriveAuthToggle
6. buildRcloneConf produces valid rclone.conf for all 17 backends
**Plans:** 4/4 plans complete
Plans:
- [x] 13-01-PLAN.md — Registry refactoring: derive BackendType, add categories, 11 new backend entries (18 total), auto-generate schemas
- [ ] 13-02-PLAN.md — New components: OAuthInstructions, GdriveAuthToggle, BackendIcons
- [ ] 13-03-PLAN.md — BackendSelectionStep UX overhaul: categories, search, icons
- [ ] 13-04-PLAN.md — RemoteConfigStep wiring for all new backends + visual verification
## Progress
| Phase | Milestone | Plans Complete | Status | Completed |
|-------|-----------|----------------|--------|-----------|
| 1. Foundation | v1.0 | 4/4 | Complete | 2026-03-26 |
| 2. Generators | v1.0 | 4/4 | Complete | 2026-03-26 |
| 3. Wizard UI | v1.0 | 5/5 | Complete | 2026-03-27 |
| 4. Review, Download & Security | v1.0 | 5/5 | Complete | 2026-03-27 |
| 5. Tech Debt | v1.1 | 4/4 | Complete | 2026-03-30 |
| 6. New Backends | v1.1 | 4/4 | Complete | 2026-03-31 |
| 7. Validation & UX Polish | v1.1 | 3/3 | Complete | 2026-03-31 |
| 8. Theme Foundation | v1.2 | 2/2 | Complete | 2026-04-01 |
| 9. MD3 Components | v1.2 | 5/5 | Complete | 2026-04-01 |
| 10. Content & Clarity | v1.2 | 3/3 | Complete | 2026-04-01 |
| 11. Polish & Responsiveness | v1.2 | 2/2 | Complete | 2026-04-01 |
| 12. Dark mode visibility fixes | v1.2 | 2/2 | Complete | 2026-04-01 |
| 13. Add remaining RClone remotes | 4/4 | Complete | 2026-04-01 | — |
@@ -126,3 +126,15 @@ None - no external service configuration required.
--- ---
*Phase: 10-content-clarity* *Phase: 10-content-clarity*
*Completed: 2026-04-01* *Completed: 2026-04-01*
## Self-Check: PASSED
- `src/App.tsx` — FOUND
- `src/App.test.tsx` — FOUND
- `src/components/wizard/BackendSelectionStep.tsx` — FOUND
- `src/components/wizard/RemoteConfigStep.tsx` — FOUND
- `src/components/wizard/DeploymentStep.tsx` — FOUND
- `src/components/wizard/ReviewStep.tsx` — FOUND
- `.planning/phases/10-content-clarity/10-01-SUMMARY.md` — FOUND
- Commit `c1d1633` — FOUND
- Commit `4b837b1` — FOUND
@@ -0,0 +1,107 @@
---
phase: 13-add-remaining-rclone-remotes
plan: "02"
subsystem: ui
tags: [react, tailwind, svg, rclone, oauth, wizard, components]
# Dependency graph
requires:
- phase: 13-add-remaining-rclone-remotes
provides: "BackendType derived from BACKEND_REGISTRY (18 backends), registry as const pattern"
provides:
- "OAuthInstructions: collapsible step-by-step OAuth token guide (TDD, 4 tests)"
- "GdriveAuthToggle: two-tab OAuth/Service Account toggle for Google Drive"
- "BACKEND_ICONS: inline SVG icon map for all 18 BackendType entries"
affects: [13-03-BackendSelectionStep, 13-04-RemoteConfigStep]
# Tech tracking
tech-stack:
added: []
patterns:
- "TDD for UI components: test → implement → verify cycle"
- "Collapsible disclosure via useState(false) + conditional render"
- "Both form fields always registered, visibility toggled via CSS hidden/block"
- "Inline SVG icons with fill=currentColor for dark-mode compatibility"
- "Partial<Record<BackendType, FC<IconProps>>> for optional icon map"
key-files:
created:
- src/components/wizard/OAuthInstructions.tsx
- src/components/wizard/OAuthInstructions.test.tsx
- src/components/wizard/GdriveAuthToggle.tsx
- src/components/icons/BackendIcons.tsx
modified: []
key-decisions:
- "OAuthInstructions renders authorizeCommand in a <code> block (detected via step text prefix), not inline text — ensures test getByText exact-match works"
- "BACKEND_ICONS is Partial<Record<BackendType,...>> — missing entries mean no icon rendered on the card; allows incremental icon addition"
- "GdriveAuthToggle defaults to oauth tab (not service-account) — OAuth is the more common flow for personal use"
- "BackendIcons uses text SVG elements for letter-based icons (S3, B2, G, p) — simpler than path-based glyphs for recognizable short labels at 24px"
patterns-established:
- "AuthToggle pattern: segmented control + CSS hidden/block for always-registered dual fields"
- "OAuthInstructions: generic collapsible guide reusable for any OAuth backend (backendName + authorizeCommand props)"
- "IconProps = { className?: string } — minimal prop surface for SVG components"
requirements-completed: [REMOTE-04]
# Metrics
duration: 4min
completed: 2026-04-01
---
# Phase 13 Plan 02: Component Library for OAuth and Icons Summary
**Collapsible OAuthInstructions, GdriveAuthToggle dual-auth toggle, and 18-backend inline SVG icon map — all with zero external dependencies**
## Performance
- **Duration:** 4 min
- **Started:** 2026-04-01T14:24:33Z
- **Completed:** 2026-04-01T14:28:00Z
- **Tasks:** 2 (Task 1 TDD, Task 2 standard)
- **Files modified:** 4 created
## Accomplishments
- OAuthInstructions: collapsible disclosure (collapsed by default), shows backend-specific rclone authorize command in code block, 5-step generic OAuth flow, MD3 styled — TDD with 4 passing tests
- GdriveAuthToggle: two-tab segmented control (OAuth Token / Service Account) following AzureAuthToggle pattern exactly; OAuth tab embeds OAuthInstructions; both fields always registered via CSS hidden/block
- BackendIcons: inline SVG icon map covering all 18 BackendType keys, fill="currentColor" throughout, viewBox 0 0 24 24, no external icon library
## Task Commits
Each task was committed atomically:
1. **Task 1: OAuthInstructions component with tests** - `e00fc2b` (feat + test TDD)
2. **Task 2: GdriveAuthToggle and BackendIcons components** - `683faf6` (feat)
## Files Created/Modified
- `src/components/wizard/OAuthInstructions.tsx` - Collapsible OAuth step-by-step guide component
- `src/components/wizard/OAuthInstructions.test.tsx` - 4 behavior tests (collapsed default, expand on click, authorize command display, backend name in button)
- `src/components/wizard/GdriveAuthToggle.tsx` - Google Drive dual-auth toggle (OAuth Token + Service Account tabs)
- `src/components/icons/BackendIcons.tsx` - Inline SVG icon map for all 18 backends
## Decisions Made
- OAuthInstructions uses step-prefix detection to render the authorize command in a `<code>` element — the step text "Run the authorize command: ..." is matched by prefix so the command value is extracted cleanly for the code block while keeping getByText test assertions working correctly.
- BACKEND_ICONS is `Partial<Record<BackendType,...>>` so missing icons simply render nothing — plans 03/04 handle the absent-icon case in BackendCard.
- GdriveAuthToggle defaults to oauth tab since OAuth is the typical personal-use flow; service account is the secondary option.
- Letter-based SVG icons (S3, B2, G, p) use `<text>` elements for simplicity at 24px — path-based glyph construction for single characters is over-engineered.
## Deviations from Plan
None - plan executed exactly as written.
## Issues Encountered
None.
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- OAuthInstructions ready for embedding in any OAuth backend config step
- GdriveAuthToggle ready for use in RemoteConfigStep (plan 13-04) as gdrive's auth renderer
- BACKEND_ICONS ready for BackendCard integration in BackendSelectionStep (plan 13-03)
- Full test suite (270 tests, 17 files) remains green
---
*Phase: 13-add-remaining-rclone-remotes*
*Completed: 2026-04-01*
+2 -2
View File
@@ -19,11 +19,11 @@ function IntroSection({ onStart }: { onStart: () => void }) {
<h2 className="text-2xl font-bold text-on-surface mb-4"> <h2 className="text-2xl font-bold text-on-surface mb-4">
Go from zero to a deployable rclone setup in minutes Go from zero to a deployable rclone setup in minutes
</h2> </h2>
<p className="text-base text-on-surface-variant max-w-lg mb-3"> <p className="text-base text-on-surface-container max-w-lg mb-3">
Ready2Blob walks you through configuring rclone for Azure Blob, S3, OneDrive, and 4 more Ready2Blob walks you through configuring rclone for Azure Blob, S3, OneDrive, and 4 more
cloud backends no syntax knowledge required. cloud backends no syntax knowledge required.
</p> </p>
<p className="text-base text-on-surface-variant max-w-lg mb-8"> <p className="text-base text-on-surface-container max-w-lg mb-8">
The wizard generates a ready-to-use <code className="font-mono text-sm">rclone.conf</code>{' '} The wizard generates a ready-to-use <code className="font-mono text-sm">rclone.conf</code>{' '}
and deployment scripts (Intune / RMM) you can download and run immediately. and deployment scripts (Intune / RMM) you can download and run immediately.
</p> </p>
@@ -85,7 +85,7 @@ export function BackendSelectionStep() {
return ( return (
<div> <div>
<h2 className="text-2xl font-bold text-on-surface mb-2">Step 1: Select Backend</h2> <h2 className="text-2xl font-bold text-on-surface mb-2">Step 1: Select Backend</h2>
<p className="text-sm text-on-surface-variant mt-1 mb-4"> <p className="text-sm text-on-surface-container mt-1 mb-4">
Name your remote and pick the cloud storage provider you want to connect to. Name your remote and pick the cloud storage provider you want to connect to.
Your choice determines which credentials are required in the next step. Your choice determines which credentials are required in the next step.
</p> </p>
@@ -104,7 +104,7 @@ export function BackendSelectionStep() {
value={searchQuery} value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)} onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search backends..." placeholder="Search backends..."
className="w-full px-4 py-2.5 rounded-xl border border-outline bg-surface-container text-on-surface placeholder:text-on-surface-variant/50 focus:outline-none focus:ring-2 focus:ring-primary mt-4" className="w-full px-4 py-2.5 rounded-xl border border-outline bg-surface-container text-on-surface placeholder:text-on-surface-container/50 focus:outline-none focus:ring-2 focus:ring-primary mt-4"
/> />
<div data-testid="backend-cards" className="mt-4"> <div data-testid="backend-cards" className="mt-4">
{CATEGORY_ORDER.map(cat => { {CATEGORY_ORDER.map(cat => {
+1 -1
View File
@@ -22,7 +22,7 @@ export function DeploymentStep() {
return ( return (
<div> <div>
<h2 className="text-2xl font-bold text-on-surface mb-2">Step 3: Deployment Options</h2> <h2 className="text-2xl font-bold text-on-surface mb-2">Step 3: Deployment Options</h2>
<p className="text-sm text-on-surface-variant mt-1 mb-4"> <p className="text-sm text-on-surface-container mt-1 mb-4">
Choose how the generated config gets deployed to the target machine and which deployment Choose how the generated config gets deployed to the target machine and which deployment
script formats to produce. script formats to produce.
</p> </p>
+1 -1
View File
@@ -46,7 +46,7 @@ export function OAuthInstructions({ backendName, authorizeCommand, steps }: OAut
{expanded && ( {expanded && (
<div className="border-t border-outline px-4 py-3"> <div className="border-t border-outline px-4 py-3">
<ol className="flex flex-col gap-2 list-decimal list-inside text-sm text-on-surface-variant"> <ol className="flex flex-col gap-2 list-decimal list-inside text-sm text-on-surface-container">
{resolvedSteps.map((step, index) => { {resolvedSteps.map((step, index) => {
// The second step contains the command — render it with a code block // The second step contains the command — render it with a code block
if (step.startsWith('Run the authorize command:')) { if (step.startsWith('Run the authorize command:')) {
+1 -1
View File
@@ -225,7 +225,7 @@ export function RemoteConfigStep() {
return ( return (
<div> <div>
<h2 className="text-2xl font-bold text-on-surface mb-2">Step 2: Configure {backendLabel[backendType]}</h2> <h2 className="text-2xl font-bold text-on-surface mb-2">Step 2: Configure {backendLabel[backendType]}</h2>
<p className="text-sm text-on-surface-variant mt-1 mb-4"> <p className="text-sm text-on-surface-container mt-1 mb-4">
Enter the credentials for your selected backend. These values are written directly into the Enter the credentials for your selected backend. These values are written directly into the
generated rclone.conf and are never sent to any server. generated rclone.conf and are never sent to any server.
</p> </p>
+1 -1
View File
@@ -73,7 +73,7 @@ export function ReviewStep() {
return ( return (
<div> <div>
<h2 className="text-2xl font-bold text-on-surface mb-2">Step 4: Review &amp; Download</h2> <h2 className="text-2xl font-bold text-on-surface mb-2">Step 4: Review &amp; Download</h2>
<p className="text-sm text-on-surface-variant mt-1 mb-4"> <p className="text-sm text-on-surface-container mt-1 mb-4">
Review your generated files and download them. Acknowledge the security notice before Review your generated files and download them. Acknowledge the security notice before
copying or downloading credentials. copying or downloading credentials.
</p> </p>