Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3fe3666
Add ADR 018: Settings architecture
claude Jan 24, 2026
2658a27
Add settings system specification and task list
claude Jan 25, 2026
f4525a2
Add Settings dialog with Ark UI and declarative registry
claude Jan 25, 2026
cd9ac77
Fix unused imports in test file
claude Jan 25, 2026
27b25d5
Format settings page
claude Jan 25, 2026
3cd04bf
Fix all ESLint errors in settings module
claude Jan 25, 2026
5846f5a
Add eslint-disable comments for console statements in settings-store
claude Jan 25, 2026
56c9565
Fix rustfmt formatting in settings module
claude Jan 25, 2026
dc72221
Add E2E tests for Settings window on Linux
claude Jan 25, 2026
d0a9876
Add keyboard shortcut customization feature
claude Jan 25, 2026
d463ae2
Update shortcut-settings-tasks.md with completion status
claude Jan 25, 2026
1bf7d2c
Fix Stylelint and knip issues
claude Jan 25, 2026
0db10ba
Reduce keyboard handler complexity in +page.svelte
claude Jan 25, 2026
6e940e2
Fix Clippy errors and undefined CSS class
claude Jan 25, 2026
95e7afd
Fix Clippy and coverage threshold errors
claude Jan 25, 2026
2004dc0
Fix checks
vdavid Jan 28, 2026
c5f5101
A bunch of Settings window fixes
vdavid Jan 31, 2026
f2d20ba
Wire up file size format and app icons settings
vdavid Jan 31, 2026
3a7f5e9
Wire up all settings to actual functionality
vdavid Jan 31, 2026
57031e8
Settings: A ton of fixes
vdavid Feb 1, 2026
c21c5b3
Settings: Fix up search feature
vdavid Feb 1, 2026
503f0bd
Settings: Fix up shortcut management
vdavid Feb 1, 2026
42c361d
Settings: Make shortcut editing really work
vdavid Feb 1, 2026
6b570b2
Settings: Fix Advanced section
vdavid Feb 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"mcpServers": {
"cmdr": {
"type": "http",
"url": "http://127.0.0.1:9224/mcp"
},
"tauri": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@hypothesi/tauri-mcp-server"
],
"env": {}
}
}
}
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ This snippet will likely come handy:
}
```

Or add it via CLI like:

Since the agent shares the context with your IDE/client, enabling the MCP server makes the tools available to the agent
automatically.

Expand Down
36 changes: 36 additions & 0 deletions apps/desktop/coverage-allowlist.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
"file-explorer/FileIcon.svelte": { "reason": "Simple UI component, display only" },
"file-explorer/FilePane.svelte": { "reason": "Tested in integration.test.ts, complex component" },
"file-explorer/FullList.svelte": { "reason": "Logic tested in full-list-utils.ts, component mounting heavy" },
"file-explorer/full-list-utils.ts": {
"reason": "measureDateColumnWidth depends on Canvas API for text measurement"
},
"file-explorer/NewFolderDialog.svelte": { "reason": "UI modal, logic tested in new-folder-utils.test.ts" },
"file-explorer/NetworkBrowser.svelte": { "reason": "Network component, needs Tauri integration" },
"file-explorer/PaneResizer.svelte": { "reason": "Mouse drag UI component, difficult to unit test" },
Expand All @@ -30,7 +33,40 @@
"network-store.svelte.ts": { "reason": "Depends on Tauri APIs" },
"onboarding/FullDiskAccessPrompt.svelte": { "reason": "UI component" },
"settings-store.ts": { "reason": "Depends on Tauri store APIs" },
"settings/components/SettingNumberInput.svelte": { "reason": "UI component, logic is simple" },
"settings/components/SettingRadioGroup.svelte": { "reason": "UI component, logic is simple" },
"settings/components/SectionSummary.svelte": { "reason": "UI component, simple navigation cards" },
"settings/format-utils.ts": { "reason": "Simple date formatting, low complexity" },
"settings/network-settings.ts": { "reason": "Thin wrapper over getSetting, requires mocking settings store" },
"settings/reactive-settings.svelte.ts": { "reason": "Svelte reactive state, depends on settings store" },
"settings/settings-applier.ts": { "reason": "Depends on DOM APIs (document.documentElement.style)" },
"settings/components/SettingRow.svelte": { "reason": "UI component, layout only" },
"settings/components/SettingSelect.svelte": { "reason": "UI component, logic is simple" },
"settings/components/SettingSlider.svelte": { "reason": "UI component, logic is simple" },
"settings/components/SettingSwitch.svelte": { "reason": "UI component, logic is simple" },
"settings/components/SettingToggleGroup.svelte": { "reason": "UI component, logic is simple" },
"settings/components/SettingsContent.svelte": { "reason": "UI layout component" },
"settings/components/SettingsSidebar.svelte": { "reason": "UI component, search logic tested via registry" },
"settings/sections/AdvancedSection.svelte": { "reason": "UI section, depends on Tauri APIs" },
"settings/sections/AppearanceSection.svelte": { "reason": "UI section, simple rendering" },
"settings/sections/FileOperationsSection.svelte": { "reason": "UI section, simple rendering" },
"settings/sections/KeyboardShortcutsSection.svelte": {
"reason": "UI section, logic tested in shortcuts/*.test.ts"
},
"settings/sections/LoggingSection.svelte": { "reason": "UI section, simple rendering" },
"settings/mcp-settings-bridge.ts": { "reason": "MCP bridge, depends on Tauri APIs and events" },
"settings/sections/McpServerSection.svelte": { "reason": "UI section, depends on Tauri APIs" },
"settings/sections/NetworkSection.svelte": { "reason": "UI section, simple rendering" },
"settings/sections/ThemesSection.svelte": { "reason": "UI section, simple rendering" },
"settings/sections/UpdatesSection.svelte": { "reason": "UI section, simple rendering" },
"settings/settings-store.ts": { "reason": "Depends on Tauri store APIs" },
"settings/settings-window.ts": { "reason": "Depends on Tauri window APIs" },
"shortcuts/conflict-detector.ts": { "reason": "Logic tested via shortcuts.test.ts" },
"shortcuts/mcp-shortcuts-listener.ts": { "reason": "MCP listener, depends on Tauri events" },
"shortcuts/keyboard-handler.ts": { "reason": "DOM event handling, tested via integration" },
"shortcuts/shortcuts-store.ts": { "reason": "Depends on Tauri store APIs" },
"tauri-commands.ts": { "reason": "Tauri command wrappers, tested via integration" },
"utils/confirm-dialog.ts": { "reason": "Thin wrapper over Tauri dialog API" },
"updater.svelte.ts": { "reason": "Depends on Tauri updater APIs" },
"window-state.ts": { "reason": "Depends on Tauri window APIs" },
"write-operations/CopyDialog.svelte": { "reason": "UI modal, logic tested in copy-dialog-utils.test.ts" },
Expand Down
6 changes: 5 additions & 1 deletion apps/desktop/knip.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
"src/routes/+layout.ts",
"src/lib/benchmark.ts",
"src/lib/tauri-commands.ts",
"src/lib/licensing-store.svelte.ts"
"src/lib/licensing-store.svelte.ts",
"src/lib/settings/settings-store.ts",
"src/lib/settings/settings-window.ts",
"src/lib/settings/types.ts",
"src/lib/shortcuts/**"
],
"ignoreDependencies": [
"@tauri-apps/cli",
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@
},
"license": "SEE LICENSE IN LICENSE",
"dependencies": {
"@ark-ui/svelte": "^5.15.0",
"@crabnebula/tauri-plugin-drag": "^2.1.0",
"@leeoniya/ufuzzy": "^1.0.19",
"@logtape/logtape": "^2.0.0",
"@lottiefiles/dotlottie-svelte": "^0.8.12",
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-dialog": "^2.6.0",
"@tauri-apps/plugin-fs": "^2.4.4",
"@tauri-apps/plugin-opener": "^2",
"@tauri-apps/plugin-process": "^2.3.1",
Expand Down
88 changes: 88 additions & 0 deletions apps/desktop/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apps/desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ tauri = { version = "2", features = [] }
tauri-plugin-opener = "2"
tauri-plugin-mcp-bridge = "0.6"
tauri-plugin-store = "2"
tauri-plugin-dialog = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
notify = "8"
Expand Down
3 changes: 2 additions & 1 deletion apps/desktop/src-tauri/benches/icon_benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ fn bench_icon_fetching(c: &mut Criterion) {
BenchmarkId::new("refresh_directory", count),
&(paths.clone(), extensions.clone()),
|b, (dir_paths, exts)| {
b.iter(|| cmdr_lib::icons::refresh_icons_for_directory(dir_paths.clone(), exts.clone()))
// Benchmark with app icons enabled (the more expensive path)
b.iter(|| cmdr_lib::icons::refresh_icons_for_directory(dir_paths.clone(), exts.clone(), true))
},
);
}
Expand Down
16 changes: 16 additions & 0 deletions apps/desktop/src-tauri/capabilities/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "settings",
"description": "Capability for the settings window",
"windows": [
"settings"
],
"permissions": [
"core:window:allow-close",
"core:event:default",
"core:app:allow-set-app-theme",
"core:webview:allow-internal-toggle-devtools",
"store:default",
"dialog:allow-ask"
]
}
7 changes: 5 additions & 2 deletions apps/desktop/src-tauri/src/commands/file_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ pub fn cancel_write_operation(operation_id: String, rollback: bool) {
/// the actual copy operation.
///
/// # Events emitted
/// * `scan-preview-progress` - Every 100ms with current counts
/// * `scan-preview-progress` - Based on progress_interval_ms setting
/// * `scan-preview-complete` - When scanning finishes
/// * `scan-preview-error` - On error
/// * `scan-preview-cancelled` - If cancelled
Expand All @@ -402,15 +402,18 @@ pub fn cancel_write_operation(operation_id: String, rollback: bool) {
/// * `sources` - List of source file/directory paths. Supports tilde expansion (~).
/// * `sort_column` - Column to sort files by.
/// * `sort_order` - Sort order (ascending/descending).
/// * `progress_interval_ms` - Progress update interval in milliseconds (default: 500).
#[tauri::command]
pub fn start_scan_preview(
app: tauri::AppHandle,
sources: Vec<String>,
sort_column: SortColumn,
sort_order: SortOrder,
progress_interval_ms: Option<u64>,
) -> ScanPreviewStartResult {
let sources: Vec<PathBuf> = sources.iter().map(|s| PathBuf::from(expand_tilde(s))).collect();
ops_start_scan_preview(app, sources, sort_column, sort_order)
let progress_interval = progress_interval_ms.unwrap_or(500);
ops_start_scan_preview(app, sources, sort_column, sort_order, progress_interval)
}

/// Cancels a running scan preview.
Expand Down
26 changes: 22 additions & 4 deletions apps/desktop/src-tauri/src/commands/icons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,33 @@ use std::collections::HashMap;
/// Gets icon data URLs for the requested icon IDs.
/// Returns a map of icon_id -> base64 WebP data URL.
/// Only fetches icons not already cached; clients should cache returned icons.
///
/// When `use_app_icons_for_documents` is true and on macOS, extension-based icons
/// are fetched from app bundles (showing the app's icon as fallback). When false,
/// the system's default document icons are used (Finder-style with app badge).
#[tauri::command]
pub fn get_icons(icon_ids: Vec<String>) -> HashMap<String, String> {
icons::get_icons(icon_ids)
pub fn get_icons(icon_ids: Vec<String>, use_app_icons_for_documents: bool) -> HashMap<String, String> {
icons::get_icons(icon_ids, use_app_icons_for_documents)
}

/// Refreshes icons for a directory listing.
/// Fetches icons in parallel for all directories and extensions.
/// Returns all fetched icons (frontend can compare with cache to detect changes).
///
/// When `use_app_icons_for_documents` is true, falls back to app icons for files without
/// document-specific icons. When false, uses Finder-style document icons.
#[tauri::command]
pub fn refresh_directory_icons(directory_paths: Vec<String>, extensions: Vec<String>) -> HashMap<String, String> {
icons::refresh_icons_for_directory(directory_paths, extensions)
pub fn refresh_directory_icons(
directory_paths: Vec<String>,
extensions: Vec<String>,
use_app_icons_for_documents: bool,
) -> HashMap<String, String> {
icons::refresh_icons_for_directory(directory_paths, extensions, use_app_icons_for_documents)
}

/// Clears cached extension icons.
/// Called when the "use app icons for documents" setting changes.
#[tauri::command]
pub fn clear_extension_icon_cache() {
icons::clear_extension_icon_cache();
}
1 change: 1 addition & 0 deletions apps/desktop/src-tauri/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod icons;
pub mod licensing;
#[cfg(target_os = "macos")]
pub mod network;
pub mod settings;
pub mod sync_status; // Has both macOS and non-macOS implementations
pub mod ui;
#[cfg(target_os = "macos")]
Expand Down
Loading
Loading