Commit graph

295 commits

Author SHA1 Message Date
Tristan Michael 5e5773d146 fix: add safe area padding to scrollable content
Add safe area padding to scrollable main content areas and
adjust drawer footer padding to prevent content from being
hidden behind notches and home indicators on mobile devices.
This ensures proper spacing on devices with safe area insets.
2026-04-06 11:21:29 -07:00
Tristan Michael c772e3251a fix: add safe area bottom padding to DateTimePicker
Add bottom padding to DateTimePicker component to prevent content
from being obscured by notches or home indicators on mobile devices.
Uses CSS max() function to ensure minimum 0.75rem spacing while
respecting device safe area insets.
2026-04-06 11:21:29 -07:00
Tristan Michael a705a5ca26 feat: add Claude tool hooks configuration
Add .claude/settings.json to configure pre and post tool use
hooks for Edit, MultiEdit, and Write operations. These hooks
integrate with the but CLI to execute validation and cleanup
commands before and after file modifications, and on stop
events.
2026-04-06 11:21:29 -07:00
Tristan Michael 9a9dc80e26 fix: set fixed height on calendar grid
Add content-start alignment and fixed 192px height to the
calendar grid to prevent layout shift when switching between
months with different numbers of weeks. This ensures consistent
spacing and prevents the calendar from changing size based on
the month being displayed.
2026-04-06 11:21:29 -07:00
Tristan Michael e9f7f7ab69 feat: move Onyx header above card and enlarge on setup screen 2026-04-06 11:21:29 -07:00
Tristan Michael 3d65aaf502 feat: left-edge swipe gesture for drawer and back navigation on mobile 2026-04-06 10:30:36 -07:00
SteelDynamite a508879fab
Merge pull request #36 from SteelDynamite/fix-bugs
fix-bugs
2026-04-06 09:37:52 -07:00
Tristan Michael 50800f0c2d feat: add separate sync interval for unfocused state
Introduce a new sync interval configuration for when the app is
in the background, allowing users to set a longer sync period to
reduce resource usage. Replace the fixed focus threshold with
dynamic interval switching based on app focus state. Add
syncIntervalUnfocused setting to the UI with a new background
sync interval selector. Refactor sync interval management into
a restartSyncInterval function to handle both focused and
unfocused intervals consistently.
2026-04-06 09:37:06 -07:00
Tristan Michael f5295b5980 fix: reset tasks and remount screen on workspace change
Reset the tasks array when switching workspaces to prevent stale
task data from persisting. Wrap TasksScreen in a keyed block to
force remounting when the current workspace changes, ensuring a
clean state for each workspace.

Configure line endings to use LF across all files for consistency.
2026-04-06 09:10:36 -07:00
SteelDynamite 8772338e0e
Merge pull request #35 from SteelDynamite/claude/audit-consistency-A816f
Fix documentation and code inconsistencies found during audit
2026-04-06 05:09:27 -07:00
Claude 7c6001291c
Fix documentation and code inconsistencies found during audit
- CLAUDE.md: Fix drawer width unit (80vw -> 80cqi), clarify group-by-due-date
  is toggle-only (sorting not yet implemented), clarify Phase 4 status
- PLAN.md: Fix conflict resolution label (last-write-wins -> remote wins),
  add username to keyring key format, mark fallback credential storage as
  not yet implemented
- sync.rs: Fix comment table to say "conflict" instead of "last-write-wins"
  for both-modified and both-added cases (matches actual Conflict action)
- config.rs: Add temp file cleanup on atomic write rename failure to match
  the pattern already used in storage.rs

https://claude.ai/code/session_01YWvxpzeT3hEUxD9aNvfNuL
2026-04-06 12:08:22 +00:00
SteelDynamite fd1ebce9ce
Merge pull request #34 from SteelDynamite/claude/audit-test-coverage-6gTRR
Add tests for models.rs, error.rs, and repository.rs edge cases
2026-04-06 04:59:01 -07:00
Claude ba7ac15d0c
Add tests for models.rs, error.rs, and repository.rs edge cases
Addresses critical test coverage gaps in onyx-core:
- models.rs: 20 tests for Task, TaskList, TaskStatus (builder, serde, CRUD)
- error.rs: 15 tests for Display impls and From conversions
- repository.rs: 5 new tests for version increment, move_task errors,
  subtask creation, multi-list independence

