- Replace split 'Default Download Path' and 'All Categories Base' with a canonical baseDownloadFolder and categorySubfolders model - Retain absolute category overrides only where required - Update Add window to accurately reflect intended destination: - Single URL: specific category path - Multiple URLs: base folder with explanatory text - Manual Browse override: selected folder - Centralize path resolution logic shared by Rust backend and UI
34 KiB
Firelink — UI Interaction Inventory
A read-only audit of every user-facing action in Firelink, mapping each to the component/handler, Zustand store action, IPC command, Rust function, expected behavior, and the likely validation method. No code was changed.
Legend:
- ✅ = wired end-to-end (UI → store → IPC → Rust)
- ⚠️ = present in UI/store but with a gap, mismatch, or risk (see notes)
- ❌ = declared/wired in one layer but missing in another (dead path)
1. Main Window
1.1 Title bar / Toolbar (DownloadTable.tsx)
| # | Action | Component / fn | Store action | IPC command | Rust fn | Expected behavior | Validation |
|---|---|---|---|---|---|---|---|
| M1 | Add Download (+) | DownloadTable → toggleAddModal(true) |
useDownloadStore.toggleAddModal |
— | — | Opens Add Downloads modal | None |
| M2 | Resume All (▶) | DownloadTable maps over paused items → handleResume |
useDownloadStore.resumeDownload (per item) |
resume_download |
lib.rs:2211 resume_download |
Resumes every paused download in the current filter | Per-item status check |
| M3 | Pause All (⏸) | DownloadTable maps over downloading items → handlePause |
— (direct) | pause_download |
lib.rs:2137 pause_download |
Pauses every downloading item in the current filter | Per-item aria2 status |
| M4 | Show Sidebar (PanelLeft, when hidden) | DownloadTable → toggleSidebar |
useSettingsStore.toggleSidebar |
— | — | Reveals sidebar; persisted | None |
| M5 | Drag window | WindowDragRegion / data-tauri-drag-region divs → getCurrentWindow().startDragging() |
— | core window | Tauri core | Moves window | Native |
| M6 | Sidebar resize handle | App.startSidebarResize (pointer drag) |
setSidebarWidth (local) → localStorage |
— | — | Resizes sidebar 190–260px | Clamped in handler |
⚠️ M2/M3: "Resume All"/"Pause All" only act on items in the current sidebar
filter (e.g. active vs. all). A user on the "Active" filter pausing all won't
touch paused items elsewhere — could surprise users who read the button as global.
Contrast with the tray's true global Pause/Resume All (downloadStore.ts:79).
1.2 Download row actions (DownloadItem.tsx, DownloadTable.tsx)
| # | Action | Component / fn | Store action | IPC command | Rust fn | Expected behavior | Validation |
|---|---|---|---|---|---|---|---|
| M7 | Move Up / Move Down (queued) | DownloadItem → moveInQueue(id,'up'|'down') |
useDownloadStore.moveInQueue |
move_in_queue |
lib.rs:2613 move_in_queue |
Reorders pending queue; disabled at bounds | queueIndex === 0 / length-1 |
| M8 | Pause (row, active) | DownloadItem.handlePause |
— | pause_download |
pause_download |
Pauses aria2/native/media task | aria2 status check |
| M9 | Resume (row, paused) | DownloadItem.handleResume |
resumeDownload |
resume_download |
resume_download |
Resumes; re-enqueues if no gid | gid presence |
| M10 | Options menu (⋮) / right-click | DownloadItem → setContextMenu |
— | — | — | Opens context menu | — |
| M11 | Double-click row | handleDownloadDoubleClick |
— | open_downloaded_file |
commands.rs:27 open_downloaded_file |
Completed → open file; else → Properties | item.status === 'completed' |
1.3 Row context menu (DownloadTable.tsx, floating menu)
| # | Action | Component / fn | Store action | IPC command | Rust fn | Expected behavior | Validation |
|---|---|---|---|---|---|---|---|
| C1 | Open | openDownloadFile |
— | open_downloaded_file |
open_downloaded_file |
Opens completed file | status==='completed'; path authorization |
| C2 | Show in Finder | revealDownloadFile |
— | reveal_in_file_manager |
commands.rs:6 reveal_in_file_manager |
Reveals file/part in Finder | status==='completed'; path authorization |
| C3 | Pause | handlePause |
— | pause_download |
pause_download |
Pauses | status in {downloading,queued,retrying} |
| C4 | Resume | handleResume |
resumeDownload |
resume_download |
resume_download |
Resumes | status in {paused,failed,retrying} |
| C5 | Redownload | redownload(contextItem.id) |
useDownloadStore.redownload |
enqueue_download (via dispatchItem) |
enqueue_download |
Creates a new queued copy | status in {completed,failed,paused} |
| C6 | Copy Address | navigator.clipboard.writeText(url) |
— | — | — | Copies URL | try/catch toast |
| C7 | Copy File Path | clipboard write | — | — | — | Copies resolved path | status==='completed'; filename present |
| C8 | Remove | handleDelete → openDeleteModal(id) |
useDownloadStore.openDeleteModal |
— | — | Opens Delete modal | — |
| C9 | Properties | openProperties → setSelectedPropertiesDownloadId |
useDownloadStore.setSelectedPropertiesDownloadId |
— | — | Opens Properties modal | — |
1.4 Column resize
| # | Action | Component / fn | Store action | IPC command | Rust fn | Expected behavior | Validation |
|---|---|---|---|---|---|---|---|
| M12 | Resize column handle | DownloadTable.startColumnResize |
setColumnWidths (local) |
— | — | Resizes table columns | per-column minimums |
1.5 Sidebar (Sidebar.tsx)
| # | Action | Component / fn | Store action | IPC command | Rust fn | Expected behavior | Validation |
|---|---|---|---|---|---|---|---|
| S1 | Library filters (All/Active/Completed/Unfinished) | NavItem → onSelectFilter |
App.setFilter + setActiveView('downloads') |
— | — | Filters download list | — |
| S2 | Folder filters (Musics/Movies/…/Other) | NavItem |
as above | — | — | Filters by category |
— |
| S3 | Queue item select | QueueItem → onSelectFilter('queue:<id>') |
as above | — | — | Filters by queueId |
— |
| S4 | Add new queue | "Add new queue" button → input → handleAddQueueSubmit |
useDownloadStore.addQueue |
— | — | Adds queue (persisted to store.bin) | name trimmed non-empty |
| S5 | Queue rename | context → Rename → handleRenameQueueSubmit |
useDownloadStore.renameQueue |
— | — | Renames queue | name trimmed non-empty |
| S6 | Queue delete | context → Delete | useDownloadStore.removeQueue |
— | — | Deletes queue; reassigns downloads to Main | id !== MAIN_QUEUE_ID |
| S7 | Start Queue | context → Start Queue | useDownloadStore.startQueue |
enqueue_download/resume_download |
dispatchItem | Dispatches/resumes queue's runnable items | queued/paused/failed |
| S8 | Pause Queue | context → Pause Queue | useDownloadStore.pauseQueue |
pause_download |
pause_download |
Pauses all downloading in queue | status downloading |
| S9 | Tools: Scheduler | ToolItem view='scheduler' |
setActiveView('scheduler') |
— | — | Shows Scheduler view | — |
| S10 | Tools: Speed Limiter | ToolItem view='speedLimiter' |
setActiveView('speedLimiter') |
— | — | Shows Speed Limiter view | — |
| S11 | Tools: Diagnostics | ToolItem view='diagnostics' |
setActiveView('diagnostics') |
— | — | Shows Diagnostics console | — |
| S12 | Settings | footer button | setActiveView('settings') |
— | — | Shows Settings view | — |
| S13 | Hide Sidebar | toggleSidebar |
useSettingsStore.toggleSidebar |
— | — | Collapses sidebar | — |
1.6 Global handlers (App-level useEffect listeners)
| # | Action | Trigger | Handler | Store/IPC | Rust fn | Expected behavior | Validation |
|---|---|---|---|---|---|---|---|
| G1 | Paste URLs | window paste (non-input) |
extractValidDownloadUrls → openAddModalWithUrls |
store | — | Opens Add modal with pasted URLs | URL scheme allowlist |
| G2 | Deep link | deep-link-add-download event |
openAddModalWithUrls(payload) |
store | dispatch_deep_links → parse_firelink_urls |
Opens Add modal | scheme/host/length caps |
| G3 | Extension add | extension-add-download event |
handleExtensionDownload |
store | extension_server | Opens Add modal | dedupe URLs |
| G4 | Extension queued batch | extension-downloads-queued event |
merges into downloads/pendingOrder |
setState | extension_server | Appends new items | id dedupe |
| G5 | Schedule trigger | schedule-trigger event ('start'/'stop') |
startQueue/pauseQueue + setSchedulerRunning |
store+IPC | scheduler.rs | Starts/stops main queue | — |
| G6 | Post-queue system action | scheduler end + postQueueAction !== 'none' |
invoke('perform_system_action') |
— | lib.rs:2555 perform_system_action |
Sleep/restart/shutdown | automation permission |
| G7 | Notification | download-state terminal |
sendNotification |
— | — | OS notification | permission; showNotifications |
2. Add Download Window (AddDownloadsModal.tsx)
| # | Action | Component / fn | Store action | IPC command | Rust fn | Expected behavior | Validation |
|---|---|---|---|---|---|---|---|
| A1 | Paste/edit URLs textarea | setUrls (local) → debounce parse |
— | fetch_metadata / fetch_media_metadata (via fetchMediaMetadataDeduped) |
lib.rs:616 fetch_metadata, lib.rs:784 fetch_media_metadata |
Resolves filename/size/formats | new URL(); isMediaUrl |
| A2 | Refresh Metadata | setMetadataRefreshNonce(n+1) (re-runs effect) |
— | as above | as above | Re-fetches metadata | active guard |
| A3 | Select preview row | setSelectedItemIndex(i) |
— | — | — | Selects item for media-format panel | bounds |
| A4 | Select media format | selectMediaFormat(idx) |
— (local parsedItems) | — | — | Sets chosen stream; updates filename/size | selectedFormat bounds |
| A5 | Browse Save Location | handleBrowse → open({directory:true}) |
— | — | — | Sets save location; marks manual | dialog result |
| A6 | Target Queue select | setSelectedQueueId |
— | — | — | Sets target queue | queue exists |
| A7 | Connections slider | setConnections |
— | — | — | 1–16 (disabled for media) | min/max |
| A8 | Limit speed per file (checkbox+input) | setSpeedLimitEnabled/setSpeedLimit |
— | — | — | Per-file KiB/s cap | numeric |
| A9 | Use authorization (checkbox+user/pass) | setUseAuth/setUsername/setPassword |
— | — | — | Sends basic auth | useAuth flag |
| A10 | Advanced toggle | setAdvancedExpanded |
— | — | — | Expands advanced fields | — |
| A11 | Verify Checksum (algo+digest) | setChecksumEnabled/setChecksumAlgo/setChecksumValue |
— | — | — | Sends algo=digest |
checksumEnabled |
| A12 | Headers / Cookies / Mirrors | setHeaders/setCookies/setMirrors |
— | — | — | Raw strings forwarded | trim/empty |
| A13 | Add to Queue | handleStart(false) → executeAddDownloads(false,…) |
addDownload (status 'paused') |
enqueue_download (via dispatchItem) |
enqueue_download |
Adds paused items | duplicate detection |
| A14 | Start Downloads | handleStart(true) → executeAddDownloads(true,…) |
addDownload (status 'queued') + immediate dispatch |
enqueue_download |
enqueue_download |
Adds + starts items | duplicate detection |
| A15 | Cancel | toggleAddModal(false) |
useDownloadStore.toggleAddModal |
— | — | Closes modal | — |
Duplicate detection (handleStart → DuplicateResolutionModal):
- Checks URL already in queue (
status !== failed/completed) and file exists on disk viacheck_file_existsand in-store path match. DuplicateResolutionModal: per-conflict Rename / Replace (file only) / Skip select → on Confirm callsexecuteAddDownloads(…,resolutions).
| # | Action | Component / fn | Store/IPC | Rust fn | Expected behavior | Validation |
|---|---|---|---|---|---|---|
| A16 | Pick resolution per conflict | updateResolution (local) |
— | — | rename/replace/skip | reason.type gates replace |
| A17 | Continue | onConfirm(resolutions) → executeAddDownloads |
store+IPC | enqueue_download; delete_file (replace) |
Applies resolutions | loop bounds |
⚠️ A14 "Start Downloads" does not show any progress/confirmation that
duplicate conflicts were resolved; resolution loop uses check_file_exists only
to gate 'rename', but the rename loop caps at count < 1000 and silently keeps
the last tried name if exceeded.
3. Download Properties Window (PropertiesModal.tsx)
| # | Action | Component / fn | Store action | IPC command | Rust fn | Expected behavior | Validation |
|---|---|---|---|---|---|---|---|
| P1 | Edit URL | setUrl |
— | — | — | Saves on Save | non-empty on save |
| P2 | Edit File name | setFileName |
— | — | — | Saves on Save | non-empty on save |
| P3 | Select Save Location | handleBrowse → open({directory}) |
— | — | — | Sets destination | dialog result |
| P4 | Connections | setConnections (1–16) |
— | — | — | per-file connections | disabled if transferLocked |
| P5 | Limit speed (checkbox+value) | setSpeedLimitEnabled/setSpeedLimitValue |
— | — | — | per-file KiB/s | disabled if transferLocked |
| P6 | Login mode toggle (matching/custom/none) | setLoginMode |
— | — | — | Credentials source | disabled if transferLocked |
| P7 | Custom username/password | setUsername/setPassword |
— | — | — | Override login | custom mode only |
| P8 | Checksum (verify+algo+digest) | setChecksumEnabled/setChecksumAlgorithm/setChecksumValue |
— | — | — | algo=digest |
disabled if transferLocked |
| P9 | Cookies / Headers / Mirrors | setCookies/setHeaders/setMirrors |
— | — | — | Advanced fields | disabled if transferLocked |
| P10 | Advanced toggle | setAdvancedExpanded |
— | — | — | Expand/collapse | — |
| P11 | Save | handleSave → applyProperties(id, updates) |
useDownloadStore.applyProperties |
remove_from_queue/detach_download_for_reconfigure/enqueue_download (re-dispatch) |
remove_from_queue, detach_download_for_reconfigure, enqueue_download |
Updates item; re-attaches backend | status guard (throws if active) |
| P12 | Cancel | setSelectedPropertiesDownloadId(null) |
as above | — | — | Closes modal | — |
applyProperties rules (useDownloadStore.ts:313):
- downloading/processing/retrying → throws "Cannot change properties while transfer is active."
- completed/failed → updates store only (read-only identity; settings saved for redownload).
- queued → removes from queue, updates, re-dispatches; on dispatch failure removes from queue.
- paused →
detach_download_for_reconfigure, unregister, update (no re-dispatch; resumes on next resume).
⚠️ P5/P8/P9 use isTransferLocked (downloading/processing/retrying) to
disable inputs, but isLocked also includes completed. For a completed
item the URL/filename/destination are disabled (correct), yet speed-limit,
checksum, cookies, headers, mirrors are editable — these only matter for
redownload, which is the documented intent, but the disable logic is split
across two booleans (isLocked vs isTransferLocked) and is easy to misread.
4. Settings Window (SettingsView.tsx, tabbed)
| # | Action | Tab | Store setter | IPC command | Rust fn | Validation |
|---|---|---|---|---|---|---|
| T1 | Tab select | — | setActiveSettingsTab |
— | — | enum |
| Downloads | ||||||
| SD1 | Default connections (1–16) | downloads | setPerServerConnections |
— | — | onBlur clamp 1–16 |
| SD2 | Parallel downloads (1–12) | downloads | setMaxConcurrentDownloads |
set_concurrent_limit (App effect) |
set_concurrent_limit |
onBlur clamp 1–12 |
| SD3 | Global speed limit | downloads | setGlobalSpeedLimit |
set_global_speed_limit (App effect) |
set_global_speed_limit |
free text |
| SD4 | Automatic retries (0–10) | downloads | setMaxAutomaticRetries |
— | — | onBlur clamp 0–10 |
| SD5 | Show notification on completion | downloads | setShowNotifications |
— | — | toggle |
| SD6 | Play sound on completion | downloads | setPlayCompletionSound |
— | — | requires SD5 |
| Look & Feel | ||||||
| SL1 | Theme (system/light/dark/dracula/nord) | lookandfeel | setTheme |
— | — | App effect applies classes |
| SL2 | Font size | lookandfeel | setAppFontSize |
— | — | data-font-size attr |
| SL3 | List row density | lookandfeel | setListRowDensity |
— | — | enum |
| SL4 | Dock badge | lookandfeel | setShowDockBadge |
update_dock_badge (setter + effect) |
update_dock_badge |
toggle; clears badge when off |
| SL5 | Menu bar icon | lookandfeel | setShowMenuBarIcon |
toggle_tray_icon (App effect) |
toggle_tray_icon |
toggle |
| Network | ||||||
| SN1 | Proxy mode (none/system/custom) | network | setProxyMode |
— | — | radio |
| SN2 | Proxy host | network | setProxyHost |
— | — | shown if custom |
| SN3 | Proxy port (1–65535) | network | setProxyPort |
— | — | onBlur clamp |
| SN4 | Custom User Agent | network | setCustomUserAgent |
— | — | free text + datalist |
| Locations | ||||||
| SO1 | Default download path (text+Browse) | locations | setDefaultDownloadPath |
— | — | pickDirectory |
| SO2 | Ask where to save each file | locations | setAskWhereToSaveEachFile |
— | — | toggle |
| SO3 | All Categories Base Browse | locations | setCategoryDirectory ×7 |
create_category_directories |
parity::create_category_directories |
creates dirs on disk |
| SO4 | Per-category path (text+Browse) | locations | setCategoryDirectory |
— | — | handleBrowseCategory |
| SO5 | Reset Defaults | locations | resetCategoryDirectories |
— | — | restores defaults |
| Site Logins | ||||||
| SLg1 | Delete login | sitelogins | removeSiteLogin |
delete_keychain_password |
delete_keychain_password |
removes keychain entry |
| SLg2 | Add login (pattern/user/pass) | sitelogins | addSiteLogin |
set_keychain_password |
set_keychain_password |
pattern+user non-empty |
| Power | ||||||
| SP1 | Prevent sleep while downloading | power | setPreventsSleepWhileDownloading |
set_prevent_sleep (syncSystemIntegrations + setter) |
set_prevent_sleep |
keepawake on/off |
| Engine | ||||||
| SE1 | Recheck engines | engine | — | get_*_engine_status ×4 |
check_* |
force re-run; cached otherwise |
| SE2 | Show/hide technical details | engine | — (local) | — | — | expand card |
| SE3 | Browser Cookies Source | engine | setMediaCookieSource |
— | — | enum (none/safari/chrome/firefox/edge/brave) |
| Integrations | ||||||
| SI1 | Copy Token | integrations | — (clipboard) | — | — | copies pairing token |
| SI2 | Regenerate token | integrations | regeneratePairingToken |
set_keychain_password + set_extension_pairing_token (App effect) |
both | mints new token |
| SI3 | Get Extension links | integrations | — (anchor) | — | — | opens external URLs |
| About | ||||||
| SA1 | Check for Updates | about | — | check_for_updates |
parity::check_for_updates |
toast result |
| SA2 | Auto-check updates toggle | about | setAutoCheckUpdates |
— | — | switch |
5. Tools Views
5.1 Scheduler (SchedulerView.tsx)
| # | Action | Component / fn | Store action | IPC command | Rust fn | Expected behavior | Validation |
|---|---|---|---|---|---|---|---|
| SC1 | Enable Scheduler | updateDraft('enabled') |
— (draft) | — | — | Toggles draft (saved on Save) | — |
| SC2 | Start/Stop time | updateDraft('startTime'/'stopTime'/'stopTimeEnabled') |
— | — | — | Sets schedule window | time input |
| SC3 | Run Every Day | updateDraft('everyday') |
— | — | — | All days | — |
| SC4 | Day toggles | toggleDay |
— | — | — | Selected days | — |
| SC5 | Post-queue action radio | updateDraft('postQueueAction') |
— | — | — | none/sleep/restart/shutdown | — |
| SC6 | Run Now | runNow → startQueue(MAIN_QUEUE_ID) |
setSchedulerRunning(true) |
enqueue/resume | dispatchItem | Starts main queue now | count>0 |
| SC7 | Pause | pauseNow → pauseQueue(MAIN_QUEUE_ID) |
setSchedulerRunning(false) |
pause_download |
pause_download |
Pauses main queue | — |
| SC8 | Save Settings | save → setScheduler(normalized) |
setScheduler |
— | — | Persists scheduler; registered by scheduler.rs | selectedDays fallback |
| SC9 | Grant/Revoke Automation | handlePermissionAction |
— | request_automation_permission / open_automation_settings |
both | macOS Automation for Finder | platform isMac |
5.2 Speed Limiter (SpeedLimiterView.tsx)
| # | Action | Component / fn | Store action | IPC command | Rust fn | Expected behavior | Validation |
|---|---|---|---|---|---|---|---|
| VL1 | Enable toggle | setEnabled |
— (local) | — | — | Enables draft | — |
| VL2 | Value input | setValue |
— | — | — | numeric | min 1; clamp |
| VL3 | Unit KB/s · MB/s | setUnit |
— | — | — | unit select | — |
| VL4 | Quick presets (1/5/10 MB/s) | preset(n) |
— | — | — | sets value+unit | — |
| VL5 | Save Limit | save → setGlobalSpeedLimit + setLastCustomSpeedLimitKiB |
both | set_global_speed_limit (App effect) |
set_global_speed_limit |
Applies global cap | clamp; disabled→'' |
5.3 Diagnostics (DiagnosticsView.tsx)
| # | Action | Component / fn | Store action | IPC command | Rust fn | Expected behavior | Validation |
|---|---|---|---|---|---|---|---|
| DG1 | Clear console | handleClear → setLogs([]) |
— (local) | — | — | Empties log view | — |
| DG2 | Export Logs | handleExport → save({…}) |
— | export_logs |
lib.rs:2800 export_logs |
Writes log to chosen path | path from save dialog |
6. Menus / Tray
6.1 Tray menu (built twice — see risk note)
Primary tray built in setup() (lib.rs:3192) with items: Show Firelink,
Pause All, Resume All, Quit. Menu events emit tray-action events handled by
downloadStore.ts:79 (true global pause/resume across all queues). Left-click
tray icon restores main window.
toggle_tray_icon (lib.rs:2822) rebuilds the tray when "Menu bar icon" is
toggled, but only registers Show / Quit — it omits the Pause All / Resume All
items. Because toggle_tray_icon checks tray_by_id("main") and the setup tray
already used id "main", toggling on is a no-op while the setup tray exists.
| # | Action | Trigger | Handler | IPC / Event | Rust fn | Expected behavior |
|---|---|---|---|---|---|---|
| TR1 | Show Firelink | tray menu | restore_main_window |
— | restore_main_window |
Unminimize/show/focus |
| TR2 | Pause All | tray menu | emits tray-action 'pause-all' |
event | — | Pauses all queues |
| TR3 | Resume All | tray menu | emits tray-action 'resume-all' |
event | — | Starts all queues |
| TR4 | Quit | tray menu | app.exit(0) |
— | — | Exits app |
| TR5 | Tray left-click | icon click | restore_main_window |
— | — | Show window |
⚠️ There is no native macOS app menu (no Edit menu with Cut/Copy/Paste / Cmd+Q, no Window menu). Standard keyboard shortcuts and clipboard editing rely on the webview default. This is a UX gap on macOS.
6.2 Window controls
Close/minimize/maximize are the native Tauri decorations (title bar hidden;
WindowDragRegion provides drag only). Close behavior default (no
minimize-to-tray override observed).
7. Delete Confirmation (DeleteConfirmationModal.tsx)
| # | Action | Component / fn | Store action | IPC command | Rust fn | Expected behavior | Validation |
|---|---|---|---|---|---|---|---|
| D1 | Remove (from list only) | handleRemoveFromList → removeDownload(id,false) |
useDownloadStore.removeDownload |
remove_download (filepath null) |
remove_download |
Removes item; keeps file | isRemoving flag |
| D2 | Delete file | handleDeleteFile → removeDownload(id,true) |
removeDownload |
trash_download_assets + remove_download |
trash_download_assets, remove_download |
Trashes file+.aria2+.part, removes item | path resolve |
| D3 | Cancel | handleCancel → closeDeleteModal |
closeDeleteModal |
— | — | Closes modal | — |
8. IPC Command → Rust function map
All commands declared in src/ipc.ts CommandMap. Registered handlers:
src-tauri/src/lib.rs:3550 generate_handler![…].
| IPC command | Rust fn | File:line | Status |
|---|---|---|---|
fetch_metadata |
fetch_metadata |
lib.rs:616 | ✅ |
fetch_media_metadata |
fetch_media_metadata |
lib.rs:784 | ✅ |
get_engine_status |
get_engine_status |
lib.rs:1674 | ✅ |
get_aria2_engine_status |
get_aria2_engine_status |
lib.rs:1694 | ✅ |
get_ytdlp_engine_status |
get_ytdlp_engine_status |
lib.rs:1702 | ✅ |
get_ffmpeg_engine_status |
get_ffmpeg_engine_status |
lib.rs:1707 | ✅ |
get_deno_engine_status |
get_deno_engine_status |
lib.rs:1712 | ✅ |
open_file |
open_file |
lib.rs:1018 | ✅ (unused by UI; UI uses open_downloaded_file) |
show_in_folder |
show_in_folder |
lib.rs:1031 | ✅ (unused by UI; UI uses reveal_in_file_manager) |
reveal_in_file_manager |
commands::reveal_in_file_manager |
commands.rs:6 | ✅ |
open_downloaded_file |
commands::open_downloaded_file |
commands.rs:27 | ✅ |
trash_download_assets |
commands::trash_download_assets |
commands.rs:45 | ✅ |
pause_download |
pause_download |
lib.rs:2137 | ✅ |
resume_download |
resume_download |
lib.rs:2211 | ✅ |
remove_download |
remove_download |
lib.rs:2292 | ✅ |
detach_download_for_reconfigure |
detach_download_for_reconfigure |
lib.rs:2364 | ✅ |
enqueue_download |
enqueue_download |
lib.rs:2565 | ✅ |
enqueue_many |
enqueue_many |
lib.rs:2586 | ✅ |
move_in_queue |
move_in_queue |
lib.rs:2613 | ✅ |
remove_from_queue |
remove_from_queue |
lib.rs:2622 | ✅ |
get_pending_order |
get_pending_order |
lib.rs:2560 | ✅ |
set_concurrent_limit |
set_concurrent_limit |
lib.rs:2636 | ✅ |
set_global_speed_limit |
set_global_speed_limit |
lib.rs:2663 | ✅ |
update_dock_badge |
update_dock_badge |
lib.rs:2507 | ✅ |
set_prevent_sleep |
set_prevent_sleep |
lib.rs:2526 | ✅ |
perform_system_action |
perform_system_action |
lib.rs:2555 | ✅ |
request_automation_permission |
request_automation_permission |
lib.rs:2680 | ✅ |
open_automation_settings |
open_automation_settings |
lib.rs:2707 | ✅ |
get_free_space |
get_free_space |
lib.rs:2721 | ✅ |
set_keychain_password |
set_keychain_password |
lib.rs:2758 | ✅ |
get_keychain_password |
get_keychain_password |
lib.rs:2765 | ✅ |
delete_keychain_password |
delete_keychain_password |
lib.rs:2771 | ✅ |
check_file_exists |
check_file_exists |
lib.rs:2778 | ✅ |
delete_file |
delete_file |
lib.rs:2787 | ✅ |
toggle_tray_icon |
toggle_tray_icon |
lib.rs:2823 | ⚠️ see TR note |
set_extension_pairing_token |
set_extension_pairing_token |
lib.rs:2875 | ✅ |
set_extension_frontend_ready |
set_extension_frontend_ready |
lib.rs:2892 | ✅ |
get_system_proxy |
parity::get_system_proxy |
parity.rs:7 | ✅ |
get_file_category |
parity::get_file_category |
parity.rs:22 | ✅ (UI uses TS categoryForFileName instead) |
check_for_updates |
parity::check_for_updates |
parity.rs:84 | ✅ |
is_supported_media |
parity::is_supported_media |
parity.rs:180 | ✅ (declared in ipc.ts but UI uses TS isMediaUrl/get_supported_media_domains) |
create_category_directories |
parity::create_category_directories |
parity.rs:144 | ✅ |
export_logs |
export_logs |
lib.rs:2800 | ✅ |
db_save_settings |
— | — | ❌ declared in ipc.ts but not registered in Rust |
db_load_settings |
— | — | ❌ declared in ipc.ts but not registered in Rust |
db_get_all_downloads |
— | — | ❌ declared in ipc.ts but not registered in Rust |
db_save_download |
— | — | ❌ declared in ipc.ts but not registered in Rust |
db_delete_download |
— | — | ❌ declared in ipc.ts but not registered in Rust |
start_download |
— | — | ❌ declared in ipc.ts (StartDownloadArgs) but not registered in Rust |
start_media_download |
— | — | ❌ declared in ipc.ts (StartMediaDownloadArgs) but not registered in Rust |
get_supported_media_domains |
parity::get_supported_media_domains |
parity.rs:175 | ✅ (called directly via invoke in downloads.ts, not in typed CommandMap) |
Persistence actually runs through the Tauri
plugin-store(LazyStoreonstore.bin) in the Zustand subscriber (useDownloadStore.ts:646), not thedb_*commands — those are dead declarations. Likewisestart_download/start_media_downloadare superseded byenqueue_download/enqueue_manyvia thedispatchItemflow.
Backend events (Rust → Frontend)
| Event | Payload | Emitted by | Listened by |
|---|---|---|---|
download-progress |
DownloadProgressEvent |
download/queue tasks | downloadStore.ts:31 |
download-state |
DownloadStateEvent |
pause/resume/remove + tasks | downloadStore.ts:49, App.tsx:210 |
download-complete / download-failed |
string | (declared; not emitted in read code) | — |
schedule-trigger |
'start'|'stop' | scheduler.rs | App.tsx:113 |
tray-action |
'pause-all'|'resume-all' | tray menu | downloadStore.ts:80 |
extension-add-download |
ExtensionDownload |
extension_server | App.tsx:231 |
extension-downloads-queued |
DownloadItem[] |
extension_server | App.tsx:234 |
deep-link-add-download |
string | deep-link dispatch | App.tsx:251 |
log |
{level,message} |
tauri-plugin-log | DiagnosticsView.tsx:19 |
9. Highest-Risk Areas
Ranked by likelihood × impact:
-
Tray menu divergence / duplicate tray builder (
lib.rs:3192vslib.rs:2822). The setup tray registers Pause All / Resume All; the toggle builder does not and silently no-ops when the setup tray already exists. Toggling "Menu bar icon" off then on can leave the user with a tray that has no pause/resume items, or no tray at all depending on ordering. Risk: lost global queue control + confusing state. -
Resume/Pause All scope mismatch (M2/M3 vs TR2/TR3). Toolbar Resume/Pause All only act on the current filter, while tray Resume/Pause All act on all queues. Same icon, different semantics — users will mis-pause and leave downloads running, or fail to resume paused items.
-
perform_system_action(Sleep/Restart/Shutdown) is invoked with no confirmation dialog from the scheduler path (App.tsx:139) once the queue drains. A user who enables "Shut down" and walks away loses unsaved work in other apps. Permission is checked, but no undo. Risk: destructive, cross-application. -
applyPropertiesstate machine (useDownloadStore.ts:313). Five branches (active throws, completed/failed store-only, queued re-dispatch, paused detach) with backend registration tracked in a separateSet. A faileddetach_download_for_reconfigurethrows and preserves old props, but a failed re-dispatch on queued silently removes from queue. Easy to reach a state where the UI shows "queued" but the backend has no task (backendRegisteredIdsdesync). -
dispatchItemin-flight dedupe + failure → 'failed' (useDownloadStore.ts:21). On enqueue failure the item is markedfailedwithout surfacing the error to the user (onlyconsole.error). Users see a mysteriously failed download with no diagnostic. -
Path authorization surface (
commands.rs).open_downloaded_file,reveal_in_file_manager,trash_download_assetsall gate onknown_download_paths(registered viadownload_ownership). If an item is redownloaded or its destination changes, stale registration could either block a legitimate open or — worse — thedelete_filepath used by duplicate "Replace" (AddDownloadsModalA17) bypasses ownership checks and usesis_safe_pathonly. Risk: deleting the wrong file if a crafted path passesis_safe_path. -
Dead IPC declarations (
start_download,start_media_download, alldb_*). Not exploitable, but a maintenance trap: any future caller using the typedinvokeCommandfor these will get a runtime "command not found" with no compile-time signal. -
No native macOS app menu. No Edit menu (cut/copy/paste/select-all), Cmd+Q, or Window menu. Clipboard paste into the Add-URLs textarea works only via the webview default; the global paste handler (G1) intentionally ignores inputs, so Cmd+V in the textarea is the only path — fine, but no menu affordance.
-
Duplicate-resolution rename loop (A17) caps at 1000 attempts and on exhaustion keeps the last tried name without warning, which could collide and overwrite via the subsequent
addDownloadif the backend doesn't guard. -
SpeedLimiterViewSave applies via effect (App.tsx:101), not directly. ThepreviousSpeedLimitref dedupes, but the indirection means the visible toast ("Global limit saved…") can fire before the backend actually accepts the limit, and a failedset_global_speed_limitis only logged.