diff --git a/CLAUDE.md b/CLAUDE.md index 5b915fb..de91fb4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -37,7 +37,7 @@ 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. Atomic writes (temp file + rename, with temp cleanup on failure) for all metadata files. Input validation: task titles max 500 chars, descriptions max 1MB, list names max 255 chars, YAML frontmatter max 64KB. Delete operations update metadata before removing files to prevent orphaned metadata on crash. - **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 workspaces keyed by UUID string. `WorkspaceConfig` stores `name`, `path`, `mode` (Local/Webdav/GoogleTasks), `webdav_url`, `webdav_path` (user-selected remote folder), `google_account` (display name/email for GoogleTasks workspaces), `theme`, `sync_interval_secs` (focused polling interval), and `sync_interval_unfocused_secs` (lower-frequency polling when window loses focus, for mobile battery optimization). `add_workspace` returns a generated UUID. Stored in platform-specific config dirs via the `directories` crate. Atomic writes (temp file + rename) prevent corruption on crash. +- **Config** (`config.rs`): `AppConfig` manages workspaces keyed by UUID string. `WorkspaceConfig` stores `name`, `path`, `mode` (Local/Webdav/GoogleTasks), `webdav_url`, `webdav_path` (user-selected remote folder), `google_account` (display name/email for GoogleTasks workspaces), `last_sync` (timestamp of last successful sync), `theme`, `sync_interval_secs` (focused polling interval), and `sync_interval_unfocused_secs` (lower-frequency polling when window loses focus, for mobile battery optimization). `add_workspace` returns a generated UUID. Stored in platform-specific config dirs via the `directories` crate. Atomic writes (temp file + rename) prevent corruption on crash. - **Sync** (`sync.rs`): Three-way diff sync with offline queue. File-based `.sync.lock` prevents concurrent sync operations (auto-cleaned after 5 minutes if stale). Checksum-based conflict resolution: downloads remote, compares SHA-256 — identical content is a false conflict (skipped); when different, remote wins and local is recovered as a duplicate with a new UUID and `[RECOVERED FROM CONFLICT]` prefix (duplicate file cleaned up if metadata update fails). Auto-sync lifecycle: periodic polling (configurable interval, default 60s), debounced file-change (5s), window-focus (30s stale threshold). Wrapped in `tokio::time::timeout` (60s) to handle unreachable servers on Windows. Path traversal validation rejects `..` components and backslashes anywhere in sync paths. Atomic writes for sync state and queue files (temp cleanup on failure). - **WebDAV** (`webdav.rs`): reqwest client with rustls-tls, 30s request timeout, 10s connect timeout. Rejects non-HTTPS URLs. `Zeroizing` for credential fields. `move_resource` method for WebDAV MOVE (workspace rename). 10MB cap on both PROPFIND responses and file downloads. Desktop credentials via `keyring` crate (feature-gated); Tauri GUI uses `tauri-plugin-credentials` for cross-platform support (Android Keystore + desktop keychain). - **Google Tasks** (`google_tasks.rs`): Read-only Google Tasks API client using reqwest with Bearer auth. `gt_id_to_uuid()` converts Google Task IDs to stable UUID v5 values for consistent cross-sync identity. Fetches all task lists and tasks from the Google Tasks REST API and writes them locally via `FileSystemStorage`. Remote always wins (read-only workspace mode). OAuth flow is partially implemented — client ID/secret are placeholders pending real credentials. @@ -80,7 +80,7 @@ Pre-alpha. No users, no released builds, no data to migrate. Breaking changes to - Sliding lists drawer with checkmark selection - Settings popup overlay - Workspace switcher drop-up with add/remove -- Per-workspace theme system (System default, Light, Dark, Nord, Dracula, Solarized Dark, Ink) via CSS `data-theme` attribute +- Per-workspace theme system (System default, Light, Dark, Nord, Dracula, Solarized Dark, Black and Gold, Ink) via CSS `data-theme` attribute - Completed tasks section with animated show/hide - 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) @@ -108,6 +108,7 @@ Pre-alpha. No users, no released builds, no data to migrate. Breaking changes to - Custom confirmation dialogs (ConfirmDialog component replaces native confirm()) - Workspace path validation (rejects system directories) - Task detail auto-cleanup (taskStack clears when viewed task is deleted or list switches) +- Swipe gestures on mobile: swipe left/right on a task to toggle completion (swipe direction depends on current status) - Accessibility: ARIA labels/roles on interactive components, keyboard handlers, `prefers-reduced-motion` CSS support ### GUI features NOT yet done diff --git a/PLAN.md b/PLAN.md index c19f9c7..cc28dc1 100644 --- a/PLAN.md +++ b/PLAN.md @@ -123,6 +123,7 @@ WorkspaceConfig { webdav_url: Option, webdav_path: Option, // User-selected remote folder google_account: Option, // Email/display name (GoogleTasks workspaces) + last_sync: Option, // Timestamp of last successful sync theme: Option, sync_interval_secs: Option, // Auto-sync polling interval (focused) sync_interval_unfocused_secs: Option, // Auto-sync interval when unfocused @@ -750,10 +751,10 @@ WorkspaceConfig { - [x] Mark tasks complete/incomplete with animated transitions - [x] Drag-and-drop task reordering - [x] Sliding lists drawer (80cqi wide, left side) -- [x] Settings popup overlay (WebDAV config, dark mode toggle) -- [x] Dark mode (GNOME-style neutral theme, cyan-blue accent) +- [x] Settings popup overlay (WebDAV config, theme selector, window decorations) +- [x] Per-workspace theme system (System default, Light, Dark, Nord, Dracula, Solarized Dark, Black and Gold, Ink) - [x] Animated completed section show/hide -- [x] Move task between lists (kebab menu → "Move to..." submenu in task detail view) +- [x] Move task between lists (inline list in task kebab menu, no submenu) - [x] Optional time on due dates (`has_time: bool` field on Task with `#[serde(default)]` for backward compat; replaces the hours==0 heuristic) - [x] Due date picker/editor (DateTimePicker component in both new task toast + task detail view) - [x] WebDAV setup flow with credentials (settings auto-populates URL/username/password from config + keychain on open) @@ -908,7 +909,8 @@ npm run tauri ios build - [ ] Multiple windows (optional) #### Mobile-Specific -- [x] Swipe gestures (swipe to complete, swipe to delete) +- [x] Swipe gestures (swipe to toggle completion; direction depends on current task status) +- [ ] Swipe to delete - [ ] Pull-to-refresh - [ ] Touch-optimized UI elements - [ ] Larger touch targets @@ -977,7 +979,7 @@ npm run tauri ios build #### Google Tasks Importer - [x] `google_tasks.rs` module in `onyx-core` — client, UUID mapping, read-only sync (remote always wins) - [x] `GoogleTasks` workspace mode and `google_account` config field -- [x] Tauri commands: `google_tasks_authorize()`, `google_tasks_sync()` +- [x] Tauri commands: `start_google_oauth()`, `add_google_tasks_workspace()`, `sync_google_tasks_workspace()` - [ ] Complete OAuth flow (client ID/secret placeholders need real credentials) - [ ] Migrate tasks, lists, due dates, notes with full UI integration - [ ] Preserve task hierarchy and order diff --git a/README.md b/README.md index c2438aa..af7c30b 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ onyx/ - Drag-and-drop reordering - Sliding lists drawer, settings popup - Workspace switcher with add/remove -- Dark mode (GNOME-style neutral grays, cyan-blue accent) +- Per-workspace theme system (System default, Light, Dark, Nord, Dracula, Solarized Dark, Black and Gold, Ink) - Due date picker/editor with optional time - Subtask hierarchy with three-panel slide navigation - Move tasks between lists @@ -64,6 +64,7 @@ onyx/ - WebDAV setup flow with credential auto-population - File watcher (auto-reloads on external changes) - Auto-sync with configurable interval, status indicators +- Swipe gestures on mobile (swipe to toggle completion) - Custom confirmation dialogs - Desktop packaging (Linux: AppImage + .deb; Windows: MSI) diff --git a/docs/API.md b/docs/API.md index 2be2283..f8b3c1d 100644 --- a/docs/API.md +++ b/docs/API.md @@ -207,6 +207,20 @@ let list = repo.get_list(list_id)?; repo.delete_list(list_id)?; ``` +#### Rename List + +```rust +repo.rename_list(list_id, "New Name".to_string())?; +``` + +#### Move Task Between Lists + +```rust +// Atomically moves a task from one list to another. +// If the delete-from-source step fails, the copy in the destination is rolled back. +repo.move_task(from_list_id, to_list_id, task_id)?; +``` + ### Task Ordering #### Reorder Task