Total: 122 -> 162 tests passing.

https://claude.ai/code/session_01XiAFtZ7CAvm9FhrNCwAwsr
2026-04-06 11:50:52 +00:00
SteelDynamite 36fa591799
Merge pull request #33 from SteelDynamite/claude/audit-project-report-qoEsr
Fix critical and high-severity issues from project audit
2026-04-06 04:44:32 -07:00
Claude 7eec8e22c8
Add tests for audit fixes: 15 new tests covering security and data integrity
New sync tests:
- validate_sync_path rejects backslashes and .. components
- validate_sync_path allows valid paths
- SyncLock prevents concurrent access
- SyncLock released on drop, lock file cleaned up
- SyncState save leaves no .tmp files
- OfflineQueue save leaves no .tmp files
- parse_frontmatter_for_conflict rejects oversized YAML (>64KB)

New storage tests:
- Deduplication with equal versions uses mtime tiebreaker
- Frontmatter parsing rejects oversized YAML (>64KB)
- Frontmatter parsing accepts normal-sized content
- Version saturates at u64::MAX (no panic or wrap)
- Delete task removes from metadata before file
- atomic_write leaves no .tmp files

https://claude.ai/code/session_01AJoK28N4vqLqzskq6ybGri
2026-04-06 11:09:35 +00:00
Claude 7e9d35d6d6
Update documentation to reflect audit fixes
- CLAUDE.md: Document sync lock, temp cleanup on failure, metadata-first
  deletes, YAML 64KB limit, mtime dedup tiebreaker, saturating version,
  watcher-error event, and accessibility features
- docs/API.md: Add YAML frontmatter row to size limits table, document
  sync lock and delete ordering in sync strategy, update path traversal
  and atomic write descriptions
- PLAN.md: Update version field comment to note saturating behavior

https://claude.ai/code/session_01AJoK28N4vqLqzskq6ybGri
2026-04-06 11:07:29 +00:00
Claude 6174836b7f
Fix critical and high-severity issues from project audit
Security:
- Fix path traversal via backslash bypass in sync validate_sync_path()
- Replace silent HTTP client fallback with proper error propagation
- Add 64KB YAML frontmatter size limit to prevent DoS via crafted files

Data integrity:
- Reorder delete operations: update metadata before removing files to
  prevent orphaned metadata entries on crash
- Fix task deduplication to use file mtime as tiebreaker when versions
  are equal, preventing non-deterministic data loss
- Add rollback on conflict recovery failure (remove orphaned duplicate
  files when metadata update fails)
- Clean up temp files on atomic write rename failure
- Add file-based sync lock to prevent concurrent sync operations
- Use saturating_add for task version to prevent overflow

Error handling:
- Surface move_task rollback failures as structured errors instead of
  silent warnings
- Log WebDAV parallel request failures instead of silently swallowing
- Emit watcher-error events to frontend instead of only printing to stderr

Frontend:
- Fix focus listener leak in auto-sync (clean up if stopAutoSync called
  while promise pending)
- Add prefers-reduced-motion CSS media query for accessibility
- Add ARIA labels, roles, and keyboard handlers to TaskItem, BottomSheet,
  and ConfirmDialog components
- Replace BottomSheet children: any with Snippet type

https://claude.ai/code/session_01AJoK28N4vqLqzskq6ybGri
2026-04-06 11:03:11 +00:00
SteelDynamite bcc301525c
Merge pull request #32 from SteelDynamite/claude/audit-project-geahs
Audit fixes: panic safety, path hardening, code quality
2026-04-06 03:49:14 -07:00
Claude b690aad4d7
Remove stale AUDIT.md
The audit document was significantly outdated — 9 of 12 findings
already fixed in code but still listed as open. Audit findings are
better tracked as actionable code changes than a static document.

