docs(13): create phase plan for backend expansion

4 plans across 3 waves to expand from 7 to 17 rclone backends:
- Plan 01 (W1): Registry refactoring, BackendType derivation, 10 new entries
- Plan 02 (W2): OAuthInstructions, GdriveAuthToggle, BackendIcons components
- Plan 03 (W2): BackendSelectionStep UX overhaul with categories, search, icons
- Plan 04 (W3): RemoteConfigStep wiring for all new backends + visual verify

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-01 15:58:51 +02:00
co-authored by Claude Opus 4.6
parent fac90462f7
commit 4bf3f1e45f
6 changed files with 1019 additions and 30 deletions
+24 -6
View File
@@ -41,6 +41,17 @@
- [x] **DARK-02**: DeploymentStep native form controls (labels, legends, checkboxes, radios) are visible and styled with semantic tokens in dark mode - [x] **DARK-02**: DeploymentStep native form controls (labels, legends, checkboxes, radios) are visible and styled with semantic tokens in dark mode
- [x] **DARK-03**: FieldRenderer select element has proper background and text color in dark mode - [x] **DARK-03**: FieldRenderer select element has proper background and text color in dark mode
## v1.3 Requirements
### Remote Backend Expansion
- [ ] **REMOTE-01**: BACKEND_REGISTRY contains all 17 backends (7 existing + 10 new) with correct rclone field definitions, displayName, description, and category
- [ ] **REMOTE-02**: BackendType is derived from registry keys (`keyof typeof BACKEND_REGISTRY`) instead of an explicit union literal, eliminating a touch-point when adding backends
- [ ] **REMOTE-03**: BackendSelectionStep displays backends grouped by category (Cloud Object Storage, Cloud Drives, Protocol-based) with instant search filtering and inline SVG icons
- [ ] **REMOTE-04**: OAuth-token backends (Google Drive, Dropbox, Box, pCloud, OneDrive) display a collapsible OAuthInstructions component with backend-specific `rclone authorize` command
- [ ] **REMOTE-05**: RemoteConfigStep renders the correct form fields for every backend, including GdriveAuthToggle for Google Drive's dual-auth (OAuth token vs service account)
- [ ] **REMOTE-06**: buildRcloneConf produces valid rclone.conf output with correct type strings for all 17 backends
## Future Requirements ## Future Requirements
- Accent color presets (5-8 curated themes) — deferred from v1.2 scoping - Accent color presets (5-8 curated themes) — deferred from v1.2 scoping
@@ -77,15 +88,22 @@
| POLISH-02 | Phase 11 | Complete | | POLISH-02 | Phase 11 | Complete |
| POLISH-03 | Phase 11 | Complete | | POLISH-03 | Phase 11 | Complete |
| POLISH-04 | Phase 11 | Complete | | POLISH-04 | Phase 11 | Complete |
| DARK-01 | Phase 12 | Planned | | DARK-01 | Phase 12 | Complete |
| DARK-02 | Phase 12 | Planned | | DARK-02 | Phase 12 | Complete |
| DARK-03 | Phase 12 | Planned | | DARK-03 | Phase 12 | Complete |
| REMOTE-01 | Phase 13 | Planned |
| REMOTE-02 | Phase 13 | Planned |
| REMOTE-03 | Phase 13 | Planned |
| REMOTE-04 | Phase 13 | Planned |
| REMOTE-05 | Phase 13 | Planned |
| REMOTE-06 | Phase 13 | Planned |
**Coverage:** **Coverage:**
- v1.2 requirements: 18 total - v1.2 requirements: 18 total (18 complete)
- Mapped to phases: 18 - v1.3 requirements: 6 total (0 complete)
- Mapped to phases: 24
- Unmapped: 0 - Unmapped: 0
--- ---
*Requirements defined: 2026-03-31* *Requirements defined: 2026-03-31*
*Last updated: 2026-04-01 after Phase 12 planning* *Last updated: 2026-04-01 after Phase 13 planning*
+43 -24
View File
@@ -4,7 +4,8 @@
-**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 811 (in progress) - **v1.2 UI Polish & Clarity** — Phases 812 (shipped 2026-04-01)
- **v1.3 Backend Expansion** — Phase 13 (in progress)
## Phases ## Phases
@@ -31,14 +32,20 @@ Full phase details: [.planning/milestones/v1.1-ROADMAP.md](milestones/v1.1-ROADM
</details> </details>
### v1.2 UI Polish & Clarity (In Progress) <details>
<summary>✅ v1.2 UI Polish & Clarity (Phases 812) — SHIPPED 2026-04-01</summary>
**Milestone Goal:** Transform the bare functional wizard into a polished, self-explanatory experience with Material Design 3 styling, dark mode, contextual help, and responsive layout.
- [x] **Phase 8: Theme Foundation** - MD3 color token system and dark mode infrastructure (completed 2026-04-01) - [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 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 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 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 Details
@@ -103,22 +110,6 @@ Plans:
- [ ] 11-01-PLAN.md — Mobile responsive layout + MD3 focus-visible indicators - [ ] 11-01-PLAN.md — Mobile responsive layout + MD3 focus-visible indicators
- [ ] 11-02-PLAN.md — Step transition animation + auto-scroll to first error - [ ] 11-02-PLAN.md — Step transition animation + auto-scroll to first error
## 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 |
### Phase 12: Dark mode visibility fixes ### 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 **Goal:** Fix dark mode visibility regressions where step headings, DeploymentStep form controls, and FieldRenderer select elements are invisible or unstyled against the dark background
@@ -136,10 +127,38 @@ Plans:
### Phase 13: Add remaining RClone remotes ### Phase 13: Add remaining RClone remotes
**Goal:** [To be planned] **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**: TBD **Requirements**: REMOTE-01, REMOTE-02, REMOTE-03, REMOTE-04, REMOTE-05, REMOTE-06
**Depends on:** Phase 12 **Depends on:** Phase 12
**Plans:** 0 plans **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 plans
Plans: Plans:
- [ ] TBD (run /gsd:plan-phase 13 to break down) - [ ] 13-01-PLAN.md — Registry refactoring: derive BackendType, add categories, 10 new backend entries, 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 | v1.3 | 0/4 | In Progress | — |
@@ -0,0 +1,235 @@
---
phase: 13-add-remaining-rclone-remotes
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- src/schemas/registry.ts
- src/schemas/index.ts
- src/store/types.ts
- src/generators/rclone-conf.ts
- src/schemas/registry.test.ts
- src/schemas/index.test.ts
- src/generators/rclone-conf.test.ts
autonomous: true
requirements: [REMOTE-01, REMOTE-02, REMOTE-06]
must_haves:
truths:
- "BACKEND_REGISTRY contains all 17 backends with correct fields and category"
- "BackendType is derived from registry keys, not an explicit union"
- "BACKEND_SCHEMAS auto-generates from registry (no manual per-backend calls)"
- "RCLONE_TYPE_MAP has correct type strings for all 17 backends"
- "buildRcloneConf produces valid rclone.conf for every new backend"
artifacts:
- path: "src/schemas/registry.ts"
provides: "17 backend entries with category field, BackendType derived via keyof"
contains: "as const"
- path: "src/schemas/index.ts"
provides: "Auto-generated BACKEND_SCHEMAS from registry keys"
contains: "Object.fromEntries"
- path: "src/generators/rclone-conf.ts"
provides: "RCLONE_TYPE_MAP entries for all 17 backends"
key_links:
- from: "src/schemas/registry.ts"
to: "src/store/types.ts"
via: "BackendType re-export"
pattern: "export type.*BackendType"
- from: "src/schemas/registry.ts"
to: "src/schemas/index.ts"
via: "BACKEND_SCHEMAS auto-generation"
pattern: "Object\\.keys\\(BACKEND_REGISTRY\\)"
---
<objective>
Refactor the backend registry to support 17 backends with categories, derive BackendType from registry keys, auto-generate BACKEND_SCHEMAS, and add all 10 new backend entries with correct rclone type mappings.
Purpose: This is the data foundation every other plan in Phase 13 depends on. No UI work can proceed until the registry has all backends, the types are derived, and schemas auto-generate.
Output: Updated registry.ts, index.ts, types.ts, rclone-conf.ts with all 17 backends, full test coverage.
</objective>
<execution_context>
@C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md
@C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/13-add-remaining-rclone-remotes/13-CONTEXT.md
@.planning/phases/13-add-remaining-rclone-remotes/13-RESEARCH.md
<interfaces>
<!-- Current contracts that will be refactored -->
From src/schemas/registry.ts:
```typescript
export type BackendType = 'azureblob' | 's3' | 's3-compatible' | 'onedrive' | 'sftp' | 'gcs' | 'b2';
export interface FieldDef {
key: string;
label: string;
inputType: 'text' | 'password' | 'select' | 'toggle';
required: boolean;
placeholder?: string;
helpText?: string;
options?: { value: string; label: string }[];
validate?: { regex: RegExp; message: string };
tooltipText?: string;
}
// Current cast at bottom (must be removed):
// } as Record<BackendType, { displayName: string; description: string; fields: FieldDef[] }>;
```
From src/schemas/index.ts:
```typescript
export const BACKEND_SCHEMAS = {
azureblob: buildZodSchema('azureblob'),
// ... explicit per-backend calls
} as const;
```
From src/generators/rclone-conf.ts:
```typescript
const RCLONE_TYPE_MAP: Record<string, string> = {
azureblob: 'azureblob',
s3: 's3',
's3-compatible': 's3',
onedrive: 'onedrive',
sftp: 'sftp',
gcs: 'google cloud storage',
b2: 'b2',
};
```
From src/store/types.ts:
```typescript
import type { BackendType } from '../schemas/registry';
export type { BackendType };
```
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: Refactor registry types and add all 10 new backend entries</name>
<files>src/schemas/registry.ts, src/store/types.ts</files>
<action>
1. Add `BackendCategory` type: `'cloud-object-storage' | 'cloud-drives' | 'protocol-based'`
2. Add `category` field to the backend entry shape. Update the interface used by entries to include `category: BackendCategory`.
3. Remove the explicit `BackendType` union literal. Instead, add `as const` to the BACKEND_REGISTRY object and derive: `export type BackendType = keyof typeof BACKEND_REGISTRY;`
4. CRITICAL: Remove the `as Record<BackendType, ...>` cast at the bottom of the registry object. With `as const`, TypeScript infers the precise type. The cast creates a circular reference when BackendType is derived from keyof.
5. Add `category` to all 7 existing entries:
- azureblob: 'cloud-object-storage'
- s3: 'cloud-object-storage'
- s3-compatible: 'cloud-object-storage'
- gcs: 'cloud-object-storage'
- b2: 'cloud-object-storage'
- onedrive: 'cloud-drives'
- sftp: 'protocol-based'
6. Add 10 new backend entries (see 13-RESEARCH.md for field details):
Cloud Object Storage:
- `azure-files`: displayName 'Azure Files', fields: account (text, required), key (password, required). Verify field names against rclone.org/azurefiles/ — account + key is the standard pattern.
- `swift`: displayName 'OpenStack Swift', fields: user (text, required), key (password, required), auth (text, required, placeholder 'https://auth.example.com/v3'), tenant (text, required), region (text, optional)
Cloud Drives (OAuth token-paste):
- `gdrive`: displayName 'Google Drive', fields: token (password, required, helpText about rclone authorize "drive"), service_account_credentials (password, optional, helpText for service account JSON), root_folder_id (text, optional)
- `dropbox`: displayName 'Dropbox', fields: token (password, required, helpText about rclone authorize "dropbox")
- `box`: displayName 'Box', fields: token (password, required), box_sub_type (select: user/enterprise, default 'user')
- `pcloud`: displayName 'pCloud', fields: token (password, required), hostname (select: api.pcloud.com/eapi.pcloud.com with labels 'US (default)'/'EU')
Protocol-based:
- `ftp`: displayName 'FTP', fields: host (text, required), user (text, optional, placeholder 'anonymous'), pass (password, optional), port (text, optional, placeholder '21'), explicit_tls (select: ''/true with labels 'Plain FTP'/'Explicit FTPS')
- `webdav`: displayName 'WebDAV', fields: url (text, required, placeholder 'https://example.com/dav'), user (text, required), pass (password, required), vendor (select: nextcloud/owncloud/sharepoint/sharepoint-ntlm/other)
- `smb`: displayName 'SMB / Windows Share', fields: host (text, required), user (text, required), pass (password, optional), domain (text, optional, placeholder 'WORKGROUP'), port (text, optional, placeholder '445')
- `http`: displayName 'HTTP (read-only)', fields: url (text, required, placeholder 'https://example.com/path/')
- `seafile`: displayName 'Seafile', fields: url (text, required, placeholder 'https://cloud.seafile.com'), user (text, required), pass (password, required)
7. Add appropriate helpText and tooltipText to new backend fields following existing patterns (see 13-RESEARCH.md pitfalls section for OAuth token fields).
8. Verify src/store/types.ts still works — it does `import type { BackendType } from '../schemas/registry'` and `export type { BackendType }`. No changes needed since the derived type exports identically.
IMPORTANT: Do NOT pass real placeholder text to fields that will be rendered by TextFieldMD3 — TextFieldMD3 uses `placeholder=" "` internally for floating labels. Use `helpText` instead for descriptive hints. Placeholders are only safe on fields NOT rendered via TextFieldMD3 (select fields, or fields where the placeholder is a format example like 'AKIAIOSFODNN7EXAMPLE').
</action>
<verify>
<automated>npx vitest run src/schemas/registry.test.ts src/store/ --reporter=verbose 2>&1 | tail -30</automated>
</verify>
<done>BACKEND_REGISTRY has 17 entries each with displayName, description, category, and fields. BackendType is derived from keyof typeof BACKEND_REGISTRY. No circular type reference. TypeScript compiles cleanly.</done>
</task>
<task type="auto">
<name>Task 2: Auto-generate BACKEND_SCHEMAS, update RCLONE_TYPE_MAP, update tests</name>
<files>src/schemas/index.ts, src/generators/rclone-conf.ts, src/schemas/index.test.ts, src/generators/rclone-conf.test.ts, src/schemas/registry.test.ts</files>
<action>
1. In src/schemas/index.ts: Replace the explicit BACKEND_SCHEMAS object with auto-generation:
```typescript
export const BACKEND_SCHEMAS = Object.fromEntries(
(Object.keys(BACKEND_REGISTRY) as BackendType[]).map(t => [t, buildZodSchema(t)])
) as Record<BackendType, ReturnType<typeof buildZodSchema>>;
```
This eliminates the need to add a manual `buildZodSchema()` call per new backend.
2. In src/generators/rclone-conf.ts: Add entries to RCLONE_TYPE_MAP for all 10 new backends. Verified type strings from rclone docs:
- 'azure-files': 'azurefiles'
- 'swift': 'swift'
- 'gdrive': 'drive'
- 'dropbox': 'dropbox'
- 'box': 'box'
- 'pcloud': 'pcloud'
- 'ftp': 'ftp'
- 'webdav': 'webdav'
- 'smb': 'smb'
- 'http': 'http'
- 'seafile': 'seafile'
3. Update src/schemas/registry.test.ts:
- Add test: every backend has displayName, description, category, and at least one field
- Add test: BackendCategory values are one of the three valid categories
- Verify all 17 backend keys are present
4. Update/create src/schemas/index.test.ts:
- Test that BACKEND_SCHEMAS has an entry for every key in BACKEND_REGISTRY (no undefined)
- Test that each schema is a ZodObject (not undefined)
5. Update src/generators/rclone-conf.test.ts:
- Add test: RCLONE_TYPE_MAP has entry for every BackendType
- Add a buildRcloneConf snapshot test for at least 3 new backends (gdrive, ftp, smb) to confirm correct output format
</action>
<verify>
<automated>npx vitest run src/schemas/ src/generators/rclone-conf.test.ts --reporter=verbose 2>&1 | tail -40</automated>
</verify>
<done>BACKEND_SCHEMAS auto-generates for all 17 backends. RCLONE_TYPE_MAP covers all 17 backends. All tests pass including new coverage for auto-generation, type mapping, and rclone.conf output for new backends.</done>
</task>
</tasks>
<verification>
- `npx vitest run` — full suite green (no regressions)
- TypeScript compiles: `npx tsc --noEmit` — no errors
- All 17 backend keys present in BACKEND_REGISTRY
- BackendType derived (no explicit union)
- BACKEND_SCHEMAS auto-generated (no manual calls)
</verification>
<success_criteria>
- 17 backends in BACKEND_REGISTRY with category field
- BackendType = keyof typeof BACKEND_REGISTRY (derived, not explicit)
- BACKEND_SCHEMAS auto-generated from registry keys
- RCLONE_TYPE_MAP has all 17 entries with verified rclone type strings
- All existing + new tests pass
- TypeScript compiles without errors
</success_criteria>
<output>
After completion, create `.planning/phases/13-add-remaining-rclone-remotes/13-01-SUMMARY.md`
</output>
@@ -0,0 +1,193 @@
---
phase: 13-add-remaining-rclone-remotes
plan: 02
type: execute
wave: 2
depends_on: [13-01]
files_modified:
- src/components/wizard/OAuthInstructions.tsx
- src/components/wizard/OAuthInstructions.test.tsx
- src/components/wizard/GdriveAuthToggle.tsx
- src/components/icons/BackendIcons.tsx
autonomous: true
requirements: [REMOTE-04]
must_haves:
truths:
- "OAuthInstructions renders collapsed by default and expands on click"
- "OAuthInstructions shows backend-specific rclone authorize command"
- "GdriveAuthToggle switches between OAuth Token and Service Account tabs"
- "Backend icons are inline SVGs with no external dependency"
artifacts:
- path: "src/components/wizard/OAuthInstructions.tsx"
provides: "Collapsible OAuth step-by-step guide"
exports: ["OAuthInstructions"]
- path: "src/components/wizard/GdriveAuthToggle.tsx"
provides: "Google Drive dual-auth toggle (OAuth vs Service Account)"
exports: ["GdriveAuthToggle"]
- path: "src/components/icons/BackendIcons.tsx"
provides: "Inline SVG icon map keyed by BackendType"
exports: ["BACKEND_ICONS"]
- path: "src/components/wizard/OAuthInstructions.test.tsx"
provides: "Tests for collapsed/expanded behavior"
key_links:
- from: "src/components/wizard/GdriveAuthToggle.tsx"
to: "src/components/wizard/OAuthInstructions.tsx"
via: "import and render"
pattern: "OAuthInstructions"
- from: "src/components/icons/BackendIcons.tsx"
to: "src/schemas/registry.ts"
via: "BackendType key mapping"
pattern: "Record.*BackendType"
---
<objective>
Build the three new components needed by Phase 13: OAuthInstructions (collapsible guide for OAuth token-paste backends), GdriveAuthToggle (dual-auth toggle for Google Drive), and BackendIcons (inline SVG icon map).
Purpose: These components are consumed by plans 03 (BackendSelectionStep) and 04 (RemoteConfigStep). Building them in isolation ensures clean interfaces and testability.
Output: Three new component files with tests for OAuthInstructions.
</objective>
<execution_context>
@C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md
@C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/13-add-remaining-rclone-remotes/13-CONTEXT.md
@.planning/phases/13-add-remaining-rclone-remotes/13-RESEARCH.md
@.planning/phases/13-add-remaining-rclone-remotes/13-01-SUMMARY.md
<interfaces>
<!-- Existing patterns to follow -->
From src/components/wizard/AzureAuthToggle.tsx (AuthToggle pattern):
```typescript
interface AzureAuthToggleProps {
register: UseFormRegister<any>;
errors: {
key?: FieldError;
sas_url?: FieldError;
};
}
// Uses useState for tab, segmented control buttons, CSS hidden/block for inactive/active
```
From src/components/ui/PasswordField.tsx (used for token fields):
```typescript
// PasswordField wraps TextFieldMD3 with show/hide toggle
// Used for all password/token inputs
```
From src/components/ui/BackendCard.tsx (icon will be added here):
```typescript
interface BackendCardProps {
name: string;
description: string;
selected?: boolean;
onClick: () => void;
}
```
MD3 styling tokens used throughout:
- bg-surface, bg-surface-container, bg-primary
- text-on-surface, text-on-surface-variant, text-on-primary
- border-outline, rounded-xl, shadow
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: OAuthInstructions component with tests</name>
<files>src/components/wizard/OAuthInstructions.tsx, src/components/wizard/OAuthInstructions.test.tsx</files>
<behavior>
- Test 1: Renders collapsed by default (instructions not visible)
- Test 2: Expands when user clicks the toggle button, showing numbered steps
- Test 3: Shows the backend-specific `authorizeCommand` in the expanded content
- Test 4: Shows backend name in the toggle button text
</behavior>
<action>
Create OAuthInstructions component with these props:
```typescript
interface OAuthInstructionsProps {
backendName: string; // e.g. "Google Drive"
authorizeCommand: string; // e.g. 'rclone authorize "drive"'
steps?: string[]; // optional custom steps (defaults to generic OAuth flow)
}
```
Implementation:
- Use `useState(false)` for expanded state
- Render a button/disclosure that reads "How to get your OAuth token" (or similar with backendName)
- When expanded, show numbered steps: (1) Install rclone on a machine with a browser, (2) Run the authorize command (show in a code block), (3) Authenticate in the browser, (4) Copy the JSON token from terminal, (5) Paste it in the field above
- Use `steps` prop to override default steps if provided
- Style with MD3 tokens: bg-surface-container, text-on-surface, border-outline, rounded-xl
- Use an expand/collapse chevron icon (inline SVG, simple caret)
- Collapse transition is optional (CSS-only if added)
Write tests first (RED), then implement (GREEN).
</action>
<verify>
<automated>npx vitest run src/components/wizard/OAuthInstructions.test.tsx --reporter=verbose</automated>
</verify>
<done>OAuthInstructions renders collapsed by default, expands on click, shows backend-specific command and steps. All 4 tests pass.</done>
</task>
<task type="auto">
<name>Task 2: GdriveAuthToggle and BackendIcons components</name>
<files>src/components/wizard/GdriveAuthToggle.tsx, src/components/icons/BackendIcons.tsx</files>
<action>
1. Create GdriveAuthToggle following the exact same pattern as AzureAuthToggle:
```typescript
interface GdriveAuthToggleProps {
register: UseFormRegister<any>;
errors: {
token?: FieldError;
service_account_credentials?: FieldError;
};
}
```
- Two tabs: "OAuth Token" and "Service Account"
- Tab 1 (OAuth Token): OAuthInstructions component (backendName="Google Drive", authorizeCommand='rclone authorize "drive"') + PasswordField for token
- Tab 2 (Service Account): PasswordField for service_account_credentials with helpText about JSON key file
- Both fields always registered (CSS hidden/block), same as Azure/SFTP pattern
- Use same segmented control styling as AzureAuthToggle
2. Create BackendIcons.tsx with inline SVG icons for all 17 backends:
- Export `BACKEND_ICONS: Partial<Record<BackendType, React.FC<IconProps>>>` where `IconProps = { className?: string }`
- Each icon is a simple monochrome SVG using `fill="currentColor"` for MD3 dark mode compatibility
- Icons should be recognizable at 24x24px size (viewBox="0 0 24 24")
- Use simplified/stylized versions of brand icons (cloud shape for generic cloud storage, drive icon for Google Drive, folder for file-based, server for protocol-based, etc.)
- For backends where a distinctive icon is hard to create (swift, seafile), use a generic category icon (cloud, server, globe)
- The icon map is Partial — missing entries just mean no icon rendered on the card
Create the icons directory: src/components/icons/
</action>
<verify>
<automated>npx tsc --noEmit 2>&1 | tail -20 && npx vitest run src/components/wizard/ --reporter=verbose 2>&1 | tail -30</automated>
</verify>
<done>GdriveAuthToggle renders two-tab auth toggle with OAuthInstructions in OAuth tab. BackendIcons exports BACKEND_ICONS map with inline SVG icons for backends. TypeScript compiles, existing tests pass.</done>
</task>
</tasks>
<verification>
- `npx vitest run` — full suite green
- `npx tsc --noEmit` — no type errors
- OAuthInstructions test file passes all 4 behavior tests
- GdriveAuthToggle follows AzureAuthToggle pattern exactly
</verification>
<success_criteria>
- OAuthInstructions component: collapsed by default, expands on click, shows authorize command
- GdriveAuthToggle: two-tab toggle (OAuth Token + Service Account), both fields always registered
- BackendIcons: inline SVG map keyed by BackendType, monochrome, no external dependency
- All tests pass
</success_criteria>
<output>
After completion, create `.planning/phases/13-add-remaining-rclone-remotes/13-02-SUMMARY.md`
</output>
@@ -0,0 +1,246 @@
---
phase: 13-add-remaining-rclone-remotes
plan: 03
type: execute
wave: 2
depends_on: [13-01]
files_modified:
- src/components/wizard/BackendSelectionStep.tsx
- src/components/ui/BackendCard.tsx
- src/components/wizard/BackendSelectionStep.test.tsx
autonomous: true
requirements: [REMOTE-03]
must_haves:
truths:
- "Backends are grouped under category headings: Cloud Object Storage, Cloud Drives, Protocol-based"
- "Search bar filters backends instantly as user types"
- "Search matches displayName, description, category label, and field labels"
- "Categories with no matching backends are hidden (not rendered)"
- "Each backend card shows an inline SVG icon"
- "Existing remote name input and validation still work"
artifacts:
- path: "src/components/wizard/BackendSelectionStep.tsx"
provides: "Category-grouped, searchable backend selection with icons"
contains: "CATEGORY_ORDER"
- path: "src/components/ui/BackendCard.tsx"
provides: "Backend card with optional icon prop"
- path: "src/components/wizard/BackendSelectionStep.test.tsx"
provides: "Tests for search filter and category collapse"
key_links:
- from: "src/components/wizard/BackendSelectionStep.tsx"
to: "src/schemas/registry.ts"
via: "BACKEND_REGISTRY iteration with category grouping"
pattern: "BACKEND_REGISTRY.*category"
- from: "src/components/wizard/BackendSelectionStep.tsx"
to: "src/components/icons/BackendIcons.tsx"
via: "BACKEND_ICONS import for card rendering"
pattern: "BACKEND_ICONS"
- from: "src/components/wizard/BackendSelectionStep.tsx"
to: "src/components/ui/TextFieldMD3.tsx"
via: "Search bar input"
pattern: "TextFieldMD3"
---
<objective>
Overhaul BackendSelectionStep from a flat card grid to a categorized, searchable layout with icons. The step must handle 17 backends without overwhelming the user.
Purpose: With 17 backends (up from 7), a flat grid is unusable. Categories group by IT-pro mental model, search enables quick access, icons aid visual scanning.
Output: Refactored BackendSelectionStep.tsx, updated BackendCard.tsx, new test cases.
</objective>
<execution_context>
@C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md
@C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/13-add-remaining-rclone-remotes/13-CONTEXT.md
@.planning/phases/13-add-remaining-rclone-remotes/13-RESEARCH.md
@.planning/phases/13-add-remaining-rclone-remotes/13-01-SUMMARY.md
<interfaces>
<!-- From Plan 01 output (registry with categories) -->
From src/schemas/registry.ts (after Plan 01):
```typescript
export type BackendCategory = 'cloud-object-storage' | 'cloud-drives' | 'protocol-based';
export type BackendType = keyof typeof BACKEND_REGISTRY;
// Each entry has: displayName, description, category, fields
```
From src/components/icons/BackendIcons.tsx (from Plan 02):
```typescript
interface IconProps { className?: string; }
export const BACKEND_ICONS: Partial<Record<BackendType, React.FC<IconProps>>>;
```
From src/components/ui/TextFieldMD3.tsx:
```typescript
interface TextFieldMD3Props {
id: string;
label: string;
registration: UseFormRegisterReturn;
error?: FieldError;
required?: boolean;
helpText?: string;
// ... more props
}
```
From src/components/ui/BackendCard.tsx (current):
```typescript
interface BackendCardProps {
name: string;
description: string;
selected?: boolean;
onClick: () => void;
}
```
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: Add icon prop to BackendCard and build category/search into BackendSelectionStep</name>
<files>src/components/ui/BackendCard.tsx, src/components/wizard/BackendSelectionStep.tsx</files>
<action>
1. Update BackendCard to accept an optional `icon` prop:
```typescript
interface BackendCardProps {
name: string;
description: string;
selected?: boolean;
onClick: () => void;
icon?: React.ReactNode; // NEW — rendered at top-left of card
}
```
- Render icon before the name span, in a flex row: `<div className="flex items-center gap-2"><span className="w-6 h-6 shrink-0">{icon}</span><span>{name}</span></div>`
- If icon is undefined/null, render name without the icon wrapper (no empty space)
2. Refactor BackendSelectionStep to add category grouping and search:
a. Add search state: `const [searchQuery, setSearchQuery] = useState('')`
b. Add search bar ABOVE the backend cards (below the remote name input). Use a plain `<input>` styled with MD3 tokens (NOT TextFieldMD3 — it uses placeholder=" " which conflicts with a search placeholder). Style it as:
```
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"
placeholder="Search backends..."
```
Use `onChange={(e) => setSearchQuery(e.target.value)}` — no debounce needed for 17 items.
c. Add category constants:
```typescript
const CATEGORY_ORDER: BackendCategory[] = ['cloud-object-storage', 'cloud-drives', 'protocol-based'];
const CATEGORY_LABELS: Record<BackendCategory, string> = {
'cloud-object-storage': 'Cloud Object Storage',
'cloud-drives': 'Cloud Drives',
'protocol-based': 'Protocol-based',
};
```
d. Add matchesSearch function that checks displayName, description, category label, and field labels:
```typescript
function matchesSearch(entry: { displayName: string; description: string; category: BackendCategory; fields: { label: string }[] }, query: string): boolean {
const q = query.toLowerCase();
return entry.displayName.toLowerCase().includes(q)
|| entry.description.toLowerCase().includes(q)
|| CATEGORY_LABELS[entry.category].toLowerCase().includes(q)
|| entry.fields.some(f => f.label.toLowerCase().includes(q));
}
```
e. Replace the flat `data-testid="backend-cards"` grid with category sections:
```tsx
{CATEGORY_ORDER.map(cat => {
const backends = Object.entries(BACKEND_REGISTRY)
.filter(([, e]) => e.category === cat)
.filter(([, e]) => !searchQuery || matchesSearch(e, searchQuery));
if (backends.length === 0) return null;
return (
<section key={cat} className="mb-6">
<h3 className="text-lg font-semibold text-on-surface mb-3">{CATEGORY_LABELS[cat]}</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
{backends.map(([type, entry]) => (
<BackendCard
key={type}
name={entry.displayName}
description={entry.description}
selected={state.remote.backendType === type}
onClick={() => handleCardClick(type as BackendType)}
icon={BACKEND_ICONS[type as BackendType] ? React.createElement(BACKEND_ICONS[type as BackendType]!, { className: 'w-6 h-6' }) : undefined}
/>
))}
</div>
</section>
);
})}
```
f. Keep the existing remote name TextFieldMD3 and RemoteNamePreview at the top. Keep the Next button at the bottom. Keep form validation logic unchanged.
g. Keep `data-testid="backend-cards"` on a wrapping div around all category sections so existing tests that query within it still work.
3. Import BACKEND_ICONS from '../icons/BackendIcons' and BackendCategory from '../../schemas/registry'.
</action>
<verify>
<automated>npx tsc --noEmit 2>&1 | tail -10 && npx vitest run src/components/wizard/BackendSelectionStep.test.tsx --reporter=verbose 2>&1 | tail -30</automated>
</verify>
<done>BackendSelectionStep renders 17 backends grouped by category with search bar. BackendCard shows icon when provided. Existing tests pass.</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: Add search filter and category collapse tests</name>
<files>src/components/wizard/BackendSelectionStep.test.tsx</files>
<behavior>
- Test 1: All three category headings render when no search query
- Test 2: Typing "ftp" in search shows FTP and SFTP, hides unrelated backends
- Test 3: Typing a query that matches no backends shows no category headings
- Test 4: Searching "Cloudflare" finds S3-Compatible (via description match)
- Test 5: All 17 backend cards render when no search active
- Test 6: Category heading is not rendered when all its backends are filtered out
</behavior>
<action>
Add new test cases to the existing BackendSelectionStep.test.tsx file. Use the existing test setup pattern (WizardProvider wrapper, render helper).
For search tests:
- Find the search input by placeholder "Search backends..."
- Use `fireEvent.change()` or `userEvent.type()` to enter search text
- Assert on presence/absence of backend card text and category headings
For category tests:
- Assert "Cloud Object Storage", "Cloud Drives", "Protocol-based" headings are present
- After filtering, assert missing headings are NOT in the document
Write tests first (RED), verify they fail, then ensure Task 1 implementation makes them pass (GREEN).
</action>
<verify>
<automated>npx vitest run src/components/wizard/BackendSelectionStep.test.tsx --reporter=verbose</automated>
</verify>
<done>6 new test cases pass covering search filtering, category heading visibility, cross-field search matching, and full backend card rendering.</done>
</task>
</tasks>
<verification>
- `npx vitest run` — full suite green
- `npx tsc --noEmit` — no type errors
- BackendSelectionStep renders categories, search works, icons display
- No empty category headings when search filters out all backends in a category
</verification>
<success_criteria>
- 17 backends rendered in 3 category sections with headings
- Search bar filters instantly across displayName, description, category, field labels
- Empty categories hidden (conditional rendering, not CSS)
- Backend cards show inline SVG icons
- Existing remote name validation unchanged
- All tests pass (existing + 6 new)
</success_criteria>
<output>
After completion, create `.planning/phases/13-add-remaining-rclone-remotes/13-03-SUMMARY.md`
</output>
@@ -0,0 +1,278 @@
---
phase: 13-add-remaining-rclone-remotes
plan: 04
type: execute
wave: 3
depends_on: [13-01, 13-02, 13-03]
files_modified:
- src/components/wizard/RemoteConfigStep.tsx
- src/components/wizard/RemoteConfigStep.test.tsx
autonomous: false
requirements: [REMOTE-05]
must_haves:
truths:
- "RemoteConfigStep renders correct form fields for every new backend"
- "Google Drive shows GdriveAuthToggle with OAuth Token and Service Account tabs"
- "Dropbox, Box, pCloud show OAuthInstructions + token field"
- "FTP, WebDAV, SMB, HTTP, Seafile, Azure Files, Swift render via generic FieldRenderer loop"
- "backendLabel is derived from registry displayName (not duplicated)"
- "All form submissions produce correct remote params"
artifacts:
- path: "src/components/wizard/RemoteConfigStep.tsx"
provides: "Form rendering for all 17 backends"
- path: "src/components/wizard/RemoteConfigStep.test.tsx"
provides: "Tests for new backend form rendering"
key_links:
- from: "src/components/wizard/RemoteConfigStep.tsx"
to: "src/components/wizard/GdriveAuthToggle.tsx"
via: "import and render for gdrive backend"
pattern: "GdriveAuthToggle"
- from: "src/components/wizard/RemoteConfigStep.tsx"
to: "src/components/wizard/OAuthInstructions.tsx"
via: "import and render for OAuth backends"
pattern: "OAuthInstructions"
- from: "src/components/wizard/RemoteConfigStep.tsx"
to: "src/schemas/registry.ts"
via: "BACKEND_REGISTRY field lookup"
pattern: "BACKEND_REGISTRY\\["
---
<objective>
Wire all 10 new backends into RemoteConfigStep so every backend type renders the correct configuration form with appropriate auth handling (AuthToggle, OAuthInstructions, or generic FieldRenderer loop).
Purpose: This is the final integration plan — connecting registry data to the form UI. After this, every backend is fully functional end-to-end.
Output: Updated RemoteConfigStep with all backend branches, updated tests, visual verification.
</objective>
<execution_context>
@C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md
@C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/13-add-remaining-rclone-remotes/13-CONTEXT.md
@.planning/phases/13-add-remaining-rclone-remotes/13-RESEARCH.md
@.planning/phases/13-add-remaining-rclone-remotes/13-01-SUMMARY.md
@.planning/phases/13-add-remaining-rclone-remotes/13-02-SUMMARY.md
@.planning/phases/13-add-remaining-rclone-remotes/13-03-SUMMARY.md
<interfaces>
<!-- From Plan 01: Registry with 17 backends -->
From src/schemas/registry.ts:
```typescript
export type BackendType = keyof typeof BACKEND_REGISTRY;
// Includes: azureblob, s3, s3-compatible, onedrive, sftp, gcs, b2,
// azure-files, swift, gdrive, dropbox, box, pcloud,
// ftp, webdav, smb, http, seafile
```
<!-- From Plan 02: New components -->
From src/components/wizard/OAuthInstructions.tsx:
```typescript
interface OAuthInstructionsProps {
backendName: string;
authorizeCommand: string;
steps?: string[];
}
```
From src/components/wizard/GdriveAuthToggle.tsx:
```typescript
interface GdriveAuthToggleProps {
register: UseFormRegister<any>;
errors: {
token?: FieldError;
service_account_credentials?: FieldError;
};
}
```
<!-- Current RemoteConfigStep pattern -->
From src/components/wizard/RemoteConfigStep.tsx:
```typescript
// Three-branch ternary: azureblob -> AzureAuthToggle, sftp -> SftpAuthToggle, else -> generic loop
// backendLabel: Record<NonNullable<typeof backendType>, string> — manual duplication
```
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: Wire all new backends into RemoteConfigStep</name>
<files>src/components/wizard/RemoteConfigStep.tsx</files>
<action>
1. Replace the manual `backendLabel` record with auto-derived version:
```typescript
const backendLabel = Object.fromEntries(
Object.entries(BACKEND_REGISTRY).map(([k, v]) => [k, v.displayName])
) as Record<BackendType, string>;
```
This eliminates the duplicate displayName maintenance per Pitfall 2 in the research.
2. Import new components:
```typescript
import { OAuthInstructions } from './OAuthInstructions';
import { GdriveAuthToggle } from './GdriveAuthToggle';
```
3. Expand the rendering branch logic. The current three-branch ternary (azureblob/sftp/else) needs new branches for backends with special auth handling:
a. `gdrive` — GdriveAuthToggle (handles token + service_account_credentials) + FieldRenderer for root_folder_id:
```tsx
) : backendType === 'gdrive' ? (
<>
<GdriveAuthToggle
register={register}
errors={{
token: errors.token as FieldError | undefined,
service_account_credentials: errors.service_account_credentials as FieldError | undefined,
}}
/>
{BACKEND_REGISTRY.gdrive.fields
.filter(f => f.key !== 'token' && f.key !== 'service_account_credentials')
.map(field => (
<FieldRenderer key={field.key} field={field} register={register} error={errors[field.key] as FieldError | undefined} />
))}
</>
```
b. `dropbox` — OAuthInstructions + generic FieldRenderer loop:
```tsx
) : backendType === 'dropbox' ? (
<>
<OAuthInstructions backendName="Dropbox" authorizeCommand='rclone authorize "dropbox"' />
{BACKEND_REGISTRY.dropbox.fields.map(field => (
<FieldRenderer key={field.key} field={field} register={register} error={errors[field.key] as FieldError | undefined} />
))}
</>
```
c. `box` — OAuthInstructions + generic FieldRenderer loop:
```tsx
) : backendType === 'box' ? (
<>
<OAuthInstructions backendName="Box" authorizeCommand='rclone authorize "box"' />
{BACKEND_REGISTRY.box.fields.map(field => (
<FieldRenderer key={field.key} field={field} register={register} error={errors[field.key] as FieldError | undefined} />
))}
</>
```
d. `pcloud` — OAuthInstructions + generic FieldRenderer loop:
```tsx
) : backendType === 'pcloud' ? (
<>
<OAuthInstructions backendName="pCloud" authorizeCommand='rclone authorize "pcloud"' />
{BACKEND_REGISTRY.pcloud.fields.map(field => (
<FieldRenderer key={field.key} field={field} register={register} error={errors[field.key] as FieldError | undefined} />
))}
</>
```
e. All other new backends (azure-files, swift, ftp, webdav, smb, http, seafile) fall through to the existing generic `else` branch which iterates `BACKEND_REGISTRY[backendType].fields` — NO new branches needed for these since they have no auth toggles.
f. `onedrive` already has OAuthInstructions-worthy fields but currently works via the generic loop. Add OAuthInstructions to onedrive too for consistency:
```tsx
) : backendType === 'onedrive' ? (
<>
<OAuthInstructions backendName="OneDrive" authorizeCommand='rclone authorize "onedrive"' />
{BACKEND_REGISTRY.onedrive.fields.map(field => (
<FieldRenderer key={field.key} field={field} register={register} error={errors[field.key] as FieldError | undefined} />
))}
</>
```
4. The branch order should be: azureblob -> sftp -> gdrive -> onedrive -> dropbox -> box -> pcloud -> else (generic loop). Consider refactoring the deep ternary into a helper function or switch-like pattern for readability with 7 branches. A `renderBackendFields()` function with a switch statement is cleaner than nested ternaries.
5. Verify the form still submits correctly — the onNext handler and validation logic should remain unchanged since BACKEND_SCHEMAS auto-generates for all types.
</action>
<verify>
<automated>npx tsc --noEmit 2>&1 | tail -10 && npx vitest run src/components/wizard/RemoteConfigStep.test.tsx --reporter=verbose 2>&1 | tail -30</automated>
</verify>
<done>RemoteConfigStep renders correct form for all 17 backends. GdriveAuthToggle shown for gdrive. OAuthInstructions shown for onedrive, dropbox, box, pcloud. Generic loop for all others. backendLabel derived from registry. TypeScript compiles, existing tests pass.</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: Add tests for new backend form rendering</name>
<files>src/components/wizard/RemoteConfigStep.test.tsx</files>
<behavior>
- Test 1: gdrive renders GdriveAuthToggle with "OAuth Token" and "Service Account" tabs
- Test 2: dropbox renders OAuthInstructions with 'rclone authorize "dropbox"' command
- Test 3: ftp renders host, username, password, port, TLS mode fields
- Test 4: webdav renders URL, username, password, vendor select fields
- Test 5: smb renders host, username fields
- Test 6: http renders only URL field
</behavior>
<action>
Add test cases to RemoteConfigStep.test.tsx following the existing test pattern:
- Each test sets up WizardState with the target backendType
- Renders RemoteConfigStep within WizardProvider
- Asserts form fields are present by label text or role
- For gdrive: assert the segmented control buttons exist (OAuth Token, Service Account)
- For dropbox: assert OAuthInstructions toggle button is present
- For protocol backends: assert expected fields render
Use the existing test setup pattern from the file. Each new backend test should be a focused assertion on field presence — not full form submission (submission is already tested for existing backends and the mechanism is generic).
Write tests first, then verify they pass against the implementation from Task 1.
</action>
<verify>
<automated>npx vitest run src/components/wizard/RemoteConfigStep.test.tsx --reporter=verbose</automated>
</verify>
<done>6 new test cases pass verifying form rendering for gdrive, dropbox, ftp, webdav, smb, and http backends.</done>
</task>
<task type="checkpoint:human-verify" gate="blocking">
<name>Task 3: Visual verification of complete backend expansion</name>
<files>n/a</files>
<action>
Human verifies the complete backend expansion visually. What was built:
Complete backend expansion — 17 backends with categories, search, icons, OAuth instructions, and correct form rendering for each backend type.
</action>
<verify>
1. Run `npm run dev` and open http://localhost:5173
2. Step 1 (Backend Selection):
- Verify 3 category headings appear: "Cloud Object Storage", "Cloud Drives", "Protocol-based"
- Verify all 17 backend cards are visible with icons
- Type "ftp" in search — only FTP and SFTP should be visible
- Type "Cloudflare" in search — S3-Compatible should appear
- Clear search — all backends visible again
3. Select "Google Drive" — verify Step 2 shows OAuth Token / Service Account toggle
- OAuth Token tab: collapsible instructions + token field
- Service Account tab: service account credentials field
4. Go back, select "Dropbox" — verify Step 2 shows collapsible OAuth instructions + token field
5. Go back, select "FTP" — verify Step 2 shows host, username, password, port, TLS mode fields
6. Go back, select "WebDAV" — verify Step 2 shows URL, username, password, vendor select
7. Go back, select "SMB / Windows Share" — verify host, user, password, domain, port fields
8. Go back, select "HTTP (read-only)" — verify only URL field
9. Toggle dark mode — verify all new backends render correctly in dark mode
10. Run `npx vitest run` — all tests pass
</verify>
<done>User approves visual appearance and functionality of all 17 backends in both light and dark modes.</done>
</task>
</tasks>
<verification>
- `npx vitest run` — full suite green (all existing + new tests)
- `npx tsc --noEmit` — no type errors
- Visual verification of all 17 backends in both light and dark mode
- Form submission works for at least 3 new backends (gdrive, ftp, webdav)
</verification>
<success_criteria>
- Every backend type in BACKEND_REGISTRY has a working form in RemoteConfigStep
- OAuth backends (gdrive, dropbox, box, pcloud, onedrive) show OAuthInstructions
- gdrive shows GdriveAuthToggle with two auth paths
- Protocol backends render via generic FieldRenderer loop
- backendLabel derived from registry (no manual duplication)
- User approves visual appearance in both light and dark modes
</success_criteria>
<output>
After completion, create `.planning/phases/13-add-remaining-rclone-remotes/13-04-SUMMARY.md`
</output>