diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..197c6b7 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,60 @@ +name: Release + +# Cut a release by pushing a version tag, e.g. +# git tag v2.0.1 && git push origin v2.0.1 +on: + push: + tags: + - 'v*' + workflow_dispatch: + +jobs: + release: + permissions: + contents: write + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Set up Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: aarch64-apple-darwin,x86_64-apple-darwin + + - name: Cache cargo + uses: Swatinem/rust-cache@v2 + with: + workspaces: src-tauri + + - name: Install frontend dependencies + run: npm install + + - name: Build, sign, notarize & publish + uses: tauri-apps/tauri-action@v0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # --- Code signing (Developer ID Application) --- + APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} + # --- Notarization (Apple ID + app-specific password) --- + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + # --- Updater artifact signing --- + TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} + TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + with: + tagName: ${{ github.ref_name }} + releaseName: 'RocketNote ${{ github.ref_name }}' + releaseBody: 'Download the .dmg below. The app is signed and notarized; existing installs update automatically.' + releaseDraft: true + prerelease: false + # Universal binary (Apple Silicon + Intel). tauri-action emits the + # signed updater bundle + latest.json and attaches them to the release. + args: --target universal-apple-darwin diff --git a/.gitignore b/.gitignore index 819dd69..4e44592 100644 --- a/.gitignore +++ b/.gitignore @@ -31,5 +31,7 @@ backup-*/ icon.iconset/ fix-icons.sh generate-icons.sh -src-tauri/Entitlements.plist src-tauri/Info.plist + +# Tauri v2 generated schemas +src-tauri/gen/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e218e9f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,30 @@ +# Changelog + +All notable changes to RocketNote are documented here. +Format based on [Keep a Changelog](https://keepachangelog.com/). + +## [Unreleased] + +### Added +- **CodeMirror 6 editor** with true viewport virtualization — multi-megabyte logs and minified bundles open without freezing the UI. +- **Auto-updater** — signed update channel served from GitHub Releases (Tauri updater + `latest.json`). +- **Signed & notarized macOS builds** — GitHub Actions release pipeline (`.github/workflows/release.yml`) with Developer ID signing and notarization. See [docs/distribution-setup.md](docs/distribution-setup.md). +- **PKCE (S256)** on the cloud OAuth authorization-code flow for all providers. + +### Changed +- **Migrated to Tauri 2** — capabilities/ACL permission model, split plugins, v2 menu API. +- Production bundle is **code-split** into cacheable vendor chunks (app-shell entry ~174 kB instead of a single ~1.7 MB chunk). +- Secrets are stored in the macOS Keychain via the **native Security framework** instead of the `security` CLI, so secret values never appear on the process command line. + +### Security +- **Git:** every git invocation is hardened (`GIT_CONFIG_NOSYSTEM`, `core.fsmonitor` / `core.hooksPath` disabled, `ext`/`file` transports blocked) — opening an untrusted repository can no longer execute code via its `.git/config`. +- **Terminal:** script interpreters removed from the quick-run allowlist; `pty_write_force` only accepts confirmation keystrokes for a pending danger prompt (one-shot). +- **Filesystem:** snippet/macro ids are sanitized; `validate_path` denies credential locations (`~/.ssh`, `~/.aws`, `~/.gnupg`, `~/Library/Keychains`, …); `file_exists` is scope-checked; read and global-search size caps prevent memory-exhaustion hangs. +- **Cloud / Git:** OAuth token material is redacted from error messages and logs; git branch/ref names are guarded against option injection. + +### Fixed +- License metadata in `Cargo.toml` aligned to GPL-3.0 (matches `LICENSE`). +- Broken README header image. + +## [2.0.0] +- Initial public release — encryption, cloud sync, AI assistant, Git, terminal, global search, developer tools. diff --git a/README.md b/README.md index 48ce52b..885b6b8 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ Most code editors either collect your data or require an internet connection. Ro - **Military-grade encryption** — AES-256-GCM with Argon2id key derivation - **100% local** — your files never leave your device unless you choose to sync - **Native performance** — built with Rust and Tauri, not Electron +- **Signed, notarized & auto-updating** — verified macOS builds that update themselves over a signed channel --- @@ -53,12 +54,12 @@ Most code editors either collect your data or require an internet connection. Ro Explain, improve, fix bugs, refactor code — all with your own API key stored in macOS Keychain. -### Code Editor +### Code Editor (CodeMirror 6) +- **Viewport virtualization** — opens multi-megabyte logs and minified bundles without freezing - Syntax highlighting for 30+ languages -- Minimap with virtualized rendering -- Bracket matching, word wrap, line numbers (absolute & relative) +- Minimap, bracket matching, word wrap, line numbers - Split view and focus mode -- Undo/redo with full history stack +- Native undo/redo history, find & replace ### Git Integration (⌘⇧G) - Stage, commit, push, pull @@ -128,18 +129,19 @@ Download the latest `.dmg` from [Releases](https://github.com/Anic888/rocketnote ### Build from Source -**Prerequisites:** macOS 10.13+, [Node.js](https://nodejs.org/) 18+, [Rust](https://rustup.rs/), Tauri CLI v1 +**Prerequisites:** macOS 10.13+, [Node.js](https://nodejs.org/) 18+, [Rust](https://rustup.rs/) ```bash git clone https://github.com/Anic888/rocketnote.git cd rocketnote -npm install -cargo install tauri-cli --version "^1" +npm install # also installs the Tauri 2 CLI -cargo tauri dev # development -cargo tauri build # production .app +npm run tauri dev # development +npm run tauri build # production .app ``` +Signing, notarization and the auto-updater are wired in `.github/workflows/release.yml` — see [docs/distribution-setup.md](docs/distribution-setup.md). + --- ## Tech Stack @@ -147,7 +149,8 @@ cargo tauri build # production .app | Layer | Technology | |-------|-----------| | Frontend | React 18, TypeScript | -| Backend | Rust, Tauri 1.5 | +| Editor | CodeMirror 6 (virtualized) | +| Backend | Rust, Tauri 2 | | Terminal | portable-pty | | Encryption | AES-256-GCM, Argon2id | | Search | Rayon (parallel), ignore (gitignore) | diff --git a/app-icon.png b/app-icon.png new file mode 100644 index 0000000..6484aee Binary files /dev/null and b/app-icon.png differ diff --git a/docs/distribution-setup.md b/docs/distribution-setup.md new file mode 100644 index 0000000..02b5652 --- /dev/null +++ b/docs/distribution-setup.md @@ -0,0 +1,84 @@ +# RocketNote — Distribution Setup (signing · notarization · auto-update) + +This closes audit findings **C4** (unsigned/un-notarized macOS build) and **H3** (no update channel). Everything below is wired in `.github/workflows/release.yml`; you only need to add the GitHub **secrets** and cut a tag. + +> The build is signed + notarized **in CI** with your Apple credentials. Nothing here can be verified on a machine without your Apple Developer account, so treat this as a checklist, not a finished build. + +--- + +## 1. One-time: Apple code-signing identity + +1. In the Apple Developer portal create a **Developer ID Application** certificate (this is the cert for apps distributed *outside* the App Store). +2. In **Keychain Access** → export it (with its private key) as a `.p12` file, set an export password. +3. Base64-encode it for the secret: + ```sh + base64 -i DeveloperID.p12 | pbcopy + ``` +4. Find the exact identity string: + ```sh + security find-identity -v -p codesigning + # e.g. "Developer ID Application: Your Name (TEAMID1234)" + ``` + +## 2. One-time: notarization credentials + +- Use your **Apple ID** + an **app-specific password** (appleid.apple.com → Sign-In and Security → App-Specific Passwords). +- Your **Team ID** is the 10-char code in the identity above / in the developer portal membership page. + +## 3. Updater signing key — already generated ✅ + +A Tauri updater keypair was generated for you: + +- **Public key** is already committed in `src-tauri/tauri.conf.json` → `updater.pubkey`. +- **Private key** is at `~/.tauri/rocketnote_updater.key` on this machine (it was created with an **empty** password). + +Add its *contents* as the `TAURI_PRIVATE_KEY` secret: +```sh +cat ~/.tauri/rocketnote_updater.key | pbcopy +``` +> Prefer to control the key yourself? Regenerate with +> `cargo tauri signer generate --ci -w ~/.tauri/rocketnote_updater.key -f`, +> then paste the **new** public key into `updater.pubkey`. Keep the private key out of git. + +## 4. GitHub repository secrets + +Settings → Secrets and variables → Actions → **New repository secret**: + +| Secret | Value | +|---|---| +| `APPLE_CERTIFICATE` | base64 of the `.p12` (step 1.3) | +| `APPLE_CERTIFICATE_PASSWORD` | the `.p12` export password | +| `APPLE_SIGNING_IDENTITY` | `Developer ID Application: Your Name (TEAMID)` | +| `APPLE_ID` | your Apple ID email | +| `APPLE_PASSWORD` | the app-specific password | +| `APPLE_TEAM_ID` | your 10-char Team ID | +| `TAURI_PRIVATE_KEY` | contents of `~/.tauri/rocketnote_updater.key` | +| `TAURI_KEY_PASSWORD` | empty string (the key has no password) | + +`GITHUB_TOKEN` is provided automatically — do not add it. + +## 5. Cut a release + +```sh +git tag v2.0.1 +git push origin v2.0.1 +``` + +The workflow builds a **universal** (Apple Silicon + Intel) binary, signs it with Developer ID, notarizes + staples it, and creates a **draft** GitHub Release with: +- `RocketNote_*.dmg` — the installer users download; +- `RocketNote.app.tar.gz` + `RocketNote.app.tar.gz.sig` — the signed update bundle; +- `latest.json` — the updater manifest. + +Review the draft, then **Publish**. Publishing makes it the `latest` release, which is exactly what `updater.endpoints` points to: +`https://github.com/Anic888/rocketnote/releases/latest/download/latest.json`. + +## 6. How auto-update works after this + +On launch the app fetches `latest.json`, compares versions, verifies the bundle signature against the embedded `pubkey`, and (because `updater.dialog = true`) prompts the user to install. Ship a fix by bumping the version in `package.json` + `src-tauri/tauri.conf.json` and pushing a new tag. + +## Notes / gotchas + +- Bump the version in **both** `package.json` and `src-tauri/tauri.conf.json` (`package.version`) before tagging; the tag and the config version should match. +- First notarization can take a few minutes; tauri-action waits for it. +- If notarization fails on "hardened runtime" entitlements, the exceptions live in `src-tauri/Entitlements.plist`. +- `package-lock.json` is git-ignored, so CI uses `npm install` (not `npm ci`). diff --git a/package.json b/package.json index 6deb859..bcbcb28 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,34 @@ "tauri": "tauri" }, "dependencies": { - "@tauri-apps/api": "^1.5.3", + "@codemirror/commands": "^6.10.4", + "@codemirror/lang-cpp": "^6.0.3", + "@codemirror/lang-css": "^6.3.1", + "@codemirror/lang-html": "^6.4.11", + "@codemirror/lang-java": "^6.0.2", + "@codemirror/lang-javascript": "^6.2.5", + "@codemirror/lang-json": "^6.0.2", + "@codemirror/lang-markdown": "^6.5.0", + "@codemirror/lang-php": "^6.0.2", + "@codemirror/lang-python": "^6.2.1", + "@codemirror/lang-rust": "^6.0.2", + "@codemirror/lang-sql": "^6.10.0", + "@codemirror/lang-xml": "^6.1.0", + "@codemirror/lang-yaml": "^6.1.3", + "@codemirror/language": "^6.12.3", + "@codemirror/search": "^6.7.1", + "@codemirror/state": "^6.7.0", + "@codemirror/theme-one-dark": "^6.1.3", + "@codemirror/view": "^6.43.2", + "@tauri-apps/api": "^2.11.1", + "@tauri-apps/plugin-clipboard-manager": "^2.3.2", + "@tauri-apps/plugin-dialog": "^2.7.1", + "@tauri-apps/plugin-fs": "^2.5.1", + "@tauri-apps/plugin-http": "^2.5.9", + "@tauri-apps/plugin-notification": "^2.3.3", + "@tauri-apps/plugin-shell": "^2.3.5", + "@tauri-apps/plugin-updater": "^2.10.1", + "codemirror": "^6.0.2", "html-to-image": "^1.11.11", "lucide-react": "^0.577.0", "react": "^18.2.0", @@ -23,7 +50,7 @@ "xterm-addon-web-links": "^0.9.0" }, "devDependencies": { - "@tauri-apps/cli": "^1.5.14", + "@tauri-apps/cli": "^2.11.3", "@types/react": "^18.2.55", "@types/react-dom": "^18.2.19", "@vitejs/plugin-react": "^4.2.1", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index f4ef02b..b147c6a 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -60,9 +60,9 @@ checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" [[package]] name = "alloc-stdlib" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" dependencies = [ "alloc-no-stdlib", ] @@ -78,9 +78,18 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.101" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arbitrary" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] [[package]] name = "arboard" @@ -89,7 +98,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" dependencies = [ "clipboard-win", - "image 0.25.9", + "image", "log", "objc2", "objc2-app-kit", @@ -141,9 +150,9 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.3" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" dependencies = [ "async-task", "concurrent-queue", @@ -208,14 +217,14 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", ] [[package]] name = "async-signal" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" dependencies = [ "async-io", "async-lock", @@ -243,31 +252,30 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", ] [[package]] name = "atk" -version = "0.15.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" dependencies = [ "atk-sys", - "bitflags 1.3.2", "glib", "libc", ] [[package]] name = "atk-sys" -version = "0.15.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58aeb089fb698e06db8089971c7ee317ab9644bade33383f63631437b03aafb6" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.2.2", + "system-deps", ] [[package]] @@ -278,15 +286,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "base64" -version = "0.13.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "base64" @@ -306,6 +308,21 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitflags" version = "1.3.2" @@ -314,9 +331,12 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +dependencies = [ + "serde_core", +] [[package]] name = "blake2" @@ -327,12 +347,6 @@ dependencies = [ "digest", ] -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - [[package]] name = "block-buffer" version = "0.10.4" @@ -366,9 +380,9 @@ dependencies = [ [[package]] name = "brotli" -version = "7.0.0" +version = "8.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -377,14 +391,23 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.3" +version = "5.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a334ef7c9e23abf0ce748e8cd309037da93e606ad52eb372e4ce327a0dcfbdfd" +checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", ] +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + [[package]] name = "bstr" version = "1.12.1" @@ -397,9 +420,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytemuck" @@ -421,52 +444,85 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" dependencies = [ "serde", ] [[package]] name = "cairo-rs" -version = "0.15.12" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.13.0", "cairo-sys-rs", "glib", "libc", + "once_cell", "thiserror 1.0.69", ] [[package]] name = "cairo-sys-rs" -version = "0.15.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" dependencies = [ "glib-sys", "libc", - "system-deps 6.2.2", + "system-deps", +] + +[[package]] +name = "camino" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce8d3bd5823c7504d3f579f13e7b2f3da252fcb938c594d5680ee508bf846f" +dependencies = [ + "serde_core", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.18", ] [[package]] name = "cargo_toml" -version = "0.15.3" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "599aa35200ffff8f04c1925aa1acc92fa2e08874379ef42e210a80e527e60838" +checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" dependencies = [ "serde", - "toml 0.7.8", + "toml 0.9.12+spec-1.1.0", ] [[package]] name = "cc" -version = "1.2.55" +version = "1.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" dependencies = [ "find-msvc-tools", "shlex", @@ -489,15 +545,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "cfg-expr" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3431df59f28accaf4cb4eed4a9acc66bea3f3c3753aa6cdc2f024174ef232af7" -dependencies = [ - "smallvec", -] - [[package]] name = "cfg-expr" version = "0.15.8" @@ -514,11 +561,17 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" -version = "0.4.43" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", "js-sys", @@ -547,42 +600,6 @@ dependencies = [ "error-code", ] -[[package]] -name = "cocoa" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" -dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" -dependencies = [ - "bitflags 1.3.2", - "block", - "core-foundation", - "core-graphics-types", - "libc", - "objc", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - [[package]] name = "combine" version = "4.6.7" @@ -603,10 +620,33 @@ dependencies = [ ] [[package]] -name = "convert_case" -version = "0.4.0" +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206" +dependencies = [ + "cookie", + "document-features", + "idna", + "log", + "publicsuffix", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] [[package]] name = "core-foundation" @@ -618,6 +658,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -626,25 +676,25 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core-graphics" -version = "0.22.3" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" dependencies = [ - "bitflags 1.3.2", - "core-foundation", + "bitflags 2.13.0", + "core-foundation 0.10.1", "core-graphics-types", - "foreign-types", + "foreign-types 0.5.0", "libc", ] [[package]] name = "core-graphics-types" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 1.3.2", - "core-foundation", + "bitflags 2.13.0", + "core-foundation 0.10.1", "libc", ] @@ -719,19 +769,15 @@ dependencies = [ [[package]] name = "cssparser" -version = "0.27.2" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" dependencies = [ "cssparser-macros", "dtoa-short", - "itoa 0.4.8", - "matches", - "phf 0.8.0", - "proc-macro2", - "quote", + "itoa", + "phf", "smallvec", - "syn 1.0.109", ] [[package]] @@ -741,19 +787,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.114", + "syn 2.0.118", ] [[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.114", + "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 = "ctr" version = "0.9.2" @@ -765,9 +817,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.21.3" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" dependencies = [ "darling_core", "darling_macro", @@ -775,50 +827,84 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.21.3" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" dependencies = [ - "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.114", + "syn 2.0.118", ] [[package]] name = "darling_macro" -version = "0.21.3" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core", "quote", - "syn 2.0.114", + "syn 2.0.118", +] + +[[package]] +name = "data-url" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" + +[[package]] +name = "dbus" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b942602992bb7acfd1f51c49811c58a610ef9181b6e66f3e519d79b540a3bf73" +dependencies = [ + "libc", + "libdbus-sys", + "windows-sys 0.61.2", ] [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ - "powerfmt", "serde_core", ] +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "derive_more" -version = "0.99.20" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "convert_case", "proc-macro2", "quote", "rustc_version", - "syn 2.0.114", + "syn 2.0.118", ] [[package]] @@ -838,17 +924,16 @@ version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "dirs-sys", + "dirs-sys 0.4.1", ] [[package]] -name = "dirs-next" -version = "2.0.0" +name = "dirs" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "cfg-if", - "dirs-sys-next", + "dirs-sys 0.5.0", ] [[package]] @@ -859,46 +944,90 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.4.6", "windows-sys 0.48.0", ] [[package]] -name = "dirs-sys-next" -version = "0.1.2" +name = "dirs-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", - "redox_users", - "winapi", + "option-ext", + "redox_users 0.5.2", + "windows-sys 0.61.2", ] -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - [[package]] name = "dispatch2" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.13.0", + "block2", + "libc", "objc2", ] [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "dlopen2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "dom_query" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89" +dependencies = [ + "bit-set", + "cssparser", + "foldhash 0.2.0", + "html5ever", + "precomputed-hash", + "selectors", + "tendril", ] [[package]] @@ -907,6 +1036,15 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +dependencies = [ + "serde", +] + [[package]] name = "dtoa" version = "1.0.11" @@ -922,6 +1060,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" @@ -936,22 +1089,22 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "embed-resource" -version = "2.5.2" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d506610004cfc74a6f5ee7e8c632b355de5eca1f03ee5e5e0ec11b77d4eb3d61" +checksum = "c31a88c8d26de40ed18fe748c547845aa39de1db3afd958f8cb91579f3644bcb" dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.8.23", + "toml 1.1.2+spec-1.1.0", "vswhom", - "winreg 0.52.0", + "winreg 0.55.0", ] [[package]] @@ -993,7 +1146,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", ] [[package]] @@ -1002,6 +1155,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + [[package]] name = "errno" version = "0.3.14" @@ -1041,33 +1205,19 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "fax" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" -dependencies = [ - "fax_derive", -] +checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" [[package]] -name = "fax_derive" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "fdeflate" -version = "0.3.7" +name = "fdeflate" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" dependencies = [ @@ -1097,13 +1247,12 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.27" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" dependencies = [ "cfg-if", "libc", - "libredox", ] [[package]] @@ -1128,15 +1277,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "fluent-uri" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1149,13 +1289,40 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared", + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", ] [[package]] @@ -1164,6 +1331,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1173,36 +1346,26 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "futf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] - [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -1211,9 +1374,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-lite" @@ -1230,32 +1393,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", ] [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", "futures-io", @@ -1264,26 +1427,15 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - [[package]] name = "gdk" -version = "0.15.4" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" dependencies = [ - "bitflags 1.3.2", "cairo-rs", "gdk-pixbuf", "gdk-sys", @@ -1295,35 +1447,35 @@ dependencies = [ [[package]] name = "gdk-pixbuf" -version = "0.15.11" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" dependencies = [ - "bitflags 1.3.2", "gdk-pixbuf-sys", "gio", "glib", "libc", + "once_cell", ] [[package]] name = "gdk-pixbuf-sys" -version = "0.15.10" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "140b2f5378256527150350a8346dbdb08fadc13453a7a2d73aecd5fab3c402a7" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" dependencies = [ "gio-sys", "glib-sys", "gobject-sys", "libc", - "system-deps 6.2.2", + "system-deps", ] [[package]] name = "gdk-sys" -version = "0.15.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e7a08c1e8f06f4177fb7e51a777b8c1689f743a7bc11ea91d44d2226073a88" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -1333,47 +1485,48 @@ dependencies = [ "libc", "pango-sys", "pkg-config", - "system-deps 6.2.2", + "system-deps", ] [[package]] name = "gdkwayland-sys" -version = "0.15.3" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cca49a59ad8cfdf36ef7330fe7bdfbe1d34323220cc16a0de2679ee773aee2c2" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" dependencies = [ "gdk-sys", "glib-sys", "gobject-sys", "libc", "pkg-config", - "system-deps 6.2.2", + "system-deps", ] [[package]] -name = "gdkx11-sys" -version = "0.15.1" +name = "gdkx11" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b7f8c7a84b407aa9b143877e267e848ff34106578b64d1e0a24bf550716178" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" dependencies = [ - "gdk-sys", - "glib-sys", + "gdk", + "gdkx11-sys", + "gio", + "glib", "libc", - "system-deps 6.2.2", "x11", ] [[package]] -name = "generator" -version = "0.7.5" +name = "gdkx11-sys" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" dependencies = [ - "cc", + "gdk-sys", + "glib-sys", "libc", - "log", - "rustversion", - "windows 0.48.0", + "system-deps", + "x11", ] [[package]] @@ -1398,36 +1551,40 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", + "js-sys", "libc", - "wasi 0.9.0+wasi-snapshot-preview1", + "wasi", + "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.2.17" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "r-efi 5.3.0", + "wasip2", + "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "libc", - "r-efi", - "wasip2", + "r-efi 6.0.0", ] [[package]] @@ -1442,49 +1599,54 @@ dependencies = [ [[package]] name = "gio" -version = "0.15.12" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68fdbc90312d462781a395f7a16d96a2b379bb6ef8cd6310a2df272771c4283b" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" dependencies = [ - "bitflags 1.3.2", "futures-channel", "futures-core", "futures-io", + "futures-util", "gio-sys", "glib", "libc", "once_cell", + "pin-project-lite", + "smallvec", "thiserror 1.0.69", ] [[package]] name = "gio-sys" -version = "0.15.10" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32157a475271e2c4a023382e9cab31c4584ee30a97da41d3c4e9fdd605abcf8d" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.2.2", + "system-deps", "winapi", ] [[package]] name = "glib" -version = "0.15.12" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.13.0", "futures-channel", "futures-core", "futures-executor", "futures-task", + "futures-util", + "gio-sys", "glib-macros", "glib-sys", "gobject-sys", "libc", + "memchr", "once_cell", "smallvec", "thiserror 1.0.69", @@ -1492,27 +1654,26 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.15.13" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10c6ae9f6fa26f4fb2ac16b528d138d971ead56141de489f8111e259b9df3c4a" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" dependencies = [ - "anyhow", "heck 0.4.1", - "proc-macro-crate 1.3.1", + "proc-macro-crate 2.0.2", "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.118", ] [[package]] name = "glib-sys" -version = "0.15.10" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" dependencies = [ "libc", - "system-deps 6.2.2", + "system-deps", ] [[package]] @@ -1536,23 +1697,22 @@ dependencies = [ [[package]] name = "gobject-sys" -version = "0.15.10" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" dependencies = [ "glib-sys", "libc", - "system-deps 6.2.2", + "system-deps", ] [[package]] name = "gtk" -version = "0.15.5" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e3004a2d5d6d8b5057d2b57b3712c9529b62e82c77f25c1fecde1fd5c23bd0" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" dependencies = [ "atk", - "bitflags 1.3.2", "cairo-rs", "field-offset", "futures-channel", @@ -1563,16 +1723,15 @@ dependencies = [ "gtk-sys", "gtk3-macros", "libc", - "once_cell", "pango", "pkg-config", ] [[package]] name = "gtk-sys" -version = "0.15.3" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5bc2f0587cba247f60246a0ca11fe25fb733eabc3de12d1965fc07efab87c84" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" dependencies = [ "atk-sys", "cairo-sys-rs", @@ -1583,21 +1742,20 @@ dependencies = [ "gobject-sys", "libc", "pango-sys", - "system-deps 6.2.2", + "system-deps", ] [[package]] name = "gtk3-macros" -version = "0.15.6" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684c0456c086e8e7e9af73ec5b84e35938df394712054550e81558d21c44ab0d" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" dependencies = [ - "anyhow", "proc-macro-crate 1.3.1", "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.118", ] [[package]] @@ -1611,8 +1769,27 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", - "indexmap 2.13.0", + "http 0.2.12", + "indexmap 2.14.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.4.2", + "indexmap 2.14.0", "slab", "tokio", "tokio-util", @@ -1642,23 +1819,14 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "foldhash", + "foldhash 0.1.5", ] [[package]] name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" - -[[package]] -name = "heck" -version = "0.3.3" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "heck" @@ -1686,16 +1854,12 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "html5ever" -version = "0.26.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +checksum = "1054432bae2f14e0061e33d23402fbaa67a921d319d56adc6bcf887ddad1cbc2" dependencies = [ "log", - "mac", "markup5ever", - "proc-macro2", - "quote", - "syn 1.0.109", ] [[package]] @@ -1706,7 +1870,17 @@ checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", - "itoa 1.0.17", + "itoa", +] + +[[package]] +name = "http" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +dependencies = [ + "bytes", + "itoa", ] [[package]] @@ -1716,15 +1890,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.12", "pin-project-lite", ] [[package]] -name = "http-range" -version = "0.1.5" +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.4.2", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http 1.4.2", + "http-body 1.0.1", + "pin-project-lite", +] [[package]] name = "httparse" @@ -1748,12 +1939,12 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", - "itoa 1.0.17", + "itoa", "pin-project-lite", "socket2 0.5.10", "tokio", @@ -1762,6 +1953,43 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2 0.4.15", + "http 1.4.2", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http 1.4.2", + "hyper 1.10.1", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -1769,12 +1997,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper", + "hyper 0.14.32", "native-tls", "tokio", "tokio-native-tls", ] +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-util", + "http 1.4.2", + "http-body 1.0.1", + "hyper 1.10.1", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2 0.6.4", + "system-configuration 0.7.0", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + [[package]] name = "iana-time-zone" version = "0.1.65" @@ -1801,9 +2054,9 @@ dependencies = [ [[package]] name = "ico" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98" +checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" dependencies = [ "byteorder", "png 0.17.16", @@ -1811,12 +2064,13 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -1824,9 +2078,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -1837,9 +2091,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -1851,15 +2105,15 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ "icu_collections", "icu_locale_core", @@ -1871,15 +2125,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", @@ -1909,9 +2163,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -1919,9 +2173,9 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.25" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a" +checksum = "b915661dd01db3f05050265b2477bcc6527b3792388e2749b41623cc592be67d" dependencies = [ "crossbeam-deque", "globset", @@ -1935,27 +2189,15 @@ dependencies = [ [[package]] name = "image" -version = "0.24.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "num-traits", -] - -[[package]] -name = "image" -version = "0.25.9" +version = "0.25.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" dependencies = [ "bytemuck", "byteorder-lite", "moxcms", "num-traits", - "png 0.18.0", + "png 0.18.1", "tiff", ] @@ -1972,21 +2214,21 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.1", "serde", "serde_core", ] [[package]] name = "infer" -version = "0.13.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f551f8c3a39f68f986517db0d1759de85881894fdc7db798bd2a9df9cb04b7fc" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" dependencies = [ "cfb", ] @@ -2000,15 +2242,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "ioctl-rs" version = "0.1.6" @@ -2020,9 +2253,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.11.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] name = "is-docker" @@ -2045,21 +2278,15 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "javascriptcore-rs" -version = "0.16.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf053e7843f2812ff03ef5afe34bb9c06ffee120385caad4f6b9967fcd37d41c" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" dependencies = [ "bitflags 1.3.2", "glib", @@ -2068,51 +2295,106 @@ dependencies = [ [[package]] name = "javascriptcore-rs-sys" -version = "0.4.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "905fbb87419c5cde6e3269537e4ea7d46431f3008c5d057e915ef3f115e7793c" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 5.0.0", + "system-deps", ] [[package]] name = "jni" -version = "0.20.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" dependencies = [ "cesu8", + "cfg-if", "combine", - "jni-sys", + "jni-sys 0.3.1", "log", "thiserror 1.0.69", "walkdir", + "windows-sys 0.45.0", ] [[package]] -name = "jni-sys" -version = "0.3.0" +name = "jni" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - -[[package]] -name = "js-sys" -version = "0.3.85" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys 0.4.1", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "windows-link 0.2.1", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" dependencies = [ - "once_cell", + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.118", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.118", +] + +[[package]] +name = "js-sys" +version = "0.3.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" +dependencies = [ + "cfg-if", + "futures-util", "wasm-bindgen", ] [[package]] name = "json-patch" -version = "2.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" dependencies = [ "jsonptr", "serde", @@ -2122,26 +2404,35 @@ dependencies = [ [[package]] name = "jsonptr" -version = "0.4.7" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c6e529149475ca0b2820835d3dce8fcc41c6b943ca608d32f35b449255e4627" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" dependencies = [ - "fluent-uri", "serde", "serde_json", ] [[package]] -name = "kuchikiki" -version = "0.8.2" +name = "keyboard-types" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" dependencies = [ - "cssparser", - "html5ever", - "indexmap 1.9.3", - "matches", - "selectors", + "bitflags 2.13.0", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "keyring" +version = "3.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c" +dependencies = [ + "log", + "security-framework 2.11.1", + "security-framework 3.7.0", + "zeroize", ] [[package]] @@ -2150,34 +2441,81 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading", + "once_cell", +] + [[package]] name = "libc" -version = "0.2.180" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] [[package]] name = "libredox" -version = "0.1.12" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" dependencies = [ - "bitflags 2.10.0", "libc", - "redox_syscall 0.7.0", ] [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "lock_api" @@ -2190,81 +2528,41 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "loom" -version = "0.5.6" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" -dependencies = [ - "cfg-if", - "generator", - "scoped-tls", - "serde", - "serde_json", - "tracing", - "tracing-subscriber", -] +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] -name = "mac" -version = "0.1.1" +name = "lru-slab" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "mac-notification-sys" -version = "0.6.9" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fd3f75411f4725061682ed91f131946e912859d0044d39c4ec0aac818d7621" +checksum = "fd604973958ddcc11b561193c0fb96ba146506ef2f231ef2e7c35fd2cbc9beca" dependencies = [ "cc", + "log", "objc2", "objc2-foundation", "time", -] - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", + "uuid", ] [[package]] name = "markup5ever" -version = "0.11.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +checksum = "8983d30f2915feeaaab2d6babdd6bc7e9ed1a00b66b5e6d74df19aa9c0e91862" dependencies = [ "log", - "phf 0.10.1", - "phf_codegen 0.10.0", - "string_cache", - "string_cache_codegen", "tendril", + "web_atoms", ] -[[package]] -name = "matchers" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" -dependencies = [ - "regex-automata", -] - -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - [[package]] name = "md5" version = "0.7.0" @@ -2273,9 +2571,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" [[package]] name = "memoffset" @@ -2286,15 +2584,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] - [[package]] name = "memoffset" version = "0.9.1" @@ -2310,6 +2599,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minisign-verify" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f9645cb765ea72b8111f36c522475d2daa0d22c957a9826437e97534bc4e9e" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -2322,30 +2617,51 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", "windows-sys 0.61.2", ] [[package]] name = "moxcms" -version = "0.7.11" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" dependencies = [ "num-traits", "pxfm", ] +[[package]] +name = "muda" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd04e60bc0b07438a6771710ee1698f98f6ebbc7f89b61264af1563b8aeb878" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "once_cell", + "png 0.18.1", + "serde", + "thiserror 2.0.18", + "windows-sys 0.61.2", +] + [[package]] name = "native-tls" -version = "0.2.14" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" dependencies = [ "libc", "log", @@ -2353,37 +2669,33 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "security-framework", + "security-framework 3.7.0", "security-framework-sys", "tempfile", ] [[package]] name = "ndk" -version = "0.6.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 1.3.2", - "jni-sys", + "bitflags 2.13.0", + "jni-sys 0.3.1", + "log", "ndk-sys", "num_enum", + "raw-window-handle", "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.3.0" +version = "0.6.0+11769913" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" dependencies = [ - "jni-sys", + "jni-sys 0.3.1", ] [[package]] @@ -2406,24 +2718,6 @@ dependencies = [ "pin-utils", ] -[[package]] -name = "nix" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset 0.7.1", -] - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - [[package]] name = "nom" version = "8.0.0" @@ -2441,21 +2735,29 @@ dependencies = [ "argon2", "base64 0.21.7", "chrono", - "dirs", + "dirs 5.0.1", "ignore", + "keyring", "lazy_static", "md5", - "open 5.3.3", + "open", "portable-pty", - "rand 0.8.5", + "rand 0.8.6", "rayon", "regex", - "reqwest", + "reqwest 0.11.27", "serde", "serde_json", "sha2", "tauri", "tauri-build", + "tauri-plugin-clipboard-manager", + "tauri-plugin-dialog", + "tauri-plugin-fs", + "tauri-plugin-http", + "tauri-plugin-notification", + "tauri-plugin-shell", + "tauri-plugin-updater", "tempfile", "tokio", "urlencoding", @@ -2465,9 +2767,9 @@ dependencies = [ [[package]] name = "notify-rust" -version = "4.12.0" +version = "4.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21af20a1b50be5ac5861f74af1a863da53a11c38684d9818d82f1c42f7fdc6c2" +checksum = "c5b4c1b4f2aa9f25f63a7a49d3dd0ed567b3670da15330a66b29434be899b891" dependencies = [ "futures-lite", "log", @@ -2477,20 +2779,11 @@ dependencies = [ "zbus", ] -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "num-conv" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-traits" @@ -2503,64 +2796,68 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.11" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" dependencies = [ "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.5.11" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.118", ] [[package]] -name = "objc" -version = "0.2.7" +name = "objc2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" dependencies = [ - "malloc_buf", - "objc_exception", + "objc2-encode", + "objc2-exception-helper", ] [[package]] -name = "objc-foundation" -version = "0.1.1" +name = "objc2-app-kit" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "block", - "objc", - "objc_id", + "bitflags 2.13.0", + "block2", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", ] [[package]] -name = "objc2" -version = "0.6.3" +name = "objc2-cloud-kit" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" dependencies = [ - "objc2-encode", + "bitflags 2.13.0", + "objc2", + "objc2-foundation", ] [[package]] -name = "objc2-app-kit" +name = "objc2-core-data" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" dependencies = [ - "bitflags 2.10.0", "objc2", - "objc2-core-graphics", "objc2-foundation", ] @@ -2570,7 +2867,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.13.0", "dispatch2", "objc2", ] @@ -2581,26 +2878,67 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.13.0", "dispatch2", "objc2", "objc2-core-foundation", "objc2-io-surface", ] +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "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-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + [[package]] name = "objc2-encode" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" +[[package]] +name = "objc2-exception-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" +dependencies = [ + "cc", +] + [[package]] name = "objc2-foundation" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.13.0", "block2", "libc", "objc2", @@ -2613,57 +2951,99 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.13.0", "objc2", "objc2-core-foundation", ] [[package]] -name = "objc_exception" -version = "0.1.2" +name = "objc2-osa-kit" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0" dependencies = [ - "cc", + "bitflags 2.13.0", + "objc2", + "objc2-app-kit", + "objc2-foundation", ] [[package]] -name = "objc_id" -version = "0.1.1" +name = "objc2-quartz-core" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ - "objc", + "bitflags 2.13.0", + "objc2", + "objc2-core-foundation", + "objc2-foundation", ] [[package]] -name = "once_cell" -version = "1.21.3" +name = "objc2-ui-kit" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags 2.13.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]] -name = "opaque-debug" -version = "0.3.1" +name = "objc2-user-notifications" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", +] [[package]] -name = "open" -version = "3.2.0" +name = "objc2-web-kit" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8" +checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f" dependencies = [ - "pathdiff", - "windows-sys 0.42.0", + "bitflags 2.13.0", + "block2", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", ] +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "open" -version = "5.3.3" +version = "5.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43bb73a7fa3799b198970490a51174027ba0d4ec504b03cd08caf513d40024bc" +checksum = "2fbaa89d2ddc8473c78a3adf69eea8cffa28c483b8e02a971ef31527cd0fc92c" dependencies = [ + "dunce", "is-wsl", "libc", "pathdiff", @@ -2671,15 +3051,14 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.75" +version = "0.10.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.13.0", "cfg-if", - "foreign-types", + "foreign-types 0.3.2", "libc", - "once_cell", "openssl-macros", "openssl-sys", ] @@ -2692,20 +3071,20 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", ] [[package]] name = "openssl-probe" -version = "0.1.6" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-sys" -version = "0.9.111" +version = "0.9.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" dependencies = [ "cc", "libc", @@ -2739,13 +3118,27 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "osakit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b" +dependencies = [ + "objc2", + "objc2-foundation", + "objc2-osa-kit", + "serde", + "serde_json", + "thiserror 2.0.18", +] + [[package]] name = "pango" -version = "0.15.10" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" dependencies = [ - "bitflags 1.3.2", + "gio", "glib", "libc", "once_cell", @@ -2754,14 +3147,14 @@ dependencies = [ [[package]] name = "pango-sys" -version = "0.15.10" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2a00081cde4661982ed91d80ef437c20eacaf6aa1a5962c0279ae194662c3aa" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.2.2", + "system-deps", ] [[package]] @@ -2788,7 +3181,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.18", + "redox_syscall", "smallvec", "windows-link 0.2.1", ] @@ -2824,148 +3217,67 @@ checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" dependencies = [ "fixedbitset", "hashbrown 0.15.5", - "indexmap 2.13.0", -] - -[[package]] -name = "phf" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" -dependencies = [ - "phf_macros 0.8.0", - "phf_shared 0.8.0", - "proc-macro-hack", -] - -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_shared 0.10.0", + "indexmap 2.14.0", ] [[package]] name = "phf" -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", -] - -[[package]] -name = "phf_codegen" -version = "0.8.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", + "phf_macros", + "phf_shared", + "serde", ] [[package]] name = "phf_codegen" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", -] - -[[package]] -name = "phf_generator" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" -dependencies = [ - "phf_shared 0.8.0", - "rand 0.7.3", -] - -[[package]] -name = "phf_generator" -version = "0.10.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" dependencies = [ - "phf_shared 0.10.0", - "rand 0.8.5", + "phf_generator", + "phf_shared", ] [[package]] name = "phf_generator" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" -dependencies = [ - "phf_shared 0.11.3", - "rand 0.8.5", -] - -[[package]] -name = "phf_macros" -version = "0.8.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn 1.0.109", + "fastrand", + "phf_shared", ] [[package]] name = "phf_macros" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", + "phf_generator", + "phf_shared", "proc-macro2", "quote", - "syn 2.0.114", -] - -[[package]] -name = "phf_shared" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" -dependencies = [ - "siphasher 0.3.11", + "syn 2.0.118", ] [[package]] name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher 0.3.11", -] - -[[package]] -name = "phf_shared" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" dependencies = [ - "siphasher 1.0.2", + "siphasher", ] [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pin-utils" @@ -2975,9 +3287,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piper" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" dependencies = [ "atomic-waker", "fastrand", @@ -2986,19 +3298,19 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "plist" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" +checksum = "092791278e026273c1b65bbdcfbba3a300f2994c896bd01ab01da613c29c46f1" dependencies = [ "base64 0.22.1", - "indexmap 2.13.0", - "quick-xml 0.38.4", + "indexmap 2.14.0", + "quick-xml 0.39.4", "serde", "time", ] @@ -3018,11 +3330,11 @@ dependencies = [ [[package]] name = "png" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.13.0", "crc32fast", "fdeflate", "flate2", @@ -3068,7 +3380,7 @@ dependencies = [ "lazy_static", "libc", "log", - "nix 0.25.1", + "nix", "serial", "shared_library", "shell-words", @@ -3078,9 +3390,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "zerovec", ] @@ -3110,19 +3422,29 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" name = "proc-macro-crate" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" dependencies = [ - "once_cell", - "toml_edit 0.19.15", + "toml_datetime 0.6.3", + "toml_edit 0.20.2", ] [[package]] name = "proc-macro-crate" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.23.10+spec-1.0.0", + "toml_edit 0.25.12+spec-1.1.0", ] [[package]] @@ -3149,12 +3471,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - [[package]] name = "proc-macro2" version = "1.0.106" @@ -3165,14 +3481,27 @@ dependencies = [ ] [[package]] -name = "pxfm" -version = "0.1.27" +name = "psl-types" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + +[[package]] +name = "publicsuffix" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" dependencies = [ - "num-traits", + "idna", + "psl-types", ] +[[package]] +name = "pxfm" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" + [[package]] name = "quick-error" version = "2.0.1" @@ -3190,18 +3519,73 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.38.4" +version = "0.39.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" dependencies = [ "memchr", ] +[[package]] +name = "quinn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2 0.6.4", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.4", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.6.4", + "tracing", + "windows-sys 0.60.2", +] + [[package]] name = "quote" -version = "1.0.44" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" dependencies = [ "proc-macro2", ] @@ -3213,24 +3597,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] -name = "rand" -version = "0.7.3" +name = "r-efi" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", - "rand_pcg", -] +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -3238,13 +3614,13 @@ dependencies = [ ] [[package]] -name = "rand_chacha" -version = "0.2.2" +name = "rand" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_chacha 0.9.0", + "rand_core 0.9.5", ] [[package]] @@ -3258,12 +3634,13 @@ dependencies = [ ] [[package]] -name = "rand_core" -version = "0.5.1" +name = "rand_chacha" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ - "getrandom 0.1.16", + "ppv-lite86", + "rand_core 0.9.5", ] [[package]] @@ -3276,34 +3653,25 @@ dependencies = [ ] [[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" +name = "rand_core" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "rand_core 0.5.1", + "getrandom 0.3.4", ] [[package]] name = "raw-window-handle" -version = "0.5.2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "rayon" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" dependencies = [ "either", "rayon-core", @@ -3325,27 +3693,29 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.13.0", ] [[package]] -name = "redox_syscall" -version = "0.7.0" +name = "redox_users" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "bitflags 2.10.0", + "getrandom 0.2.17", + "libredox", + "thiserror 1.0.69", ] [[package]] name = "redox_users" -version = "0.4.6" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.17", "libredox", - "thiserror 1.0.69", + "thiserror 2.0.18", ] [[package]] @@ -3365,14 +3735,14 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", ] [[package]] name = "regex" -version = "1.12.3" +version = "1.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" dependencies = [ "aho-corasick", "memchr", @@ -3393,9 +3763,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "reqwest" @@ -3408,10 +3778,10 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2", - "http", - "http-body", - "hyper", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", "hyper-tls", "ipnet", "js-sys", @@ -3425,44 +3795,144 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", - "system-configuration", + "sync_wrapper 0.1.2", + "system-configuration 0.5.1", "tokio", "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg 0.50.0", +] + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "cookie", + "cookie_store", + "encoding_rs", + "futures-core", + "h2 0.4.15", + "http 1.4.2", + "http-body 1.0.1", + "http-body-util", + "hyper 1.10.1", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", +] + +[[package]] +name = "reqwest" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http 1.4.2", + "http-body 1.0.1", + "http-body-util", + "hyper 1.10.1", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "sync_wrapper 1.0.2", + "tokio", + "tokio-rustls", "tokio-util", + "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "winreg 0.50.0", ] [[package]] name = "rfd" -version = "0.10.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0149778bd99b6959285b0933288206090c50e2327f47a9c463bfdbf45c8823ea" +checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672" dependencies = [ - "block", - "dispatch", + "block2", + "dispatch2", "glib-sys", "gobject-sys", "gtk-sys", "js-sys", - "lazy_static", "log", - "objc", - "objc-foundation", - "objc_id", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", "raw-window-handle", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows 0.37.0", + "windows-sys 0.60.2", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", ] +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + [[package]] name = "rustc_version" version = "0.4.1" @@ -3474,17 +3944,43 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.13.0", "errno", "libc", "linux-raw-sys", "windows-sys 0.61.2", ] +[[package]] +name = "rustls" +version = "0.23.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework 3.7.0", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -3494,6 +3990,54 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni 0.22.4", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework 3.7.0", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -3502,9 +4046,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "same-file" @@ -3517,13 +4061,28 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "indexmap 1.9.3", + "schemars_derive", + "serde", + "serde_json", + "url", + "uuid", +] + [[package]] name = "schemars" version = "0.9.0" @@ -3549,10 +4108,16 @@ dependencies = [ ] [[package]] -name = "scoped-tls" -version = "1.0.1" +name = "schemars_derive" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.118", +] [[package]] name = "scopeguard" @@ -3566,8 +4131,21 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.10.0", - "core-foundation", + "bitflags 2.13.0", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.13.0", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -3575,9 +4153,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -3585,29 +4163,28 @@ dependencies = [ [[package]] name = "selectors" -version = "0.22.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +checksum = "c5d9c0c92a92d33f08817311cf3f2c29a3538a8240e94a6a3c622ce652d7e00c" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.13.0", "cssparser", "derive_more", - "fxhash", "log", - "matches", - "phf 0.8.0", - "phf_codegen 0.8.0", + "new_debug_unreachable", + "phf", + "phf_codegen", "precomputed-hash", + "rustc-hash", "servo_arc", "smallvec", - "thin-slice", ] [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" dependencies = [ "serde", "serde_core", @@ -3623,6 +4200,18 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-untagged" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -3640,17 +4229,27 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", ] [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ - "indexmap 2.13.0", - "itoa 1.0.17", + "itoa", "memchr", "serde", "serde_core", @@ -3665,7 +4264,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", ] [[package]] @@ -3677,6 +4276,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -3684,22 +4292,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.17", + "itoa", "ryu", "serde", ] [[package]] name = "serde_with" -version = "3.16.1" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" dependencies = [ "base64 0.22.1", + "bs58", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.13.0", + "indexmap 2.14.0", "schemars 0.9.0", "schemars 1.2.1", "serde_core", @@ -3710,14 +4319,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.16.1" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", ] [[package]] @@ -3781,16 +4390,15 @@ checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", ] [[package]] name = "servo_arc" -version = "0.1.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" dependencies = [ - "nodrop", "stable_deref_trait", ] @@ -3805,15 +4413,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - [[package]] name = "shared_child" version = "1.1.1" @@ -3843,9 +4442,9 @@ checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77" [[package]] name = "shlex" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "sigchld" @@ -3880,21 +4479,31 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] -name = "siphasher" -version = "0.3.11" +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "siphasher" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "slab" @@ -3904,9 +4513,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "socket2" @@ -3920,40 +4529,60 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] -name = "soup2" -version = "0.2.1" +name = "softbuffer" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b4d76501d8ba387cf0fefbe055c3e0a59891d09f0f995ae4e4b16f6b60f3c0" +checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" dependencies = [ - "bitflags 1.3.2", + "bytemuck", + "js-sys", + "ndk", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "objc2-quartz-core", + "raw-window-handle", + "redox_syscall", + "tracing", + "wasm-bindgen", + "web-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", "gio", "glib", "libc", - "once_cell", - "soup2-sys", + "soup3-sys", ] [[package]] -name = "soup2-sys" -version = "0.2.0" +name = "soup3-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "009ef427103fcb17f802871647a7fa6c60cbb654b4c4e4c0ac60a31c5f6dc9cf" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" dependencies = [ - "bitflags 1.3.2", "gio-sys", "glib-sys", "gobject-sys", "libc", - "system-deps 5.0.0", + "system-deps", ] [[package]] @@ -3962,36 +4591,26 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" -[[package]] -name = "state" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b" -dependencies = [ - "loom", -] - [[package]] name = "string_cache" -version = "0.8.9" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" dependencies = [ "new_debug_unreachable", "parking_lot", - "phf_shared 0.11.3", + "phf_shared", "precomputed-hash", - "serde", ] [[package]] name = "string_cache_codegen" -version = "0.5.4" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", + "phf_generator", + "phf_shared", "proc-macro2", "quote", ] @@ -4008,6 +4627,17 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "swift-rs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + [[package]] name = "syn" version = "1.0.109" @@ -4015,15 +4645,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", - "quote", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.114" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", @@ -4036,6 +4665,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + [[package]] name = "synstructure" version = "0.13.2" @@ -4044,7 +4682,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", ] [[package]] @@ -4054,8 +4692,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", + "core-foundation 0.9.4", + "system-configuration-sys 0.5.0", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.13.0", + "core-foundation 0.9.4", + "system-configuration-sys 0.6.0", ] [[package]] @@ -4069,16 +4718,13 @@ dependencies = [ ] [[package]] -name = "system-deps" -version = "5.0.0" +name = "system-configuration-sys" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18db855554db7bd0e73e06cf7ba3df39f97812cb11d3f75e71c39bf45171797e" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ - "cfg-expr 0.9.1", - "heck 0.3.3", - "pkg-config", - "toml 0.5.11", - "version-compare 0.0.11", + "core-foundation-sys", + "libc", ] [[package]] @@ -4087,57 +4733,50 @@ version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" dependencies = [ - "cfg-expr 0.15.8", + "cfg-expr", "heck 0.5.0", "pkg-config", - "toml 0.8.23", - "version-compare 0.2.1", + "toml 0.8.2", + "version-compare", ] [[package]] name = "tao" -version = "0.16.10" +version = "0.35.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d298c441a1da46e28e8ad8ec205aab7fd8cd71b9d10e05454224eef422e1ae" +checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9" dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "cc", - "cocoa", - "core-foundation", + "bitflags 2.13.0", + "block2", + "core-foundation 0.10.1", "core-graphics", "crossbeam-channel", - "dispatch", - "gdk", - "gdk-pixbuf", - "gdk-sys", + "dbus", + "dispatch2", + "dlopen2", + "dpi", "gdkwayland-sys", "gdkx11-sys", - "gio", - "glib", - "glib-sys", "gtk", - "image 0.24.9", - "instant", - "jni", - "lazy_static", + "jni 0.21.1", "libc", "log", "ndk", - "ndk-context", "ndk-sys", - "objc", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", "once_cell", "parking_lot", - "png 0.17.16", + "percent-encoding", "raw-window-handle", - "scopeguard", - "serde", "tao-macros", "unicode-segmentation", - "uuid", - "windows 0.39.0", - "windows-implement 0.39.0", + "url", + "windows", + "windows-core 0.61.2", + "windows-version", "x11-dl", ] @@ -4149,14 +4788,14 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", ] [[package]] name = "tar" -version = "0.4.44" +version = "0.4.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" dependencies = [ "filetime", "libc", @@ -4171,202 +4810,385 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "1.8.3" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae1f57c291a6ab8e1d2e6b8ad0a35ff769c9925deb8a89de85425ff08762d0c" +checksum = "c2616f96cb644bf2c5c456d9de4d5d5100e592d7424c74d8b55c5cb96e359e93" dependencies = [ "anyhow", "bytes", - "cocoa", - "dirs-next", + "cookie", + "dirs 6.0.0", "dunce", "embed_plist", - "encoding_rs", - "flate2", - "futures-util", - "getrandom 0.2.17", - "glib", + "getrandom 0.3.4", "glob", "gtk", "heck 0.5.0", - "http", - "ignore", - "indexmap 1.9.3", + "http 1.4.2", + "jni 0.21.1", + "libc", "log", - "nix 0.26.4", - "notify-rust", - "objc", - "once_cell", - "open 3.2.0", - "os_pipe", + "mime", + "muda", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", "percent-encoding", "plist", - "rand 0.8.5", "raw-window-handle", + "reqwest 0.13.4", + "serde", + "serde_json", + "serde_repr", + "serialize-to-javascript", + "swift-rs", + "tauri-build", + "tauri-macros", + "tauri-runtime", + "tauri-runtime-wry", + "tauri-utils", + "thiserror 2.0.18", + "tokio", + "tray-icon", + "url", + "webkit2gtk", + "webview2-com", + "window-vibrancy", + "windows", +] + +[[package]] +name = "tauri-build" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc9ce40b16101cb6ea63d3e221567affd1c3a9205f95d7bc574941a10636b632" +dependencies = [ + "anyhow", + "cargo_toml", + "dirs 6.0.0", + "glob", + "heck 0.5.0", + "json-patch", + "schemars 0.8.22", + "semver", + "serde", + "serde_json", + "tauri-utils", + "tauri-winres", + "walkdir", +] + +[[package]] +name = "tauri-codegen" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08279169ff42f8fc45a1dbc9dcae888893ba95288142e5880c59b93a26d2cfc5" +dependencies = [ + "base64 0.22.1", + "brotli", + "ico", + "json-patch", + "plist", + "png 0.17.16", + "proc-macro2", + "quote", + "semver", + "serde", + "serde_json", + "sha2", + "syn 2.0.118", + "tauri-utils", + "thiserror 2.0.18", + "time", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-macros" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8b394794f399a421811d06966343e7933fcae92d59f5180b9388d1174497a45" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.118", + "tauri-codegen", + "tauri-utils", +] + +[[package]] +name = "tauri-plugin" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74be5dd4bed9afbd145e5716b5fa2ec28cbc29c34ffa61c258c9273d896c8020" +dependencies = [ + "anyhow", + "glob", + "plist", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri-utils", + "walkdir", +] + +[[package]] +name = "tauri-plugin-clipboard-manager" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206dc20af4ed210748ba945c2774e60fd0acd52b9a73a028402caf809e9b6ecf" +dependencies = [ + "arboard", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.18", +] + +[[package]] +name = "tauri-plugin-dialog" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65981abb771e74e571a38196c3baa11c459379164791eba0e67abc1a5fac9884" +dependencies = [ + "log", + "raw-window-handle", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.18", + "url", +] + +[[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-http" +version = "2.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5bd512048e1985b7ec78f96d99083e2ddaf7e0d906b2b63c44ce5bb8b894067" +dependencies = [ + "bytes", + "cookie_store", + "data-url", + "http 1.4.2", "regex", - "reqwest", - "rfd", - "semver", + "reqwest 0.12.28", + "schemars 0.8.22", "serde", "serde_json", - "serde_repr", - "serialize-to-javascript", - "shared_child", - "state", - "tar", - "tauri-macros", - "tauri-runtime", - "tauri-runtime-wry", - "tauri-utils", - "tempfile", - "thiserror 1.0.69", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.18", "tokio", "url", - "uuid", - "webkit2gtk", - "webview2-com", - "windows 0.39.0", + "urlpattern", ] [[package]] -name = "tauri-build" -version = "1.5.6" +name = "tauri-plugin-notification" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2db08694eec06f53625cfc6fff3a363e084e5e9a238166d2989996413c346453" +checksum = "01fc2c5ff41105bd1f7242d8201fdf3efd70749b82fa013a17f2126357d194cc" dependencies = [ - "anyhow", - "cargo_toml", - "dirs-next", - "heck 0.5.0", - "json-patch", - "semver", + "log", + "notify-rust", + "rand 0.9.4", "serde", "serde_json", - "tauri-utils", - "tauri-winres", - "walkdir", + "serde_repr", + "tauri", + "tauri-plugin", + "thiserror 2.0.18", + "time", + "url", ] [[package]] -name = "tauri-codegen" -version = "1.4.6" +name = "tauri-plugin-shell" +version = "2.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53438d78c4a037ffe5eafa19e447eea599bedfb10844cb08ec53c2471ac3ac3f" +checksum = "8457dbf9e2bab1edd8df22bb2c20857a59a9868e79cb3eac5ed639eec4d0c73b" dependencies = [ - "base64 0.21.7", - "brotli", - "ico", - "json-patch", - "plist", - "png 0.17.16", - "proc-macro2", - "quote", + "encoding_rs", + "log", + "open", + "os_pipe", "regex", - "semver", + "schemars 0.8.22", "serde", "serde_json", - "sha2", - "tauri-utils", - "thiserror 1.0.69", - "time", - "uuid", - "walkdir", + "shared_child", + "tauri", + "tauri-plugin", + "thiserror 2.0.18", + "tokio", ] [[package]] -name = "tauri-macros" -version = "1.4.7" +name = "tauri-plugin-updater" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233988ac08c1ed3fe794cd65528d48d8f7ed4ab3895ca64cdaa6ad4d00c45c0b" +checksum = "806d9dac662c2e4594ff03c647a552f2c9bd544e7d0f683ec58f872f952ce4af" dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 1.0.109", - "tauri-codegen", - "tauri-utils", + "base64 0.22.1", + "dirs 6.0.0", + "flate2", + "futures-util", + "http 1.4.2", + "infer", + "log", + "minisign-verify", + "osakit", + "percent-encoding", + "reqwest 0.13.4", + "rustls", + "semver", + "serde", + "serde_json", + "tar", + "tauri", + "tauri-plugin", + "tempfile", + "thiserror 2.0.18", + "time", + "tokio", + "url", + "windows-sys 0.60.2", + "zip", ] [[package]] name = "tauri-runtime" -version = "0.14.6" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8066855882f00172935e3fa7d945126580c34dcbabab43f5d4f0c2398a67d47b" +checksum = "b0b4bc95aed361b0019067d189a1174a603d460d0f6c72606512d59fc9c12ec8" dependencies = [ + "cookie", + "dpi", "gtk", - "http", - "http-range", - "rand 0.8.5", + "http 1.4.2", + "jni 0.21.1", + "objc2", + "objc2-ui-kit", + "objc2-web-kit", "raw-window-handle", "serde", "serde_json", "tauri-utils", - "thiserror 1.0.69", + "thiserror 2.0.18", "url", - "uuid", + "webkit2gtk", "webview2-com", - "windows 0.39.0", + "windows", ] [[package]] name = "tauri-runtime-wry" -version = "0.14.11" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce361fec1e186705371f1c64ae9dd2a3a6768bc530d0a2d5e75a634bb416ad4d" +checksum = "fe41e015bf8fc4d6477ff4926a0ef769dc64ff34c7b0038b6f7cacae892acb5c" dependencies = [ - "arboard", - "cocoa", "gtk", + "http 1.4.2", + "jni 0.21.1", + "log", + "objc2", + "objc2-app-kit", + "once_cell", "percent-encoding", - "rand 0.8.5", "raw-window-handle", + "softbuffer", + "tao", "tauri-runtime", "tauri-utils", - "uuid", + "url", "webkit2gtk", "webview2-com", - "windows 0.39.0", + "windows", "wry", ] [[package]] name = "tauri-utils" -version = "1.6.2" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c357952645e679de02cd35007190fcbce869b93ffc61b029f33fe02648453774" +checksum = "3e176a18e67764923c4f1ce66f25ae4abe5f688384d5eb1a0fa6c77f3d90f887" dependencies = [ + "anyhow", "brotli", + "cargo_metadata", "ctor", + "dom_query", "dunce", "glob", - "heck 0.5.0", - "html5ever", + "http 1.4.2", "infer", "json-patch", - "kuchikiki", "log", "memchr", - "phf 0.11.3", + "phf", + "plist", "proc-macro2", "quote", + "regex", + "schemars 0.8.22", "semver", "serde", + "serde-untagged", "serde_json", "serde_with", - "thiserror 1.0.69", + "swift-rs", + "thiserror 2.0.18", + "toml 1.1.2+spec-1.1.0", "url", + "urlpattern", + "uuid", "walkdir", - "windows-version", ] [[package]] name = "tauri-winres" -version = "0.1.1" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5993dc129e544393574288923d1ec447c857f3f644187f4fbf7d9a875fbfc4fb" +checksum = "cc65d45c68858bfe420dd29e834b5d15dbecf8a07a8a16cf4d532c7b1f69d4b6" dependencies = [ + "dunce", "embed-resource", - "toml 0.7.8", + "toml 1.1.2+spec-1.1.0", ] [[package]] @@ -4377,18 +5199,18 @@ checksum = "0b1e66e07de489fe43a46678dd0b8df65e0c973909df1b60ba33874e297ba9b9" dependencies = [ "quick-xml 0.37.5", "thiserror 2.0.18", - "windows 0.61.3", + "windows", "windows-version", ] [[package]] name = "tempfile" -version = "3.24.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.3", "once_cell", "rustix", "windows-sys 0.61.2", @@ -4396,12 +5218,11 @@ dependencies = [ [[package]] name = "tendril" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +checksum = "c4790fc369d5a530f4b544b094e31388b9b3a37c0f4652ade4505945f5660d24" dependencies = [ - "futf", - "mac", + "new_debug_unreachable", "utf-8", ] @@ -4414,12 +5235,6 @@ dependencies = [ "libc", ] -[[package]] -name = "thin-slice" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" - [[package]] name = "thiserror" version = "1.0.69" @@ -4446,7 +5261,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", ] [[package]] @@ -4457,23 +5272,14 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", -] - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", + "syn 2.0.118", ] [[package]] name = "tiff" -version = "0.10.3" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" dependencies = [ "fax", "flate2", @@ -4485,12 +5291,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.47" +version = "0.3.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327" dependencies = [ "deranged", - "itoa 1.0.17", "num-conv", "powerfmt", "serde_core", @@ -4500,15 +5305,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "dcef1a61bdb119096e153208ec5cbec23944ce8bca13be5c7f60c634f7403935" dependencies = [ "num-conv", "time-core", @@ -4516,19 +5321,34 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" -version = "1.49.0" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", @@ -4536,20 +5356,20 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.2", + "socket2 0.6.4", "tokio-macros", "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", ] [[package]] @@ -4562,6 +5382,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.18" @@ -4577,42 +5407,51 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.11" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" dependencies = [ "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "toml_edit 0.20.2", ] [[package]] name = "toml" -version = "0.7.8" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ - "serde", - "serde_spanned", - "toml_datetime 0.6.11", - "toml_edit 0.19.15", + "indexmap 2.14.0", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 0.7.15", ] [[package]] name = "toml" -version = "0.8.23" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ - "serde", - "serde_spanned", - "toml_datetime 0.6.11", - "toml_edit 0.22.27", + "indexmap 2.14.0", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.3", ] [[package]] name = "toml_datetime" -version = "0.6.11" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" dependencies = [ "serde", ] @@ -4626,59 +5465,104 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.13.0", - "serde", - "serde_spanned", - "toml_datetime 0.6.11", + "indexmap 2.14.0", + "toml_datetime 0.6.3", "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.22.27" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.14.0", "serde", - "serde_spanned", - "toml_datetime 0.6.11", - "toml_write", - "winnow 0.7.14", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.23.10+spec-1.0.0" +version = "0.25.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" dependencies = [ - "indexmap 2.13.0", - "toml_datetime 0.7.5+spec-1.1.0", + "indexmap 2.14.0", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 0.7.14", + "winnow 1.0.3", ] [[package]] name = "toml_parser" -version = "1.0.6+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 0.7.14", + "winnow 1.0.3", ] [[package]] -name = "toml_write" -version = "0.1.2" +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 1.0.2", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags 2.13.0", + "bytes", + "futures-util", + "http 1.4.2", + "http-body 1.0.1", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" @@ -4705,7 +5589,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", ] [[package]] @@ -4715,36 +5599,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", ] [[package]] -name = "tracing-subscriber" -version = "0.3.22" +name = "tray-icon" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +checksum = "65ba1e5f6b9ef9fd87e21b9c6f351554dbd717960089168fcfdef854686961dc" dependencies = [ - "matchers", - "nu-ansi-term", + "crossbeam-channel", + "dirs 6.0.0", + "libappindicator", + "muda", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", "once_cell", - "regex-automata", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", + "png 0.18.1", + "serde", + "thiserror 2.0.18", + "windows-sys 0.61.2", ] [[package]] @@ -4764,34 +5640,81 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "uds_windows" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset 0.9.1", "tempfile", - "winapi", + "windows-sys 0.61.2", +] + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", ] [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" [[package]] name = "universal-hash" @@ -4803,6 +5726,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.8" @@ -4822,6 +5751,18 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", +] + [[package]] name = "utf-8" version = "0.7.6" @@ -4836,34 +5777,22 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.20.0" +version = "1.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" +checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", "js-sys", "serde_core", "wasm-bindgen", ] -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "version-compare" -version = "0.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" - [[package]] name = "version-compare" version = "0.2.1" @@ -4915,12 +5844,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -4929,18 +5852,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" dependencies = [ "cfg-if", "once_cell", @@ -4951,23 +5874,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.58" +version = "0.4.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" dependencies = [ - "cfg-if", - "futures-util", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4975,31 +5894,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" dependencies = [ "unicode-ident", ] [[package]] name = "wasm-streams" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" dependencies = [ "futures-util", "js-sys", @@ -5010,9 +5929,9 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.12" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee64194ccd96bf648f42a65a7e589547096dfa702f7cadef84347b66ad164f9" +checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" dependencies = [ "cc", "downcast-rs", @@ -5023,11 +5942,11 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.12" +version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e6faa537fbb6c186cb9f1d41f2f811a4120d1b57ec61f50da451a0c5122bec" +checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.13.0", "rustix", "wayland-backend", "wayland-scanner", @@ -5035,11 +5954,11 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.10" +version = "0.32.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baeda9ffbcfc8cd6ddaade385eaf2393bd2115a69523c735f12242353c3df4f3" +checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.13.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -5047,11 +5966,11 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.10" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9597cdf02cf0c34cd5823786dce6b5ae8598f05c2daf5621b6e178d4f7345f3" +checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.13.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5060,39 +5979,61 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.8" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5423e94b6a63e68e439803a3e153a9252d5ead12fd853334e2ad33997e3889e3" +checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a" dependencies = [ "proc-macro2", - "quick-xml 0.38.4", + "quick-xml 0.39.4", "quote", ] [[package]] name = "wayland-sys" -version = "0.31.8" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6dbfc3ac5ef974c92a2235805cc0114033018ae1290a72e474aa8b28cbbdfd" +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" dependencies = [ "pkg-config", ] [[package]] name = "web-sys" -version = "0.3.85" +version = "0.3.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", "wasm-bindgen", ] +[[package]] +name = "web_atoms" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "075474b12bcb3d2e3d4546580e9de478eeeead668a1761e2a8860c836b7ef297" +dependencies = [ + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", +] + [[package]] name = "webkit2gtk" -version = "0.18.2" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8f859735e4a452aeb28c6c56a852967a8a76c8eb1cc32dbf931ad28a13d6370" +checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" dependencies = [ "bitflags 1.3.2", "cairo-rs", @@ -5108,20 +6049,18 @@ dependencies = [ "javascriptcore-rs", "libc", "once_cell", - "soup2", + "soup3", "webkit2gtk-sys", ] [[package]] name = "webkit2gtk-sys" -version = "0.18.0" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d76ca6ecc47aeba01ec61e480139dda143796abcae6f83bcddf50d6b5b1dcf3" +checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" dependencies = [ - "atk-sys", "bitflags 1.3.2", "cairo-sys-rs", - "gdk-pixbuf-sys", "gdk-sys", "gio-sys", "glib-sys", @@ -5129,48 +6068,63 @@ dependencies = [ "gtk-sys", "javascriptcore-rs-sys", "libc", - "pango-sys", "pkg-config", - "soup2-sys", - "system-deps 6.2.2", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" +dependencies = [ + "rustls-pki-types", ] [[package]] name = "webview2-com" -version = "0.19.1" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4a769c9f1a64a8734bde70caafac2b96cada12cd4aefa49196b3a386b8b4178" +checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows 0.39.0", - "windows-implement 0.39.0", + "windows", + "windows-core 0.61.2", + "windows-implement", + "windows-interface", ] [[package]] name = "webview2-com-macros" -version = "0.6.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaebe196c01691db62e9e4ca52c5ef1e4fd837dcae27dae3ada599b5a8fd05ac" +checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.118", ] [[package]] name = "webview2-com-sys" -version = "0.19.0" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac48ef20ddf657755fdcda8dfed2a7b4fc7e4581acce6fe9b88c3d64f29dee7" +checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" dependencies = [ - "regex", - "serde", - "serde_json", - "thiserror 1.0.69", - "windows 0.39.0", - "windows-bindgen", - "windows-metadata", + "thiserror 2.0.18", + "windows", + "windows-core 0.61.2", ] [[package]] @@ -5211,39 +6165,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" -dependencies = [ - "windows_aarch64_msvc 0.37.0", - "windows_i686_gnu 0.37.0", - "windows_i686_msvc 0.37.0", - "windows_x86_64_gnu 0.37.0", - "windows_x86_64_msvc 0.37.0", -] - -[[package]] -name = "windows" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" -dependencies = [ - "windows-implement 0.39.0", - "windows_aarch64_msvc 0.39.0", - "windows_i686_gnu 0.39.0", - "windows_i686_msvc 0.39.0", - "windows_x86_64_gnu 0.39.0", - "windows_x86_64_msvc 0.39.0", -] - -[[package]] -name = "windows" -version = "0.48.0" +name = "window-vibrancy" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" dependencies = [ - "windows-targets 0.48.5", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "raw-window-handle", + "windows-sys 0.59.0", + "windows-version", ] [[package]] @@ -5259,16 +6192,6 @@ dependencies = [ "windows-numerics", ] -[[package]] -name = "windows-bindgen" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68003dbd0e38abc0fb85b939240f4bce37c43a5981d3df37ccbaaa981b47cb41" -dependencies = [ - "windows-metadata", - "windows-tokens", -] - [[package]] name = "windows-collections" version = "0.2.0" @@ -5284,7 +6207,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-implement 0.60.2", + "windows-implement", "windows-interface", "windows-link 0.1.3", "windows-result 0.3.4", @@ -5297,7 +6220,7 @@ version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-implement 0.60.2", + "windows-implement", "windows-interface", "windows-link 0.2.1", "windows-result 0.4.1", @@ -5315,16 +6238,6 @@ dependencies = [ "windows-threading", ] -[[package]] -name = "windows-implement" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba01f98f509cb5dc05f4e5fc95e535f78260f15fea8fe1a8abdd08f774f1cee7" -dependencies = [ - "syn 1.0.109", - "windows-tokens", -] - [[package]] name = "windows-implement" version = "0.60.2" @@ -5333,7 +6246,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", ] [[package]] @@ -5344,7 +6257,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", ] [[package]] @@ -5359,12 +6272,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-metadata" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278" - [[package]] name = "windows-numerics" version = "0.2.0" @@ -5375,6 +6282,17 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + [[package]] name = "windows-result" version = "0.3.4" @@ -5413,17 +6331,11 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.42.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.42.2", ] [[package]] @@ -5444,6 +6356,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.60.2" @@ -5462,6 +6383,21 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -5519,12 +6455,6 @@ dependencies = [ "windows-link 0.1.3", ] -[[package]] -name = "windows-tokens" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597" - [[package]] name = "windows-version" version = "0.1.7" @@ -5558,18 +6488,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -5594,18 +6512,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" - -[[package]] -name = "windows_i686_gnu" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -5642,18 +6548,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" - -[[package]] -name = "windows_i686_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -5678,18 +6572,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -5738,18 +6620,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -5785,9 +6655,15 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.14" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" + +[[package]] +name = "winnow" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" dependencies = [ "memchr", ] @@ -5813,19 +6689,19 @@ dependencies = [ [[package]] name = "winreg" -version = "0.52.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] name = "wit-bindgen" -version = "0.51.0" +version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] name = "wl-clipboard-rs" @@ -5847,46 +6723,52 @@ dependencies = [ [[package]] name = "writeable" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "wry" -version = "0.24.11" +version = "0.55.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55c80b12287eb1ff7c365fc2f7a5037cb6181bd44c9fce81c8d1cf7605ffad6" +checksum = "186f9871daa55fd9c016578b810d149de58367113db7fb72b462d2323ce19514" dependencies = [ - "base64 0.13.1", - "block", - "cocoa", - "core-graphics", + "base64 0.22.1", + "block2", + "cookie", "crossbeam-channel", + "dirs 6.0.0", + "dom_query", + "dpi", "dunce", - "gdk", - "gio", - "glib", + "gdkx11", "gtk", - "html5ever", - "http", - "kuchikiki", + "http 1.4.2", + "javascriptcore-rs", + "jni 0.21.1", "libc", - "log", - "objc", - "objc_id", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", "once_cell", - "serde", - "serde_json", + "percent-encoding", + "raw-window-handle", "sha2", - "soup2", - "tao", - "thiserror 1.0.69", + "soup3", + "tao-macros", + "thiserror 2.0.18", "url", "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows 0.39.0", - "windows-implement 0.39.0", + "windows", + "windows-core 0.61.2", + "windows-version", + "x11-dl", ] [[package]] @@ -5939,9 +6821,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -5950,21 +6832,21 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", "synstructure", ] [[package]] name = "zbus" -version = "5.13.2" +version = "5.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfeff997a0aaa3eb20c4652baf788d2dfa6d2839a0ead0b3ff69ce2f9c4bdd1" +checksum = "eee682d202a77e4a9f3b2c2bdf48a7b28af5c08c34ddf66f98c93e5e39464285" dependencies = [ "async-broadcast", "async-executor", @@ -5989,7 +6871,7 @@ dependencies = [ "uds_windows", "uuid", "windows-sys 0.61.2", - "winnow 0.7.14", + "winnow 1.0.3", "zbus_macros", "zbus_names", "zvariant", @@ -5997,14 +6879,14 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.13.2" +version = "5.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bbd5a90dbe8feee5b13def448427ae314ccd26a49cac47905cafefb9ff846f1" +checksum = "adf1bd45a81a103745b1757754762a26e8cd01e4532e4d6c8ec431624b80d1d6" dependencies = [ - "proc-macro-crate 3.4.0", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", "zbus_names", "zvariant", "zvariant_utils", @@ -6012,61 +6894,67 @@ dependencies = [ [[package]] name = "zbus_names" -version = "4.3.1" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" +checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d" dependencies = [ "serde", - "winnow 0.7.14", + "winnow 1.0.3", "zvariant", ] [[package]] name = "zerocopy" -version = "0.8.39" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.39" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", ] [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", "synstructure", ] +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", "yoke", @@ -6075,9 +6963,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "yoke", "zerofrom", @@ -6086,72 +6974,84 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", +] + +[[package]] +name = "zip" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1" +dependencies = [ + "arbitrary", + "crc32fast", + "indexmap 2.14.0", + "memchr", ] [[package]] name = "zmij" -version = "1.0.19" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] name = "zune-core" -version = "0.4.12" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" [[package]] name = "zune-jpeg" -version = "0.4.21" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" dependencies = [ "zune-core", ] [[package]] name = "zvariant" -version = "5.9.2" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b64ef4f40c7951337ddc7023dd03528a57a3ce3408ee9da5e948bd29b232c4" +checksum = "a192a0bde63360d77a7523c833d4b4ce6070a927e2c53246e4c540b1a3e27be0" dependencies = [ "endi", "enumflags2", "serde", - "winnow 0.7.14", + "winnow 1.0.3", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.9.2" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "484d5d975eb7afb52cc6b929c13d3719a20ad650fea4120e6310de3fc55e415c" +checksum = "90bc6cde9c01c511074be97f7ccb6c19d0da89e3f8662e812e999dcfd4638737" dependencies = [ - "proc-macro-crate 3.4.0", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.118", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" +checksum = "1e8535915cfa75547e559d8c68e8139909a4aeee076831e4ef7fc59d8172c4d6" dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.114", - "winnow 0.7.14", + "syn 2.0.118", + "winnow 1.0.3", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 9455ccc..451a88e 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -3,15 +3,15 @@ name = "notepad-mac" version = "2.0.0" description = "A modern code editor for macOS with AI, Terminal & Git" authors = ["you"] -license = "MIT" +license = "GPL-3.0" repository = "" edition = "2021" [build-dependencies] -tauri-build = { version = "1.5", features = [] } +tauri-build = { version = "2", features = [] } [dependencies] -tauri = { version = "1.5", features = ["dialog-all", "fs-all", "path-all", "shell-open", "shell-execute", "window-all", "clipboard-all", "notification-all", "http-request"] } +tauri = { version = "2", features = [] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" chrono = "0.4" @@ -26,6 +26,8 @@ urlencoding = "2.1" aes-gcm = "0.10" rand = "0.8" argon2 = "0.5" +# Secure secret storage via the native macOS Keychain (no secrets on argv) +keyring = { version = "3", features = ["apple-native"] } # Fast search walkdir = "2.4" ignore = "0.4" @@ -37,6 +39,12 @@ tokio = { version = "1", features = ["full"] } # PTY Terminal portable-pty = "0.8" lazy_static = "1.4" +tauri-plugin-notification = "2" +tauri-plugin-http = "2" +tauri-plugin-fs = "2" +tauri-plugin-shell = "2" +tauri-plugin-clipboard-manager = "2" +tauri-plugin-dialog = "2" [dev-dependencies] tempfile = "3" @@ -44,3 +52,6 @@ tempfile = "3" [features] default = ["custom-protocol"] custom-protocol = ["tauri/custom-protocol"] + +[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] +tauri-plugin-updater = "2" diff --git a/src-tauri/Entitlements.plist b/src-tauri/Entitlements.plist new file mode 100644 index 0000000..1543f50 --- /dev/null +++ b/src-tauri/Entitlements.plist @@ -0,0 +1,14 @@ + + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + + com.apple.security.cs.disable-library-validation + + + diff --git a/src-tauri/capabilities/desktop.json b/src-tauri/capabilities/desktop.json new file mode 100644 index 0000000..ba6d080 --- /dev/null +++ b/src-tauri/capabilities/desktop.json @@ -0,0 +1,14 @@ +{ + "identifier": "desktop-capability", + "platforms": [ + "macOS", + "windows", + "linux" + ], + "windows": [ + "main" + ], + "permissions": [ + "updater:default" + ] +} \ No newline at end of file diff --git a/src-tauri/capabilities/migrated.json b/src-tauri/capabilities/migrated.json new file mode 100644 index 0000000..98e99b9 --- /dev/null +++ b/src-tauri/capabilities/migrated.json @@ -0,0 +1,115 @@ +{ + "identifier": "migrated", + "description": "permissions that were migrated from v1", + "local": true, + "windows": [ + "main" + ], + "permissions": [ + "core:default", + "fs:allow-read-file", + "fs:allow-write-file", + "fs:allow-read-dir", + "fs:allow-copy-file", + "fs:allow-mkdir", + "fs:allow-remove", + "fs:allow-remove", + "fs:allow-rename", + "fs:allow-exists", + { + "identifier": "fs:scope", + "allow": [ + "$HOME/**", + "$DOCUMENT/**", + "$DESKTOP/**", + "$DOWNLOAD/**" + ] + }, + "core:window:allow-create", + "core:window:allow-center", + "core:window:allow-request-user-attention", + "core:window:allow-set-resizable", + "core:window:allow-set-maximizable", + "core:window:allow-set-minimizable", + "core:window:allow-set-closable", + "core:window:allow-set-title", + "core:window:allow-maximize", + "core:window:allow-unmaximize", + "core:window:allow-minimize", + "core:window:allow-unminimize", + "core:window:allow-show", + "core:window:allow-hide", + "core:window:allow-close", + "core:window:allow-set-decorations", + "core:window:allow-set-always-on-top", + "core:window:allow-set-content-protected", + "core:window:allow-set-size", + "core:window:allow-set-min-size", + "core:window:allow-set-max-size", + "core:window:allow-set-position", + "core:window:allow-set-fullscreen", + "core:window:allow-set-focus", + "core:window:allow-set-icon", + "core:window:allow-set-skip-taskbar", + "core:window:allow-set-cursor-grab", + "core:window:allow-set-cursor-visible", + "core:window:allow-set-cursor-icon", + "core:window:allow-set-cursor-position", + "core:window:allow-set-ignore-cursor-events", + "core:window:allow-start-dragging", + "core:webview:allow-print", + { + "identifier": "shell:allow-execute", + "allow": [ + { + "args": true, + "cmd": "git", + "name": "git", + "sidecar": false + } + ] + }, + "shell:allow-open", + "dialog:allow-open", + "dialog:allow-save", + "dialog:allow-message", + "dialog:allow-ask", + "dialog:allow-confirm", + { + "identifier": "http:default", + "allow": [ + { + "url": "https://api.openai.com/*" + }, + { + "url": "https://api.anthropic.com/*" + }, + { + "url": "https://api.deepseek.com/*" + }, + { + "url": "https://www.googleapis.com/*" + }, + { + "url": "https://oauth2.googleapis.com/*" + }, + { + "url": "https://api.dropboxapi.com/*" + }, + { + "url": "https://content.dropboxapi.com/*" + }, + { + "url": "https://graph.microsoft.com/*" + }, + { + "url": "https://login.microsoftonline.com/*" + } + ] + }, + "notification:default", + "clipboard-manager:allow-read-text", + "clipboard-manager:allow-write-text", + "http:default" + ] +} \ No newline at end of file diff --git a/src-tauri/src/commands/bookmarks.rs b/src-tauri/src/commands/bookmarks.rs index 480391d..00a9c7d 100644 --- a/src-tauri/src/commands/bookmarks.rs +++ b/src-tauri/src/commands/bookmarks.rs @@ -1,6 +1,6 @@ -use std::fs; -use crate::models::Bookmark; use crate::helpers::get_data_dir; +use crate::models::Bookmark; +use std::fs; #[tauri::command] pub fn save_bookmarks(bookmarks: Vec) -> Result<(), String> { diff --git a/src-tauri/src/commands/cloud.rs b/src-tauri/src/commands/cloud.rs index 959249e..217bb85 100644 --- a/src-tauri/src/commands/cloud.rs +++ b/src-tauri/src/commands/cloud.rs @@ -1,8 +1,57 @@ +use crate::models::{CloudFile, OAuthCallbackResult, OAuthTokenResponse}; use std::io::{Read, Write}; -use crate::models::{OAuthCallbackResult, OAuthTokenResponse, CloudFile}; +/// Redact OAuth token material from strings before they reach error messages, +/// the UI, logs or crash reports. +fn redact_secrets(input: &str) -> String { + use regex::Regex; + // JSON form: "access_token": "value" + let json_re = + Regex::new(r#"(?i)"(access_token|refresh_token|id_token)"\s*:\s*"[^"]*""#).unwrap(); + let s = json_re + .replace_all(input, r#""${1}":"***REDACTED***""#) + .into_owned(); + // form / query form: access_token=value + let form_re = Regex::new(r"(?i)(access_token|refresh_token|id_token)=[^&\s]+").unwrap(); + form_re.replace_all(&s, "${1}=***REDACTED***").into_owned() +} + +#[derive(serde::Serialize)] +pub struct PkcePair { + pub verifier: String, + pub challenge: String, +} + +/// RFC 7636 S256 transform: base64url(SHA-256(verifier)), no padding. +fn pkce_challenge(verifier: &str) -> String { + use sha2::{Digest, Sha256}; + let mut hasher = Sha256::new(); + hasher.update(verifier.as_bytes()); + base64::Engine::encode( + &base64::engine::general_purpose::URL_SAFE_NO_PAD, + hasher.finalize(), + ) +} + +/// Generate a PKCE (verifier, S256 challenge) pair for an OAuth authorization-code flow. #[tauri::command] -pub async fn open_oauth_window(url: String, expected_state: String) -> Result { +pub fn generate_pkce() -> PkcePair { + use rand::RngCore; + let mut bytes = [0u8; 32]; + rand::rngs::OsRng.fill_bytes(&mut bytes); // 32 bytes -> 43-char base64url verifier + let verifier = base64::Engine::encode(&base64::engine::general_purpose::URL_SAFE_NO_PAD, bytes); + let challenge = pkce_challenge(&verifier); + PkcePair { + verifier, + challenge, + } +} + +#[tauri::command] +pub async fn open_oauth_window( + url: String, + expected_state: String, +) -> Result { use std::net::TcpListener; use std::time::Duration; @@ -34,65 +83,70 @@ pub async fn open_oauth_window(url: String, expected_state: String) -> Result { - let mut buffer = [0; 4096]; - let read_result = stream.read(&mut buffer); - let request = match read_result { - Ok(size) => String::from_utf8_lossy(&buffer[..size]).to_string(), - Err(err) => { - tx.send(Err(err.to_string())).ok(); - return; - } - }; - - let first_line = request.lines().next().unwrap_or_default(); - let path = first_line.split_whitespace().nth(1).unwrap_or_default().to_string(); - - let mut code = None; - let mut state = None; - let mut oauth_error = None; - if let Some(query) = path.split('?').nth(1) { - for pair in query.split('&') { - let mut parts = pair.splitn(2, '='); - let key = parts.next().unwrap_or_default(); - let value = parts.next().unwrap_or_default(); - let decoded = urlencoding::decode(value).unwrap_or_else(|_| value.into()).to_string(); - match key { - "code" => code = Some(decoded), - "state" => state = Some(decoded), - "error" => oauth_error = Some(decoded), - _ => {} - } + let handle = std::thread::spawn(move || match listener.accept() { + Ok((mut stream, _)) => { + let mut buffer = [0; 4096]; + let read_result = stream.read(&mut buffer); + let request = match read_result { + Ok(size) => String::from_utf8_lossy(&buffer[..size]).to_string(), + Err(err) => { + tx.send(Err(err.to_string())).ok(); + return; + } + }; + + let first_line = request.lines().next().unwrap_or_default(); + let path = first_line + .split_whitespace() + .nth(1) + .unwrap_or_default() + .to_string(); + + let mut code = None; + let mut state = None; + let mut oauth_error = None; + if let Some(query) = path.split('?').nth(1) { + for pair in query.split('&') { + let mut parts = pair.splitn(2, '='); + let key = parts.next().unwrap_or_default(); + let value = parts.next().unwrap_or_default(); + let decoded = urlencoding::decode(value) + .unwrap_or_else(|_| value.into()) + .to_string(); + match key { + "code" => code = Some(decoded), + "state" => state = Some(decoded), + "error" => oauth_error = Some(decoded), + _ => {} } } + } - let response_body = if oauth_error.is_some() { - "

