Commit graph

259 commits

Author SHA1 Message Date
Tristan Michael 0ae0705331 Add per-workspace sync interval and fix download timestamp recording
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.
2026-04-05 16:35:22 -07:00
Tristan Michael 7e5b3ea7b0 Remove list footer divider and reverse sync spinner
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.
2026-04-05 16:24:17 -07:00
Tristan Michael 2f90805594 Remove manual sync UI; add auto-sync lifecycle and status
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.
2026-04-05 16:19:43 -07:00
Tristan Michael e33fb9dd0b Replace timestamp LWW with checksum-based conflict resolution
Rework sync conflict handling to stop using timestamp-based
last-write-wins and use a single Conflict action. The conflict handler
now downloads the remote file and compares SHA-256 checksums: identical
content is treated as a false conflict and skipped; when different, the
remote version wins and the local task file is recovered as a duplicate
with a new UUID and a "[RECOVERED FROM CONFLICT]" prefix. Duplicates are
inserted adjacent to the original in .listdata.json; non-task files
still use remote-wins without duplication. Removed local_wins() and its
tests, simplified action variants to a single Conflict, and updated
conflict-related tests and reporting accordingly.
2026-04-05 15:44:49 -07:00
Tristan Michael 25358a9eec Handle remote-deleted + local-unchanged as local delete
Fix compute_sync_actions to properly treat the case where a file has
been deleted on the remote but remains unchanged locally: previously the
code always emitted an Upload (re-uploading the local copy). Now it
checks the base checksum and emits DeleteLocal when the local copy
matches the base (unchanged), and emits Upload only when the local copy
differs (modified).

Updated tests: adjusted test_remote_deleted_local_unchanged to assert
DeleteLocal, and added test_remote_deleted_local_modified to verify the
upload behavior when the local file is modified while remote was
deleted.
2026-04-05 15:26:55 -07:00
Tristan Michael 4c57851e15 Rename workspace and remote folders with confirmation
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.
2026-04-05 15:10:44 -07:00
Tristan Michael 50d859ef80 Refactor workspaces to use UUID keys
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.
2026-04-05 14:58:31 -07:00
Tristan Michael a709df609f Prevent workspace name collisions
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.
2026-04-05 14:49:55 -07:00
Tristan Michael 095ac8fa97 Parallelize workspace detection in folder listing
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.
2026-04-05 14:37:58 -07:00
Tristan Michael ac789e8d56 Handle deleted/moved workspace folders (missing state)
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.
2026-04-05 14:30:22 -07:00
Tristan Michael 753cb1cad5 Rename workspace metadata and add WebDAV folder browsing
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.
2026-04-05 14:30:22 -07:00
Tristan Michael 70af83ccfc Show parent path for non-WebDAV workspaces
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.
2026-04-05 14:30:22 -07:00
Tristan Michael bb735ecd4a Add workspace rename and restructure settings screen
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.
2026-04-05 14:30:22 -07:00
Tristan Michael 8df0edf163 setup gitbutler 2026-04-05 14:30:22 -07:00
SteelDynamite 4670cffe69
Merge pull request #29 from SteelDynamite/workspace-folder-root
update workspace creation to append workspace name
2026-04-05 07:05:18 -07:00
Tristan Michael ca0a5c8ab3 update workspace creation to append workspace name 2026-04-05 07:04:40 -07:00
SteelDynamite f8ebd2ce9e
Merge pull request #28 from SteelDynamite/gitbutler-hooks
add hooks
2026-04-05 06:14:12 -07:00
Tristan Michael 781b13b891 add hooks 2026-04-05 06:13:49 -07:00
SteelDynamite d192fed44f
Merge pull request #27 from SteelDynamite/polish
feat: sort workspaces and prevent redundant switches
2026-04-03 17:47:17 -07:00
Tristan Michael 9bde08c2d5 feat: sort workspaces and prevent redundant switches
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.
2026-04-03 16:57:45 -07:00
SteelDynamite 8bbd3128e6
Merge pull request #26 from SteelDynamite/docs-webdav-sync-theme-updates
docs-webdav-sync-theme-updates
2026-04-03 11:26:29 -07:00
Tristan Michael 513acc5606 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.
2026-04-03 11:26:12 -07:00
Tristan Michael fa87dbe12b security: additional credential hardening
- Use :: separator in scoped keyring keys to prevent ambiguity with
  usernames containing dots (e.g. com.onyx.webdav.host::user)
- Auto-migrate legacy credentials to scoped format on load, removing
  old unscoped entries after successful migration
- Add 10MB response size limit on PROPFIND to prevent memory exhaustion
  from malicious servers (checks Content-Length header + actual body)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 10:43:09 -07:00
