Add a configurable per-workspace sync interval (sync_interval_secs) with
Tauri command set_sync_interval, UI dropdown in Settings, and store
support to restart auto-sync when changed. Remove the redundant
main-panel sync status chip and surface upload/download counts in the
drawer footer and Tasks screen. Fix the sync logic to record the remote
file's last_modified timestamp when downloading (instead of local mtime)
so subsequent diffs don’t falsely trigger downloads.
These changes were needed to allow users to control auto-sync frequency
per workspace, simplify the UI by moving sync counts to the drawer
footer, and correct a bug where downloads were always considered new
because the local file mtime was used instead of the authoritative
remote timestamp.
Remove the bottom divider from the lists/drawer footer and adjust the
sync icon animation to spin in the correct (reverse) direction when
syncing.
- Remove the border-t divider classes from the drawer footer to eliminate the
visual divider at the bottom of the lists panel.
- Replace the Tailwind animate-spin class with an inline CSS animation that
runs in reverse when app.syncing is true so the sync icon spins the
expected direction.
Remove deprecated sync mode UI and manual sync controls from
SettingsScreen and TasksScreen. Implement an always-full sync mode and a
new auto-sync lifecycle in the app store: startAutoSync/stopAutoSync,
periodic polling (60s), debounced file-change-triggered sync (5s), and
window-focus-triggered sync when last sync is older than 30s. Track
syncStatus (idle/synced/error/offline), lastSyncTime, and surface status
in the Tasks drawer with a status dot and a manual sync button. Wire
auto-sync startup/teardown into loadConfig, switchWorkspace,
addWebdavWorkspace, and removeWorkspace.
These changes simplify sync configuration by removing legacy sync modes
and UI elements, provide robust automatic syncing for WebDAV workspaces,
and improve user feedback on sync state and errors.
Add WebDAV MOVE support and update workspace rename flow to handle both
local and WebDAV-backed workspaces. The Tauri rename_workspace command
is made async and now performs filesystem rename for local workspaces
and issues a WebDAV MOVE (via a new WebDavClient::move_resource) for
remote workspaces, updating stored paths and credentials accordingly. A
confirmation dialog is added to SettingsScreen to prompt users before
renaming, and minor UI/default tweaks are included (SetupScreen default
name). This ensures renames update both local folders and remote WebDAV
folders reliably and with user confirmation.
Change workspace identifiers from display names to UUID strings so
multiple workspaces can share the same display name. WorkspaceConfig now
stores a name field; add_workspace returns a generated UUID. Update all
CLI, Tauri commands, frontend stores and screens, WebDAV managed
directories, and tests to use/resolve workspace IDs; add helpers
find_by_name/resolve_name to map display names to IDs when needed. This
removes duplicate-name checks, avoids filesystem conflicts, and
preserves display names while using stable unique IDs internally.
Adding explicit duplicate-name checks to the Tauri commands that create
workspaces prevents a new workspace from silently overwriting an
existing one. The change returns an error if a workspace with the given
name already exists.
Also set sane defaults in the setup UI: default workspace name to "Onyx"
for both local and WebDAV flows, default the local folder to the user's
Documents directory via documentDir(), and ensure the create flow resets
the name to the "Onyx" default when starting creation.
Speed up initial folder listing by checking each subfolder for the
.onyx-workspace.json marker in parallel instead of sequentially. This
uses futures::future::join_all to run multiple PROPFIND/list_files calls
concurrently, reducing latency when detecting workspaces across many
subdirectories.
Also add the futures 0.3 dependency to the Tauri Cargo.toml and lockfile
so the async utilities are available.
Detect when the current workspace folder cannot be opened and show a
dedicated "missing" screen that explains the workspace was not found.
Catch failures from get_lists during loadConfig, set a missingWorkspace
state and switch to the missing screen, and provide
forgetMissingWorkspace() to remove the missing workspace from the config
and either switch to the next available workspace or fall back to the
setup screen. Add UI in App.svelte to present the workspace name,
explanation, and a Continue button that invokes forgetting the missing
workspace.
Rework WebDAV workspace setup to use .onyx-workspace.json instead of
.metadata.json and to let users pick a remote folder instead of forcing
an Onyx/ subfolder. This updates storage, sync, config types, tests, and
CLI/Tauri commands to store a webdav_path in WorkspaceConfig and to
combine webdav_url + webdav_path for sync.
Changes include:
- Rename .metadata.json → .onyx-workspace.json across storage, sync, and tests so workspace detection and root metadata use the new filename.
- Remove hardcoded automatic "Onyx/" subfolder in sync and use the user-selected remote path directly.
- Add webdav_path field to WorkspaceConfig (Rust and TypeScript types) and thread it through add_webdav_workspace and frontend addWebdavWorkspace.
- Add three Tauri commands (list_remote_folder, inspect_remote_workspace, create_remote_workspace) to support remote folder browsing, workspace preview, and remote workspace creation.
- Rewrite SetupScreen WebDAV flow to Connect → Browse (lazy folder explorer) → Preview or Create, and wire UI state/handlers to the new commands.
- Update CLAUDE.md to document the new on-disk filename and note development phase allowing breaking changes.
When displaying workspace paths in the tasks list, the previous code
showed the full path which could include a tail segment (e.g., a
filename or last folder). This change strips the last path segment for
non-WebDAV workspaces so the UI presents the parent directory instead,
improving clarity for users who expect to see the workspace root rather
than a trailing item.
Fix fallback rendering of workspace path
The previous change grouped the nullish coalescing incorrectly, which
caused the fallback empty string to be applied to the result of
replace(...) rather than to ws?.path. This could lead to rendering
'undefined' or leaving the replace expression unresolved when ws.path is
nullish. Adjust the expression so the nullish fallback applies to
ws?.path, ensuring a clean empty string fallback when no path is
present.
Full-stack workspace rename: renames folder on disk, updates config
key/path, refreshes frontend. Restructure settings screen with generic
'Workspace Settings' header, workspace name row with kebab menu
(Rename + Delete). Replace per-workspace kebab dropdown in workspace
list with a direct settings gear button. Remove Appearance heading
and border box from theme section. Clean up unused wsMenuName state.
Sort workspace names alphabetically in the drawer for
better organization. Add a check to prevent switching to
the currently active workspace, avoiding unnecessary
operations and improving user experience.
- Handle Result from WebDavClient::new in CLI sync, core sync, and Tauri
- Unwrap Zeroizing<String> at Tauri serialization boundary
- Use .as_str() for basic_auth calls with Zeroizing<String> fields
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Setup screen now offers Local vs WebDAV mode choice with cancel button
when workspaces exist. Settings moved from drawer bottom into workspace
kebab menu, scoped per-workspace. WebDAV section hidden for local
workspaces, theme dropdown replaces dark mode toggle.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces in-memory darkMode toggle with persisted per-workspace theme
selection. Adds dark, nord, dracula, and solarized CSS theme definitions.
Theme is applied via data-theme attribute and derived isDark class.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduces WorkspaceMode enum with local and webdav variants, plus a
theme field on WorkspaceConfig. Adds set_workspace_theme and
add_webdav_workspace Tauri commands. WebDAV workspaces auto-manage
local files in app data dir.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add toggling guard to TaskItem preventing double-toggle from rapid clicks
or swipes. Fix debounced save in TaskDetailView to read task at timeout
time instead of capturing a stale snapshot at call time. Remove unused
_unlistenFs variable. Surface reorder_task and watch_workspace errors
instead of silently swallowing them.
Replace all .unwrap() calls on repo with repo_ref()/repo_mut() helpers
that return error strings instead of panicking. Stop the old file watcher
before starting a new one on workspace switch to prevent accumulation.
Add object-src and base-uri directives to CSP.
Back up local files before overwriting during ConflictRemoteWins so data
is never silently lost. Fix false-positive change detection by parsing
timestamps before comparing (different formats like RFC3339 vs HTTP date
were never equal as strings). Add zeroize crate to zero WebDAV credentials
in memory on drop, preventing exposure in core dumps.
Fix nested interactive elements in TaskItem (button inside button) by
restructuring as div + button with aria-label. Replace native confirm()
with ConfirmDialog for workspace removal. Store fs-changed event unlisten
function for cleanup. Log file watcher errors instead of swallowing them.
Fix var usage to const. Add Firefox scrollbar-width support.
Replace .expect() in config path resolution with a fallback default.
Replace all .lock().unwrap() on Tauri AppState mutex with a helper that
converts poisoned locks into error strings. Replace .expect() on Android
app data dir with proper error propagation. Handle LAST_WRITE and WATCHER
global mutex locks gracefully. Propagate subtask cascade delete errors
instead of silently ignoring them.
Validate that resolved list paths stay within the workspace root to prevent
directory traversal via malicious list names. Enable Content Security Policy
in Tauri config instead of leaving it null. Fix CLI domain extraction to
strip userinfo (user:pass@) from URLs before using as keyring service name.
- Remove subtask expand/collapse foldout from main task list
- Add three-panel slide navigation (task list → task detail → subtask detail) via taskStack
- Create ConfirmDialog component replacing native confirm() dialogs
- Cascade delete: deleting a task with subtasks warns and deletes all children
- Subtask detail view: no nested subtasks, collapsible completed section, inline add at top
- Redesign Move To as inline list in kebab menu (no overflow submenu)
- Main panel header: list name + kebab below divider (matching task detail layout)
- Move list management kebab from drawer to main panel; drawer shows chevrons
- Add Delete Completed to both main panel and subtask kebab menus
- Fix debouncedSave stale reference by snapshotting task before timer
- New subtasks prepend to top of list
Wire up the existing parent_id data model into a full subtask UI:
- Backend: create_task accepts parent_id, cascade delete/toggle for subtasks
- Store: subtask-aware derived state (root tasks only in pending/completed),
childrenMap for O(1) subtask lookup, createTask accepts parentId
- TaskItem: depth-based indentation, subtask completion counter badge
- TasksScreen: expand/collapse chevron for parent tasks, indented subtask
rendering with left border, subtasks shown under completed parents
- TaskDetailView: "Add subtask" inline input, subtask list with toggleable
checkboxes, parent task breadcrumb for subtasks
https://claude.ai/code/session_01XWcSekaCAJ7qnr4hN3tgL8
Make keyring optional behind keyring-storage feature in onyx-core.
Make notify/notify-debouncer-mini optional behind desktop feature in Tauri.
Gate all file watcher code behind #[cfg(not(target_os = "android"))].
Provide env-var-only credential fallbacks when keyring is disabled.
Remove the redundant call to _done() in the close icon's onTap handler
and simplify the callback to a single-expression setState. The change
keeps behavior focused on hiding the time view (_showTime = false) and
avoids invoking _done() on close, which aligns with the intended UI flow
signaled by the task notification that the Flutter app relaunch
completed successfully.
Previously clicking the clear-time button set includeTime to false and
also called done(), which closed the surrounding toast. This change
removes the done() call so clearing the time only clears the time input
state (includeTime = false) without dismissing the toast UI.
Replace using a foldout state to indicate time presence with an explicit
time section and a simple "Set time" trigger. This makes the UI
semantics clearer (foldouts shouldn't represent function) and mirrors
the Tauri approach: show a compact "Set time" label when time is hidden,
expand inline time controls when shown, and provide a close icon to
dismiss and apply the time. Minor spacing adjustments were applied to
improve alignment.
Users couldn't clear the time part of a task's due date because the
"clear time" button only toggled includeTime=false without signalling
the picker to finish. Call done() when clearing the time so the picker
closes/emits the updated value, ensuring the task's has_time state is
updated and the UI reflects a date-only value.
Add WebDAV configuration, credential storage/testing, and sync controls
across Flutter UI and Rust API. This implements a stateful Settings
screen with fields to enter server URL, username, and password, plus
Test and Save actions; persist/load credentials and workspace WebDAV URL
via the Rust API; add sync mode selection, a Sync Now action, and a sync
status indicator in Tasks screen; thread has_time through date/time
pickers, new task creation, task detail updates, and task DTOs;
implement async Rust functions for testing connections, storing/loading
credentials, setting workspace WebDAV config, and triggering workspace
sync with a SyncResult mapped back to Flutter; add tokio runtime
dependency. These changes were needed to enable full WebDAV-based
synchronization and provide users controls and feedback for configuring
and running syncs from the Flutter app.
Ensure the sync mode <select> uses theme-aware background and text
colors so it matches the application's dark theme. The change adds
appearance-none, explicit light-theme surface/text classes and dark:
variants (dark:bg-surface-dark dark:text-text-dark) so the dropdown no
longer appears white in dark mode and conforms to the rest of the themed
UI.
Remove the incorrect $schema pointing to a NiceGUI URL. Change bundle
targets from "all" to ["appimage", "deb"] to avoid rpm build failures
on machines without rpm tools.
Add workspace_name and mode params to sync_workspace Tauri command.
After each sync, persist last_sync to WorkspaceConfig and save config.
Frontend gains lastSyncResult and syncMode state; triggerSync reloads
config post-sync and stores the result. SettingsScreen shows a sync
direction selector (Full/Push/Pull) and a last-sync status line.
TasksScreen shows an upload/download count chip after sync completes.
Replace the hours==0 && minutes==0 heuristic with an explicit has_time
bool field on Task. Existing files without the field deserialize as false
(date-only), preserving current behavior. Frontend components pass and
receive has_time through DateTimePicker's onchange callback.
Remove auto-generated plugin registrant files from tracking and add
them to .gitignore. These are regenerated by flutter pub get.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rust API: add move_task, rename_list, set/get_group_by_due_date,
watch_workspace_changes (stream-based via StreamSink) with notify
crate. Self-change suppression via mute_watcher().
Dart frontend: moveTask, renameList, setGroupByDueDate in AppState.
Move-to bottom sheet in TaskDetailView. Rename dialog and group-by-
due-date toggle in list context menu. File watcher stream subscription
on workspace load/switch.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add Tauri commands: move_task, rename_list, set/get_group_by_due_date,
watch_workspace. Implement file watcher using notify crate with 500ms
debounce and self-change suppression via mute_watcher().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix resize overflow by replacing AnimatedPositioned/AnimatedContainer
with OverflowBox + AnimatedSlide (transform-based, no layout reflow)
- Add slide-up animation for new task toast using AnimationController
- Add fade+scale animation for settings screen overlay
- Add fade+scale animation for kebab menu in task detail view
- Convert workspace switcher from inline dropdown to floating popup
menu with fade+scale animation and hover states
- Add hover highlights to workspace menu items and fix kebab menu
hover highlights extending to edges
- Add drawer shadow casting onto main content when open
- Standardize all transition durations to 150ms
- Skip custom border/resize zones on Windows (native frame suffices)
Add Windows runner files from flutter create, lower Dart SDK
constraint to ^3.11.1 for compatibility, and remove native
border/shadow on Windows since the OS provides it.
- Standardize header height (h-11) across detail view and main panel
- Move kebab menu below header in task detail view
- Move workspace switcher to drawer header with window drag support
- Move settings button to drawer footer with label
- Add mark-as-completed/restore to kebab menu, remove bottom action bar
- Enlarge checkbox hit target without changing visual size
- Show checkmark animation before task collapse transition
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add load_credentials Tauri command and use it in triggerSync() instead
of passing empty username/password strings
- Replace raw __TAURI_INTERNALS__.invoke() with proper invoke import in
SettingsScreen
- Wrap window event listeners in $effect() with cleanup to prevent
memory leak on component remount
- Return created Task from createTask() and use it directly in
NewTaskInput instead of guessing from array index
- Add confirm() dialogs before deleting tasks, lists, and workspaces
- Add GPL-3.0 LICENSE file
- Update README with current project status (all 3 phases), fix structure
- Update PLAN.md Phase 2 API signatures to match actual implementation
- Add WebDAV & Sync section to docs/API.md
- Update docs/DEVELOPMENT.md with Tauri app structure and setup
- Add metadata (description, license, repository) to all Cargo.toml files
- Update .gitignore with node_modules, .env, dist, build entries
- Remove stale Cargo.lock from .gitignore (apps should track it)
- Update example dates from 2025 to 2026 across all docs
- Update CLAUDE.md date to 2026-03-30
Use the same visual structure as TaskDetailView: large bold title input,
description textarea with notes icon, and date/time row with
DateTimePicker. Uses fixed positioning with a combined backdrop wrapper
to avoid layout shifts and ensure proper slide-up animation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Transparent windows require platform-specific workarounds (WebView2 on
Windows, compositor support on Linux) and don't work on mobile. Use an
opaque window instead, removing the outer padding, rounded corners, and
drop shadow that depended on transparency.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace inline task editing with a full-viewport detail panel that
slides in from the right. Includes editable title, description, custom
calendar-based date/time picker (bottom sheet), kebab menu with delete,
and mark complete/restore button. Simplify TaskItem to remove inline
editing and kebab menu, add chevron hint and onopen callback. Use list
icon for drawer toggle instead of back arrow.
Remove native window decorations and add transparent background with
rounded corners, border, and drop shadow. Add custom close button
(Linux) and minimize/maximize/close (Windows) in the header. Add
programmatic window dragging via mousedown and double-click to maximize.
Install tauri-plugin-os for platform detection. Move sync spinner to
bottom-right corner. Convert drawer layout from vw to cqi units to
support the padding-based shadow approach.
- Remove Flutter app and egui placeholder crate, commit to Tauri as sole GUI
- Update PLAN.md to replace egui with Tauri across all phases (v4.0)
- Redesign task screen: sliding drawer for list picker, floating FAB for new tasks,
bottom sheet toast for task creation with title + description fields
- Add description support to create_task Tauri command
- Lighten dark theme to GNOME-style neutral grays, shift primary to cyan-blue
- Fix Wayland compatibility (dev port change)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>