You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Search + Select: one-click Files/Folders type filter, folder-size matching, and a leaner chip strip
Adds a `Both | Files | Folders` toggle to both query dialogs so users can constrain
results to files or folders in one click, makes the size filter work on folders by
their recursive size, and removes the "+ Add filter" affordance (all filters are now
always visible).
- **Type filter (M4).** New cross-consumer core state `typeFilter` (`both`/`file`/`folder`, default `both`) in `createQueryFilterState()`. It maps to the EXISTING IPC `SearchQuery.isDirectory: Option<bool>` in `buildBaseSearchQuery` (`both → null`, `file → false`, `folder → true`): no new `SearchQuery` field, no engine change. Rendered as a `ToggleGroup` leading the shared `FilterChips` strip, so both Search and Select show it.
- **Select matcher.** `selection-matching.ts` gains a `type` predicate + a `getIsDirFor` accessor. `SelectionDialog` now builds its `MatchAccessors` through a single `buildAccessors()` helper used at BOTH the `runQuery` preview and `commitMatches` sites, so preview and commit can't drift. `getSizeFor` returns `entry.size` for files and `entry.recursiveSize` for directories (index-derived; `undefined` until the index computes it, in which case the folder honestly can't match a size bound). `hasActiveFilter()` now counts a non-`both` type filter, so a type-only query (empty bar) runs.
- **History round-trip.** Additive `is_directory: Option<bool>` on the Rust `HistoryFilters` (shared by Search and Select) with `#[serde(default)]`, so recent-items history round-trips the type filter with NO schema bump (old files load as `None`). Added to both canonical dedupe keys. Bindings regenerated.
- **MCP prefill.** `open_search_dialog` accepts an optional `isDirectory` (true = folders, false = files), threaded through `SearchPrefill` / `applySearchPrefill`.
- **Remove "+ Add filter".** Deleted the trailing add-filter chip + dropdown from `FilterChips`; all filters are always visible now.
Verified: the live Select snapshot (`getFileRange`) carries `recursiveSize` for dirs because listing entries are enriched at cache-write time, so no backend enrichment was needed.
TDD: red→green for the matcher type predicate and folder-size fallback; new tests for the `typeFilter → isDirectory` mapping, the history round-trip (asserting no schema bump), the toggle render/selection + a11y, and the removed Add-filter menu.
Copy file name to clipboardExpand all lines: apps/desktop/src-tauri/src/mcp/DETAILS.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -34,7 +34,7 @@ Expose Cmdr functionality to AI agents via the Model Context Protocol (MCP). Age
34
34
- File operations (6): `copy`, `move`, `delete`, `mkdir`, `mkfile`, `refresh` (a round-trip that forces a backend re-read of the focused pane's listing — local volumes re-read from disk; watcher-backed MTP/SMB listings short-circuit). `copy`/`move`/`delete` fast-fail with the real cause when there's nothing to act on (no selection and the cursor is on `..`, or the pane shows no files) instead of a misleading ack timeout. `copy`/`move` accept optional `autoConfirm` (bool) and `onConflict` (`skip_all`|`overwrite_all`|`rename_all`). `onConflict` governs clashing **files only** — folders always merge (a source folder landing on a same-named dest folder merges into it; the policy then applies to the files inside). `delete` accepts optional `autoConfirm`. When `autoConfirm` is true, the dialog opens and immediately confirms.
- Tabs (1): `tab` (unified: `action` = `new` | `close` | `close_others` | `activate` | `set_pinned`; `tabId` defaults to active tab for close/close_others/set_pinned, required for activate; `pinned` boolean for set_pinned)
37
-
- Dialogs (2): `dialog` (unified open/focus/close/confirm). `action: "confirm"` programmatically confirms an open dialog. For `transfer-confirmation`: accepts optional `onConflict`. For `delete-confirmation`: just confirms. `type: "transfer-confirmation"` is the primary name (covers copy and move); `"copy-confirmation"` is accepted as an alias. `open_search_dialog` opens the whole-drive search overlay with optional pre-filled `query`, `mode` (`ai`/`filename`/`regex`), `sizeMin`/`sizeMax` (bytes), `modifiedAfter`/`modifiedBefore` (ISO date), `scope` (chip syntax), `caseSensitive`, `excludeSystemDirs`, and `autoRun` (default true: runs the search after open). Acks on `SoftDialogAppeared("search")` within the 1500 ms budget. **Race-with-close caveat**: if the dialog is mid-close when the event lands, the new mount may race; the ack times out and the tool surfaces a clean failure rather than a false-positive OK (per plan §5.7 risk register).
37
+
- Dialogs (2): `dialog` (unified open/focus/close/confirm). `action: "confirm"` programmatically confirms an open dialog. For `transfer-confirmation`: accepts optional `onConflict`. For `delete-confirmation`: just confirms. `type: "transfer-confirmation"` is the primary name (covers copy and move); `"copy-confirmation"` is accepted as an alias. `open_search_dialog` opens the whole-drive search overlay with optional pre-filled `query`, `mode` (`ai`/`filename`/`regex`), `sizeMin`/`sizeMax` (bytes), `modifiedAfter`/`modifiedBefore` (ISO date), `isDirectory` (true = folders only, false = files only, omit for both), `scope` (chip syntax), `caseSensitive`, `excludeSystemDirs`, and `autoRun` (default true: runs the search after open). Acks on `SoftDialogAppeared("search")` within the 1500 ms budget. **Race-with-close caveat**: if the dialog is mid-close when the event lands, the new mount may race; the ack times out and the tool surfaces a clean failure rather than a false-positive OK (per plan §5.7 risk register).
38
38
- App (3): `switch_pane`, `swap_panes`, `quit`
39
39
- Search (2): `search` (structured file search across the drive index, optional `scope` for path/exclude filtering), `ai_search` (natural language search using configured LLM, optional `scope` merged with AI-inferred scope)
40
40
- Settings (1): `set_setting` (change a setting value via round-trip to frontend)
// The type filter is an additive `#[serde(default)]` field: a new value serializes
666
+
// and deserializes cleanly, AND an old file missing the key still loads (as `None`),
667
+
// all on schema v1. This pins "no schema bump needed".
668
+
assert_eq!(CURRENT_SCHEMA_VERSION,1,"M4's type filter must NOT bump the schema");
669
+
670
+
letmut e = entry(HistoryMode::Filename,"*.png");
671
+
e.filters.is_directory = Some(true);
672
+
let json = serde_json::to_string(&e).unwrap();
673
+
assert!(json.contains("\"isDirectory\":true"));
674
+
let back:HistoryEntry = serde_json::from_str(&json).unwrap();
675
+
assert_eq!(back.filters.is_directory,Some(true));
676
+
677
+
// An old entry (filters object with no `isDirectory` key) loads as `None`.
678
+
let legacy = r#"{"id":"x","timestamp":1,"mode":"filename","query":"*.png","filters":{"sizeMin":1024},"scope":"","caseSensitive":false,"excludeSystemDirs":true,"resultCount":0}"#;
679
+
let parsed:HistoryEntry = serde_json::from_str(legacy).unwrap();
0 commit comments