- Use `e.detail === 2` for double clicks in `DownloadTable.tsx`
- Add `user-select: none` to list items in `index.css`
- Delete existing assets before enqueueing in `redownload` action
- Remove redundant `onDoubleClick` props from `DownloadItem.tsx`
The root cause was hydrate_extension_pairing_token accessing the
keychain when keychainAccessGranted was true in the DB. Any keychain
access on macOS triggers the system prompt when the binary signature
changes after an update.
Architecture change: two-store model.
- The SQLite settings DB is now the primary store for the token.
hydrate_extension_pairing_token reads from it exclusively -- it
never touches the OS keychain. No system prompt on startup.
- The OS keychain remains defence-in-depth: grant_keychain_access
still writes the token there, but it is only reached from the
explicit Grant Access button, so any system prompt is user-initiated.
DB helpers: load_pairing_token_from_settings / save_pairing_token_to_settings
hydrate_extension_pairing_token: reads from DB, skips keychain entirely
grant_keychain_access: syncs token to DB after keychain access
Frontend: extensionPairingToken included in partialize for auto-persist
- Revert hydrate_extension_pairing_token to consult the DB's
keychainAccessGranted flag instead of unconditionally skipping the
keychain. When the flag is true the backend tries the keychain; if
macOS trusts the current binary (no code-signature change) the read
succeeds silently and the modal is suppressed. When the flag is
false the keychain is skipped and the modal is shown exactly once.
- Fix the Grant Access button in the KeychainPermissionModal by
writing the result (token, persistent flag) directly into the store
instead of calling hydratePairingToken() again. The re-hydration
raced with Zustand's async persist middleware, which could read
keychainAccessGranted=false from the DB and flip persistent back
to false, re‑showing the modal in an endless loop.
- The scheduler defaults (enabled: false) were already correct in
both the Rust default_settings() and the frontend Zustand store;
no code change needed.
- Disable log capture by default in both backend (LOG_PAUSED=true) and
frontend (isPaused=true) to avoid unnecessary disk I/O and battery drain.
- Add a global contextmenu listener that prevents the webview's default
right-click menu (Reload, etc.) so the app behaves like a native macOS
window. Custom context menus in DownloadItem, LogsView, and Sidebar
still work because their handlers preventDefault() before the document
listener fires.
- Sort the download table by queuePosition when viewing a specific queue
so the move-up/down controls produce a visible reorder instead of a
silent position swap with no UI feedback.
- Rework the keychain access flow to eliminate the OS credential prompt
that appeared before the app's own KeychainPermissionModal after every
binary update. hydrate_extension_pairing_token now always skips the
keychain; only the explicit Grant Access button (grant_keychain_access)
triggers the OS prompt, which is user-initiated and therefore acceptable
even when macOS trust resets after a code-signature change.
- Ensure yt-dlp partial files are cleaned up on error exits.
- Handle Semaphore closure gracefully without panicking.
- Replace busy-polling in wait_permit_released with Notify.
- Fix extension server silent failure with retry loop and logging.
- Ignore malformed existing scheduler settings instead of failing.
- Add final symlink checks before revealing/opening downloads.
- Delay listener registration in App.tsx until core is ready.
- Sanitize Referer header to prevent injection.
This entirely removes 'sanitize_current_settings_and_restore_token' from 'db::init' and removes the keychain hydration from 'setup()'. The backend will now never access the keychain during bootstrapping, relying purely on the frontend to explicitly trigger token hydration and migration via Tauri IPC when the UI mounts. This guarantees the macOS system prompt will never block the app from opening, and will only appear after the window is fully visible.
This fixes a severe issue where 'hydrate_pairing_token' was hardcoded to fetch from the keychain on startup and via the 'hydrate_extension_pairing_token' command. This caused multiple macOS keychain prompts to appear back-to-back before the app even loaded. We now correctly check the user's 'keychainAccessGranted' preference from the DB payload and skip keychain operations entirely if they haven't explicitly granted access yet.
This fixes the issue where the app forces a macOS system prompt for credential store access on first launch/update. Instead, the legacy token is safely preserved in the persistent JSON document and is now only forcefully migrated to the macOS keychain when the user manually hits 'Grant Access' in the UI modal.
- Prevent UI hang by processing aria2 unpause actions asynchronously
- Bypass internal aria2 limits to honor Firelink's max concurrent setting
- Improve visibility of the 'Recheck engines' button in Settings
- Refactor Speed Limiter checkbox into a toggle and fix text selection
- Update Add window font and placeholder to match modern design standards
- update npm dependencies, including Vite 8.1.0 and TypeScript 6.0.3
- update bundled Deno engine locks and macOS arm64 binary to 2.8.3
- add npm run check:updates for npm and engine freshness checks
- ignore local agent/planning notes and remove tracked internal docs
Migrate from CSS keyframes to a React state-machine using useLayoutEffect and CSS transitions. Fixes animation interruption jumps, double-timeout memory leaks, layout snapping, and correctly handles duration: 0.
- Default LOG_PAUSED to true in backend.
- Combine tauri_plugin_log filters to drop backend logs when paused.
- Create frontend logger wrapper to intercept and drop logs before IPC when paused.
- Sync LogsView state directly with logger wrapper and handle initialization races.
Disabled implicit system/environment variable proxy fallbacks in reqwest, yt-dlp, and aria2c when no proxy is explicitly provided. Updated useDownloadStore to strictly return 'none' when 'none' is selected or when 'system' proxy is empty, matching IDM/FDM behavior and ignoring http_proxy environment variables.
- Implemented `clear_logs` Tauri command to physically wipe local log files.
- Added a global `ErrorBoundary` to cleanly intercept React render loops (e.g. Error #185) and provide readable stack traces.
- Hardened `tauri_plugin_log` to filter out `webview`, `hyper`, `reqwest`, `rustls` log noise.
- Replaced the default browser right-click menu in LogsView with a custom native React menu for copying text securely.
- Disabled Backspace/Delete keyboard shortcuts when any app modal is active
- Synced frontend LogsView pause state with the backend on initial mount
- Added a privacy and telemetry note in the Logs view to inform users
- Removed legacy polling dead code from LogsView
- Replaced LogViewer setInterval polling with reactive attachLogger
- Added backend LOG_PAUSED toggle to fully halt logs and save I/O
- Added native text selection to logs console
- Implemented Backspace/Delete keyboard shortcuts across DownloadTable and Sidebar with modal-aware safeties
- Integrated cross-platform sysinfo logging for telemetry
- Implement scheduler hydration barrier to prevent premature triggers
- Track scheduler exact runs using keys to avoid false stops
- Use 'System Events' for accurate macOS automation permissions
- Prevent system-sleep via proper idle assertions
- Ensure download pauses use channel acknowledgements (PauseWithAck)
- Require Firelink ownership before replacing files in add/conflict UI
- Retain partial download assets when removing entries without deletion
- Clear progress state in store when downloads complete or pause to reduce churn
- Handle empty/invalid queue selections gracefully
- launch Firefox handoffs through authenticated reconnect
- preserve usable drafts when metadata lookup fails
- reject stale metadata results and retry failed rows only
- Reset isRemoving state in DeleteConfirmationModal on open and close to prevent UI freeze
- Filter sidebar queue count to exclude completed downloads
- Return fully resolved URL from backend metadata fetch to avoid 403s on one-time tokens