https://claude.ai/code/session_01EnSrQsowc64rAwzD9BnJpc
2026-04-06 10:48:50 +00:00
Claude 85748f4c95
Audit fixes: panic safety, path hardening, code quality
- Replace 2 production unwrap() calls in workspace.rs with proper error
  handling to prevent panics on inconsistent state
- Add AppState::save_config() helper to eliminate 11 duplicated
  save_to_file patterns in Tauri lib.rs
- Log file watcher errors instead of silently swallowing them
- Harden path traversal check in storage.rs: re-verify after
  canonicalization to catch symlink escapes
- Add Windows reserved device name handling (CON, NUL, etc.) to
  sanitize_filename
- Clean up stale .tmp files from interrupted atomic writes on startup

All 107 core tests pass.

https://claude.ai/code/session_01EnSrQsowc64rAwzD9BnJpc
2026-04-06 10:41:03 +00:00
SteelDynamite 4de300cb76
Merge pull request #31 from SteelDynamite/claude/audit-docs-quality-5dXc8
Add comprehensive project audit document
2026-04-06 03:22:11 -07:00
Claude e471534b58
Docs: update for new safety features and fix SyncMode enum names
- CLAUDE.md: document atomic writes, input size limits, path validation,
  file download size cap, workspace path validation, taskStack auto-cleanup
- API.md: add Input Validation & Safety section with size limits table,
  atomic writes list, and path safety documentation
- API.md: fix SyncMode enum names (PushOnly/PullOnly -> Push/Pull)
- API.md: add missing on_progress parameter to sync_workspace examples
- Update current state date to 2026-04-06

https://claude.ai/code/session_01F67yfLLmSaBtT7aKKNus1M
2026-04-06 10:20:39 +00:00
Claude a12deb5182
Harden codebase: fix security, quality, and maintainability issues
- Replace dangerous unwrap() with proper error handling (storage.rs, webdav.rs)
- Add atomic writes (temp + rename) for config, sync state, and metadata files
- Add path traversal validation in sync executor
- Add workspace path validation in Tauri commands
- Add input size limits for task titles, descriptions, and list names
- Add file download size limit (10MB) to WebDAV get_file
- Fix move_task rollback to log failures instead of silently ignoring
- Fix JSON serialization unwrap in Tauri create_remote_workspace
- Fix swallowed errors in sync queue backup, metadata writes, sync state load
- Extract hardcoded strings into named constants (filenames, extensions, limits)
- Use REQUEST_TIMEOUT/CONNECT_TIMEOUT constants in WebDAV client builder
- Fix frontend: clear taskStack when viewed task is deleted or list is switched
- Fix frontend: surface credential loading and focus listener errors

https://claude.ai/code/session_01F67yfLLmSaBtT7aKKNus1M
2026-04-06 10:17:30 +00:00
Claude b45f39c96c
Add comprehensive project audit document
Full codebase audit covering security, data integrity, code quality,
testing, frontend, and dependencies. 67 findings identified across
5 severity levels with prioritized recommendations in 4 phases.

https://claude.ai/code/session_01F67yfLLmSaBtT7aKKNus1M
2026-04-06 10:01:11 +00:00
SteelDynamite ee8721e71a
Merge pull request #30 from SteelDynamite/workspace-settings-overhaul
workspace-settings-overhaul
2026-04-05 19:24:27 -07:00
Tristan Michael aac43d77d2 Docs: update frontmatter examples and Task struct for version counter
Remove created/updated timestamps from Task struct docs and frontmatter
examples in PLAN.md, README.md, docs/API.md. Add version field. Update
CLAUDE.md on-disk format section to document version counter, default
behavior for legacy files, and self-healing dedup in list_tasks.
2026-04-05 19:23:52 -07:00
Tristan Michael 5e33416b22 Slim task frontmatter: remove timestamps, add version counter + self-healing dedup
Remove created_at/updated_at from Task struct and frontmatter. Add a
version counter (u64) that increments on every write, defaults to 1 for
old files. list_tasks now groups by UUID and auto-deletes stale
duplicates (keeping highest version), preventing sync-induced dupes from
surfacing in the UI. has_time and parent are omitted from frontmatter
when false/null.