SteelDynamite c4df1413dd
Merge pull request #25 from SteelDynamite/fix/webdav-sync-and-themes
fix/webdav-sync-and-themes
2026-04-03 10:32:58 -07:00
Tristan Michael 58f37b08d6 fix: harden WebDAV sync — async credentials, consolidated command, Onyx subfolder 2026-04-03 10:11:46 -07:00
Tristan Michael 9e57f1df3c chore: update lock files for log dependency
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 10:11:40 -07:00
Tristan Michael be6b8d0d90 security: update callers for hardened credential API
- 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>
2026-04-03 10:11:40 -07:00
Tristan Michael 0c4073c998 security: harden credential management in onyx-core
- Enforce HTTPS for WebDAV URLs (reject http:// to prevent plaintext credentials)
- Replace String with Zeroizing<String> for credential fields and load_credentials return
- Remove manual Drop impl (Zeroizing handles zeroize-on-drop automatically)
- Scope keyring password entries by domain+username to prevent collisions
- Add migration fallback for legacy unscoped keyring entries
- Sanitize error messages to not leak keyring service patterns or env var names
- Add log warnings when falling back to env var credentials
- Add log dependency to onyx-core

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 10:11:40 -07:00
Tristan Michael d03cc92a53 Use reqwest with rustls-tls and disable default features
Enable rustls TLS support and disable reqwest's default features to
avoid pulling in unwanted native TLS dependencies. This ensures the
application uses rustls for TLS connections and keeps the build more
portable and consistent across platforms.
2026-04-03 10:11:40 -07:00
Tristan Michael ef266f9b0d Relax reqwest features in Cargo.toml
Remove the custom default-features and rustls-tls feature flags for
reqwest in Cargo.toml. This simplifies the dependency specification and
avoids potential TLS configuration or feature mismatch issues that could
make HTTP requests behave unexpectedly (e.g., reachability or URL
formatting issues). The change restores reqwest to its simpler default
feature set to resolve connectivity confusion.
2026-04-03 10:11:40 -07:00
Tristan Michael 2a2e362a8f fix: WebDAV sync — Onyx subfolder, timeouts, error display, light theme 2026-04-03 10:11:40 -07:00
Tristan Michael 12afc91110 feat: setup mode selection, per-workspace settings in kebab menu
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>
2026-04-03 04:02:39 -07:00
Tristan Michael a1e97bc0fe feat: per-workspace theme system with 5 theme options
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>
2026-04-03 04:02:39 -07:00
Tristan Michael a60b1a997b feat: add WorkspaceMode (local/webdav) and per-workspace theme to config
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>
2026-04-03 04:02:39 -07:00
SteelDynamite 4483a6450f
Merge pull request #24 from SteelDynamite/docs/audit-update
docs: audit and update all markdown files to reflect current progress
2026-04-03 11:58:19 +01:00
Tristan Michael 52de450a0f docs: audit and update all markdown files to reflect current progress
Phase 2 (WebDAV sync) and Phase 3 (GUI MVP) are complete but docs still
showed them as in-progress. Phase 4 Android steps 1-4 are done. Windows
MSI packaging was added. Multiple doc inaccuracies fixed: missing Error
variants, stale struct definitions, wrong filenames in directory trees.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 03:52:58 -07:00
SteelDynamite 1b8febcb45
Merge pull request #23 from SteelDynamite/packaging/windows-msi
Packaging/windows msi
2026-04-03 11:50:28 +01:00
Tristan Michael 27e6c07fb3 refactor: rename onyx-tauri to onyx-tasks, add onyx-standalone binary
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 03:48:25 -07:00
Tristan Michael 2a1866112c feat: add MSI to Windows bundle targets
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 03:48:21 -07:00
SteelDynamite 45cfa8e5ec
Merge pull request #22 from SteelDynamite/audit/fixes
audit/fixes
2026-04-02 17:39:00 +01:00
Tristan Michael ab71de9110 docs: update stale documentation after audit fixes
Fix outdated thread safety note in API.md — Storage trait now requires
Send + Sync and Tauri uses Mutex<AppState>. Mark completed Phase 5
features in PLAN.md (subtasks, due dates, move tasks, keyboard shortcuts).
Fix CLAUDE.md formatting.
2026-04-02 09:37:43 -07:00
Tristan Michael 7fde1a09f9 fix: harden frontend — toggle race guard, fresh debounce snapshot, error surfacing
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.
2026-04-02 09:37:43 -07:00
Tristan Michael 3c11539f02 fix: harden Tauri backend — replace unwrap panics, fix watcher lifecycle, strengthen CSP
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.
2026-04-02 09:37:43 -07:00
Tristan Michael e0c7292a7e fix: harden sync safety — conflict backup, timestamp parsing, credential zeroization
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.
2026-04-02 09:37:43 -07:00
Tristan Michael fa1125bfeb fix: harden core data integrity — move_task rollback, path traversal, migration safety
Add rollback to move_task: if delete-from-source fails after write-to-
destination, clean up the duplicate. Reject list names with path separators
or '..' to prevent traversal; canonicalize() failures now return errors
instead of silently falling back to unchecked paths. Add validation and
rollback to CLI workspace migration: check destination is empty, track
moved files, and reverse on failure.
2026-04-02 09:37:43 -07:00
Tristan Michael de11e0a8c3 chore: remove Flutter GUI entirely, Tauri is the sole frontend
Flutter (apps/flutter/) was at feature parity but maintaining two GUI
frontends doubles effort. Consolidate on Tauri v2 which handles both
desktop and mobile. Remove all Flutter source, config, and documentation
references.
2026-04-02 09:37:43 -07:00
Tristan Michael 0b2abe1b55 chore: remove orphaned bevy-tasks-cli crate
This crate references a non-existent bevy-tasks-core dependency and is
an artifact from the project's predecessor. It is not in the workspace
members list and cannot compile.
2026-04-02 09:35:38 -07:00
Tristan Michael 54836f14e7 fix: improve frontend accessibility, consistency, and resource cleanup
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.
2026-04-02 09:35:38 -07:00
Tristan Michael 056cd8ee49 fix: harden sync file validation and offline queue corruption handling
Restrict is_syncable() to validate path depth: .md files and .listdata.json
must be at depth 2 (inside list dirs), .metadata.json only at depth 1 (root).
Prevents syncing arbitrary files at unexpected depths. Back up corrupted
sync queue files before resetting, and log warnings on parse failures
instead of silently dropping queued operations.
2026-04-02 09:35:38 -07:00
Tristan Michael 3b2cb12272 fix: replace panicking unwrap/expect calls with graceful error handling
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.
2026-04-02 09:35:38 -07:00