docs: sync markdown documentation with codebase

- CLAUDE.md: add last_sync field to WorkspaceConfig description
- README.md: update Phase 4 status, replace dark mode with multi-theme
  system, add has_time/parent fields to data format example
- PLAN.md: add last_sync to Phase 1 WorkspaceConfig, update dark mode
  entries to reflect theme selector, fix Google Tasks Tauri command
  names, add rename_list/move_task to Core Library API, fix "Move to..."
  description (inline, not submenu)
- docs/API.md: document rename_list and move_task repository methods
- docs/DEVELOPMENT.md: add dateFormat.ts to frontend file structure

https://claude.ai/code/session_01NCtJ5PNhaDh21kYnDZXYsN
This commit is contained in:
Claude 2026-04-16 08:34:32 +00:00
parent 85400b68bc
commit 4cc15a96fe
No known key found for this signature in database
5 changed files with 25 additions and 7 deletions

View file

@ -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. - **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. - **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). - **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<String>` 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). - **WebDAV** (`webdav.rs`): reqwest client with rustls-tls, 30s request timeout, 10s connect timeout. Rejects non-HTTPS URLs. `Zeroizing<String>` 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. - **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.

11
PLAN.md
View file

@ -123,6 +123,7 @@ WorkspaceConfig {
webdav_url: Option<String>, webdav_url: Option<String>,
webdav_path: Option<String>, // User-selected remote folder webdav_path: Option<String>, // User-selected remote folder
google_account: Option<String>, // Email/display name (GoogleTasks workspaces) google_account: Option<String>, // Email/display name (GoogleTasks workspaces)
last_sync: Option<DateTime>, // Timestamp of last successful sync
theme: Option<String>, theme: Option<String>,
sync_interval_secs: Option<u64>, // Auto-sync polling interval (focused) sync_interval_secs: Option<u64>, // Auto-sync polling interval (focused)
sync_interval_unfocused_secs: Option<u64>, // Auto-sync interval when unfocused sync_interval_unfocused_secs: Option<u64>, // Auto-sync interval when unfocused
@ -223,6 +224,8 @@ impl TaskRepository {
pub fn get_lists(&self) -> Result<Vec<TaskList>>; pub fn get_lists(&self) -> Result<Vec<TaskList>>;
pub fn get_list(&self, list_id: Uuid) -> Result<TaskList>; pub fn get_list(&self, list_id: Uuid) -> Result<TaskList>;
pub fn delete_list(&mut self, id: Uuid) -> Result<()>; pub fn delete_list(&mut self, id: Uuid) -> Result<()>;
pub fn rename_list(&mut self, list_id: Uuid, new_name: String) -> Result<()>;
pub fn move_task(&mut self, from_list_id: Uuid, to_list_id: Uuid, task_id: Uuid) -> Result<()>;
// Task ordering (modifies .listdata.json) // Task ordering (modifies .listdata.json)
pub fn reorder_task(&mut self, list_id: Uuid, task_id: Uuid, new_position: usize) -> Result<()>; pub fn reorder_task(&mut self, list_id: Uuid, task_id: Uuid, new_position: usize) -> Result<()>;
@ -750,10 +753,10 @@ WorkspaceConfig {
- [x] Mark tasks complete/incomplete with animated transitions - [x] Mark tasks complete/incomplete with animated transitions
- [x] Drag-and-drop task reordering - [x] Drag-and-drop task reordering
- [x] Sliding lists drawer (80cqi wide, left side) - [x] Sliding lists drawer (80cqi wide, left side)
- [x] Settings popup overlay (WebDAV config, dark mode toggle) - [x] Settings popup overlay (WebDAV config, theme selector)
- [x] Dark mode (GNOME-style neutral theme, cyan-blue accent) - [x] Per-workspace theme system (System default, Light, Dark, Nord, Dracula, Solarized Dark, Ink)
- [x] Animated completed section show/hide - [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 (kebab menu → "Move to..." inline list in task detail view, not a 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] 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] 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) - [x] WebDAV setup flow with credentials (settings auto-populates URL/username/password from config + keychain on open)
@ -977,7 +980,7 @@ npm run tauri ios build
#### Google Tasks Importer #### Google Tasks Importer
- [x] `google_tasks.rs` module in `onyx-core` — client, UUID mapping, read-only sync (remote always wins) - [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] `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) - [ ] Complete OAuth flow (client ID/secret placeholders need real credentials)
- [ ] Migrate tasks, lists, due dates, notes with full UI integration - [ ] Migrate tasks, lists, due dates, notes with full UI integration
- [ ] Preserve task hierarchy and order - [ ] Preserve task hierarchy and order

View file

@ -29,7 +29,7 @@ onyx/
- **Phase 1** (Core + CLI): Complete - **Phase 1** (Core + CLI): Complete
- **Phase 2** (WebDAV Sync): Complete — backend, CLI, and GUI all wired - **Phase 2** (WebDAV Sync): Complete — backend, CLI, and GUI all wired
- **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): In progress — Android preliminaries done (file-watcher gating, tauri-plugin-credentials, safe area insets, Android targets configured); needs build verification and iOS setup
### Core Library (`onyx-core`) ### Core Library (`onyx-core`)
- Data models (Task, TaskList, AppConfig, WorkspaceConfig) - Data models (Task, TaskList, AppConfig, WorkspaceConfig)
@ -55,7 +55,7 @@ onyx/
- Drag-and-drop reordering - Drag-and-drop reordering
- Sliding lists drawer, settings popup - Sliding lists drawer, settings popup
- Workspace switcher with add/remove - 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, Ink)
- Due date picker/editor with optional time - Due date picker/editor with optional time
- Subtask hierarchy with three-panel slide navigation - Subtask hierarchy with three-panel slide navigation
- Move tasks between lists - Move tasks between lists
@ -169,6 +169,8 @@ id: 550e8400-e29b-41d4-a716-446655440000
status: backlog status: backlog
version: 3 version: 3
date: 2026-11-15T14:00:00Z date: 2026-11-15T14:00:00Z
has_time: true
parent: 550e8400-e29b-41d4-a716-446655440001
--- ---
Task description and notes go here in **markdown** format. Task description and notes go here in **markdown** format.

View file

@ -207,6 +207,18 @@ let list = repo.get_list(list_id)?;
repo.delete_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
repo.move_task(from_list_id, to_list_id, task_id)?;
```
### Task Ordering ### Task Ordering
#### Reorder Task #### Reorder Task

View file

@ -76,6 +76,7 @@ onyx/
│ │ ├── screens/ # Full-page views │ │ ├── screens/ # Full-page views
│ │ ├── components/ # Reusable UI components │ │ ├── components/ # Reusable UI components
│ │ ├── stores/ # Svelte state (app.svelte.ts) │ │ ├── stores/ # Svelte state (app.svelte.ts)
│ │ ├── dateFormat.ts # Date formatting utilities
│ │ └── types.ts # TypeScript type definitions │ │ └── types.ts # TypeScript type definitions
│ ├── tauri-plugin-credentials/ # Cross-platform credential storage plugin │ ├── tauri-plugin-credentials/ # Cross-platform credential storage plugin
│ │ ├── Cargo.toml │ │ ├── Cargo.toml