Document WebDAV sync, workspace theming, and credential hardening

Update CLAUDE.md to reflect completed WebDAV sync and expanded config/sync features. The docs now describe AppConfig fields (mode, theme, WebDAV URL), WorkspaceMode enum, WebDAV client details and credential handling (scoped keyring keys `com.onyx.webdav.<domain>::<username>` with auto-migration from legacy dot-separated format on load), three-way sync with a 60s timeout and auto-creation of the Onyx/ remote subfolder, a 10MB PROPFIND response cap, plus UI changes (setup flow, per-workspace theme options) and other sync/setup UI improvements.
This commit is contained in:
Tristan Michael 2026-04-03 10:34:44 -07:00
parent fa87dbe12b
commit 513acc5606
2 changed files with 14 additions and 7 deletions

View file

@ -37,7 +37,9 @@ Two-crate workspace (`resolver = "2"`, edition 2021) plus a Tauri app:
- **Storage trait** (`storage.rs`): Strategy pattern for task persistence. `FileSystemStorage` reads/writes markdown files with YAML frontmatter and JSON metadata files. - **Storage trait** (`storage.rs`): Strategy pattern for task persistence. `FileSystemStorage` reads/writes markdown files with YAML frontmatter and JSON metadata files.
- **Repository** (`repository.rs`): `TaskRepository` wraps a `Storage` impl and provides the public API for task/list CRUD, ordering, and grouping. Tests live here. - **Repository** (`repository.rs`): `TaskRepository` wraps a `Storage` impl and provides the public API for task/list CRUD, ordering, and grouping. Tests live here.
- **Config** (`config.rs`): `AppConfig` manages named workspaces with paths. Stored in platform-specific config dirs via the `directories` crate. - **Config** (`config.rs`): `AppConfig` manages named workspaces with paths, mode (local/webdav), theme, and WebDAV URL. Stored in platform-specific config dirs via the `directories` crate.
- **Sync** (`sync.rs`): Three-way diff sync with offline queue. Auto-appends `Onyx/` to WebDAV URL. Wrapped in `tokio::time::timeout` (60s) to handle unreachable servers on Windows.
- **WebDAV** (`webdav.rs`): reqwest client with rustls-tls, 30s request timeout, 10s connect timeout. Credentials stored via `keyring` crate (feature-gated). `Zeroizing<String>` for credential fields. Scoped keyring keys (`com.onyx.webdav.<domain>::<username>`); auto-migrates legacy dot-separated format on load. 10MB PROPFIND response cap.
### On-disk format ### On-disk format
@ -49,8 +51,8 @@ The GUI uses Svelte 5 runes mode (`$state`, `$derived`, `$effect`, `$props()`).
- **Sliding drawer**: Left panel (lists) slides with main content as one piece via `translateX`. 80vw wide. List items show checkmark for active list and chevron on hover. - **Sliding drawer**: Left panel (lists) slides with main content as one piece via `translateX`. 80vw wide. List items show checkmark for active list and chevron on hover.
- **Three-panel slide**: Main content area is 300% wide with three panels (task list, task detail, subtask detail) that slide via `translateX` using a `taskStack` array. Stack depth 0 = list, 1 = task detail, 2 = subtask detail. - **Three-panel slide**: Main content area is 300% wide with three panels (task list, task detail, subtask detail) that slide via `translateX` using a `taskStack` array. Stack depth 0 = list, 1 = task detail, 2 = subtask detail.
- **Settings popup**: Floating overlay card with backdrop, not a sliding panel. - **Settings modal**: Per-workspace settings opened from workspace kebab menu. Shows WebDAV config (for webdav workspaces), sync controls, and theme selector.
- **Workspace switcher**: Custom drop-up menu in drawer footer (left), settings gear (right). - **Workspace switcher**: Custom drop-up menu in drawer footer (left), kebab menu per workspace (right) with Settings option.
- **Task animations**: Grid-rows `0fr`/`1fr` trick for smooth collapse/expand. Module-level `animateInIds` Set coordinates expand-in after toggle. - **Task animations**: Grid-rows `0fr`/`1fr` trick for smooth collapse/expand. Module-level `animateInIds` Set coordinates expand-in after toggle.
- **Inline editing**: Click task to edit, auto-save on blur. `debouncedSave` snapshots task before timer to prevent stale-reference errors on component destroy. - **Inline editing**: Click task to edit, auto-save on blur. `debouncedSave` snapshots task before timer to prevent stale-reference errors on component destroy.
- **Kebab menus**: Tasks and lists use kebab menus with custom `ConfirmDialog` component (not native `confirm()`). "Move to..." is inline in the menu (not a submenu) to avoid overflow. - **Kebab menus**: Tasks and lists use kebab menus with custom `ConfirmDialog` component (not native `confirm()`). "Move to..." is inline in the menu (not a submenu) to avoid overflow.
@ -60,7 +62,7 @@ The GUI uses Svelte 5 runes mode (`$state`, `$derived`, `$effect`, `$props()`).
### Current state (2026-04-03) ### Current state (2026-04-03)
- **Phase 1** (Core + CLI): Complete - **Phase 1** (Core + CLI): Complete
- **Phase 2** (WebDAV sync): Backend done, CLI done, GUI wired (settings auto-populates credentials) - **Phase 2** (WebDAV sync): Complete — CLI + GUI sync working, auto-creates `Onyx/` subfolder on remote
- **Phase 3** (GUI MVP): Complete - **Phase 3** (GUI MVP): Complete
- **Phase 4** (Mobile): Tauri Android cfg-gated, needs `tauri android init` + build - **Phase 4** (Mobile): Tauri Android cfg-gated, needs `tauri android init` + build
@ -73,7 +75,7 @@ The GUI uses Svelte 5 runes mode (`$state`, `$derived`, `$effect`, `$props()`).
- Sliding lists drawer with checkmark selection - Sliding lists drawer with checkmark selection
- Settings popup overlay - Settings popup overlay
- Workspace switcher drop-up with add/remove - Workspace switcher drop-up with add/remove
- Dark mode (GNOME-style neutral grays, cyan-blue accent) - Per-workspace theme system (System default, Light, Dark, Nord, Dracula, Solarized Dark) via CSS `data-theme` attribute
- Completed tasks section with animated show/hide - Completed tasks section with animated show/hide
- Due date picker/editor (DateTimePicker in new task + task detail); `has_time: bool` field tracks whether time is set - Due date picker/editor (DateTimePicker in new task + task detail); `has_time: bool` field tracks whether time is set
- Move task between lists (inline list in kebab menu, no submenu) - Move task between lists (inline list in kebab menu, no submenu)
@ -81,11 +83,13 @@ The GUI uses Svelte 5 runes mode (`$state`, `$derived`, `$effect`, `$props()`).
- Group-by-due-date toggle per list (main panel kebab) - Group-by-due-date toggle per list (main panel kebab)
- Delete completed tasks (main panel kebab + subtask kebab, with confirmation dialogs) - Delete completed tasks (main panel kebab + subtask kebab, with confirmation dialogs)
- Keyboard shortcuts (Escape priority chain: settings → detail → list menu → drawer → menus) - Keyboard shortcuts (Escape priority chain: settings → detail → list menu → drawer → menus)
- WebDAV setup flow (settings auto-populates URL/credentials from config + keychain) - Setup screen with 2-step mode selection (Local Folder vs WebDAV Server), window dragging, "Open Existing Folder" option
- WebDAV setup flow with connection test, credential storage in system keychain
- WebDAV sync: auto-creates `Onyx/` subfolder on remote, 60s hard timeout, sync error display in settings
- File watcher (notify crate, 500ms debounce, auto-reloads on external changes) - File watcher (notify crate, 500ms debounce, auto-reloads on external changes)
- Setup screen with window dragging + "Open Existing Folder" option
- Sync status indicators (last-sync time + upload/download counts chip) - Sync status indicators (last-sync time + upload/download counts chip)
- Push/pull/full sync mode selection (session-only, in settings) - Push/pull/full sync mode selection (session-only, in settings)
- WorkspaceMode enum (local/webdav) with per-workspace config
- Desktop packaging (Linux: AppImage + .deb; Windows: MSI) - Desktop packaging (Linux: AppImage + .deb; Windows: MSI)
- Tauri desktop-only deps (notify, keyring) feature-gated for Android compilation - Tauri desktop-only deps (notify, keyring) feature-gated for Android compilation
- Subtask hierarchy: subtask count shown on parent tasks in list, subtask detail via three-panel slide navigation, inline add at top of subtask list (new subtasks prepend), collapsible completed subtasks section, cascade delete (parent deletion removes all subtasks with confirmation warning) - Subtask hierarchy: subtask count shown on parent tasks in list, subtask detail via three-panel slide navigation, inline add at top of subtask list (new subtasks prepend), collapsible completed subtasks section, cascade delete (parent deletion removes all subtasks with confirmation warning)