OAuth failed

You can close this window and return to RocketNote.

" - } else { - "

✔ Connected!

You can close this window and return to RocketNote.

" - }; - let response = format!( + let response_body = if oauth_error.is_some() { + "

OAuth failed

You can close this window and return to RocketNote.

" + } else { + "

✔ Connected!

You can close this window and return to RocketNote.

" + }; + let response = format!( "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: {}\r\n\r\n{}", response_body.len(), response_body ); - let _ = stream.write_all(response.as_bytes()); + let _ = stream.write_all(response.as_bytes()); - let result = match oauth_error { - Some(err) => Err(format!("OAuth provider returned error: {}", err)), - None => Ok((code, state)), - }; - tx.send(result).ok(); - } - Err(e) => { - tx.send(Err(e.to_string())).ok(); - } + let result = match oauth_error { + Some(err) => Err(format!("OAuth provider returned error: {}", err)), + None => Ok((code, state)), + }; + tx.send(result).ok(); + } + Err(e) => { + tx.send(Err(e.to_string())).ok(); } }); - let result = rx.recv_timeout(Duration::from_secs(120)) + let result = rx + .recv_timeout(Duration::from_secs(120)) .map_err(|_| "OAuth timed out after 120 seconds. Please try again.")?; let _ = handle.join(); @@ -119,10 +173,11 @@ pub async fn exchange_oauth_token( client_id: String, client_secret: String, redirect_uri: String, + code_verifier: Option, ) -> Result { let client = reqwest::Client::new(); - let (token_url, params) = match provider.as_str() { + let (token_url, mut params) = match provider.as_str() { "google" => ( "https://oauth2.googleapis.com/token", vec![ @@ -157,6 +212,12 @@ pub async fn exchange_oauth_token( _ => return Err("Unknown provider".to_string()), }; + // PKCE: bind the authorization code to the verifier so an intercepted + // loopback code cannot be redeemed without it. + if let Some(ref verifier) = code_verifier { + params.push(("code_verifier", verifier.as_str())); + } + let response = client .post(token_url) .form(¶ms) @@ -167,7 +228,11 @@ pub async fn exchange_oauth_token( if !response.status().is_success() { let status = response.status(); let body = response.text().await.unwrap_or_default(); - return Err(format!("Token exchange failed ({}): {}", status, body)); + return Err(format!( + "Token exchange failed ({}): {}", + status, + redact_secrets(&body) + )); } let data: serde_json::Value = response @@ -178,7 +243,12 @@ pub async fn exchange_oauth_token( let access_token = data["access_token"] .as_str() .map(|s| s.to_string()) - .ok_or_else(|| format!("No access_token in response: {}", data))?; + .ok_or_else(|| { + format!( + "No access_token in response: {}", + redact_secrets(&data.to_string()) + ) + })?; Ok(OAuthTokenResponse { access_token, @@ -240,7 +310,11 @@ pub async fn refresh_oauth_token( if !response.status().is_success() { let status = response.status(); let body = response.text().await.unwrap_or_default(); - return Err(format!("Token refresh failed ({}): {}", status, body)); + return Err(format!( + "Token refresh failed ({}): {}", + status, + redact_secrets(&body) + )); } let data: serde_json::Value = response @@ -251,11 +325,19 @@ pub async fn refresh_oauth_token( let access_token = data["access_token"] .as_str() .map(|s| s.to_string()) - .ok_or_else(|| format!("No access_token in refresh response: {}", data))?; + .ok_or_else(|| { + format!( + "No access_token in refresh response: {}", + redact_secrets(&data.to_string()) + ) + })?; Ok(OAuthTokenResponse { access_token, - refresh_token: data["refresh_token"].as_str().map(|s| s.to_string()).or(Some(refresh_token)), + refresh_token: data["refresh_token"] + .as_str() + .map(|s| s.to_string()) + .or(Some(refresh_token)), expires_in: data["expires_in"].as_u64(), token_type: data["token_type"].as_str().map(|s| s.to_string()), scope: data["scope"].as_str().map(|s| s.to_string()), @@ -263,7 +345,11 @@ pub async fn refresh_oauth_token( } #[tauri::command] -pub async fn cloud_list_files(provider: String, token: String, path: String) -> Result, String> { +pub async fn cloud_list_files( + provider: String, + token: String, + path: String, +) -> Result, String> { let client = reqwest::Client::new(); let files = match provider.as_str() { @@ -288,7 +374,11 @@ pub async fn cloud_list_files(provider: String, token: String, path: String) -> if !response.status().is_success() { let status = response.status(); let body = response.text().await.unwrap_or_default(); - return Err(format!("Google Drive API error ({}): {}", status, body)); + return Err(format!( + "Google Drive API error ({}): {}", + status, + redact_secrets(&body) + )); } let data: serde_json::Value = response.json().await.map_err(|e| e.to_string())?; @@ -324,7 +414,11 @@ pub async fn cloud_list_files(provider: String, token: String, path: String) -> if !response.status().is_success() { let status = response.status(); let body = response.text().await.unwrap_or_default(); - return Err(format!("Dropbox API error ({}): {}", status, body)); + return Err(format!( + "Dropbox API error ({}): {}", + status, + redact_secrets(&body) + )); } let data: serde_json::Value = response.json().await.map_err(|e| e.to_string())?; @@ -347,7 +441,10 @@ pub async fn cloud_list_files(provider: String, token: String, path: String) -> let url = if path == "/" { "https://graph.microsoft.com/v1.0/me/drive/root/children".to_string() } else { - format!("https://graph.microsoft.com/v1.0/me/drive/items/{}/children", path) + format!( + "https://graph.microsoft.com/v1.0/me/drive/items/{}/children", + path + ) }; let response = client @@ -360,7 +457,11 @@ pub async fn cloud_list_files(provider: String, token: String, path: String) -> if !response.status().is_success() { let status = response.status(); let body = response.text().await.unwrap_or_default(); - return Err(format!("OneDrive API error ({}): {}", status, body)); + return Err(format!( + "OneDrive API error ({}): {}", + status, + redact_secrets(&body) + )); } let data: serde_json::Value = response.json().await.map_err(|e| e.to_string())?; @@ -391,7 +492,7 @@ pub async fn cloud_upload_file( token: String, path: String, file_name: String, - content: String + content: String, ) -> Result<(), String> { let client = reqwest::Client::new(); @@ -415,7 +516,10 @@ pub async fn cloud_upload_file( let response = client .post("https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart") .header("Authorization", format!("Bearer {}", token)) - .header("Content-Type", format!("multipart/related; boundary={}", boundary)) + .header( + "Content-Type", + format!("multipart/related; boundary={}", boundary), + ) .body(body) .send() .await @@ -424,7 +528,11 @@ pub async fn cloud_upload_file( if !response.status().is_success() { let status = response.status(); let body = response.text().await.unwrap_or_default(); - return Err(format!("Google Drive upload failed ({}): {}", status, body)); + return Err(format!( + "Google Drive upload failed ({}): {}", + status, + redact_secrets(&body) + )); } } "dropbox" => { @@ -437,10 +545,14 @@ pub async fn cloud_upload_file( let response = client .post("https://content.dropboxapi.com/2/files/upload") .header("Authorization", format!("Bearer {}", token)) - .header("Dropbox-API-Arg", serde_json::json!({ - "path": path_full, - "mode": "overwrite" - }).to_string()) + .header( + "Dropbox-API-Arg", + serde_json::json!({ + "path": path_full, + "mode": "overwrite" + }) + .to_string(), + ) .header("Content-Type", "application/octet-stream") .body(content) .send() @@ -450,14 +562,24 @@ pub async fn cloud_upload_file( if !response.status().is_success() { let status = response.status(); let body = response.text().await.unwrap_or_default(); - return Err(format!("Dropbox upload failed ({}): {}", status, body)); + return Err(format!( + "Dropbox upload failed ({}): {}", + status, + redact_secrets(&body) + )); } } "onedrive" => { let url = if path == "/" { - format!("https://graph.microsoft.com/v1.0/me/drive/root:/{}:/content", file_name) + format!( + "https://graph.microsoft.com/v1.0/me/drive/root:/{}:/content", + file_name + ) } else { - format!("https://graph.microsoft.com/v1.0/me/drive/items/{}:/{}:/content", path, file_name) + format!( + "https://graph.microsoft.com/v1.0/me/drive/items/{}:/{}:/content", + path, file_name + ) }; let response = client @@ -472,7 +594,11 @@ pub async fn cloud_upload_file( if !response.status().is_success() { let status = response.status(); let body = response.text().await.unwrap_or_default(); - return Err(format!("OneDrive upload failed ({}): {}", status, body)); + return Err(format!( + "OneDrive upload failed ({}): {}", + status, + redact_secrets(&body) + )); } } _ => return Err("Unknown provider".to_string()), @@ -482,12 +608,19 @@ pub async fn cloud_upload_file( } #[tauri::command] -pub async fn cloud_download_file(provider: String, token: String, file_id: String) -> Result { +pub async fn cloud_download_file( + provider: String, + token: String, + file_id: String, +) -> Result { let client = reqwest::Client::new(); let content = match provider.as_str() { "google" => { - let url = format!("https://www.googleapis.com/drive/v3/files/{}?alt=media", file_id); + let url = format!( + "https://www.googleapis.com/drive/v3/files/{}?alt=media", + file_id + ); let response = client .get(&url) @@ -499,7 +632,11 @@ pub async fn cloud_download_file(provider: String, token: String, file_id: Strin if !response.status().is_success() { let status = response.status(); let body = response.text().await.unwrap_or_default(); - return Err(format!("Google Drive download failed ({}): {}", status, body)); + return Err(format!( + "Google Drive download failed ({}): {}", + status, + redact_secrets(&body) + )); } response.text().await.map_err(|e| e.to_string())? @@ -508,9 +645,13 @@ pub async fn cloud_download_file(provider: String, token: String, file_id: Strin let response = client .post("https://content.dropboxapi.com/2/files/download") .header("Authorization", format!("Bearer {}", token)) - .header("Dropbox-API-Arg", serde_json::json!({ - "path": file_id - }).to_string()) + .header( + "Dropbox-API-Arg", + serde_json::json!({ + "path": file_id + }) + .to_string(), + ) .send() .await .map_err(|e| e.to_string())?; @@ -518,13 +659,20 @@ pub async fn cloud_download_file(provider: String, token: String, file_id: Strin if !response.status().is_success() { let status = response.status(); let body = response.text().await.unwrap_or_default(); - return Err(format!("Dropbox download failed ({}): {}", status, body)); + return Err(format!( + "Dropbox download failed ({}): {}", + status, + redact_secrets(&body) + )); } response.text().await.map_err(|e| e.to_string())? } "onedrive" => { - let url = format!("https://graph.microsoft.com/v1.0/me/drive/items/{}/content", file_id); + let url = format!( + "https://graph.microsoft.com/v1.0/me/drive/items/{}/content", + file_id + ); let response = client .get(&url) @@ -536,7 +684,11 @@ pub async fn cloud_download_file(provider: String, token: String, file_id: Strin if !response.status().is_success() { let status = response.status(); let body = response.text().await.unwrap_or_default(); - return Err(format!("OneDrive download failed ({}): {}", status, body)); + return Err(format!( + "OneDrive download failed ({}): {}", + status, + redact_secrets(&body) + )); } response.text().await.map_err(|e| e.to_string())? @@ -546,3 +698,53 @@ pub async fn cloud_download_file(provider: String, token: String, file_id: Strin Ok(content) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn redacts_json_tokens() { + let s = r#"{"access_token":"SECRET123","expires_in":3600,"refresh_token":"REFRESH456"}"#; + let out = redact_secrets(s); + assert!(!out.contains("SECRET123"), "access_token leaked: {}", out); + assert!(!out.contains("REFRESH456"), "refresh_token leaked: {}", out); + assert!(out.contains("3600"), "non-secret field should be preserved"); + } + + #[test] + fn redacts_form_tokens() { + let s = "grant_type=authorization_code&access_token=ABCDEF123&foo=bar"; + let out = redact_secrets(s); + assert!(!out.contains("ABCDEF123"), "form token leaked: {}", out); + assert!(out.contains("foo=bar")); + } + + #[test] + fn leaves_clean_text_untouched() { + let s = "Token exchange failed (400): invalid_grant"; + assert_eq!(redact_secrets(s), s); + } + + #[test] + fn pkce_challenge_matches_rfc7636_vector() { + // RFC 7636 Appendix B + let verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"; + assert_eq!( + pkce_challenge(verifier), + "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM" + ); + } + + #[test] + fn generate_pkce_pair_is_consistent_and_random() { + let pair = generate_pkce(); + assert!( + pair.verifier.len() >= 43 && pair.verifier.len() <= 128, + "verifier length {}", + pair.verifier.len() + ); + assert_eq!(pkce_challenge(&pair.verifier), pair.challenge); + assert_ne!(generate_pkce().verifier, pair.verifier); + } +} diff --git a/src-tauri/src/commands/crypto.rs b/src-tauri/src/commands/crypto.rs index d0b0c82..c55e5d8 100644 --- a/src-tauri/src/commands/crypto.rs +++ b/src-tauri/src/commands/crypto.rs @@ -1,13 +1,13 @@ -use std::fs; +use crate::models::{ + EncryptedData, ARGON2_ITERATIONS, ARGON2_LANES, ARGON2_MEMORY_KIB, ENCRYPTION_FORMAT_VERSION, +}; use aes_gcm::{ aead::{Aead, KeyInit, OsRng}, Aes256Gcm, Nonce, }; -use argon2::{Argon2, Algorithm, Params, Version}; +use argon2::{Algorithm, Argon2, Params, Version}; use rand::RngCore; -use crate::models::{ - EncryptedData, ENCRYPTION_FORMAT_VERSION, ARGON2_MEMORY_KIB, ARGON2_ITERATIONS, ARGON2_LANES, -}; +use std::fs; fn derive_key_from_password(password: &str, salt: &[u8]) -> Result<[u8; 32], String> { let params = Params::new(ARGON2_MEMORY_KIB, ARGON2_ITERATIONS, ARGON2_LANES, Some(32)) @@ -38,7 +38,10 @@ pub fn encrypt_text(plaintext: String, password: String) -> Result Result Result { let p = PathBuf::from(path); let canonical = if p.exists() { - p.canonicalize().map_err(|e| format!("Invalid path: {}", e))? + p.canonicalize() + .map_err(|e| format!("Invalid path: {}", e))? } else { // For paths that don't exist yet (write/create), canonicalize the parent let parent = p.parent().ok_or("Invalid path: no parent directory")?; if parent.exists() { - let canonical_parent = parent.canonicalize().map_err(|e| format!("Invalid path: {}", e))?; + let canonical_parent = parent + .canonicalize() + .map_err(|e| format!("Invalid path: {}", e))?; canonical_parent.join(p.file_name().ok_or("Invalid path: no filename")?) } else { return Err("Invalid path: parent directory does not exist".to_string()); @@ -24,6 +28,9 @@ fn validate_path(path: &str) -> Result { if !canonical.starts_with(&home) { return Err("Access denied: path is outside home directory".to_string()); } + if is_sensitive_path(&canonical, &home) { + return Err("Access denied: protected credential location".to_string()); + } } // Reject paths containing ".." components @@ -36,15 +43,67 @@ fn validate_path(path: &str) -> Result { Ok(canonical) } +/// Deny well-known credential / secret locations even inside $HOME, so a +/// compromised webview cannot read keys or implant via these paths. +fn is_sensitive_path(canonical: &Path, home: &Path) -> bool { + let rel = match canonical.strip_prefix(home) { + Ok(r) => r, + Err(_) => return false, + }; + // Secret-store directories — denied wherever they appear in the path. + const DENY_DIRS: &[&str] = &[".ssh", ".aws", ".gnupg", ".kube", ".docker"]; + let comps: Vec<&str> = rel + .components() + .filter_map(|c| match c { + std::path::Component::Normal(o) => o.to_str(), + _ => None, + }) + .collect(); + if comps.iter().any(|c| DENY_DIRS.contains(c)) { + return true; + } + // macOS keychains live under ~/Library/Keychains. + if comps.first() == Some(&"Library") && comps.get(1) == Some(&"Keychains") { + return true; + } + // Credential / history files. + const DENY_FILES: &[&str] = &[ + ".netrc", + ".git-credentials", + ".npmrc", + ".pypirc", + ".zsh_history", + ".bash_history", + ]; + if let Some(name) = canonical.file_name().and_then(|n| n.to_str()) { + if DENY_FILES.contains(&name) { + return true; + } + } + false +} + #[tauri::command] pub fn read_file(path: String) -> Result { let safe_path = validate_path(&path)?; + if exceeds_size_limit(&safe_path, MAX_READ_FILE_BYTES) { + return Err(format!( + "File is too large to open (limit {} MB).", + MAX_READ_FILE_BYTES / (1024 * 1024) + )); + } fs::read_to_string(&safe_path).map_err(|e| e.to_string()) } #[tauri::command] pub fn read_file_bytes(path: String) -> Result, String> { let safe_path = validate_path(&path)?; + if exceeds_size_limit(&safe_path, MAX_READ_FILE_BYTES) { + return Err(format!( + "File is too large to open (limit {} MB).", + MAX_READ_FILE_BYTES / (1024 * 1024) + )); + } fs::read(&safe_path).map_err(|e| e.to_string()) } @@ -94,12 +153,10 @@ pub fn read_directory(path: String) -> Result, String> { }) .collect(); - files.sort_by(|a, b| { - match (a.is_dir, b.is_dir) { - (true, false) => std::cmp::Ordering::Less, - (false, true) => std::cmp::Ordering::Greater, - _ => a.name.to_lowercase().cmp(&b.name.to_lowercase()), - } + files.sort_by(|a, b| match (a.is_dir, b.is_dir) { + (true, false) => std::cmp::Ordering::Less, + (false, true) => std::cmp::Ordering::Greater, + _ => a.name.to_lowercase().cmp(&b.name.to_lowercase()), }); Ok(files) @@ -107,7 +164,12 @@ pub fn read_directory(path: String) -> Result, String> { #[tauri::command] pub fn file_exists(path: String) -> bool { - PathBuf::from(&path).exists() + // Scope-check so this cannot be used as an existence oracle for arbitrary + // host paths (e.g. /etc/passwd, other users' files). + match validate_path(&path) { + Ok(p) => p.exists(), + Err(_) => false, + } } #[tauri::command] @@ -140,3 +202,48 @@ pub fn rename_file(old_path: String, new_path: String) -> Result<(), String> { let safe_new = validate_path(&new_path)?; fs::rename(&safe_old, &safe_new).map_err(|e| e.to_string()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn denies_credential_locations() { + let home = Path::new("/Users/x"); + assert!(is_sensitive_path(Path::new("/Users/x/.ssh/id_rsa"), home)); + assert!(is_sensitive_path( + Path::new("/Users/x/.aws/credentials"), + home + )); + assert!(is_sensitive_path( + Path::new("/Users/x/.gnupg/secring.gpg"), + home + )); + assert!(is_sensitive_path( + Path::new("/Users/x/Library/Keychains/login.keychain-db"), + home + )); + assert!(is_sensitive_path(Path::new("/Users/x/.netrc"), home)); + assert!(is_sensitive_path( + Path::new("/Users/x/.git-credentials"), + home + )); + } + + #[test] + fn allows_ordinary_files() { + let home = Path::new("/Users/x"); + assert!(!is_sensitive_path( + Path::new("/Users/x/projects/app/src/main.rs"), + home + )); + assert!(!is_sensitive_path( + Path::new("/Users/x/notes/todo.md"), + home + )); + assert!(!is_sensitive_path( + Path::new("/Users/x/.config/app/settings.json"), + home + )); + } +} diff --git a/src-tauri/src/commands/git.rs b/src-tauri/src/commands/git.rs index affadb7..0815641 100644 --- a/src-tauri/src/commands/git.rs +++ b/src-tauri/src/commands/git.rs @@ -1,9 +1,47 @@ +use crate::models::{GitCommit, GitFileStatus, GitStatus}; use std::process::Command; -use crate::models::{GitStatus, GitFileStatus, GitCommit}; + +/// Reject branch/ref names that could be interpreted as git options +/// (option injection), e.g. `--upload-pack=...` or `-B`. +fn validate_ref_name(name: &str) -> Result<(), String> { + if name.is_empty() { + return Err("Invalid name: must not be empty".to_string()); + } + // A leading '-' lets the value be parsed as a git option (option injection), + // e.g. `--upload-pack=...`, `-B`, `--orphan`. + if name.starts_with('-') { + return Err("Invalid name: must not start with '-'".to_string()); + } + if name.contains('\0') || name.contains('\n') { + return Err("Invalid name: illegal characters".to_string()); + } + Ok(()) +} + +/// Build a hardened `git` invocation. Neutralizes config-driven code-execution +/// vectors that would otherwise fire when operating on an untrusted repository +/// (e.g. `core.fsmonitor` / `core.hooksPath` running on `git status`, and the +/// `ext::`/`file::` transports). Caller still appends the subcommand + `.current_dir`. +fn git_cmd_base() -> Command { + let mut cmd = Command::new("git"); + cmd.env("GIT_CONFIG_NOSYSTEM", "1") + .env("GIT_TERMINAL_PROMPT", "0") + .args([ + "-c", + "core.fsmonitor=false", + "-c", + "core.hooksPath=/dev/null", + "-c", + "protocol.ext.allow=never", + "-c", + "protocol.file.allow=user", + ]); + cmd +} #[tauri::command] pub fn git_status(path: String) -> Result { - let output = Command::new("git") + let output = git_cmd_base() .args(["status", "--porcelain", "-b"]) .current_dir(&path) .output() @@ -35,7 +73,15 @@ pub fn git_status(path: String) -> Result { } if branch_info.contains("ahead") { if let Some(n) = branch_info.split("ahead ").nth(1) { - ahead = n.split(']').next().unwrap_or("0").split(',').next().unwrap_or("0").parse().unwrap_or(0); + ahead = n + .split(']') + .next() + .unwrap_or("0") + .split(',') + .next() + .unwrap_or("0") + .parse() + .unwrap_or(0); } } if branch_info.contains("behind") { @@ -81,8 +127,12 @@ pub fn git_status(path: String) -> Result { #[tauri::command] pub fn git_log(path: String, count: i32) -> Result, String> { - let output = Command::new("git") - .args(["log", &format!("-{}", count), "--pretty=format:%H%x00%h%x00%s%x00%an%x00%ae%x00%ci"]) + let output = git_cmd_base() + .args([ + "log", + &format!("-{}", count), + "--pretty=format:%H%x00%h%x00%s%x00%an%x00%ae%x00%ci", + ]) .current_dir(&path) .output() .map_err(|e| e.to_string())?; @@ -120,7 +170,7 @@ pub fn git_diff(path: String, file_path: Option) -> Result) -> Result Result<(), String> { - Command::new("git") + git_cmd_base() .args(["add", "--", &file_path]) .current_dir(&path) .output() @@ -141,7 +191,7 @@ pub fn git_stage(path: String, file_path: String) -> Result<(), String> { #[tauri::command] pub fn git_unstage(path: String, file_path: String) -> Result<(), String> { - Command::new("git") + git_cmd_base() .args(["reset", "HEAD", "--", &file_path]) .current_dir(&path) .output() @@ -151,7 +201,7 @@ pub fn git_unstage(path: String, file_path: String) -> Result<(), String> { #[tauri::command] pub fn git_commit(path: String, message: String) -> Result<(), String> { - let output = Command::new("git") + let output = git_cmd_base() .args(["commit", "-m", &message]) .current_dir(&path) .output() @@ -165,7 +215,7 @@ pub fn git_commit(path: String, message: String) -> Result<(), String> { #[tauri::command] pub fn git_push(path: String) -> Result { - let output = Command::new("git") + let output = git_cmd_base() .args(["push"]) .current_dir(&path) .output() @@ -181,7 +231,7 @@ pub fn git_push(path: String) -> Result { #[tauri::command] pub fn git_pull(path: String) -> Result { - let output = Command::new("git") + let output = git_cmd_base() .args(["pull"]) .current_dir(&path) .output() @@ -197,7 +247,7 @@ pub fn git_pull(path: String) -> Result { #[tauri::command] pub fn git_blame(path: String, file_path: String) -> Result { - let output = Command::new("git") + let output = git_cmd_base() .args(["blame", "--", &file_path]) .current_dir(&path) .output() @@ -208,7 +258,7 @@ pub fn git_blame(path: String, file_path: String) -> Result { #[tauri::command] pub fn git_init(path: String) -> Result<(), String> { - let output = Command::new("git") + let output = git_cmd_base() .args(["init"]) .current_dir(&path) .output() @@ -222,7 +272,7 @@ pub fn git_init(path: String) -> Result<(), String> { #[tauri::command] pub fn git_branches(path: String) -> Result, String> { - let output = Command::new("git") + let output = git_cmd_base() .args(["branch", "-a"]) .current_dir(&path) .output() @@ -239,7 +289,8 @@ pub fn git_branches(path: String) -> Result, String> { #[tauri::command] pub fn git_checkout(path: String, branch: String) -> Result<(), String> { - let output = Command::new("git") + validate_ref_name(&branch)?; + let output = git_cmd_base() .args(["checkout", &branch]) .current_dir(&path) .output() @@ -253,12 +304,12 @@ pub fn git_checkout(path: String, branch: String) -> Result<(), String> { #[tauri::command] pub fn git_add_remote(path: String, url: String) -> Result<(), String> { - let _ = Command::new("git") + let _ = git_cmd_base() .args(["remote", "remove", "origin"]) .current_dir(&path) .output(); - let output = Command::new("git") + let output = git_cmd_base() .args(["remote", "add", "origin", &url]) .current_dir(&path) .output() @@ -272,7 +323,7 @@ pub fn git_add_remote(path: String, url: String) -> Result<(), String> { #[tauri::command] pub fn git_get_remote(path: String) -> Result { - let output = Command::new("git") + let output = git_cmd_base() .args(["remote", "get-url", "origin"]) .current_dir(&path) .output() @@ -287,7 +338,8 @@ pub fn git_get_remote(path: String) -> Result { #[tauri::command] pub fn git_create_branch(path: String, name: String) -> Result<(), String> { - let output = Command::new("git") + validate_ref_name(&name)?; + let output = git_cmd_base() .args(["checkout", "-b", &name]) .current_dir(&path) .output() @@ -299,9 +351,53 @@ pub fn git_create_branch(path: String, name: String) -> Result<(), String> { Ok(()) } +#[cfg(test)] +mod tests { + use super::*; + + fn run_git(repo: &std::path::Path, args: &[&str]) { + let status = git_cmd_base().args(args).current_dir(repo).output(); + assert!(status.is_ok(), "git {:?} failed to spawn", args); + } + + #[test] + fn validate_ref_rejects_option_like_names() { + assert!(validate_ref_name("--upload-pack=touch /tmp/x").is_err()); + assert!(validate_ref_name("-B").is_err()); + assert!(validate_ref_name("").is_err()); + } + + #[test] + fn validate_ref_allows_normal_branches() { + assert!(validate_ref_name("main").is_ok()); + assert!(validate_ref_name("feature/login").is_ok()); + assert!(validate_ref_name("release-2.0").is_ok()); + } + + #[test] + fn git_status_does_not_run_untrusted_fsmonitor() { + let dir = tempfile::tempdir().unwrap(); + let repo = dir.path(); + run_git(repo, &["init"]); + run_git(repo, &["config", "user.email", "t@t"]); + run_git(repo, &["config", "user.name", "t"]); + + let sentinel = repo.join("PWNED"); + let hook = format!("sh -c \"touch '{}'\"", sentinel.display()); + run_git(repo, &["config", "core.fsmonitor", &hook]); + + let _ = git_status(repo.to_string_lossy().to_string()); + + assert!( + !sentinel.exists(), + "core.fsmonitor command executed on git_status — config-driven RCE vector is open" + ); + } +} + #[tauri::command] pub fn git_stage_all(path: String) -> Result<(), String> { - let output = Command::new("git") + let output = git_cmd_base() .args(["add", "-A"]) .current_dir(&path) .output() @@ -315,7 +411,7 @@ pub fn git_stage_all(path: String) -> Result<(), String> { #[tauri::command] pub fn git_discard(path: String, file_path: String) -> Result<(), String> { - let output = Command::new("git") + let output = git_cmd_base() .args(["checkout", "--", &file_path]) .current_dir(&path) .output() diff --git a/src-tauri/src/commands/keychain.rs b/src-tauri/src/commands/keychain.rs index 884ba7b..21ca46a 100644 --- a/src-tauri/src/commands/keychain.rs +++ b/src-tauri/src/commands/keychain.rs @@ -1,12 +1,18 @@ -use std::process::Command; +use keyring::{Entry, Error as KeyringError}; /// Validate secret name contains only safe characters fn validate_name(name: &str) -> Result<(), String> { if name.is_empty() || name.len() > 128 { return Err("Invalid secret name: must be 1-128 characters".to_string()); } - if !name.chars().all(|c| c.is_alphanumeric() || c == '-' || c == '.' || c == '_') { - return Err("Invalid secret name: only alphanumeric, hyphens, dots, and underscores allowed".to_string()); + if !name + .chars() + .all(|c| c.is_alphanumeric() || c == '-' || c == '.' || c == '_') + { + return Err( + "Invalid secret name: only alphanumeric, hyphens, dots, and underscores allowed" + .to_string(), + ); } Ok(()) } @@ -15,63 +21,34 @@ fn keychain_service(name: &str) -> String { format!("com.rocketnote.app.{}", name) } -fn store_secret_value(name: &str, value: &str) -> Result<(), String> { +/// Build a Keychain entry. Uses the native Security framework (keyring crate) +/// so the secret value is never passed on the process command line (argv is +/// world-readable via `ps`). Service/account match the previous CLI scheme, +/// so secrets stored by older versions remain readable. +fn entry(name: &str) -> Result { validate_name(name)?; - let service = keychain_service(name); - let account = "secret"; - - let _ = Command::new("security") - .args(["delete-generic-password", "-s", &service, "-a", account]) - .output(); - - let output = Command::new("security") - .args(["add-generic-password", "-s", &service, "-a", account, "-w", value, "-U"]) - .output() - .map_err(|e| format!("Failed to store secret in Keychain: {}", e))?; - - if !output.status.success() { - return Err(format!("Keychain error: {}", String::from_utf8_lossy(&output.stderr))); - } + Entry::new(&keychain_service(name), "secret").map_err(|e| format!("Keychain error: {}", e)) +} - Ok(()) +fn store_secret_value(name: &str, value: &str) -> Result<(), String> { + entry(name)? + .set_password(value) + .map_err(|e| format!("Failed to store secret in Keychain: {}", e)) } fn get_secret_value(name: &str) -> Result { - validate_name(name)?; - let service = keychain_service(name); - let account = "secret"; - - let output = Command::new("security") - .args(["find-generic-password", "-s", &service, "-a", account, "-w"]) - .output() - .map_err(|e| format!("Failed to read secret from Keychain: {}", e))?; - - if !output.status.success() { - return Err("Secret not found in Keychain".to_string()); + match entry(name)?.get_password() { + Ok(v) => Ok(v), + Err(KeyringError::NoEntry) => Err("Secret not found in Keychain".to_string()), + Err(e) => Err(format!("Failed to read secret from Keychain: {}", e)), } - - Ok(String::from_utf8_lossy(&output.stdout).trim().to_string()) } fn delete_secret_value(name: &str) -> Result<(), String> { - validate_name(name)?; - let service = keychain_service(name); - let account = "secret"; - - let output = Command::new("security") - .args(["delete-generic-password", "-s", &service, "-a", account]) - .output() - .map_err(|e| format!("Failed to delete secret from Keychain: {}", e))?; - - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr).to_string(); - if stderr.to_lowercase().contains("could not be found") { - return Ok(()); - } - return Err(format!("Keychain error: {}", stderr)); + match entry(name)?.delete_credential() { + Ok(()) | Err(KeyringError::NoEntry) => Ok(()), + Err(e) => Err(format!("Failed to delete secret from Keychain: {}", e)), } - - Ok(()) } #[tauri::command] @@ -98,3 +75,28 @@ pub fn store_api_key(provider: String, key: String) -> Result<(), String> { pub fn get_api_key(provider: String) -> Result { get_secret_value(&format!("{}-api-key", provider)) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn rejects_invalid_names() { + assert!(validate_name("").is_err()); + assert!(validate_name("a/b").is_err()); + assert!(validate_name(&"x".repeat(129)).is_err()); + assert!(validate_name("openai-api-key").is_ok()); + } + + // Touches the real login Keychain (may show a macOS access prompt), so it is + // ignored by default. Run on macOS with: cargo test -- --ignored keychain_roundtrip + #[test] + #[ignore] + fn keychain_roundtrip() { + let name = "rocketnote-selftest"; + store_secret_value(name, "s3cr3t").unwrap(); + assert_eq!(get_secret_value(name).unwrap(), "s3cr3t"); + delete_secret_value(name).unwrap(); + assert!(get_secret_value(name).is_err()); + } +} diff --git a/src-tauri/src/commands/macros.rs b/src-tauri/src/commands/macros.rs index 545748c..9c83238 100644 --- a/src-tauri/src/commands/macros.rs +++ b/src-tauri/src/commands/macros.rs @@ -1,13 +1,14 @@ -use std::fs; +use crate::helpers::{get_data_dir, sanitize_id}; use crate::models::Macro; -use crate::helpers::get_data_dir; +use std::fs; #[tauri::command] pub fn save_macro(mac: Macro) -> Result<(), String> { + let safe_id = sanitize_id(&mac.id)?; let macros_dir = get_data_dir()?.join("macros"); fs::create_dir_all(¯os_dir).map_err(|e| e.to_string())?; - let macro_path = macros_dir.join(format!("{}.json", mac.id)); + let macro_path = macros_dir.join(format!("{}.json", safe_id)); let json = serde_json::to_string_pretty(&mac).map_err(|e| e.to_string())?; fs::write(macro_path, json).map_err(|e| e.to_string()) } @@ -34,6 +35,9 @@ pub fn list_macros() -> Result, String> { #[tauri::command] pub fn delete_macro(id: String) -> Result<(), String> { - let macro_path = get_data_dir()?.join("macros").join(format!("{}.json", id)); + let safe_id = sanitize_id(&id)?; + let macro_path = get_data_dir()? + .join("macros") + .join(format!("{}.json", safe_id)); fs::remove_file(macro_path).map_err(|e| e.to_string()) } diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs index ef24ca3..fb0ce90 100644 --- a/src-tauri/src/commands/mod.rs +++ b/src-tauri/src/commands/mod.rs @@ -1,13 +1,13 @@ +pub mod bookmarks; +pub mod cloud; +pub mod crypto; pub mod filesystem; -pub mod terminal; pub mod git; -pub mod utilities; +pub mod keychain; +pub mod macros; +pub mod search; pub mod sessions; pub mod snippets; pub mod stats; -pub mod bookmarks; -pub mod macros; -pub mod crypto; -pub mod search; -pub mod cloud; -pub mod keychain; +pub mod terminal; +pub mod utilities; diff --git a/src-tauri/src/commands/search.rs b/src-tauri/src/commands/search.rs index b0ed05d..3cde18f 100644 --- a/src-tauri/src/commands/search.rs +++ b/src-tauri/src/commands/search.rs @@ -1,8 +1,9 @@ -use std::fs; -use std::path::PathBuf; +use crate::helpers::{exceeds_size_limit, MAX_SEARCH_FILE_BYTES}; +use crate::models::{SearchMatch, SearchResult}; use ignore::WalkBuilder; use rayon::prelude::*; -use crate::models::{SearchMatch, SearchResult}; +use std::fs; +use std::path::PathBuf; #[tauri::command] pub fn global_search( @@ -35,11 +36,13 @@ pub fn global_search( .case_insensitive(true) .build() } - }.map_err(|e| format!("Invalid pattern: {}", e))?; + } + .map_err(|e| format!("Invalid pattern: {}", e))?; - const BINARY_EXTENSIONS: &[&str] = &["png", "jpg", "jpeg", "gif", "ico", "pdf", "zip", "tar", "gz", - "exe", "dll", "so", "dylib", "class", "jar", "wasm", "mp3", - "mp4", "mov", "avi", "mkv", "webm", "ttf", "woff", "woff2"]; + const BINARY_EXTENSIONS: &[&str] = &[ + "png", "jpg", "jpeg", "gif", "ico", "pdf", "zip", "tar", "gz", "exe", "dll", "so", "dylib", + "class", "jar", "wasm", "mp3", "mp4", "mov", "avi", "mkv", "webm", "ttf", "woff", "woff2", + ]; let mut files_to_search: Vec = Vec::new(); @@ -51,16 +54,14 @@ pub fn global_search( .ignore(true) .build(); - let file_pattern_regex = file_pattern.as_ref().map(|p| { - regex::Regex::new(p).unwrap_or_else(|_| regex::Regex::new(".*").unwrap()) - }); + let file_pattern_regex = file_pattern + .as_ref() + .map(|p| regex::Regex::new(p).unwrap_or_else(|_| regex::Regex::new(".*").unwrap())); for entry in walker.flatten() { let path = entry.path(); if path.is_file() { - let ext = path.extension() - .and_then(|e| e.to_str()) - .unwrap_or(""); + let ext = path.extension().and_then(|e| e.to_str()).unwrap_or(""); if BINARY_EXTENSIONS.contains(&ext.to_lowercase().as_str()) { continue; @@ -83,6 +84,11 @@ pub fn global_search( .flat_map(|path| { let mut matches = Vec::new(); + // Skip oversized files to bound peak memory under rayon. + if exceeds_size_limit(path, MAX_SEARCH_FILE_BYTES) { + return matches; + } + if let Ok(content) = fs::read_to_string(path) { for (line_idx, line) in content.lines().enumerate() { for mat in pattern.find_iter(line) { @@ -119,7 +125,11 @@ pub fn global_search( } #[tauri::command] -pub fn search_in_file(path: String, query: String, case_sensitive: bool) -> Result, String> { +pub fn search_in_file( + path: String, + query: String, + case_sensitive: bool, +) -> Result, String> { let content = fs::read_to_string(&path).map_err(|e| e.to_string())?; let pattern = if case_sensitive { @@ -128,7 +138,8 @@ pub fn search_in_file(path: String, query: String, case_sensitive: bool) -> Resu regex::RegexBuilder::new(®ex::escape(&query)) .case_insensitive(true) .build() - }.map_err(|e| e.to_string())?; + } + .map_err(|e| e.to_string())?; let mut matches = Vec::new(); @@ -166,7 +177,11 @@ mod tests { #[test] fn search_case_insensitive() { let dir = tempfile::tempdir().unwrap(); - let path = create_test_file(dir.path(), "test.txt", "Hello World\nhello world\nHELLO WORLD"); + let path = create_test_file( + dir.path(), + "test.txt", + "Hello World\nhello world\nHELLO WORLD", + ); let matches = search_in_file(path, "hello".to_string(), false).unwrap(); assert_eq!(matches.len(), 3); @@ -175,7 +190,11 @@ mod tests { #[test] fn search_case_sensitive() { let dir = tempfile::tempdir().unwrap(); - let path = create_test_file(dir.path(), "test.txt", "Hello World\nhello world\nHELLO WORLD"); + let path = create_test_file( + dir.path(), + "test.txt", + "Hello World\nhello world\nHELLO WORLD", + ); let matches = search_in_file(path, "hello".to_string(), true).unwrap(); assert_eq!(matches.len(), 1); @@ -185,7 +204,11 @@ mod tests { #[test] fn search_line_numbers_correct() { let dir = tempfile::tempdir().unwrap(); - let path = create_test_file(dir.path(), "test.txt", "line one\nfind me\nline three\nfind me again"); + let path = create_test_file( + dir.path(), + "test.txt", + "line one\nfind me\nline three\nfind me again", + ); let matches = search_in_file(path, "find".to_string(), true).unwrap(); assert_eq!(matches.len(), 2); @@ -207,7 +230,8 @@ mod tests { false, None, None, - ).unwrap(); + ) + .unwrap(); assert_eq!(result.total_matches, 2); assert!(result.total_files_searched >= 3); @@ -216,7 +240,11 @@ mod tests { #[test] fn global_search_regex() { let dir = tempfile::tempdir().unwrap(); - create_test_file(dir.path(), "test.txt", "error: something\nwarning: other\nerror: again"); + create_test_file( + dir.path(), + "test.txt", + "error: something\nwarning: other\nerror: again", + ); let result = global_search( dir.path().to_string_lossy().to_string(), @@ -225,7 +253,8 @@ mod tests { true, None, None, - ).unwrap(); + ) + .unwrap(); assert_eq!(result.total_matches, 2); } diff --git a/src-tauri/src/commands/sessions.rs b/src-tauri/src/commands/sessions.rs index 819fb62..3a88dff 100644 --- a/src-tauri/src/commands/sessions.rs +++ b/src-tauri/src/commands/sessions.rs @@ -1,11 +1,12 @@ -use std::fs; -use chrono::Utc; -use crate::models::{Session, SessionFile}; use crate::helpers::get_data_dir; +use crate::models::{Session, SessionFile}; +use chrono::Utc; +use std::fs; /// Sanitize session name to prevent path traversal fn sanitize_session_name(name: &str) -> Result { - let sanitized: String = name.chars() + let sanitized: String = name + .chars() .filter(|c| c.is_alphanumeric() || *c == '-' || *c == '_' || *c == ' ' || *c == '.') .collect(); @@ -39,7 +40,9 @@ pub fn save_session(name: String, files: Vec) -> Result<(), String> #[tauri::command] pub fn load_session(name: String) -> Result { let safe_name = sanitize_session_name(&name)?; - let session_path = get_data_dir()?.join("sessions").join(format!("{}.json", safe_name)); + let session_path = get_data_dir()? + .join("sessions") + .join(format!("{}.json", safe_name)); let json = fs::read_to_string(session_path).map_err(|e| e.to_string())?; serde_json::from_str(&json).map_err(|e| e.to_string()) } @@ -71,6 +74,8 @@ pub fn list_sessions() -> Result, String> { #[tauri::command] pub fn delete_session(name: String) -> Result<(), String> { let safe_name = sanitize_session_name(&name)?; - let session_path = get_data_dir()?.join("sessions").join(format!("{}.json", safe_name)); + let session_path = get_data_dir()? + .join("sessions") + .join(format!("{}.json", safe_name)); fs::remove_file(session_path).map_err(|e| e.to_string()) } diff --git a/src-tauri/src/commands/snippets.rs b/src-tauri/src/commands/snippets.rs index 87c52ab..1ae112f 100644 --- a/src-tauri/src/commands/snippets.rs +++ b/src-tauri/src/commands/snippets.rs @@ -1,13 +1,14 @@ -use std::fs; +use crate::helpers::{get_data_dir, sanitize_id}; use crate::models::Snippet; -use crate::helpers::get_data_dir; +use std::fs; #[tauri::command] pub fn save_snippet(snippet: Snippet) -> Result<(), String> { + let safe_id = sanitize_id(&snippet.id)?; let snippets_dir = get_data_dir()?.join("snippets"); fs::create_dir_all(&snippets_dir).map_err(|e| e.to_string())?; - let snippet_path = snippets_dir.join(format!("{}.json", snippet.id)); + let snippet_path = snippets_dir.join(format!("{}.json", safe_id)); let json = serde_json::to_string_pretty(&snippet).map_err(|e| e.to_string())?; fs::write(snippet_path, json).map_err(|e| e.to_string()) } @@ -34,6 +35,9 @@ pub fn list_snippets() -> Result, String> { #[tauri::command] pub fn delete_snippet(id: String) -> Result<(), String> { - let snippet_path = get_data_dir()?.join("snippets").join(format!("{}.json", id)); + let safe_id = sanitize_id(&id)?; + let snippet_path = get_data_dir()? + .join("snippets") + .join(format!("{}.json", safe_id)); fs::remove_file(snippet_path).map_err(|e| e.to_string()) } diff --git a/src-tauri/src/commands/stats.rs b/src-tauri/src/commands/stats.rs index 7d007a9..803063a 100644 --- a/src-tauri/src/commands/stats.rs +++ b/src-tauri/src/commands/stats.rs @@ -1,7 +1,7 @@ -use std::fs; -use chrono::Utc; -use crate::models::{CodingStats, DailyStats}; use crate::helpers::get_data_dir; +use crate::models::{CodingStats, DailyStats}; +use chrono::Utc; +use std::fs; #[tauri::command] pub fn get_coding_stats() -> Result { @@ -33,7 +33,10 @@ pub fn update_coding_stats(lines: i64, characters: i64, time_seconds: i64) -> Re stats.total_time_seconds += time_seconds; let today = Utc::now().format("%Y-%m-%d").to_string(); - let daily = stats.daily_stats.entry(today).or_insert(DailyStats::default()); + let daily = stats + .daily_stats + .entry(today) + .or_insert(DailyStats::default()); daily.lines += lines; daily.characters += characters; daily.time_seconds += time_seconds; diff --git a/src-tauri/src/commands/terminal.rs b/src-tauri/src/commands/terminal.rs index 86f3f01..b91b6bd 100644 --- a/src-tauri/src/commands/terminal.rs +++ b/src-tauri/src/commands/terminal.rs @@ -1,9 +1,10 @@ +use crate::state::{PtySession, PTY_SESSIONS}; +use portable_pty::{native_pty_system, CommandBuilder, PtySize}; +use tauri::Emitter; // v2: `Window::emit` requires the Emitter trait in scope use std::io::{Read, Write}; +use std::process::{Command, Stdio}; use std::sync::{Arc, Mutex}; use std::thread; -use std::process::{Command, Stdio}; -use portable_pty::{native_pty_system, CommandBuilder, PtySize}; -use crate::state::{PtySession, PTY_SESSIONS}; /// Check if a command is dangerous. Returns a warning description if so. fn check_dangerous_command(cmd: &str) -> Option { @@ -34,8 +35,14 @@ fn check_dangerous_command(cmd: &str) -> Option { ("killall -9", "Kill all matching processes"), ("pkill -9", "Kill all matching processes"), // Permission disasters - ("chmod -r 777 /", "Remove all file permissions recursively on /"), - ("chmod -r 000 /", "Remove all file permissions recursively on /"), + ( + "chmod -r 777 /", + "Remove all file permissions recursively on /", + ), + ( + "chmod -r 000 /", + "Remove all file permissions recursively on /", + ), ("chown -r", "Recursive ownership change"), // Pipe to shell (remote code execution) ("curl|sh", "Piping remote content to shell"), @@ -63,14 +70,26 @@ fn check_dangerous_command(cmd: &str) -> Option { // Check for piped curl/wget to shell more flexibly if (lower.contains("curl ") || lower.contains("wget ")) - && (lower.contains("| sh") || lower.contains("| bash") || lower.contains("|sh") || lower.contains("|bash")) + && (lower.contains("| sh") + || lower.contains("| bash") + || lower.contains("|sh") + || lower.contains("|bash")) { - return Some("Piping remote content to shell — potential remote code execution".to_string()); + return Some( + "Piping remote content to shell — potential remote code execution".to_string(), + ); } None } +/// True only if `data` consists solely of confirmation control keystrokes +/// (Enter / newline / Ctrl-C) used to answer a danger prompt. Used to ensure +/// `pty_write_force` can never be abused to inject command text. +fn is_confirmation_keystrokes(data: &str) -> bool { + !data.is_empty() && data.bytes().all(|b| matches!(b, b'\r' | b'\n' | 0x03)) +} + #[tauri::command] pub fn pty_spawn(id: String, cwd: Option, window: tauri::Window) -> Result<(), String> { let pty_system = native_pty_system(); @@ -119,11 +138,15 @@ pub fn pty_spawn(id: String, cwd: Option, window: tauri::Window) -> Resu { let mut sessions = PTY_SESSIONS.lock().map_err(|e| e.to_string())?; - sessions.insert(id.clone(), PtySession { - writer: Arc::new(Mutex::new(writer)), - master: Arc::new(Mutex::new(pair.master)), - command_buffer: Arc::new(Mutex::new(String::new())), - }); + sessions.insert( + id.clone(), + PtySession { + writer: Arc::new(Mutex::new(writer)), + master: Arc::new(Mutex::new(pair.master)), + command_buffer: Arc::new(Mutex::new(String::new())), + pending_danger: Arc::new(Mutex::new(false)), + }, + ); } let session_id = id.clone(); @@ -170,14 +193,23 @@ pub fn pty_write(id: String, data: String, window: tauri::Window) -> Result<(), cmd_buf.clear(); if let Some(warning) = check_dangerous_command(&command) { - // Emit warning event instead of executing + // Flag a pending confirmation (consumed one-shot by + // pty_write_force) and emit a warning instead of executing. + if let Ok(mut pd) = session.pending_danger.lock() { + *pd = true; + } let _ = window.emit("pty-danger-warning", (&id, &command, &warning)); return Ok(()); } - // Safe command — write all data through + // Safe command — clear any stale pending flag and write through. + if let Ok(mut pd) = session.pending_danger.lock() { + *pd = false; + } let mut writer = session.writer.lock().map_err(|e| e.to_string())?; - writer.write_all(data.as_bytes()).map_err(|e| e.to_string())?; + writer + .write_all(data.as_bytes()) + .map_err(|e| e.to_string())?; writer.flush().map_err(|e| e.to_string())?; return Ok(()); } @@ -199,7 +231,9 @@ pub fn pty_write(id: String, data: String, window: tauri::Window) -> Result<(), // No Enter in this data — just pass through (typing characters) let mut writer = session.writer.lock().map_err(|e| e.to_string())?; - writer.write_all(data.as_bytes()).map_err(|e| e.to_string())?; + writer + .write_all(data.as_bytes()) + .map_err(|e| e.to_string())?; writer.flush().map_err(|e| e.to_string())?; Ok(()) } else { @@ -213,8 +247,25 @@ pub fn pty_write(id: String, data: String, window: tauri::Window) -> Result<(), pub fn pty_write_force(id: String, data: String) -> Result<(), String> { let sessions = PTY_SESSIONS.lock().map_err(|e| e.to_string())?; if let Some(session) = sessions.get(&id) { + // Force-write may ONLY deliver the confirmation keystrokes (Enter / Ctrl-C) + // that answer a danger prompt — never arbitrary command text — and only + // when a danger warning is actually pending (consumed one-shot). This closes + // the bypass where the webview called pty_write_force directly to run a + // blocked command. + if !is_confirmation_keystrokes(&data) { + return Err("pty_write_force only accepts confirmation keystrokes".to_string()); + } + let approved = { + let mut pd = session.pending_danger.lock().map_err(|e| e.to_string())?; + std::mem::replace(&mut *pd, false) + }; + if !approved { + return Err("No pending dangerous-command confirmation for this session".to_string()); + } let mut writer = session.writer.lock().map_err(|e| e.to_string())?; - writer.write_all(data.as_bytes()).map_err(|e| e.to_string())?; + writer + .write_all(data.as_bytes()) + .map_err(|e| e.to_string())?; writer.flush().map_err(|e| e.to_string())?; Ok(()) } else { @@ -227,12 +278,14 @@ pub fn pty_resize(id: String, rows: u16, cols: u16) -> Result<(), String> { let sessions = PTY_SESSIONS.lock().map_err(|e| e.to_string())?; if let Some(session) = sessions.get(&id) { let master = session.master.lock().map_err(|e| e.to_string())?; - master.resize(PtySize { - rows, - cols, - pixel_width: 0, - pixel_height: 0, - }).map_err(|e| format!("Failed to resize PTY: {}", e))?; + master + .resize(PtySize { + rows, + cols, + pixel_width: 0, + pixel_height: 0, + }) + .map_err(|e| format!("Failed to resize PTY: {}", e))?; Ok(()) } else { Err("Session not found".to_string()) @@ -259,7 +312,10 @@ pub fn execute_command(command: String, cwd: Option) -> Result /dev/", "dd if=", ":(){ :|:& };:"]; for pattern in &dangerous_patterns { if trimmed.contains(pattern) { - return Err(format!("Command blocked for safety: contains '{}'", pattern)); + return Err(format!( + "Command blocked for safety: contains '{}'", + pattern + )); } } @@ -272,10 +328,12 @@ pub fn execute_command(command: String, cwd: Option) -> Result = parts.collect(); + // Interpreters (bash/zsh/python3/node/swift) are deliberately excluded: they + // execute arbitrary script files and thereby defeat the metacharacter/danger + // filters above. Use the gated built-in terminal for those. let allowed_programs = [ - "git", "ls", "pwd", "cat", "echo", "head", "tail", "grep", "find", "wc", - "touch", "mkdir", "cp", "mv", "rm", "python3", "node", "npm", "cargo", - "rustc", "swift", "swiftc", "bash", "zsh" + "git", "ls", "pwd", "cat", "echo", "head", "tail", "grep", "find", "wc", "touch", "mkdir", + "cp", "mv", "rm", "npm", "cargo", "rustc", "swiftc", ]; if !allowed_programs.contains(&program) { return Err(format!("'{}' is not allowed in quick-run mode. Use the built-in terminal for unrestricted commands.", program)); @@ -365,4 +423,26 @@ mod tests { assert!(check_dangerous_command("mkdir new_dir").is_none()); assert!(check_dangerous_command("cat README.md").is_none()); } + + #[test] + fn confirmation_keystrokes_accepts_control_only() { + assert!(is_confirmation_keystrokes("\r")); + assert!(is_confirmation_keystrokes("\n")); + assert!(is_confirmation_keystrokes("\u{3}")); // Ctrl-C + } + + #[test] + fn confirmation_keystrokes_rejects_command_text() { + assert!(!is_confirmation_keystrokes("rm -rf ~\r")); + assert!(!is_confirmation_keystrokes("ls")); + assert!(!is_confirmation_keystrokes("")); // empty is not a confirmation + } + + #[test] + fn execute_command_blocks_interpreters() { + assert!(execute_command("bash evil.sh".to_string(), None).is_err()); + assert!(execute_command("zsh evil.sh".to_string(), None).is_err()); + assert!(execute_command("python3 evil.py".to_string(), None).is_err()); + assert!(execute_command("node evil.js".to_string(), None).is_err()); + } } diff --git a/src-tauri/src/commands/utilities.rs b/src-tauri/src/commands/utilities.rs index 289b7cd..a46e0d8 100644 --- a/src-tauri/src/commands/utilities.rs +++ b/src-tauri/src/commands/utilities.rs @@ -1,32 +1,31 @@ -use chrono::{Utc, TimeZone, DateTime}; use crate::models::RegexMatch; +use chrono::{DateTime, TimeZone, Utc}; #[tauri::command] pub fn format_json(input: String) -> Result { - let value: serde_json::Value = serde_json::from_str(&input) - .map_err(|e| format!("JSON Parse Error: {}", e))?; - serde_json::to_string_pretty(&value) - .map_err(|e| e.to_string()) + let value: serde_json::Value = + serde_json::from_str(&input).map_err(|e| format!("JSON Parse Error: {}", e))?; + serde_json::to_string_pretty(&value).map_err(|e| e.to_string()) } #[tauri::command] pub fn minify_json(input: String) -> Result { - let value: serde_json::Value = serde_json::from_str(&input) - .map_err(|e| format!("JSON Parse Error: {}", e))?; - serde_json::to_string(&value) - .map_err(|e| e.to_string()) + let value: serde_json::Value = + serde_json::from_str(&input).map_err(|e| format!("JSON Parse Error: {}", e))?; + serde_json::to_string(&value).map_err(|e| e.to_string()) } #[tauri::command] pub fn base64_encode(input: String) -> String { - use base64::{Engine as _, engine::general_purpose}; + use base64::{engine::general_purpose, Engine as _}; general_purpose::STANDARD.encode(input.as_bytes()) } #[tauri::command] pub fn base64_decode(input: String) -> Result { - use base64::{Engine as _, engine::general_purpose}; - let bytes = general_purpose::STANDARD.decode(&input) + use base64::{engine::general_purpose, Engine as _}; + let bytes = general_purpose::STANDARD + .decode(&input) .map_err(|e| e.to_string())?; String::from_utf8(bytes).map_err(|e| e.to_string()) } @@ -38,7 +37,7 @@ pub fn hash_md5(input: String) -> String { #[tauri::command] pub fn hash_sha256(input: String) -> String { - use sha2::{Sha256, Digest}; + use sha2::{Digest, Sha256}; let mut hasher = Sha256::new(); hasher.update(input.as_bytes()); format!("{:x}", hasher.finalize()) @@ -80,7 +79,8 @@ pub fn regex_test(pattern: String, text: String, flags: String) -> Result = re.find_iter(&text) + let matches: Vec = re + .find_iter(&text) .map(|m| RegexMatch { text: m.as_str().to_string(), start: m.start(), diff --git a/src-tauri/src/helpers.rs b/src-tauri/src/helpers.rs index 4084268..83b09c1 100644 --- a/src-tauri/src/helpers.rs +++ b/src-tauri/src/helpers.rs @@ -1,7 +1,82 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; pub(crate) fn get_data_dir() -> Result { dirs::data_dir() .map(|p| p.join("notepad-mac")) .ok_or_else(|| "Could not find data directory".to_string()) } + +/// Maximum bytes the editor will read into memory for a single file. +pub(crate) const MAX_READ_FILE_BYTES: u64 = 50 * 1024 * 1024; // 50 MiB +/// Maximum per-file size that global search will load. +pub(crate) const MAX_SEARCH_FILE_BYTES: u64 = 5 * 1024 * 1024; // 5 MiB + +/// Sanitize a filename component (e.g. a snippet/macro id) so it can never +/// escape its intended directory. Rejects empty, traversal, separators and NUL. +pub(crate) fn sanitize_id(id: &str) -> Result { + if id.is_empty() { + return Err("Invalid id: must not be empty".to_string()); + } + if id.contains("..") || id.contains('/') || id.contains('\\') || id.contains('\0') { + return Err("Invalid id: path separators and traversal are not allowed".to_string()); + } + // ids are app-generated (uuid / timestamp / slug); keep the charset conservative. + if !id + .chars() + .all(|c| c.is_alphanumeric() || c == '-' || c == '_' || c == '.') + { + return Err("Invalid id: illegal characters".to_string()); + } + Ok(id.to_string()) +} + +/// Return true if the file at `path` is larger than `max` bytes. +/// A path that cannot be stat'd is treated as not exceeding the limit. +pub(crate) fn exceeds_size_limit(path: &Path, max: u64) -> bool { + std::fs::metadata(path) + .map(|m| m.len() > max) + .unwrap_or(false) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sanitize_id_keeps_plain_ids() { + assert_eq!(sanitize_id("abc123").unwrap(), "abc123"); + assert_eq!(sanitize_id("my-snippet_1").unwrap(), "my-snippet_1"); + } + + #[test] + fn sanitize_id_rejects_traversal() { + assert!(sanitize_id("../../etc/passwd").is_err()); + assert!(sanitize_id("..").is_err()); + assert!(sanitize_id("a/b").is_err()); + assert!(sanitize_id("a\\b").is_err()); + } + + #[test] + fn sanitize_id_rejects_absolute_and_empty() { + assert!(sanitize_id("/etc/passwd").is_err()); + assert!(sanitize_id("").is_err()); + assert!(sanitize_id("foo\0bar").is_err()); + } + + #[test] + fn exceeds_size_limit_detects_large_file() { + let dir = tempfile::tempdir().unwrap(); + let p = dir.path().join("f.txt"); + std::fs::write(&p, b"hello").unwrap(); // 5 bytes + assert!(exceeds_size_limit(&p, 4)); + assert!(!exceeds_size_limit(&p, 5)); + assert!(!exceeds_size_limit(&p, 1000)); + } + + #[test] + fn exceeds_size_limit_missing_file_is_false() { + let dir = tempfile::tempdir().unwrap(); + let p = dir.path().join("does-not-exist"); + assert!(!exceeds_size_limit(&p, 0)); + } +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 21d8d4e..3ebd032 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -3,148 +3,146 @@ windows_subsystem = "windows" )] +mod commands; +mod helpers; mod models; mod state; -mod helpers; -mod commands; fn main() { - use tauri::{Menu, MenuItem, Submenu, CustomMenuItem, Manager}; - - // File menu - let file_menu = Submenu::new("File", Menu::new() - .add_item(CustomMenuItem::new("new", "New").accelerator("CmdOrCtrl+N")) - .add_item(CustomMenuItem::new("open", "Open...").accelerator("CmdOrCtrl+O")) - .add_item(CustomMenuItem::new("open_folder", "Open Folder...").accelerator("CmdOrCtrl+Shift+O")) - .add_native_item(MenuItem::Separator) - .add_item(CustomMenuItem::new("save", "Save").accelerator("CmdOrCtrl+S")) - .add_item(CustomMenuItem::new("save_as", "Save As...").accelerator("CmdOrCtrl+Shift+S")) - .add_item(CustomMenuItem::new("save_all", "Save All").accelerator("CmdOrCtrl+Alt+S")) - .add_native_item(MenuItem::Separator) - .add_item(CustomMenuItem::new("close", "Close Tab").accelerator("CmdOrCtrl+W")) - .add_item(CustomMenuItem::new("close_all", "Close All Tabs").accelerator("CmdOrCtrl+Shift+W")) - ); + use tauri::menu::{MenuBuilder, MenuItemBuilder, SubmenuBuilder}; + use tauri::{Emitter, Manager}; - // Edit menu - let edit_menu = Submenu::new("Edit", Menu::new() - .add_native_item(MenuItem::Undo) - .add_native_item(MenuItem::Redo) - .add_native_item(MenuItem::Separator) - .add_native_item(MenuItem::Cut) - .add_native_item(MenuItem::Copy) - .add_native_item(MenuItem::Paste) - .add_native_item(MenuItem::SelectAll) - .add_native_item(MenuItem::Separator) - .add_item(CustomMenuItem::new("find", "Find...").accelerator("CmdOrCtrl+F")) - .add_item(CustomMenuItem::new("find_replace", "Find and Replace...").accelerator("CmdOrCtrl+H")) - .add_item(CustomMenuItem::new("global_search", "Search in Files...").accelerator("CmdOrCtrl+Shift+F")) - ); + tauri::Builder::default() + .plugin(tauri_plugin_dialog::init()) + .plugin(tauri_plugin_clipboard_manager::init()) + .plugin(tauri_plugin_shell::init()) + .plugin(tauri_plugin_fs::init()) + .plugin(tauri_plugin_updater::Builder::new().build()) + .plugin(tauri_plugin_http::init()) + .plugin(tauri_plugin_notification::init()) + .menu(|handle| { + let file = SubmenuBuilder::new(handle, "File") + .item(&MenuItemBuilder::with_id("new", "New").accelerator("CmdOrCtrl+N").build(handle)?) + .item(&MenuItemBuilder::with_id("open", "Open...").accelerator("CmdOrCtrl+O").build(handle)?) + .item(&MenuItemBuilder::with_id("open_folder", "Open Folder...").accelerator("CmdOrCtrl+Shift+O").build(handle)?) + .separator() + .item(&MenuItemBuilder::with_id("save", "Save").accelerator("CmdOrCtrl+S").build(handle)?) + .item(&MenuItemBuilder::with_id("save_as", "Save As...").accelerator("CmdOrCtrl+Shift+S").build(handle)?) + .item(&MenuItemBuilder::with_id("save_all", "Save All").accelerator("CmdOrCtrl+Alt+S").build(handle)?) + .separator() + .item(&MenuItemBuilder::with_id("close", "Close Tab").accelerator("CmdOrCtrl+W").build(handle)?) + .item(&MenuItemBuilder::with_id("close_all", "Close All Tabs").accelerator("CmdOrCtrl+Shift+W").build(handle)?) + .build()?; - // View menu - let view_menu = Submenu::new("View", Menu::new() - .add_item(CustomMenuItem::new("sidebar", "Toggle Sidebar").accelerator("CmdOrCtrl+B")) - .add_item(CustomMenuItem::new("terminal", "Toggle Terminal").accelerator("CmdOrCtrl+`")) - .add_item(CustomMenuItem::new("split", "Split Editor").accelerator("CmdOrCtrl+Shift+E")) - .add_native_item(MenuItem::Separator) - .add_item(CustomMenuItem::new("focus_mode", "Focus Mode").accelerator("CmdOrCtrl+Shift+Enter")) - .add_native_item(MenuItem::Separator) - .add_item(CustomMenuItem::new("zoom_in", "Zoom In").accelerator("CmdOrCtrl+=")) - .add_item(CustomMenuItem::new("zoom_out", "Zoom Out").accelerator("CmdOrCtrl+-")) - .add_item(CustomMenuItem::new("zoom_reset", "Reset Zoom").accelerator("CmdOrCtrl+0")) - ); + let edit = SubmenuBuilder::new(handle, "Edit") + .undo() + .redo() + .separator() + .cut() + .copy() + .paste() + .select_all() + .separator() + .item(&MenuItemBuilder::with_id("find", "Find...").accelerator("CmdOrCtrl+F").build(handle)?) + .item(&MenuItemBuilder::with_id("find_replace", "Find and Replace...").accelerator("CmdOrCtrl+H").build(handle)?) + .item(&MenuItemBuilder::with_id("global_search", "Search in Files...").accelerator("CmdOrCtrl+Shift+F").build(handle)?) + .build()?; - // Tools menu - let tools_menu = Submenu::new("Tools", Menu::new() - .add_item(CustomMenuItem::new("git", "Git Panel").accelerator("CmdOrCtrl+Shift+G")) - .add_item(CustomMenuItem::new("ai", "AI Assistant").accelerator("CmdOrCtrl+K")) - .add_native_item(MenuItem::Separator) - .add_item(CustomMenuItem::new("encryption", "Encryption")) - .add_item(CustomMenuItem::new("cloud", "Cloud Storage")) - .add_item(CustomMenuItem::new("screenshot", "Code Screenshot")) - .add_native_item(MenuItem::Separator) - .add_item(CustomMenuItem::new("dev_tools", "Developer Tools")) - .add_item(CustomMenuItem::new("snippets", "Snippets")) - .add_item(CustomMenuItem::new("settings", "Settings...").accelerator("CmdOrCtrl+,")) - ); + let view = SubmenuBuilder::new(handle, "View") + .item(&MenuItemBuilder::with_id("sidebar", "Toggle Sidebar").accelerator("CmdOrCtrl+B").build(handle)?) + .item(&MenuItemBuilder::with_id("terminal", "Toggle Terminal").accelerator("CmdOrCtrl+`").build(handle)?) + .item(&MenuItemBuilder::with_id("split", "Split Editor").accelerator("CmdOrCtrl+Shift+E").build(handle)?) + .separator() + .item(&MenuItemBuilder::with_id("focus_mode", "Focus Mode").accelerator("CmdOrCtrl+Shift+Enter").build(handle)?) + .separator() + .item(&MenuItemBuilder::with_id("zoom_in", "Zoom In").accelerator("CmdOrCtrl+=").build(handle)?) + .item(&MenuItemBuilder::with_id("zoom_out", "Zoom Out").accelerator("CmdOrCtrl+-").build(handle)?) + .item(&MenuItemBuilder::with_id("zoom_reset", "Reset Zoom").accelerator("CmdOrCtrl+0").build(handle)?) + .build()?; - // Window menu - let window_menu = Submenu::new("Window", Menu::new() - .add_native_item(MenuItem::Minimize) - .add_native_item(MenuItem::Zoom) - .add_native_item(MenuItem::Separator) - .add_item(CustomMenuItem::new("theme_toggle", "Toggle Dark/Light Theme")) - ); + let tools = SubmenuBuilder::new(handle, "Tools") + .item(&MenuItemBuilder::with_id("git", "Git Panel").accelerator("CmdOrCtrl+Shift+G").build(handle)?) + .item(&MenuItemBuilder::with_id("ai", "AI Assistant").accelerator("CmdOrCtrl+K").build(handle)?) + .separator() + .text("encryption", "Encryption") + .text("cloud", "Cloud Storage") + .text("screenshot", "Code Screenshot") + .separator() + .text("dev_tools", "Developer Tools") + .text("snippets", "Snippets") + .item(&MenuItemBuilder::with_id("settings", "Settings...").accelerator("CmdOrCtrl+,").build(handle)?) + .build()?; - // Help menu - let help_menu = Submenu::new("Help", Menu::new() - .add_item(CustomMenuItem::new("about", "About RocketNote")) - .add_native_item(MenuItem::Separator) - .add_item(CustomMenuItem::new("documentation", "Documentation")) - .add_item(CustomMenuItem::new("shortcuts", "Keyboard Shortcuts")) - .add_item(CustomMenuItem::new("privacy", "Privacy Policy")) - .add_native_item(MenuItem::Separator) - .add_item(CustomMenuItem::new("support", "Support the Author")) - ); + let window_menu = SubmenuBuilder::new(handle, "Window") + .minimize() + .separator() + .text("theme_toggle", "Toggle Dark/Light Theme") + .build()?; - let menu = Menu::new() - .add_submenu(file_menu) - .add_submenu(edit_menu) - .add_submenu(view_menu) - .add_submenu(tools_menu) - .add_submenu(window_menu) - .add_submenu(help_menu); + let help = SubmenuBuilder::new(handle, "Help") + .text("about", "About RocketNote") + .separator() + .text("documentation", "Documentation") + .text("shortcuts", "Keyboard Shortcuts") + .text("privacy", "Privacy Policy") + .separator() + .text("support", "Support the Author") + .build()?; - tauri::Builder::default() - .menu(menu) + MenuBuilder::new(handle) + .items(&[&file, &edit, &view, &tools, &window_menu, &help]) + .build() + }) .setup(|app| { let args: Vec = std::env::args().collect(); if args.len() > 1 { let file_path = &args[1]; if !file_path.starts_with('-') && std::path::Path::new(file_path).exists() { - let window = app.get_window("main").unwrap(); - let path = file_path.clone(); - std::thread::spawn(move || { - std::thread::sleep(std::time::Duration::from_millis(500)); - let _ = window.emit("open-file", path); - }); + if let Some(window) = app.get_webview_window("main") { + let path = file_path.clone(); + std::thread::spawn(move || { + std::thread::sleep(std::time::Duration::from_millis(500)); + let _ = window.emit("open-file", path); + }); + } } } Ok(()) }) - .on_menu_event(|event| { - let window = event.window(); - match event.menu_item_id() { - "new" => { let _ = window.emit("menu-new", ()); } - "open" => { let _ = window.emit("menu-open", ()); } - "open_folder" => { let _ = window.emit("menu-open-folder", ()); } - "save" => { let _ = window.emit("menu-save", ()); } - "save_as" => { let _ = window.emit("menu-save-as", ()); } - "save_all" => { let _ = window.emit("menu-save-all", ()); } - "close" => { let _ = window.emit("menu-close", ()); } - "close_all" => { let _ = window.emit("menu-close-all", ()); } - "find" => { let _ = window.emit("menu-find", ()); } - "find_replace" => { let _ = window.emit("menu-find-replace", ()); } - "global_search" => { let _ = window.emit("menu-global-search", ()); } - "sidebar" => { let _ = window.emit("menu-sidebar", ()); } - "terminal" => { let _ = window.emit("menu-terminal", ()); } - "split" => { let _ = window.emit("menu-split", ()); } - "focus_mode" => { let _ = window.emit("menu-focus-mode", ()); } - "git" => { let _ = window.emit("menu-git", ()); } - "ai" => { let _ = window.emit("menu-ai", ()); } - "encryption" => { let _ = window.emit("menu-encryption", ()); } - "cloud" => { let _ = window.emit("menu-cloud", ()); } - "screenshot" => { let _ = window.emit("menu-screenshot", ()); } - "dev_tools" => { let _ = window.emit("menu-dev-tools", ()); } - "snippets" => { let _ = window.emit("menu-snippets", ()); } - "settings" => { let _ = window.emit("menu-settings", ()); } - "theme_toggle" => { let _ = window.emit("menu-theme-toggle", ()); } - "about" => { let _ = window.emit("menu-about", ()); } - "support" => { let _ = window.emit("menu-support", ()); } - "documentation" => { let _ = window.emit("menu-documentation", ()); } - "shortcuts" => { let _ = window.emit("menu-shortcuts", ()); } - "privacy" => { let _ = window.emit("menu-privacy", ()); } - _ => {} - } + .on_menu_event(|app, event| { + let event_name = match event.id().as_ref() { + "new" => "menu-new", + "open" => "menu-open", + "open_folder" => "menu-open-folder", + "save" => "menu-save", + "save_as" => "menu-save-as", + "save_all" => "menu-save-all", + "close" => "menu-close", + "close_all" => "menu-close-all", + "find" => "menu-find", + "find_replace" => "menu-find-replace", + "global_search" => "menu-global-search", + "sidebar" => "menu-sidebar", + "terminal" => "menu-terminal", + "split" => "menu-split", + "focus_mode" => "menu-focus-mode", + "git" => "menu-git", + "ai" => "menu-ai", + "encryption" => "menu-encryption", + "cloud" => "menu-cloud", + "screenshot" => "menu-screenshot", + "dev_tools" => "menu-dev-tools", + "snippets" => "menu-snippets", + "settings" => "menu-settings", + "theme_toggle" => "menu-theme-toggle", + "about" => "menu-about", + "support" => "menu-support", + "documentation" => "menu-documentation", + "shortcuts" => "menu-shortcuts", + "privacy" => "menu-privacy", + _ => return, + }; + let _ = app.emit(event_name, ()); }) .invoke_handler(tauri::generate_handler![ // File system @@ -222,6 +220,7 @@ fn main() { commands::search::global_search, commands::search::search_in_file, // Cloud Storage + commands::cloud::generate_pkce, commands::cloud::open_oauth_window, commands::cloud::exchange_oauth_token, commands::cloud::refresh_oauth_token, diff --git a/src-tauri/src/models.rs b/src-tauri/src/models.rs index c4688aa..1f65939 100644 --- a/src-tauri/src/models.rs +++ b/src-tauri/src/models.rs @@ -12,7 +12,10 @@ pub(crate) fn default_encryption_version() -> u8 { } pub(crate) fn default_kdf_metadata() -> String { - format!("argon2id:m={}KiB,t={},p={}", ARGON2_MEMORY_KIB, ARGON2_ITERATIONS, ARGON2_LANES) + format!( + "argon2id:m={}KiB,t={},p={}", + ARGON2_MEMORY_KIB, ARGON2_ITERATIONS, ARGON2_LANES + ) } // File System diff --git a/src-tauri/src/state.rs b/src-tauri/src/state.rs index 8988945..10e0d88 100644 --- a/src-tauri/src/state.rs +++ b/src-tauri/src/state.rs @@ -1,12 +1,16 @@ -use std::sync::{Arc, Mutex}; +use portable_pty::MasterPty; use std::collections::HashMap; use std::io::Write; -use portable_pty::MasterPty; +use std::sync::{Arc, Mutex}; pub(crate) struct PtySession { pub writer: Arc>>, pub master: Arc>>, pub command_buffer: Arc>, + /// Set when a dangerous command is flagged and awaiting user confirmation. + /// `pty_write_force` consumes it one-shot so it cannot be abused to bypass + /// the safety check with arbitrary command text. + pub pending_danger: Arc>, } lazy_static::lazy_static! { diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 878590f..7d33f58 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -2,110 +2,58 @@ "build": { "beforeDevCommand": "npm run dev", "beforeBuildCommand": "npm run build", - "devPath": "http://localhost:5173", - "distDir": "../dist" + "frontendDist": "../dist", + "devUrl": "http://localhost:5173" }, - "package": { - "productName": "RocketNote", - "version": "2.0.0" - }, - "tauri": { - "allowlist": { - "all": false, - "dialog": { - "all": true, - "ask": true, - "confirm": true, - "message": true, - "open": true, - "save": true - }, - "fs": { - "all": true, - "readFile": true, - "writeFile": true, - "readDir": true, - "copyFile": true, - "createDir": true, - "removeDir": true, - "removeFile": true, - "renameFile": true, - "exists": true, - "scope": ["$HOME/**", "$DOCUMENT/**", "$DESKTOP/**", "$DOWNLOAD/**"] - }, - "path": { - "all": true - }, - "shell": { - "all": false, - "open": true, - "execute": true, - "sidecar": false, - "scope": [ - { - "name": "git", - "cmd": "git", - "args": true - } - ] - }, - "window": { - "all": true - }, - "clipboard": { - "all": true, - "readText": true, - "writeText": true - }, - "notification": { - "all": true - }, - "http": { - "all": false, - "request": true, - "scope": ["https://api.openai.com/*", "https://api.anthropic.com/*", "https://api.deepseek.com/*", "https://www.googleapis.com/*", "https://oauth2.googleapis.com/*", "https://api.dropboxapi.com/*", "https://content.dropboxapi.com/*", "https://graph.microsoft.com/*", "https://login.microsoftonline.com/*"] - } + "bundle": { + "active": true, + "category": "DeveloperTool", + "copyright": "© 2024-2026", + "targets": "all", + "externalBin": [], + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ], + "windows": { + "certificateThumbprint": null, + "digestAlgorithm": "sha256", + "timestampUrl": "" + }, + "longDescription": "RocketNote — a powerful, privacy-first code editor for macOS with AI integration, built-in terminal, Git support, and many developer tools.", + "macOS": { + "entitlements": "Entitlements.plist", + "exceptionDomain": "", + "frameworks": [], + "providerShortName": null, + "signingIdentity": null, + "minimumSystemVersion": "10.13" }, - "bundle": { - "active": true, - "category": "DeveloperTool", - "copyright": "© 2024-2026", + "resources": [], + "shortDescription": "Code Editor with AI & Terminal", + "linux": { "deb": { "depends": [] - }, - "externalBin": [], - "icon": [ - "icons/32x32.png", - "icons/128x128.png", - "icons/128x128@2x.png", - "icons/icon.icns", - "icons/icon.ico" - ], - "identifier": "com.rocketnote.app", - "longDescription": "RocketNote — a powerful, privacy-first code editor for macOS with AI integration, built-in terminal, Git support, and many developer tools.", - "macOS": { - "entitlements": "Entitlements.plist", - "exceptionDomain": "", - "frameworks": [], - "providerShortName": null, - "signingIdentity": null, - "minimumSystemVersion": "10.13" - }, - "resources": [], - "shortDescription": "Code Editor with AI & Terminal", - "targets": "all", - "windows": { - "certificateThumbprint": null, - "digestAlgorithm": "sha256", - "timestampUrl": "" } }, - "security": { - "csp": "default-src 'self' data: blob:; script-src 'self' blob:; style-src 'self' 'unsafe-inline'; connect-src 'self' https://api.openai.com https://api.anthropic.com https://api.deepseek.com https://www.googleapis.com https://api.dropboxapi.com https://content.dropboxapi.com https://graph.microsoft.com; img-src 'self' data: blob:; font-src 'self' data:; worker-src 'self' blob:;" - }, + "createUpdaterArtifacts": "v1Compatible" + }, + "productName": "RocketNote", + "mainBinaryName": "RocketNote", + "version": "2.0.0", + "identifier": "com.rocketnote.app", + "plugins": { "updater": { - "active": false - }, + "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDNFQ0MwNkIwN0EyMkU1ODEKUldTQjVTSjZzQWJNUHIwZTYwYkNWSlhzNmpsa01qb251Y2JjOFFjSWVoTFVYRTU0eWpkNFF0TmsK", + "endpoints": [ + "https://github.com/Anic888/rocketnote/releases/latest/download/latest.json" + ] + } + }, + "app": { "windows": [ { "fullscreen": false, @@ -117,9 +65,13 @@ "minHeight": 600, "center": true, "decorations": true, - "fileDropEnabled": true, - "transparent": false + "dragDropEnabled": true, + "transparent": false, + "useHttpsScheme": true } - ] + ], + "security": { + "csp": "default-src 'self' data: blob:; script-src 'self' blob:; style-src 'self' 'unsafe-inline'; connect-src ipc: http://ipc.localhost 'self' https://api.openai.com https://api.anthropic.com https://api.deepseek.com https://www.googleapis.com https://api.dropboxapi.com https://content.dropboxapi.com https://graph.microsoft.com; img-src 'self' data: blob:; font-src 'self' data:; worker-src 'self' blob:;" + } } -} +} \ No newline at end of file diff --git a/src/components/AIPanel.tsx b/src/components/AIPanel.tsx index 8e1a2a5..e55ebb5 100644 --- a/src/components/AIPanel.tsx +++ b/src/components/AIPanel.tsx @@ -1,6 +1,6 @@ import { useState, useRef, useEffect } from 'react'; import { createPortal } from 'react-dom'; -import { invoke } from '@tauri-apps/api/tauri'; +import { invoke } from '@tauri-apps/api/core'; import { AIMessage } from '../types'; import { Bot, Zap, Circle, Key, MessageSquare, User, Copy, Trash2, Settings, Lightbulb, BookOpen, Palette, Bug, FileText, RefreshCw, Shield } from '../icons'; import './AIPanel.css'; diff --git a/src/components/CloudStoragePanel.tsx b/src/components/CloudStoragePanel.tsx index 8339fb4..2608efb 100644 --- a/src/components/CloudStoragePanel.tsx +++ b/src/components/CloudStoragePanel.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react'; import { createPortal } from 'react-dom'; -import { invoke } from '@tauri-apps/api/tauri'; +import { invoke } from '@tauri-apps/api/core'; import { Cloud, FolderOpen, Package, Check, Lock, Unlock, Loader2, File, Upload, Download, Home, Settings, Key, Shield, RefreshCw, AlertTriangle } from '../icons'; import './CloudStoragePanel.css'; @@ -213,7 +213,8 @@ function CloudStoragePanel({ onClose, currentFilePath, currentFileContent, onFil try { const state = crypto.randomUUID(); - const authUrl = getAuthUrl(provider, creds.clientId, state); + const pkce = await invoke<{ verifier: string; challenge: string }>('generate_pkce'); + const authUrl = getAuthUrl(provider, creds.clientId, state, pkce.challenge); const callback = await invoke('open_oauth_window', { url: authUrl, @@ -226,6 +227,7 @@ function CloudStoragePanel({ onClose, currentFilePath, currentFileContent, onFil clientId: creds.clientId, clientSecret: creds.clientSecret, redirectUri: callback.redirect_uri, + codeVerifier: pkce.verifier, }); const tokenPayload: StoredOAuthToken = { @@ -244,23 +246,24 @@ function CloudStoragePanel({ onClose, currentFilePath, currentFileContent, onFil setActiveProvider(provider); await loadFiles(provider); } catch (error) { - console.error('OAuth error:', error); + console.error('OAuth error'); alert(`Failed to connect to ${PROVIDERS[provider].name}: ${error}`); } setLoading(false); }; - const getAuthUrl = (provider: Provider, clientId: string, state: string): string => { + const getAuthUrl = (provider: Provider, clientId: string, state: string, codeChallenge: string): string => { const redirectUri = 'http://localhost:8765/callback'; - + const pkce = `&code_challenge=${codeChallenge}&code_challenge_method=S256`; + switch (provider) { case 'google': - return `https://accounts.google.com/o/oauth2/v2/auth?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=https://www.googleapis.com/auth/drive.file&access_type=offline&prompt=consent&state=${state}`; + return `https://accounts.google.com/o/oauth2/v2/auth?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=https://www.googleapis.com/auth/drive.file&access_type=offline&prompt=consent&state=${state}${pkce}`; case 'dropbox': - return `https://www.dropbox.com/oauth2/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&token_access_type=offline&state=${state}`; + return `https://www.dropbox.com/oauth2/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&token_access_type=offline&state=${state}${pkce}`; case 'onedrive': - return `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=Files.ReadWrite%20offline_access&state=${state}`; + return `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=Files.ReadWrite%20offline_access&state=${state}${pkce}`; default: return ''; } @@ -291,7 +294,7 @@ function CloudStoragePanel({ onClose, currentFilePath, currentFileContent, onFil setFiles(fileList); setCurrentPath(path); } catch (error) { - console.error('Load files error:', error); + console.error('Load files error'); alert(`Failed to load files: ${error}`); } @@ -332,7 +335,7 @@ function CloudStoragePanel({ onClose, currentFilePath, currentFileContent, onFil alert(`Uploaded ${fileName} to ${PROVIDERS[activeProvider].name}`); await loadFiles(activeProvider, currentPath); } catch (error) { - console.error('Upload error:', error); + console.error('Upload error'); alert(`Failed to upload: ${error}`); } @@ -437,7 +440,7 @@ function CloudStoragePanel({ onClose, currentFilePath, currentFileContent, onFil onFileLoaded(content, file.name); onClose(); } catch (error) { - console.error('Download error:', error); + console.error('Download error'); alert(`Failed to download: ${error}`); } diff --git a/src/components/Editor.css b/src/components/Editor.css index febd93a..d230564 100644 --- a/src/components/Editor.css +++ b/src/components/Editor.css @@ -12,6 +12,20 @@ background: #ffffff; } +/* CodeMirror 6 host — fills the wrapper; CM6 virtualizes the viewport (H4 fix). */ +.editor-cm-host { + flex: 1; + min-width: 0; + height: 100%; + overflow: hidden; +} +.editor-cm-host .cm-editor { + height: 100%; +} +.editor-cm-host .cm-scroller { + overflow: auto; +} + /* Line numbers */ .line-numbers { display: flex; diff --git a/src/components/Editor.tsx b/src/components/Editor.tsx index 7d6d676..37fa7c0 100644 --- a/src/components/Editor.tsx +++ b/src/components/Editor.tsx @@ -1,5 +1,27 @@ -import { forwardRef, useImperativeHandle, useRef, useEffect, useState, useCallback, useMemo } from 'react'; +import { forwardRef, useImperativeHandle, useRef, useEffect, useState, useCallback } from 'react'; import { Tab, EditorSettings } from '../types'; +import { EditorState, Compartment, Extension } from '@codemirror/state'; +import { + EditorView, keymap, lineNumbers, highlightActiveLine, highlightActiveLineGutter, + drawSelection, dropCursor, +} from '@codemirror/view'; +import { defaultKeymap, history, historyKeymap, indentWithTab, undo, redo } from '@codemirror/commands'; +import { searchKeymap, highlightSelectionMatches, search, findNext, findPrevious, setSearchQuery, SearchQuery } from '@codemirror/search'; +import { syntaxHighlighting, defaultHighlightStyle, indentOnInput, bracketMatching, indentUnit } from '@codemirror/language'; +import { oneDark } from '@codemirror/theme-one-dark'; +import { javascript } from '@codemirror/lang-javascript'; +import { html } from '@codemirror/lang-html'; +import { css } from '@codemirror/lang-css'; +import { json } from '@codemirror/lang-json'; +import { python } from '@codemirror/lang-python'; +import { rust } from '@codemirror/lang-rust'; +import { java } from '@codemirror/lang-java'; +import { cpp } from '@codemirror/lang-cpp'; +import { php } from '@codemirror/lang-php'; +import { markdown } from '@codemirror/lang-markdown'; +import { xml } from '@codemirror/lang-xml'; +import { sql } from '@codemirror/lang-sql'; +import { yaml } from '@codemirror/lang-yaml'; import './Editor.css'; interface EditorProps { @@ -23,415 +45,248 @@ export interface EditorRef { navigateSearch: (direction: 'next' | 'prev', query: string, caseSensitive: boolean, wholeWord: boolean, useRegex: boolean) => void; } -const Editor = forwardRef(({ - tab, - settings, - onChange, +function languageExtension(lang: string): Extension { + switch (lang) { + case 'javascript': return javascript({ jsx: true }); + case 'typescript': return javascript({ jsx: true, typescript: true }); + case 'html': case 'vue': case 'svelte': return html(); + case 'css': case 'scss': case 'less': return css(); + case 'json': return json(); + case 'python': return python(); + case 'rust': return rust(); + case 'java': case 'kotlin': return java(); + case 'c': case 'cpp': return cpp(); + case 'php': return php(); + case 'markdown': return markdown(); + case 'xml': return xml(); + case 'sql': case 'pgsql': case 'mysql': return sql(); + case 'yaml': return yaml(); + default: return []; + } +} + +const fontTheme = (size: number) => EditorView.theme({ + '&': { height: '100%', fontSize: `${size}px` }, + '.cm-scroller': { + fontFamily: "'SF Mono', 'Monaco', 'Menlo', 'Courier New', monospace", + lineHeight: '1.5', + }, +}); + +function escapeRegex(s: string): string { + return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +const Editor = forwardRef(({ + tab, + settings, + onChange, onCursorChange, searchQuery = '', searchCaseSensitive = false, searchWholeWord = false, searchUseRegex = false, }, ref) => { - const textareaRef = useRef(null); - const lineNumbersRef = useRef(null); - const highlightRef = useRef(null); + const hostRef = useRef(null); + const viewRef = useRef(null); const minimapRef = useRef(null); - const [lineCount, setLineCount] = useState(1); const [fontSize, setFontSize] = useState(settings.fontSize); const [minimapScroll, setMinimapScroll] = useState(0); - const [currentMatchIndex, setCurrentMatchIndex] = useState(0); - const [matchPositions, setMatchPositions] = useState([]); - - // Undo/Redo stack - const undoStackRef = useRef<{ content: string; cursorPos: number }[]>([]); - const redoStackRef = useRef<{ content: string; cursorPos: number }[]>([]); - const lastPushedContentRef = useRef(tab.content); - - // Push to undo stack on significant changes (debounced) - const pushUndoRef = useRef | null>(null); - const pushUndo = useCallback((content: string, cursorPos: number) => { - if (pushUndoRef.current) clearTimeout(pushUndoRef.current); - pushUndoRef.current = setTimeout(() => { - if (content !== lastPushedContentRef.current) { - undoStackRef.current.push({ content: lastPushedContentRef.current, cursorPos }); - // Limit stack size - if (undoStackRef.current.length > 200) undoStackRef.current.shift(); - redoStackRef.current = []; // Clear redo on new change - lastPushedContentRef.current = content; - } - }, 300); - }, []); - - // Reset undo stack when switching tabs - useEffect(() => { - undoStackRef.current = []; - redoStackRef.current = []; - lastPushedContentRef.current = tab.content; - }, [tab.id]); - - // Sync fontSize with settings - useEffect(() => { - setFontSize(settings.fontSize); - }, [settings.fontSize]); - - // Calculate line numbers - useEffect(() => { - const lines = tab.content.split('\n').length; - setLineCount(lines); - }, [tab.content]); + const [minimapViewport, setMinimapViewport] = useState(20); - // Calculate match positions for navigation - useEffect(() => { - if (!searchQuery) { - setMatchPositions([]); - setCurrentMatchIndex(0); - return; - } + // Keep latest callbacks in refs so the (once-created) update listener never goes stale. + const onChangeRef = useRef(onChange); + const onCursorRef = useRef(onCursorChange); + onChangeRef.current = onChange; + onCursorRef.current = onCursorChange; - try { - let pattern = searchQuery; - if (!searchUseRegex) { - pattern = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - } - if (searchWholeWord) { - pattern = `\\b${pattern}\\b`; - } - - const flags = searchCaseSensitive ? 'g' : 'gi'; - const regex = new RegExp(pattern, flags); - const positions: number[] = []; - let match; - - while ((match = regex.exec(tab.content)) !== null) { - positions.push(match.index); - } - - setMatchPositions(positions); - if (positions.length > 0 && currentMatchIndex === 0) { - setCurrentMatchIndex(1); - } - } catch { - setMatchPositions([]); - } - }, [searchQuery, tab.content, searchCaseSensitive, searchWholeWord, searchUseRegex]); + // Compartments for settings that can change without recreating the view. + const cmp = useRef({ + gutter: new Compartment(), + lang: new Compartment(), + theme: new Compartment(), + wrap: new Compartment(), + tab: new Compartment(), + font: new Compartment(), + hist: new Compartment(), + }).current; - // Sync scroll between line numbers, textarea and highlight - const handleScroll = useCallback(() => { - if (textareaRef.current) { - if (lineNumbersRef.current) { - lineNumbersRef.current.scrollTop = textareaRef.current.scrollTop; - } - if (highlightRef.current) { - highlightRef.current.scrollTop = textareaRef.current.scrollTop; - highlightRef.current.scrollLeft = textareaRef.current.scrollLeft; - } - - const scrollPercent = textareaRef.current.scrollTop / - (textareaRef.current.scrollHeight - textareaRef.current.clientHeight); - setMinimapScroll(isNaN(scrollPercent) ? 0 : scrollPercent); - } + const refreshMinimap = useCallback(() => { + const view = viewRef.current; + if (!view) return; + const sc = view.scrollDOM; + const denom = sc.scrollHeight - sc.clientHeight; + setMinimapScroll(denom > 0 ? sc.scrollTop / denom : 0); + setMinimapViewport(sc.scrollHeight > 0 ? Math.max(20, (sc.clientHeight / sc.scrollHeight) * 100) : 20); }, []); - // Handle content change - const handleChange = useCallback((e: React.ChangeEvent) => { - const newContent = e.target.value; - pushUndo(newContent, e.target.selectionStart); - onChange(newContent); - }, [onChange, pushUndo]); + // Create the editor once. + useEffect(() => { + if (!hostRef.current) return; + const s = settings; + const state = EditorState.create({ + doc: tab.content, + extensions: [ + cmp.gutter.of(s.lineNumbers !== 'off' ? lineNumbers() : []), + highlightActiveLineGutter(), + cmp.hist.of(history()), + drawSelection(), + dropCursor(), + EditorState.allowMultipleSelections.of(true), + indentOnInput(), + syntaxHighlighting(defaultHighlightStyle, { fallback: true }), + bracketMatching(), + highlightActiveLine(), + highlightSelectionMatches(), + search({ top: true }), + keymap.of([...defaultKeymap, ...historyKeymap, ...searchKeymap, indentWithTab]), + cmp.tab.of([EditorState.tabSize.of(s.tabSize), indentUnit.of(' '.repeat(s.tabSize))]), + cmp.wrap.of(s.wordWrap === 'on' ? EditorView.lineWrapping : []), + cmp.lang.of(languageExtension(tab.language)), + cmp.theme.of(s.theme === 'dark' ? oneDark : []), + cmp.font.of(fontTheme(s.fontSize)), + EditorView.updateListener.of((u) => { + if (u.docChanged) onChangeRef.current(u.state.doc.toString()); + if (u.selectionSet || u.docChanged) { + const pos = u.state.selection.main.head; + const line = u.state.doc.lineAt(pos); + onCursorRef.current({ line: line.number, column: pos - line.from + 1 }); + } + }), + EditorView.domEventHandlers({ scroll: () => { refreshMinimap(); } }), + ], + }); + const view = new EditorView({ state, parent: hostRef.current }); + viewRef.current = view; + refreshMinimap(); + return () => { view.destroy(); viewRef.current = null; }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - // Handle cursor position - const handleSelect = useCallback(() => { - if (textareaRef.current) { - const text = textareaRef.current.value; - const selectionStart = textareaRef.current.selectionStart; - - const textBeforeCursor = text.substring(0, selectionStart); - const lines = textBeforeCursor.split('\n'); - const line = lines.length; - const column = lines[lines.length - 1].length + 1; - - onCursorChange({ line, column }); + // External content changes (toolbar undo/redo, replace, programmatic) — sync if different. + useEffect(() => { + const view = viewRef.current; + if (!view) return; + const cur = view.state.doc.toString(); + if (cur !== tab.content) { + view.dispatch({ changes: { from: 0, to: cur.length, insert: tab.content } }); + refreshMinimap(); } - }, [onCursorChange]); + }, [tab.content, refreshMinimap]); - // Handle Tab key - const handleKeyDown = useCallback((e: React.KeyboardEvent) => { - if (e.key === 'Tab') { - e.preventDefault(); - const textarea = textareaRef.current; - if (!textarea) return; + // Switching tabs: reset undo history for the new buffer. + useEffect(() => { + viewRef.current?.dispatch({ effects: cmp.hist.reconfigure(history()) }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [tab.id]); - const start = textarea.selectionStart; - const end = textarea.selectionEnd; - const spaces = ' '.repeat(settings.tabSize); + // Reconfigure on setting / language changes. + useEffect(() => { viewRef.current?.dispatch({ effects: cmp.lang.reconfigure(languageExtension(tab.language)) }); }, [tab.language, cmp.lang]); + useEffect(() => { viewRef.current?.dispatch({ effects: cmp.theme.reconfigure(settings.theme === 'dark' ? oneDark : []) }); }, [settings.theme, cmp.theme]); + useEffect(() => { viewRef.current?.dispatch({ effects: cmp.wrap.reconfigure(settings.wordWrap === 'on' ? EditorView.lineWrapping : []) }); }, [settings.wordWrap, cmp.wrap]); + useEffect(() => { viewRef.current?.dispatch({ effects: cmp.gutter.reconfigure(settings.lineNumbers !== 'off' ? lineNumbers() : []) }); }, [settings.lineNumbers, cmp.gutter]); + useEffect(() => { viewRef.current?.dispatch({ effects: cmp.tab.reconfigure([EditorState.tabSize.of(settings.tabSize), indentUnit.of(' '.repeat(settings.tabSize))]) }); }, [settings.tabSize, cmp.tab]); - const newValue = tab.content.substring(0, start) + spaces + tab.content.substring(end); - onChange(newValue); + useEffect(() => { setFontSize(settings.fontSize); }, [settings.fontSize]); + useEffect(() => { viewRef.current?.dispatch({ effects: cmp.font.reconfigure(fontTheme(fontSize)) }); }, [fontSize, cmp.font]); - requestAnimationFrame(() => { - textarea.selectionStart = textarea.selectionEnd = start + settings.tabSize; - }); - } - }, [tab.content, settings.tabSize, onChange]); + // Live search highlighting driven by the app's find bar. + useEffect(() => { + viewRef.current?.dispatch({ + effects: setSearchQuery.of(new SearchQuery({ + search: searchQuery || '', + caseSensitive: searchCaseSensitive, + regexp: searchUseRegex, + wholeWord: searchWholeWord, + })), + }); + }, [searchQuery, searchCaseSensitive, searchWholeWord, searchUseRegex]); - // Handle zoom with Cmd/Ctrl + mouse wheel + // Cmd/Ctrl + wheel zoom. const handleWheel = useCallback((e: React.WheelEvent) => { if (e.metaKey || e.ctrlKey) { e.preventDefault(); - const delta = e.deltaY > 0 ? -1 : 1; - setFontSize(prev => Math.min(32, Math.max(10, prev + delta))); + setFontSize(prev => Math.min(32, Math.max(10, prev + (e.deltaY > 0 ? -1 : 1)))); } }, []); - // Click on minimap to scroll const handleMinimapClick = useCallback((e: React.MouseEvent) => { - if (textareaRef.current && minimapRef.current) { - const rect = minimapRef.current.getBoundingClientRect(); - const clickPercent = (e.clientY - rect.top) / rect.height; - const scrollTarget = clickPercent * (textareaRef.current.scrollHeight - textareaRef.current.clientHeight); - textareaRef.current.scrollTop = scrollTarget; - } - }, []); + const view = viewRef.current; + if (!view || !minimapRef.current) return; + const rect = minimapRef.current.getBoundingClientRect(); + const pct = (e.clientY - rect.top) / rect.height; + view.scrollDOM.scrollTop = pct * (view.scrollDOM.scrollHeight - view.scrollDOM.clientHeight); + refreshMinimap(); + }, [refreshMinimap]); - // Navigate to match position - const scrollToMatch = useCallback((index: number) => { - if (!textareaRef.current || matchPositions.length === 0) return; - - const pos = matchPositions[index]; - textareaRef.current.focus(); - textareaRef.current.setSelectionRange(pos, pos + searchQuery.length); - - // Scroll to make selection visible - const text = tab.content.substring(0, pos); - const lines = text.split('\n'); - const lineHeight = fontSize * 1.5; - const targetScroll = (lines.length - 5) * lineHeight; - textareaRef.current.scrollTop = Math.max(0, targetScroll); - }, [matchPositions, searchQuery, tab.content, fontSize]); - - // Expose methods via ref useImperativeHandle(ref, () => ({ - focus: () => textareaRef.current?.focus(), + focus: () => viewRef.current?.focus(), search: (query: string) => { - if (textareaRef.current && query) { - const text = textareaRef.current.value; - const index = text.toLowerCase().indexOf(query.toLowerCase()); - if (index !== -1) { - textareaRef.current.focus(); - textareaRef.current.setSelectionRange(index, index + query.length); - } - } + const view = viewRef.current; + if (!view || !query) return; + view.dispatch({ effects: setSearchQuery.of(new SearchQuery({ search: query })) }); + findNext(view); }, getSelectedText: () => { - if (textareaRef.current) { - const start = textareaRef.current.selectionStart; - const end = textareaRef.current.selectionEnd; - return textareaRef.current.value.substring(start, end); - } - return ''; - }, - undo: () => { - // Flush any pending undo push - if (pushUndoRef.current) { - clearTimeout(pushUndoRef.current); - pushUndoRef.current = null; - } - if (tab.content !== lastPushedContentRef.current) { - undoStackRef.current.push({ content: lastPushedContentRef.current, cursorPos: textareaRef.current?.selectionStart || 0 }); - lastPushedContentRef.current = tab.content; - } - - const entry = undoStackRef.current.pop(); - if (entry) { - redoStackRef.current.push({ content: tab.content, cursorPos: textareaRef.current?.selectionStart || 0 }); - lastPushedContentRef.current = entry.content; - onChange(entry.content); - requestAnimationFrame(() => { - if (textareaRef.current) { - textareaRef.current.selectionStart = textareaRef.current.selectionEnd = entry.cursorPos; - } - }); - } - }, - redo: () => { - const entry = redoStackRef.current.pop(); - if (entry) { - undoStackRef.current.push({ content: tab.content, cursorPos: textareaRef.current?.selectionStart || 0 }); - lastPushedContentRef.current = entry.content; - onChange(entry.content); - requestAnimationFrame(() => { - if (textareaRef.current) { - textareaRef.current.selectionStart = textareaRef.current.selectionEnd = entry.cursorPos; - } - }); - } + const view = viewRef.current; + if (!view) return ''; + const { from, to } = view.state.selection.main; + return view.state.sliceDoc(from, to); }, - replace: (search: string, replacement: string, all: boolean = false) => { - if (!textareaRef.current || !search) return 0; - - const text = textareaRef.current.value; - let count = 0; - + undo: () => { const v = viewRef.current; if (v) { undo(v); v.focus(); } }, + redo: () => { const v = viewRef.current; if (v) { redo(v); v.focus(); } }, + replace: (searchText: string, replacement: string, all = false): number => { + const view = viewRef.current; + if (!view || !searchText) return 0; + const text = view.state.doc.toString(); if (all) { - const regex = new RegExp(search.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'); + const regex = new RegExp(escapeRegex(searchText), 'gi'); const matches = text.match(regex); - count = matches ? matches.length : 0; - const newText = text.replace(regex, replacement); - onChange(newText); - } else { - const index = text.toLowerCase().indexOf(search.toLowerCase()); - if (index !== -1) { - const newText = text.substring(0, index) + replacement + text.substring(index + search.length); - onChange(newText); - count = 1; - - requestAnimationFrame(() => { - if (textareaRef.current) { - textareaRef.current.setSelectionRange(index, index + replacement.length); - } - }); - } + const count = matches ? matches.length : 0; + if (count) view.dispatch({ changes: { from: 0, to: text.length, insert: text.replace(regex, replacement) } }); + return count; } - - return count; + const idx = text.toLowerCase().indexOf(searchText.toLowerCase()); + if (idx === -1) return 0; + view.dispatch({ + changes: { from: idx, to: idx + searchText.length, insert: replacement }, + selection: { anchor: idx, head: idx + replacement.length }, + }); + return 1; }, - navigateSearch: (direction: 'next' | 'prev') => { - if (matchPositions.length === 0) return; - - let newIndex: number; - if (direction === 'next') { - newIndex = currentMatchIndex >= matchPositions.length ? 0 : currentMatchIndex; - } else { - newIndex = currentMatchIndex <= 1 ? matchPositions.length - 1 : currentMatchIndex - 2; - } - - setCurrentMatchIndex(newIndex + 1); - scrollToMatch(newIndex); + navigateSearch: (direction, query, caseSensitive, wholeWord, useRegex) => { + const view = viewRef.current; + if (!view || !query) return; + view.dispatch({ effects: setSearchQuery.of(new SearchQuery({ search: query, caseSensitive, regexp: useRegex, wholeWord })) }); + (direction === 'next' ? findNext : findPrevious)(view); + view.focus(); }, })); - const lineNumbers = useMemo(() => - Array.from({ length: lineCount }, (_, i) => i + 1), - [lineCount] - ); - - const editorStyle = { - fontSize: `${fontSize}px`, - fontFamily: "'SF Mono', 'Monaco', 'Menlo', 'Courier New', monospace", - lineHeight: '1.5', - }; - - // Generate highlighted content - const getHighlightedContent = useCallback(() => { - if (!searchQuery) return tab.content; - - try { - let pattern = searchQuery; - if (!searchUseRegex) { - pattern = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - } - if (searchWholeWord) { - pattern = `\\b${pattern}\\b`; - } - - const flags = searchCaseSensitive ? 'g' : 'gi'; - const regex = new RegExp(`(${pattern})`, flags); - - return tab.content.split(regex).map((part) => { - regex.lastIndex = 0; // Reset BEFORE test to avoid skipping matches - if (regex.test(part)) { - return `${part.replace(//g, '>')}`; - } - return part.replace(//g, '>'); - }).join(''); - } catch { - return tab.content.replace(/&/g, '&').replace(//g, '>'); - } - }, [tab.content, searchQuery, searchCaseSensitive, searchWholeWord, searchUseRegex]); - - // Calculate minimap viewport height - const minimapViewportHeight = textareaRef.current - ? Math.max(20, (textareaRef.current.clientHeight / textareaRef.current.scrollHeight) * 100) - : 20; - return (
- {settings.lineNumbers !== 'off' && ( -
- {lineNumbers.map(num => ( -
{num}
- ))} -
- )} -
- {searchQuery && ( -
- )} -