Update CLI, Tauri frontend types, and Svelte components to match.
2026-04-05 19:22:10 -07:00
Tristan Michael eaab66609c Docs: update all markdown files to reflect workspace-settings-overhaul changes
Update CLAUDE.md, PLAN.md, README.md, docs/API.md, and docs/DEVELOPMENT.md
to reflect UUID-keyed workspaces, checksum-based conflict resolution,
auto-sync lifecycle, tauri-plugin-credentials, .onyx-workspace.json rename,
and new GUI features (remote folder browsing, workspace rename, sync
interval, loading screen, safe area insets, task dedup).
2026-04-05 19:21:54 -07:00
Tristan Michael 7cc899470f Polish: suppress error banner for transient sync failures and sort remote folders
Only show the error banner for non-transient sync failures; connectivity
issues (timeout, refused, unreachable) just update the status dot. Sort
workspace entries above regular folders in the remote folder browser.
2026-04-05 19:11:10 -07:00
Tristan Michael fbf1ba69ef Fix task list UX: deduplicate tasks and clear on list switch
Deduplicate tasks by ID in loadTasks since sync conflicts can produce
files with the same UUID. Clear the task array immediately on list
switch to prevent showing stale content from the previous list.
2026-04-05 19:10:57 -07:00
Tristan Michael 2c52afa8d7 Add initial sync loading screen for new WebDAV workspaces
Show a spinner while the first sync runs after adding a WebDAV workspace
so the user doesn't land on an empty task list. The screen transitions
to tasks only after sync completes or fails.
2026-04-05 19:10:49 -07:00
Tristan Michael ca52ed9fee Rework safe area insets with CSS variables and per-element spacers
Replace container-level env(safe-area-inset-*) inline padding with
--safe-top/--safe-bottom CSS variables applied per-element: drawer,
task list panel, detail panels, FAB, settings overlay, error banner,
and setup screen. Wrap task list in {#key app.activeListId} to force
re-render on list switch (co-located with FAB safe-bottom fix).
2026-04-05 19:10:37 -07:00
Tristan Michael 501f991f2c feat: replace custom WebDAV credential IO with pluginSwitch credential storage and retrieval the tauri_plugin_credentialsplugin and thread the AppHandle into handlers that need it.
- Import Credentials and initialize its plugin inuriBuilder.
- Replace blocking webdav::load_credentials/store calls with Credentials::load and Credentials::store to avoid spawn_blocking and centralize credential handling- AppHandle parameters to commands that need credential access:
 rename_workspace, add_webdav_workspace, store_credentials, load_credentials,
 and sync_workspace- Update save flow in add_webdav_workspace to use plugin storage after updating config.

This reduces manual blocking calls, unifies credential management andintegrates with Tauri app state for safer runtime access.
2026-04-05 17:30:07 -07:00
Tristan Michael 633337da4e Add safe area insets for mobile status bar and navigation
Add viewport-fit=cover to the viewport meta tag and apply
env(safe-area-inset-top/bottom) padding on mobile platforms.
This prevents the app header from being obscured by the Android
status bar, camera notch, and bottom gesture navigation area.
2026-04-05 17:29:54 -07:00
Tristan Michael 192b449e87 Add tauri-plugin-credentials for cross-platform credential storage
Create a Tauri v2 plugin that uses EncryptedSharedPreferences (Android Keystore)
on Android and the system keychain (keyring crate) on desktop. This replaces
the direct onyx-core keyring calls in the Tauri app, which failed on Android
because keyring-storage was feature-gated to desktop only.

- New plugin crate at apps/tauri/tauri-plugin-credentials/ with Kotlin Android
  code and Rust desktop fallback
- Update all Tauri credential commands to use the plugin API
- Add security-crypto dependency for Android and ProGuard rule for Tink
- Remove onyx-core/keyring-storage dependency from Tauri app features
2026-04-05 17:29:51 -07:00
Tristan Michael 44ac2bc5b3 Show sync counts only when non-zero
Hide upload/download counts in the drawer footer when both are zero;
show each arrow count individually only if its value is greater than
zero. This avoids displaying redundant “↑0 ↓0” and ensures a
cleaner, more informative sync status line when only one direction has
activity.
2026-04-05 16:38:21 -07:00
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