View file

@ -318,6 +318,8 @@ let status = get_sync_status(Path::new("/home/user/tasks"))?;
Credentials are stored in the platform keychain (Windows Credential Manager, macOS Keychain, Linux Secret Service). Credentials are stored in the platform keychain (Windows Credential Manager, macOS Keychain, Linux Secret Service).
Keyring service keys use the format `com.onyx.webdav.<domain>::<username>` — the `::` separator prevents key collisions when usernames contain dots. On first load, credentials stored in the legacy `.`-separated format (`com.onyx.webdav.<domain>.<username>`) are automatically migrated to the scoped format and the old entries are removed.
```rust ```rust
use onyx_core::webdav::{store_credentials, load_credentials, delete_credentials}; use onyx_core::webdav::{store_credentials, load_credentials, delete_credentials};
@ -363,6 +365,7 @@ client.delete_file("old-task.md").await?;
- **Conflict resolution**: Last-write-wins using file timestamps - **Conflict resolution**: Last-write-wins using file timestamps
- **Offline queue**: Pending operations are queued and replayed when connectivity returns - **Offline queue**: Pending operations are queued and replayed when connectivity returns
- **Sync state**: Stored in `.syncstate.json` within the workspace directory - **Sync state**: Stored in `.syncstate.json` within the workspace directory
- **Response size cap**: PROPFIND responses are limited to 10 MB (checked via `Content-Length` header and actual body size) to prevent memory exhaustion from malicious servers
## Error Handling ## Error Handling