diff --git a/.github/workflows/frontend-ci.yml b/.github/workflows/frontend-ci.yml index 9a87bf94..a575a9c4 100644 --- a/.github/workflows/frontend-ci.yml +++ b/.github/workflows/frontend-ci.yml @@ -27,7 +27,9 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: 20 + # 22+: the test script passes --no-experimental-webstorage, which Node 20 rejects as an + # unknown option. Node 20 reached end of life in April 2026. + node-version: 24 cache: npm cache-dependency-path: cardinal/package-lock.json diff --git a/AGENTS.md b/AGENTS.md index 4ad23657..c349786f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,8 @@ - Desktop app lives in `cardinal/` (React UI in `src/`, Tauri/native glue in `src-tauri/`, build output in `cardinal/dist/`). - Workspace crates (root `Cargo.toml`): `lsf/` (CLI), `cardinal-sdk/` (shared types), `fswalk/`, `fs-icon/`, `namepool/`, `query-segmentation/`, `search-cache/`, `search-cancel/`, `cardinal-syntax/`, `slab-mmap/` (mmap-backed slab for the cache), `was/` (CLI that streams FSEvents via the SDK). - Tests sit next to code; cross-crate cases belong in each crate’s `tests/` directory. Generated outputs (`target/`, `cardinal/dist/`, vendor bundles) stay out of commits. -- Toolchain pinned via `rust-toolchain.toml` (`nightly-2025-05-09`); install with `rustup toolchain install nightly-2025-05-09`. +- Toolchain pinned via `rust-toolchain.toml` (`nightly-2025-12-11`); install with `rustup toolchain install nightly-2025-12-11`. Let the rustup shim pick it up rather than putting a toolchain's `bin/` on `PATH`: an older rustc still builds here from cache, but the proc-macro dylibs it emits are rejected by recent macOS dyld with `mis-aligned LINKEDIT string pool` as soon as anything has to be rebuilt. +- Keep `target/` out of iCloud-synced folders (`CARGO_TARGET_DIR`). If iCloud evicts a fingerprint file, cargo blocks forever in `read()` waiting for a download that never lands. ## Build, Test, and Development Commands - `cargo check --workspace` — fast compile validation for all crates. diff --git a/cardinal/package-lock.json b/cardinal/package-lock.json index 597030bf..f67b0b67 100644 --- a/cardinal/package-lock.json +++ b/cardinal/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@crabnebula/tauri-plugin-drag": "^2.1.0", "@tauri-apps/api": "^2", + "@tauri-apps/plugin-dialog": "^2.7.2", "@tauri-apps/plugin-global-shortcut": "^2.3.1", "@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-window-state": "^2.4.1", @@ -1348,9 +1349,9 @@ "license": "MIT" }, "node_modules/@tauri-apps/api": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.10.1.tgz", - "integrity": "sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw==", + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.11.1.tgz", + "integrity": "sha512-M2FPuYND2m+wh5hfW9ZpSdxMPdEJovPBWwoHJmwUpysTYNHaOkVFN419m/K0LIgjb/7KU2vBgsUepJWugQCvAA==", "license": "Apache-2.0 OR MIT", "funding": { "type": "opencollective", @@ -1574,6 +1575,15 @@ "node": ">= 10" } }, + "node_modules/@tauri-apps/plugin-dialog": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.7.2.tgz", + "integrity": "sha512-pX0IGm1I3I6wc+zeKYcq1GSqogK6okCNX5fOdaNU5ab1AjGS6l1E5wFNjEb7meg7ZFSp0JUs+0jQGQNyOvLrsg==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.11.0" + } + }, "node_modules/@tauri-apps/plugin-global-shortcut": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-global-shortcut/-/plugin-global-shortcut-2.3.1.tgz", diff --git a/cardinal/package.json b/cardinal/package.json index e5fe8f2d..5a86a199 100644 --- a/cardinal/package.json +++ b/cardinal/package.json @@ -8,8 +8,8 @@ "build": "vite build", "preview": "vite preview", "tauri": "tauri", - "test": "vitest --watch=false", - "test:watch": "vitest --watch", + "test": "NODE_OPTIONS=--no-experimental-webstorage vitest --watch=false", + "test:watch": "NODE_OPTIONS=--no-experimental-webstorage vitest --watch", "typecheck": "tsc --noEmit", "format": "prettier --write .", "format:check": "prettier --check ." @@ -17,6 +17,7 @@ "dependencies": { "@crabnebula/tauri-plugin-drag": "^2.1.0", "@tauri-apps/api": "^2", + "@tauri-apps/plugin-dialog": "^2.7.2", "@tauri-apps/plugin-global-shortcut": "^2.3.1", "@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-window-state": "^2.4.1", diff --git a/cardinal/src-tauri/Cargo.lock b/cardinal/src-tauri/Cargo.lock index e19f1ca0..c067c179 100644 --- a/cardinal/src-tauri/Cargo.lock +++ b/cardinal/src-tauri/Cargo.lock @@ -404,6 +404,7 @@ dependencies = [ "serde_json", "tauri", "tauri-build", + "tauri-plugin-dialog", "tauri-plugin-drag", "tauri-plugin-global-shortcut", "tauri-plugin-macos-permissions", @@ -761,14 +762,20 @@ dependencies = [ [[package]] name = "ctor" -version = "0.2.9" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" +checksum = "352d39c2f7bef1d6ad73db6f5160efcaed66d94ef8c6c573a8410c00bf909a98" dependencies = [ - "quote", - "syn 2.0.117", + "ctor-proc-macro", + "dtor", ] +[[package]] +name = "ctor-proc-macro" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" + [[package]] name = "darling" version = "0.23.0" @@ -803,6 +810,17 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "dbus" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab69f03cc8c4340c9c8e315114e1658e6775a9b16a04357973aa21cec22b32e" +dependencies = [ + "libc", + "libdbus-sys", + "windows-sys 0.61.2", +] + [[package]] name = "deranged" version = "0.5.8" @@ -984,6 +1002,21 @@ dependencies = [ "dtoa", ] +[[package]] +name = "dtor" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1057d6c64987086ff8ed0fd3fbf377a6b7d205cc7715868cd401705f715cbe4" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + [[package]] name = "dunce" version = "1.0.5" @@ -1851,7 +1884,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core 0.58.0", ] [[package]] @@ -1870,7 +1903,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" dependencies = [ "byteorder", - "png", + "png 0.17.16", ] [[package]] @@ -2287,6 +2320,15 @@ version = "0.2.184" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" +[[package]] +name = "libdbus-sys" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043" +dependencies = [ + "pkg-config", +] + [[package]] name = "libloading" version = "0.7.4" @@ -2462,9 +2504,9 @@ dependencies = [ [[package]] name = "muda" -version = "0.17.2" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c9fec5a4e89860383d778d10563a605838f8f0b2f9303868937e5ff32e86177" +checksum = "1dd04e60bc0b07438a6771710ee1698f98f6ebbc7f89b61264af1563b8aeb878" dependencies = [ "crossbeam-channel", "dpi", @@ -2475,10 +2517,10 @@ dependencies = [ "objc2-core-foundation", "objc2-foundation", "once_cell", - "png", + "png 0.18.1", "serde", "thiserror 2.0.18", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -2509,12 +2551,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "ndk-context" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" - [[package]] name = "ndk-sys" version = "0.6.0+11769913" @@ -2680,6 +2716,16 @@ dependencies = [ "objc2-foundation", ] +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + [[package]] name = "objc2-core-services" version = "0.3.2" @@ -2828,9 +2874,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ "bitflags 2.11.0", + "block2", "objc2", + "objc2-cloud-kit", + "objc2-core-data", "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", ] [[package]] @@ -2843,6 +2898,16 @@ dependencies = [ "objc2-foundation", ] +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", +] + [[package]] name = "objc2-web-kit" version = "0.3.2" @@ -2986,7 +3051,6 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_macros 0.11.3", "phf_shared 0.11.3", ] @@ -3085,19 +3149,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "phf_macros" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" -dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "phf_macros" version = "0.13.1" @@ -3196,6 +3247,19 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.11.0", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "polling" version = "3.11.0" @@ -3589,6 +3653,30 @@ dependencies = [ "web-sys", ] +[[package]] +name = "rfd" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672" +dependencies = [ + "block2", + "dispatch2", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.60.2", +] + [[package]] name = "rustc-hash" version = "2.1.2" @@ -4220,15 +4308,16 @@ dependencies = [ [[package]] name = "tao" -version = "0.34.8" +version = "0.35.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9103edf55f2da3c82aea4c7fab7c4241032bfeea0e71fa557d98e00e7ce7cc20" +checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9" dependencies = [ "bitflags 2.11.0", "block2", "core-foundation 0.10.1", "core-graphics 0.25.0", "crossbeam-channel", + "dbus", "dispatch2", "dlopen2", "dpi", @@ -4239,13 +4328,14 @@ dependencies = [ "libc", "log", "ndk", - "ndk-context", "ndk-sys", "objc2", "objc2-app-kit", "objc2-foundation", + "objc2-ui-kit", "once_cell", "parking_lot", + "percent-encoding", "raw-window-handle", "tao-macros", "unicode-segmentation", @@ -4275,9 +4365,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.10.3" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da77cc00fb9028caf5b5d4650f75e31f1ef3693459dfca7f7e506d1ecef0ba2d" +checksum = "b93bd86d231f0a8138f11a02a584769fe4b703dc36ae133d783228dbc4801405" dependencies = [ "anyhow", "bytes", @@ -4326,9 +4416,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.5.6" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bbc990d1dbf57a8e1c7fa2327f2a614d8b757805603c1b9ba5c81bade09fd4d" +checksum = "bc9ce40b16101cb6ea63d3e221567affd1c3a9205f95d7bc574941a10636b632" dependencies = [ "anyhow", "cargo_toml", @@ -4342,22 +4432,21 @@ dependencies = [ "serde_json", "tauri-utils", "tauri-winres", - "toml 0.9.12+spec-1.1.0", "walkdir", ] [[package]] name = "tauri-codegen" -version = "2.5.5" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a24476afd977c5d5d169f72425868613d82747916dd29e0a357c84c4bd6d29" +checksum = "08279169ff42f8fc45a1dbc9dcae888893ba95288142e5880c59b93a26d2cfc5" dependencies = [ "base64 0.22.1", "brotli", "ico", "json-patch", "plist", - "png", + "png 0.17.16", "proc-macro2", "quote", "semver", @@ -4375,9 +4464,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.5.5" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39b349a98dadaffebb73f0a40dcd1f23c999211e5a2e744403db384d0c33de7" +checksum = "e8b394794f399a421811d06966343e7933fcae92d59f5180b9388d1174497a45" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -4404,6 +4493,24 @@ dependencies = [ "walkdir", ] +[[package]] +name = "tauri-plugin-dialog" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2d3c1dbe38037e7f590cdf2492594d5ceebe031e7bc7e827509b22a999d2940" +dependencies = [ + "log", + "raw-window-handle", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.18", + "url", +] + [[package]] name = "tauri-plugin-drag" version = "2.1.0" @@ -4419,6 +4526,30 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "tauri-plugin-fs" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ecc274121aca0c036a2b42d1cbe83d368d348f54e0bb8a735c2b1548e8f371" +dependencies = [ + "anyhow", + "dunce", + "glob", + "log", + "objc2-foundation", + "percent-encoding", + "schemars 0.8.22", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.18", + "toml 1.1.2+spec-1.1.0", + "url", +] + [[package]] name = "tauri-plugin-global-shortcut" version = "2.3.1" @@ -4502,9 +4633,9 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.10.1" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2826d79a3297ed08cd6ea7f412644ef58e32969504bc4fbd8d7dbeabc4445ea2" +checksum = "b0b4bc95aed361b0019067d189a1174a603d460d0f6c72606512d59fc9c12ec8" dependencies = [ "cookie", "dpi", @@ -4527,9 +4658,9 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "2.10.1" +version = "2.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e11ea2e6f801d275fdd890d6c9603736012742a1c33b96d0db788c9cdebf7f9e" +checksum = "4e6fac707727b7a2f48e4ded90976324267371073edbb415ffb73bb0458d203f" dependencies = [ "gtk", "http", @@ -4553,14 +4684,15 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.8.3" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219a1f983a2af3653f75b5747f76733b0da7ff03069c7a41901a5eb3ace4557d" +checksum = "3e176a18e67764923c4f1ce66f25ae4abe5f688384d5eb1a0fa6c77f3d90f887" dependencies = [ "anyhow", "brotli", "cargo_metadata", "ctor", + "dom_query", "dunce", "glob", "html5ever 0.29.1", @@ -4570,7 +4702,8 @@ dependencies = [ "kuchikiki", "log", "memchr", - "phf 0.11.3", + "phf 0.13.1", + "plist", "proc-macro2", "quote", "regex", @@ -4582,7 +4715,7 @@ dependencies = [ "serde_with", "swift-rs", "thiserror 2.0.18", - "toml 0.9.12+spec-1.1.0", + "toml 1.1.2+spec-1.1.0", "url", "urlpattern", "uuid", @@ -4802,6 +4935,21 @@ dependencies = [ "winnow 0.7.15", ] +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap 2.13.1", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.1", +] + [[package]] name = "toml_datetime" version = "0.6.3" @@ -4988,9 +5136,9 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.21.3" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e85aa143ceb072062fc4d6356c1b520a51d636e7bc8e77ec94be3608e5e80c" +checksum = "15edbb0d80583e85ee8df283410038e17314df5cba30da2087a54a85216c0773" dependencies = [ "crossbeam-channel", "dirs", @@ -5002,10 +5150,10 @@ dependencies = [ "objc2-core-graphics", "objc2-foundation", "once_cell", - "png", + "png 0.18.1", "serde", "thiserror 2.0.18", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5569,19 +5717,6 @@ dependencies = [ "windows-strings 0.4.2", ] -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement 0.60.2", - "windows-interface 0.59.3", - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - [[package]] name = "windows-future" version = "0.2.1" @@ -5699,15 +5834,6 @@ dependencies = [ "windows-link 0.1.3", ] -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link 0.2.1", -] - [[package]] name = "windows-strings" version = "0.1.0" @@ -5727,15 +5853,6 @@ dependencies = [ "windows-link 0.1.3", ] -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link 0.2.1", -] - [[package]] name = "windows-sys" version = "0.45.0" @@ -6109,9 +6226,9 @@ checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "wry" -version = "0.54.4" +version = "0.55.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a8135d8676225e5744de000d4dff5a082501bf7db6a1c1495034f8c314edbc" +checksum = "186f9871daa55fd9c016578b810d149de58367113db7fb72b462d2323ce19514" dependencies = [ "base64 0.22.1", "block2", diff --git a/cardinal/src-tauri/Cargo.toml b/cardinal/src-tauri/Cargo.toml index 386b1944..d4b09d92 100644 --- a/cardinal/src-tauri/Cargo.toml +++ b/cardinal/src-tauri/Cargo.toml @@ -22,6 +22,7 @@ tauri = { version = "2", features = ["tray-icon"] } tauri-plugin-opener = "2" tauri-plugin-global-shortcut = "2" tauri-plugin-drag = "2" +tauri-plugin-dialog = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" crossbeam-channel = "0.5.15" diff --git a/cardinal/src-tauri/capabilities/default.json b/cardinal/src-tauri/capabilities/default.json index ce8f857a..ce95cafe 100644 --- a/cardinal/src-tauri/capabilities/default.json +++ b/cardinal/src-tauri/capabilities/default.json @@ -12,6 +12,7 @@ "drag:default", "global-shortcut:allow-is-registered", "global-shortcut:allow-register", - "global-shortcut:allow-unregister" + "global-shortcut:allow-unregister", + "dialog:allow-open" ] } diff --git a/cardinal/src-tauri/src/commands.rs b/cardinal/src-tauri/src/commands.rs index ae863611..f3300cf0 100644 --- a/cardinal/src-tauri/src/commands.rs +++ b/cardinal/src-tauri/src/commands.rs @@ -19,8 +19,10 @@ use objc2::{ use objc2_app_kit::{NSPasteboard, NSPasteboardItem, NSPasteboardTypeString, NSPasteboardWriting}; use objc2_foundation::{NSArray, NSString, NSURL}; use parking_lot::Mutex; +use rayon::prelude::*; use search_cache::{ SearchOptions, SearchOutcome, SearchQuery, SearchResultNode, SlabIndex, SlabNodeMetadata, + content_snippet, content_terms_of_query, }; use search_cancel::CancellationToken; use serde::{Deserialize, Serialize}; @@ -217,6 +219,8 @@ pub struct NodeInfo { pub path: String, pub metadata: Option, pub icon: Option, + #[serde(rename = "contentContext")] + pub content_context: Option, } #[derive(Serialize, Default)] @@ -224,6 +228,9 @@ pub struct NodeInfo { pub struct SearchResponse { pub results: Vec, pub highlights: Vec, + /// `content:` terms of this query, so the UI highlights in a snippet exactly what the search + /// looked for inside files. + pub content_terms: Vec, pub status_code: u8, } @@ -291,6 +298,10 @@ pub async fn search( search_activity::note_search_activity(); let options = options.unwrap_or_default(); + let content_terms = query + .as_deref() + .map(content_terms_of_query) + .unwrap_or_default(); let cancellation_token = CancellationToken::new_search(); let (result_tx, result_rx) = bounded(1); if let Err(e) = state.search_tx.send(SearchJob { @@ -325,6 +336,7 @@ pub async fn search( SearchResponse { results, highlights, + content_terms, status_code, } }) @@ -335,6 +347,8 @@ pub async fn search( pub fn get_nodes_info( results: Vec, include_icons: Option, + content_terms: Option>, + case_insensitive: Option, state: State<'_, SearchState>, ) -> Vec { if results.is_empty() { @@ -342,11 +356,17 @@ pub fn get_nodes_info( } let include_icons = include_icons.unwrap_or(true); + let content_terms = content_terms.unwrap_or_default(); + let case_insensitive = case_insensitive.unwrap_or_default(); let nodes = state.request_nodes(results); + // Rows are independent, and each one may read a file for its icon and its content snippet. nodes - .into_iter() + .into_par_iter() .map(|SearchResultNode { path, metadata }| { + let content_context = content_terms + .iter() + .find_map(|term| content_snippet(&path, term, case_insensitive)); let path = path.to_string_lossy().into_owned(); let icon = if include_icons { fs_icon::icon_of_path_ns(&path).map(|data| { @@ -362,6 +382,7 @@ pub fn get_nodes_info( path, icon, metadata: metadata.as_ref().map(NodeInfoMetadata::from_metadata), + content_context, } }) .collect() diff --git a/cardinal/src-tauri/src/lib.rs b/cardinal/src-tauri/src/lib.rs index 920840c0..57fa33c0 100644 --- a/cardinal/src-tauri/src/lib.rs +++ b/cardinal/src-tauri/src/lib.rs @@ -79,6 +79,7 @@ pub fn run() -> Result<()> { builder = builder .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_drag::init()) + .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_global_shortcut::Builder::new().build()) .plugin(tauri_plugin_macos_permissions::init()) .plugin(tauri_plugin_window_state::Builder::new().build()) diff --git a/cardinal/src/App.css b/cardinal/src/App.css index 5d6af384..784e2cea 100644 --- a/cardinal/src/App.css +++ b/cardinal/src/App.css @@ -117,6 +117,98 @@ main, height: 100vh; } +/* === Reordenar columnas arrastrando el título === */ +.header-cell { + cursor: grab; +} + +.header-cell--dragging { + opacity: 0.45; +} + +/* La línea marca dónde va a caer la columna. */ +.header-cell--drop-target { + box-shadow: inset 2px 0 0 var(--color-accent); +} + +/* El asa de redimensionar vive dentro de la celda arrastrable: sin esto, tomarla arrastra + la columna entera en vez de cambiarle el ancho. */ +.col-resizer { + cursor: col-resize; +} + +/* === Campo "Contiene": busca dentro de los archivos === */ +.content-search-segment { + flex: 0 1 min(15rem, 26vw); + min-width: 6rem; + gap: 0; + padding-left: 0.6rem; + overflow: hidden; +} + +.content-search-label { + flex: 0 0 auto; + padding-right: 0.4rem; + color: var(--color-muted); + font-size: 0.78rem; + white-space: nowrap; + border-right: 1px solid var(--color-border); + cursor: text; +} + +#content-search-input { + height: 100%; + width: 100%; + min-width: 0; + padding: 0.45rem 0.55rem; + border: none; + background: transparent; + box-shadow: none; + font-size: 0.92rem; + overflow: hidden; +} + +#content-search-input:focus { + outline: none; +} + +#content-search-input::placeholder { + color: var(--color-muted); +} + +#content-search-input:disabled { + cursor: not-allowed; + font-style: italic; +} + +/* Cierra el campo de carpeta; el icono de carpeta ahora abre el selector del sistema. */ +.directory-scope-close { + display: inline-flex; + align-items: center; + justify-content: center; + flex: 0 0 1.1rem; + width: 1.1rem; + height: var(--search-segment-height); + padding: 0; + background: transparent; + color: var(--color-muted); + cursor: pointer; +} + +.directory-scope-close svg { + width: 0.62rem; + height: 0.62rem; + fill: none; + stroke: currentColor; + stroke-width: 2.4; + stroke-linecap: round; + stroke-linejoin: round; +} + +.directory-scope-close:hover { + color: var(--color-text); +} + /* === Search Bar === */ .search-container { margin: var(--container-padding) var(--container-padding) 0; @@ -356,6 +448,57 @@ main, font-size: 0.78rem; } +/* Native +
+ {fileTypeEnabled ? ( + + ) : null}